=0;n--)this.value[n]=[this.value[n][0]+t,this.value[n][1]+e];return this},size:function(t,e){var i,n=this.bbox();for(i=this.value.length-1;i>=0;i--)n.width&&(this.value[i][0]=(this.value[i][0]-n.x)*t/n.width+n.x),n.height&&(this.value[i][1]=(this.value[i][1]-n.y)*e/n.height+n.y);return this},bbox:function(){return b.parser.poly.setAttribute("points",this.toString()),b.parser.poly.getBBox()}});for(var C={M:function(t,e,i){return e.x=i.x=t[0],e.y=i.y=t[1],["M",e.x,e.y]},L:function(t,e){return e.x=t[0],e.y=t[1],["L",t[0],t[1]]},H:function(t,e){return e.x=t[0],["H",t[0]]},V:function(t,e){return e.y=t[0],["V",t[0]]},C:function(t,e){return e.x=t[4],e.y=t[5],["C",t[0],t[1],t[2],t[3],t[4],t[5]]},S:function(t,e){return e.x=t[2],e.y=t[3],["S",t[0],t[1],t[2],t[3]]},Q:function(t,e){return e.x=t[2],e.y=t[3],["Q",t[0],t[1],t[2],t[3]]},T:function(t,e){return e.x=t[0],e.y=t[1],["T",t[0],t[1]]},Z:function(t,e,i){return e.x=i.x,e.y=i.y,["Z"]},A:function(t,e){return e.x=t[5],e.y=t[6],["A",t[0],t[1],t[2],t[3],t[4],t[5],t[6]]}},N="mlhvqtcsaz".split(""),A=0,P=N.length;A=0;r--)n=this.value[r][0],"M"==n||"L"==n||"T"==n?(this.value[r][1]+=t,this.value[r][2]+=e):"H"==n?this.value[r][1]+=t:"V"==n?this.value[r][1]+=e:"C"==n||"S"==n||"Q"==n?(this.value[r][1]+=t,this.value[r][2]+=e,this.value[r][3]+=t,this.value[r][4]+=e,"C"==n&&(this.value[r][5]+=t,this.value[r][6]+=e)):"A"==n&&(this.value[r][6]+=t,this.value[r][7]+=e);return this},size:function(t,e){var i,n,r=this.bbox();for(i=this.value.length-1;i>=0;i--)n=this.value[i][0],"M"==n||"L"==n||"T"==n?(this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x,this.value[i][2]=(this.value[i][2]-r.y)*e/r.height+r.y):"H"==n?this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x:"V"==n?this.value[i][1]=(this.value[i][1]-r.y)*e/r.height+r.y:"C"==n||"S"==n||"Q"==n?(this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x,this.value[i][2]=(this.value[i][2]-r.y)*e/r.height+r.y,this.value[i][3]=(this.value[i][3]-r.x)*t/r.width+r.x,this.value[i][4]=(this.value[i][4]-r.y)*e/r.height+r.y,"C"==n&&(this.value[i][5]=(this.value[i][5]-r.x)*t/r.width+r.x,this.value[i][6]=(this.value[i][6]-r.y)*e/r.height+r.y)):"A"==n&&(this.value[i][1]=this.value[i][1]*t/r.width,this.value[i][2]=this.value[i][2]*e/r.height,this.value[i][6]=(this.value[i][6]-r.x)*t/r.width+r.x,this.value[i][7]=(this.value[i][7]-r.y)*e/r.height+r.y);return this},equalCommands:function(t){var e,i,n;for(t=new b.PathArray(t),n=this.value.length===t.value.length,e=0,i=this.value.length;n&&ea);return n},bbox:function(){return b.parser.path.setAttribute("d",this.toString()),b.parser.path.getBBox()}}),b.Number=b.invent({create:function(t,e){this.value=0,this.unit=e||"","number"==typeof t?this.value=isNaN(t)?0:isFinite(t)?t:t<0?-3.4e38:3.4e38:"string"==typeof t?(e=t.match(b.regex.numberAndUnit))&&(this.value=parseFloat(e[1]),"%"==e[5]?this.value/=100:"s"==e[5]&&(this.value*=1e3),this.unit=e[5]):t instanceof b.Number&&(this.value=t.valueOf(),this.unit=t.unit)},extend:{toString:function(){return("%"==this.unit?~~(1e8*this.value)/1e6:"s"==this.unit?this.value/1e3:this.value)+this.unit},toJSON:function(){return this.toString()},valueOf:function(){return this.value},plus:function(t){return t=new b.Number(t),new b.Number(this+t,this.unit||t.unit)},minus:function(t){return t=new b.Number(t),new b.Number(this-t,this.unit||t.unit)},times:function(t){return t=new b.Number(t),new b.Number(this*t,this.unit||t.unit)},divide:function(t){return t=new b.Number(t),new b.Number(this/t,this.unit||t.unit)},to:function(t){var e=new b.Number(this);return"string"==typeof t&&(e.unit=t),e},morph:function(t){return this.destination=new b.Number(t),t.relative&&(this.destination.value+=this.value),this},at:function(t){return this.destination?new b.Number(this.destination).minus(this).times(t).plus(this):this}}}),b.Element=b.invent({create:function(t){this._stroke=b.defaults.attrs.stroke,this._event=null,this._events={},this.dom={},(this.node=t)&&(this.type=t.nodeName,this.node.instance=this,this._events=t._events||{},this._stroke=t.getAttribute("stroke")||this._stroke)},extend:{x:function(t){return this.attr("x",t)},y:function(t){return this.attr("y",t)},cx:function(t){return null==t?this.x()+this.width()/2:this.x(t-this.width()/2)},cy:function(t){return null==t?this.y()+this.height()/2:this.y(t-this.height()/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr("width",t)},height:function(t){return this.attr("height",t)},size:function(t,e){var i=l(this,t,e);return this.width(new b.Number(i.width)).height(new b.Number(i.height))},clone:function(t){this.writeDataToDom();var e=x(this.node.cloneNode(!0));return t?t.add(e):this.after(e),e},remove:function(){return this.parent()&&this.parent().removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return t.put(this)},putIn:function(t){return t.add(this)},id:function(t){return this.attr("id",t)},inside:function(t,e){var i=this.bbox();return t>i.x&&e>i.y&&t/,"").replace(/<\/svg>$/,"");i.innerHTML=""+t.replace(/\n/,"").replace(/<([\w:-]+)([^<]+?)\/>/g,"<$1$2>$1>")+" ";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 1-Math.cos(t*Math.PI/2)}},b.morph=function(t){return function(e,i){return new b.MorphObj(e,i).at(t)}},b.Situation=b.invent({create:function(t){this.init=!1,this.reversed=!1,this.reversing=!1,this.duration=new b.Number(t.duration).valueOf(),this.delay=new b.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={}}}),b.FX=b.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 b.Situation({duration:t||1e3,delay:i||0,ease:b.easing[e||"-"]||e});return this.queue(n),this},delay:function(t){var e=new b.Situation({duration:t,delay:0,ease:b.easing["-"]});return this.queue(e)},target:function(t){return t&&t instanceof b.Element?(this._target=t,this):this._target},timeToAbsPos:function(t){return(t-this.situation.start)/(this.situation.duration/this._speed)},absPosToTime:function(t){return this.situation.duration/this._speed*t+this.situation.start},startAnimFrame:function(){this.stopAnimFrame(),this.animationFrame=t.requestAnimationFrame(function(){this.step()}.bind(this))},stopAnimFrame:function(){t.cancelAnimationFrame(this.animationFrame)},start:function(){return!this.active&&this.situation&&(this.active=!0,this.startCurrent()),this},startCurrent:function(){return this.situation.start=+new Date+this.situation.delay/this._speed,this.situation.finish=this.situation.start+this.situation.duration/this._speed,this.initAnimations().step()},queue:function(t){return("function"==typeof t||t instanceof b.Situation)&&this.situations.push(t),this.situation||(this.situation=this.situations.shift()),this},dequeue:function(){return this.stop(),this.situation=this.situations.shift(),this.situation&&(this.situation instanceof b.Situation?this.start():this.situation.call(this)),this},initAnimations:function(){var t,e,i,n=this.situation;if(n.init)return this;for(t in n.animations)for(i=this.target()[t](),Array.isArray(i)||(i=[i]),Array.isArray(n.animations[t])||(n.animations[t]=[n.animations[t]]),e=i.length;e--;)n.animations[t][e]instanceof b.Number&&(i[e]=new b.Number(i[e])),n.animations[t][e]=i[e].morph(n.animations[t][e]);for(t in n.attrs)n.attrs[t]=new b.MorphObj(this.target().attr(t),n.attrs[t]);for(t in n.styles)n.styles[t]=new b.MorphObj(this.target().style(t),n.styles[t]);return n.initialTransformation=this.target().matrixify(),n.init=!0,this},clearQueue:function(){return this.situations=[],this},clearCurrent:function(){return this.situation=null,this},stop:function(t,e){var i=this.active;return this.active=!1,e&&this.clearQueue(),t&&this.situation&&(!i&&this.startCurrent(),this.atEnd()),this.stopAnimFrame(),this.clearCurrent()},reset:function(){if(this.situation){var t=this.situation;this.stop(),this.situation=t,this.atStart()}return this},finish:function(){for(this.stop(!0,!1);this.dequeue().situation&&this.stop(!0,!1););return this.clearQueue().clearCurrent(),this},atStart:function(){return this.at(0,!0)},atEnd:function(){return!0===this.situation.loops&&(this.situation.loops=this.situation.loop+1),"number"==typeof this.situation.loops?this.at(this.situation.loops,!0):this.at(1,!0)},at:function(t,e){var i=this.situation.duration/this._speed;return this.absPos=t,e||(this.situation.reversed&&(this.absPos=1-this.absPos),this.absPos+=this.situation.loop),this.situation.start=+new Date-this.absPos*i,this.situation.finish=this.situation.start+i,this.step(!0)},speed:function(t){return 0===t?this.pause():t?(this._speed=t,this.at(this.absPos,!0)):this._speed},loop:function(t,e){var i=this.last();return i.loops=null==t||t,i.loop=0,e&&(i.reversing=!0),this},pause:function(){return this.paused=!0,this.stopAnimFrame(),this},play:function(){return this.paused?(this.paused=!1,this.at(this.absPos,!0)):this},reverse:function(t){var e=this.last();return e.reversed=void 0===t?!e.reversed:t,this},progress:function(t){return t?this.situation.ease(this.pos):this.pos},after:function(t){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._callStart()},during:function(t){var e=this.last(),i=function(i){i.detail.situation==e&&t.call(this,i.detail.pos,b.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)}),this._callStart()},afterAll:function(t){var e=function e(i){t.call(this),this.off("allfinished.fx",e)};return this.target().off("allfinished.fx",e).on("allfinished.fx",e),this._callStart()},duringAll:function(t){var e=function(e){t.call(this,e.detail.pos,b.morph(e.detail.pos),e.detail.eased,e.detail.situation)};return this.target().off("during.fx",e).on("during.fx",e),this.afterAll(function(){this.off("during.fx",e)}),this._callStart()},last:function(){return this.situations.length?this.situations[this.situations.length-1]:this.situation},add:function(t,e,i){return this.last()[i||"animations"][t]=e,this._callStart()},step:function(t){if(t||(this.absPos=this.timeToAbsPos(+new Date)),!1!==this.situation.loops){var e,i,n;e=Math.max(this.absPos,0),i=Math.floor(e),!0===this.situation.loops||ithis.lastPos&&s<=r&&(this.situation.once[s].call(this.target(),this.pos,r),delete this.situation.once[s]);return this.active&&this.target().fire("during",{pos:this.pos,eased:r,fx:this,situation:this.situation}),this.situation?(this.eachAt(),1==this.pos&&!this.situation.reversed||this.situation.reversed&&0==this.pos?(this.stopAnimFrame(),this.target().fire("finished",{fx:this,situation:this.situation}),this.situations.length||(this.target().fire("allfinished"),this.situations.length||(this.target().off(".fx"),this.active=!1)),this.active?this.dequeue():this.clearCurrent()):!this.paused&&this.active&&this.startAnimFrame(),this.lastPos=r,this):this},eachAt:function(){var t,e,i,n=this,r=this.target(),s=this.situation;for(t in s.animations)i=[].concat(s.animations[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(s.ease(n.pos),n.pos):t}),r[t].apply(r,i);for(t in s.attrs)i=[t].concat(s.attrs[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(s.ease(n.pos),n.pos):t}),r.attr.apply(r,i);for(t in s.styles)i=[t].concat(s.styles[t]).map(function(t){return"string"!=typeof t&&t.at?t.at(s.ease(n.pos),n.pos):t}),r.style.apply(r,i);if(s.transforms.length){for(i=s.initialTransformation,t=0,e=s.transforms.length;t=0;--e)this[k[e]]=null!=t[k[e]]?t[k[e]]:i[k[e]]},extend:{extract:function(){var t=c(this,0,1),e=c(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 b.Matrix(this)}},clone:function(){return new b.Matrix(this)},morph:function(t){return this.destination=new b.Matrix(t),this},at:function(t){return this.destination?new b.Matrix({a:this.a+(this.destination.a-this.a)*t,b:this.b+(this.destination.b-this.b)*t,c:this.c+(this.destination.c-this.c)*t,d:this.d+(this.destination.d-this.d)*t,e:this.e+(this.destination.e-this.e)*t,f:this.f+(this.destination.f-this.f)*t}):this},multiply:function(t){return new b.Matrix(this.native().multiply(d(t).native()))},inverse:function(){return new b.Matrix(this.native().inverse())},translate:function(t,e){return new b.Matrix(this.native().translate(t||0,e||0))},scale:function(t,e,i,n){return 1==arguments.length?e=t:3==arguments.length&&(n=i,i=e,e=t),this.around(i,n,new b.Matrix(t,0,0,e,0,0))},rotate:function(t,e,i){return t=b.utils.radians(t),this.around(e,i,new b.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):"y"==t?this.scale(1,-1,0,e):this.scale(-1,-1,t,null!=e?e:t)},skew:function(t,e,i,n){
+return 1==arguments.length?e=t:3==arguments.length&&(n=i,i=e,e=t),t=b.utils.radians(t),e=b.utils.radians(e),this.around(i,n,new b.Matrix(1,Math.tan(e),Math.tan(t),1,0,0))},skewX:function(t,e,i){return this.skew(t,0,e,i)},skewY:function(t,e,i){return this.skew(0,t,e,i)},around:function(t,e,i){return this.multiply(new b.Matrix(1,0,0,1,t||0,e||0)).multiply(i).multiply(new b.Matrix(1,0,0,1,-t||0,-e||0))},native:function(){for(var t=b.parser.native.createSVGMatrix(),e=k.length-1;e>=0;e--)t[k[e]]=this[k[e]];return t},toString:function(){return"matrix("+g(this.a)+","+g(this.b)+","+g(this.c)+","+g(this.d)+","+g(this.e)+","+g(this.f)+")"}},parent:b.Element,construct:{ctm:function(){return new b.Matrix(this.node.getCTM())},screenCTM:function(){if(this instanceof b.Nested){var t=this.rect(1,1),e=t.node.getScreenCTM();return t.remove(),new b.Matrix(e)}return new b.Matrix(this.node.getScreenCTM())}}}),b.Point=b.invent({create:function(t,e){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!=t?{x:t,y:null!=e?e:t}:n,this.x=i.x,this.y=i.y},extend:{clone:function(){return new b.Point(this)},morph:function(t,e){return this.destination=new b.Point(t,e),this},at:function(t){return this.destination?new b.Point({x:this.x+(this.destination.x-this.x)*t,y:this.y+(this.destination.y-this.y)*t}):this},native:function(){var t=b.parser.native.createSVGPoint();return t.x=this.x,t.y=this.y,t},transform:function(t){return new b.Point(this.native().matrixTransform(t.native()))}}}),b.extend(b.Element,{point:function(t,e){return new b.Point(t,e).transform(this.screenCTM().inverse())}}),b.extend(b.Element,{attr:function(t,e,i){if(null==t){for(t={},e=this.node.attributes,i=e.length-1;i>=0;i--)t[e[i].nodeName]=b.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?b.defaults.attrs[t]:b.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||(b.regex.isImage.test(e)&&(e=this.doc().defs().image(e,0,0)),e instanceof b.Image&&(e=this.doc().defs().pattern(0,0,function(){this.add(e)}))),"number"==typeof e?e=new b.Number(e):b.Color.isColor(e)?e=new b.Color(e):Array.isArray(e)&&(e=new b.Array(e)),"leading"==t?this.leading&&this.leading(e):"string"==typeof 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}}),b.extend(b.Element,{transform:function(t,e){var i,n,r=this;if("object"!=typeof t)return i=new b.Matrix(r).extract(),"string"==typeof t?i[t]:i;if(i=new b.Matrix(r),e=!!e||!!t.relative,null!=t.a)i=e?i.multiply(new b.Matrix(t)):new b.Matrix(t);else if(null!=t.rotation)p(t,r),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(p(t,r),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.skew||null!=t.skewX||null!=t.skewY){if(p(t,r),t.skewX=null!=t.skew?t.skew:null!=t.skewX?t.skewX:0,t.skewY=null!=t.skew?t.skew:null!=t.skewY?t.skewY:0,!e){var s=i.extract();i=i.multiply((new b.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?("x"==t.flip||"y"==t.flip?t.offset=null==t.offset?r.bbox()["c"+t.flip]:t.offset:null==t.offset?(n=r.bbox(),t.flip=n.cx,t.offset=n.cy):t.flip=t.offset,i=(new b.Matrix).flip(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)}}),b.extend(b.FX,{transform:function(t,e){var i,n,r=this.target();return"object"!=typeof t?(i=new b.Matrix(r).extract(),"string"==typeof t?i[t]:i):(e=!!e||!!t.relative,null!=t.a?i=new b.Matrix(t):null!=t.rotation?(p(t,r),i=new b.Rotate(t.rotation,t.cx,t.cy)):null!=t.scale||null!=t.scaleX||null!=t.scaleY?(p(t,r),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 b.Scale(t.scaleX,t.scaleY,t.cx,t.cy)):null!=t.skewX||null!=t.skewY?(p(t,r),t.skewX=null!=t.skewX?t.skewX:0,t.skewY=null!=t.skewY?t.skewY:0,i=new b.Skew(t.skewX,t.skewY,t.cx,t.cy)):t.flip?("x"==t.flip||"y"==t.flip?t.offset=null==t.offset?r.bbox()["c"+t.flip]:t.offset:null==t.offset?(n=r.bbox(),t.flip=n.cx,t.offset=n.cy):t.flip=t.offset,i=(new b.Matrix).flip(t.flip,t.offset)):null==t.x&&null==t.y||(i=new b.Translate(t.x,t.y)),i?(i.relative=e,this.last().transforms.push(i),this._callStart()):this)}}),b.extend(b.Element,{untransform:function(){return this.attr("transform",null)},matrixify:function(){return(this.attr("transform")||"").split(b.regex.transforms).slice(0,-1).map(function(t){var e=t.trim().split("(");return[e[0],e[1].split(b.regex.delimiter).map(function(t){return parseFloat(t)})]}).reduce(function(t,e){return"matrix"==e[0]?t.multiply(f(e[1])):t[e[0]].apply(t,e[1])},new b.Matrix)},toParent:function(t){if(this==t)return this;var e=this.screenCTM(),i=t.screenCTM().inverse();return this.addTo(t).untransform().transform(i.multiply(e)),this},toDoc:function(){return this.toParent(this.doc())}}),b.Transformation=b.invent({create:function(t,e){if(arguments.length>1&&"boolean"!=typeof e)return this.constructor.call(this,[].slice.call(arguments));if(Array.isArray(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 b.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(c).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):h,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,e,i,n){return this.destination=new b.ViewBox(t,e,i,n),this},at:function(t){return this.destination?new b.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:b.Container,construct:{viewbox:function(t,e,i,n){return 0==arguments.length?new b.ViewBox(this):this.attr("viewBox",new b.ViewBox(t,e,i,n))}}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave","touchstart","touchmove","touchleave","touchend","touchcancel"].forEach(function(t){b.Element.prototype[t]=function(e){return null==e?b.off(this,t):b.on(this,t,e),this}}),b.listenerId=0,b.on=function(t,e,i,n,r){var s=i.bind(n||t),o=t instanceof b.Element?t.node:t;o.instance=o.instance||{_events:{}};var a=o.instance._events;i._svgjsListenerId||(i._svgjsListenerId=++b.listenerId),e.split(b.regex.delimiter).forEach(function(t){var e=t.split(".")[0],n=t.split(".")[1]||"*";a[e]=a[e]||{},a[e][n]=a[e][n]||{},a[e][n][i._svgjsListenerId]=s,o.addEventListener(e,s,r||!1)})},b.off=function(t,e,i,n){var r=t instanceof b.Element?t.node:t;if(r.instance&&("function"!=typeof i||(i=i._svgjsListenerId))){var s=r.instance._events;(e||"").split(b.regex.delimiter).forEach(function(t){var e,o,a=t&&t.split(".")[0],h=t&&t.split(".")[1];if(i)s[a]&&s[a][h||"*"]&&(r.removeEventListener(a,s[a][h||"*"][i],n||!1),delete s[a][h||"*"][i]);else if(a&&h){if(s[a]&&s[a][h]){for(o in s[a][h])b.off(r,[a,h].join("."),o);delete s[a][h]}}else if(h)for(t in s)for(e in s[t])h===e&&b.off(r,[t,h].join("."));else if(a){if(s[a]){for(e in s[a])b.off(r,[a,e].join("."));delete s[a]}}else{for(t in s)b.off(r,t);r.instance._events={}}})}},b.extend(b.Element,{on:function(t,e,i,n){return b.on(this,t,e,i,n),this},off:function(t,e){return b.off(this.node,t,e),this},fire:function(e,i){return e instanceof t.Event?this.node.dispatchEvent(e):this.node.dispatchEvent(e=new b.CustomEvent(e,{detail:i,cancelable:!0})),this._event=e,this},event:function(){return this._event}}),b.Defs=b.invent({create:"defs",inherit:b.Container}),b.G=b.invent({create:"g",inherit:b.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 b.G)}}}),b.Doc=b.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,b.create("svg")),t.appendChild(this.node),this.size("100%","100%")),this.namespace().defs())},inherit:b.Container,extend:{namespace:function(){return this.attr({xmlns:b.ns,version:"1.1"}).attr("xmlns:xlink",b.xlink,b.xmlns).attr("xmlns:svgjs",b.svgjs,b.xmlns)},defs:function(){if(!this._defs){var t;(t=this.node.getElementsByTagName("defs")[0])?this._defs=b.adopt(t):this._defs=new b.Defs,this.node.appendChild(this._defs.node)}return this._defs},parent:function(){return this.node.parentNode&&"#document"!=this.node.parentNode.nodeName&&"#document-fragment"!=this.node.parentNode.nodeName?this.node.parentNode:null},spof:function(){var t=this.node.getScreenCTM();return t&&this.style("left",-t.e%1+"px").style("top",-t.f%1+"px"),this},remove:function(){return this.parent()&&this.parent().removeChild(this.node),this},clear:function(){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return delete this._defs,b.parser.draw.parentNode||this.node.appendChild(b.parser.draw),this},clone:function(t){this.writeDataToDom();var e=this.node,i=x(e.cloneNode(!0));return t?(t.node||t).appendChild(i.node):e.parentNode.insertBefore(i.node,e.nextSibling),i}}}),b.extend(b.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 b.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 b.Doc&&t.node.appendChild(t.defs().node),this},back:function(){return this.position()>0&&this.parent().removeElement(this).add(this,0),this},before:function(t){t.remove();var e=this.position();return this.parent().add(t,e),this},after:function(t){t.remove();var e=this.position();return this.parent().add(t,e+1),this}}),b.Mask=b.invent({create:function(){this.constructor.call(this,b.create("mask")),this.targets=[]},inherit:b.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unmask();return this.targets=[],b.Element.prototype.remove.call(this),this}},construct:{mask:function(){return this.defs().put(new b.Mask)}}}),b.extend(b.Element,{maskWith:function(t){return this.masker=t instanceof b.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)}}),b.ClipPath=b.invent({create:function(){this.constructor.call(this,b.create("clipPath")),this.targets=[]},inherit:b.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 b.ClipPath)}}}),b.extend(b.Element,{clipWith:function(t){return this.clipper=t instanceof b.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)}}),b.Gradient=b.invent({create:function(t){this.constructor.call(this,b.create(t+"Gradient")),this.type=t},inherit:b.Container,extend:{at:function(t,e,i){return this.put(new b.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"),b.Container.prototype.attr.call(this,t,e,i)}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),b.extend(b.Gradient,b.FX,{from:function(t,e){return"radial"==(this._target||this).type?this.attr({fx:new b.Number(t),fy:new b.Number(e)}):this.attr({x1:new b.Number(t),y1:new b.Number(e)})},to:function(t,e){return"radial"==(this._target||this).type?this.attr({cx:new b.Number(t),cy:new b.Number(e)}):this.attr({x2:new b.Number(t),y2:new b.Number(e)})}}),b.extend(b.Defs,{gradient:function(t,e){return this.put(new b.Gradient(t)).update(e)}}),b.Stop=b.invent({create:"stop",inherit:b.Element,extend:{update:function(t){return("number"==typeof t||t instanceof b.Number)&&(t={offset:arguments[0],color:arguments[1],opacity:arguments[2]}),null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new b.Number(t.offset)),this}}}),b.Pattern=b.invent({create:"pattern",inherit:b.Container,extend:{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"),b.Container.prototype.attr.call(this,t,e,i)}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),b.extend(b.Defs,{pattern:function(t,e,i){return this.put(new b.Pattern).update(i).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),b.Shape=b.invent({create:function(t){this.constructor.call(this,t)},inherit:b.Element}),b.Bare=b.invent({create:function(t,e){if(this.constructor.call(this,b.create(t)),e)for(var i in e.prototype)"function"==typeof e.prototype[i]&&(this[i]=e.prototype[i])},inherit:b.Element,extend:{words:function(t){for(;this.node.hasChildNodes();)this.node.removeChild(this.node.lastChild);return this.node.appendChild(e.createTextNode(t)),this}}}),b.extend(b.Parent,{element:function(t,e){return this.put(new b.Bare(t,e))}}),b.Symbol=b.invent({create:"symbol",inherit:b.Container,construct:{symbol:function(){return this.put(new b.Symbol)}}}),b.Use=b.invent({create:"use",inherit:b.Shape,extend:{element:function(t,e){return this.attr("href",(e||"")+"#"+t,b.xlink)}},construct:{use:function(t,e){return this.put(new b.Use).element(t,e)}}}),b.Rect=b.invent({create:"rect",inherit:b.Shape,construct:{rect:function(t,e){return this.put(new b.Rect).size(t,e)}}}),b.Circle=b.invent({create:"circle",inherit:b.Shape,construct:{circle:function(t){return this.put(new b.Circle).rx(new b.Number(t).divide(2)).move(0,0)}}}),b.extend(b.Circle,b.FX,{rx:function(t){return this.attr("r",t)},ry:function(t){return this.rx(t)}}),b.Ellipse=b.invent({create:"ellipse",inherit:b.Shape,construct:{ellipse:function(t,e){return this.put(new b.Ellipse).size(t,e).move(0,0)}}}),b.extend(b.Ellipse,b.Rect,b.FX,{rx:function(t){return this.attr("rx",t)},ry:function(t){return this.attr("ry",t)}}),b.extend(b.Circle,b.Ellipse,{x:function(t){return null==t?this.cx()-this.rx():this.cx(t+this.rx())},y:function(t){return null==t?this.cy()-this.ry():this.cy(t+this.ry())},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",t)},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",t)},width:function(t){return null==t?2*this.rx():this.rx(new b.Number(t).divide(2))},height:function(t){return null==t?2*this.ry():this.ry(new b.Number(t).divide(2))},size:function(t,e){var i=l(this,t,e);return this.rx(new b.Number(i.width).divide(2)).ry(new b.Number(i.height).divide(2))}}),b.Line=b.invent({create:"line",inherit:b.Shape,extend:{array:function(){return new b.PointArray([[this.attr("x1"),this.attr("y1")],[this.attr("x2"),this.attr("y2")]])},plot:function(t,e,i,n){return null==t?this.array():(t=void 0!==e?{x1:t,y1:e,x2:i,y2:n}:new b.PointArray(t).toLine(),this.attr(t))},move:function(t,e){return this.attr(this.array().move(t,e).toLine())},size:function(t,e){var i=l(this,t,e);return this.attr(this.array().size(i.width,i.height).toLine())}},construct:{line:function(t,e,i,n){return b.Line.prototype.plot.apply(this.put(new b.Line),null!=t?[t,e,i,n]:[0,0,0,0])}}}),b.Polyline=b.invent({create:"polyline",inherit:b.Shape,construct:{polyline:function(t){return this.put(new b.Polyline).plot(t||new b.PointArray)}}}),b.Polygon=b.invent({create:"polygon",inherit:b.Shape,construct:{polygon:function(t){return this.put(new b.Polygon).plot(t||new b.PointArray)}}}),b.extend(b.Polyline,b.Polygon,{array:function(){return this._array||(this._array=new b.PointArray(this.attr("points")))},plot:function(t){return null==t?this.array():this.clear().attr("points","string"==typeof t?t:this._array=new b.PointArray(t))},clear:function(){return delete this._array,this},move:function(t,e){return this.attr("points",this.array().move(t,e))},size:function(t,e){var i=l(this,t,e);return this.attr("points",this.array().size(i.width,i.height))}}),b.extend(b.Line,b.Polyline,b.Polygon,{morphArray:b.PointArray,x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},width:function(t){var e=this.bbox();return null==t?e.width:this.size(t,e.height)},height:function(t){var e=this.bbox();return null==t?e.height:this.size(e.width,t)}}),b.Path=b.invent({create:"path",inherit:b.Shape,extend:{morphArray:b.PathArray,array:function(){return this._array||(this._array=new b.PathArray(this.attr("d")))},plot:function(t){return null==t?this.array():this.clear().attr("d","string"==typeof t?t:this._array=new b.PathArray(t))},clear:function(){return delete this._array,this},move:function(t,e){return this.attr("d",this.array().move(t,e))},x:function(t){return null==t?this.bbox().x:this.move(t,this.bbox().y)},y:function(t){return null==t?this.bbox().y:this.move(this.bbox().x,t)},size:function(t,e){var i=l(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 b.Path).plot(t||new b.PathArray)}}}),b.Image=b.invent({create:"image",inherit:b.Shape,extend:{load:function(e){if(!e)return this;var i=this,n=new t.Image;return b.on(n,"load",function(){b.off(n);var t=i.parent(b.Pattern);null!==t&&(0==i.width()&&0==i.height()&&i.size(n.width,n.height),t&&0==t.width()&&0==t.height()&&t.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:e}))}),b.on(n,"error",function(t){b.off(n),"function"==typeof i._error&&i._error.call(i,t)}),this.attr("href",n.src=this.src=e,b.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 b.Image).load(t).size(e||0,i||e||0)}}}),b.Text=b.invent({create:function(){this.constructor.call(this,b.create("text")),this.dom.leading=new b.Number(1.3),this._rebuild=!0,this._build=!1,this.attr("font-family",b.defaults.attrs["font-family"])},inherit:b.Shape,extend:{x:function(t){return null==t?this.attr("x"):this.attr("x",t)},y:function(t){var e=this.attr("y"),i="number"==typeof e?e-this.bbox().y:0;return null==t?"number"==typeof e?e-i:e:this.attr("y","number"==typeof t.valueOf()?t+i:t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)},text:function(t){if(void 0===t){for(var t="",e=this.node.childNodes,i=0,n=e.length;i=0;e--)null!=i[M[t][e]]&&this.attr(M.prefix(t,M[t][e]),i[M[t][e]]);return this},b.extend(b.Element,b.FX,i)}),b.extend(b.Element,b.FX,{rotate:function(t,e,i){return this.transform({rotation:t,cx:e,cy:i})},skew:function(t,e,i,n){return 1==arguments.length||3==arguments.length?this.transform({skew:t,cx:e,cy:i}):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 e="number"==typeof t?t:e,this.transform({flip:t||"both",offset:e})},matrix:function(t){return this.attr("transform",new b.Matrix(6==arguments.length?[].slice.call(arguments):t))},opacity:function(t){return this.attr("opacity",t)},dx:function(t){return this.x(new b.Number(t).plus(this instanceof b.FX?0:this.x()),!0)},dy:function(t){return this.y(new b.Number(t).plus(this instanceof b.FX?0:this.y()),!0)},dmove:function(t,e){return this.dx(t).dy(e)}}),b.extend(b.Rect,b.Ellipse,b.Circle,b.Gradient,b.FX,{radius:function(t,e){var i=(this._target||this).type;return"radial"==i||"circle"==i?this.attr("r",new b.Number(t)):this.rx(t).ry(null==e?t:e)}}),b.extend(b.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),b.extend(b.Parent,b.Text,b.Tspan,b.FX,{font:function(t,e){if("object"==typeof t)for(e in t)this.font(e,t[e]);return"leading"==t?this.leading(e):"anchor"==t?this.attr("text-anchor",e):"size"==t||"family"==t||"weight"==t||"stretch"==t||"variant"==t||"style"==t?this.attr("font-"+t,e):this.attr(t,e)}}),b.Set=b.invent({create:function(t){t instanceof b.Set?this.members=t.members.slice():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(){if(0==this.members.length)return new b.RBox;var t=this.members[0].rbox(this.members[0].doc());return this.each(function(){t=t.merge(this.rbox(this.doc()))}),t}},construct:{set:function(t){
+return new b.Set(t)}}}),b.FX.Set=b.invent({create:function(t){this.set=t}}),b.Set.inherit=function(){var t,e=[];for(var t in b.Shape.prototype)"function"==typeof b.Shape.prototype[t]&&"function"!=typeof b.Set.prototype[t]&&e.push(t);e.forEach(function(t){b.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={})}}),b.get=function(t){var i=e.getElementById(v(t)||t);return b.adopt(i)},b.select=function(t,i){return new b.Set(b.utils.map((i||e).querySelectorAll(t),function(t){return b.adopt(t)}))},b.extend(b.Parent,{select:function(t){return b.select(t,this.node)}});var k="abcdef".split("");if("function"!=typeof t.CustomEvent){var S=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};S.prototype=t.Event.prototype,b.CustomEvent=S}else b.CustomEvent=t.CustomEvent;return function(e){for(var i=0,n=["moz","webkit"],r=0;r - <%= pkg.description %>'
+ , '* @version <%= pkg.version %>'
+ , '* <%= pkg.homepage %>'
+ , '*'
+ , '* @copyright <%= pkg.author %>'
+ , '* @license <%= pkg.license %>'
+ , '*'
+ , '* BUILT: <%= pkg.buildDate %>'
+ , '*/;'
+ , ''].join('\n')
+
+var headerShort = '/*! <%= pkg.name %> v<%= pkg.version %> <%= pkg.license %>*/;'
+
+// all files in the right order (currently we don't use any dependency management system)
+var parts = [
+ 'src/svg.js'
+, 'src/regex.js'
+, 'src/utilities.js'
+, 'src/default.js'
+, 'src/color.js'
+, 'src/array.js'
+, 'src/pointarray.js'
+, 'src/patharray.js'
+, 'src/number.js'
+, 'src/element.js'
+, 'src/fx.js'
+, 'src/boxes.js'
+, 'src/matrix.js'
+, 'src/point.js'
+, 'src/attr.js'
+, 'src/transform.js'
+, 'src/style.js'
+, 'src/parent.js'
+, 'src/ungroup.js'
+, 'src/container.js'
+, 'src/viewbox.js'
+, 'src/event.js'
+, 'src/defs.js'
+, 'src/group.js'
+, 'src/doc.js'
+, 'src/arrange.js'
+, 'src/mask.js'
+, 'src/clip.js'
+, 'src/gradient.js'
+, 'src/pattern.js'
+, 'src/shape.js'
+, 'src/bare.js'
+, 'src/symbol.js'
+, 'src/use.js'
+, 'src/rect.js'
+, 'src/ellipse.js'
+, 'src/line.js'
+, 'src/poly.js'
+, 'src/pointed.js'
+, 'src/path.js'
+, 'src/image.js'
+, 'src/text.js'
+, 'src/textpath.js'
+, 'src/nested.js'
+, 'src/hyperlink.js'
+, 'src/marker.js'
+, 'src/sugar.js'
+, 'src/set.js'
+, 'src/data.js'
+, 'src/memory.js'
+, 'src/selector.js'
+, 'src/helpers.js'
+, 'src/polyfill.js'
+]
+
+gulp.task('clean', function() {
+ return del([ 'dist/*' ])
+})
+
+/**
+ * Compile everything in /src to one unified file in the order defined in the MODULES constant
+ * wrap the whole thing in a UMD wrapper (@see https://github.com/umdjs/umd)
+ * add the license information to the header plus the build time stamp
+ */
+gulp.task('unify', ['clean'], function() {
+ pkg.buildDate = Date()
+ return gulp.src(parts)
+ .pipe(concat('svg.js', { newLine: '\n' }))
+ // wrap the whole thing in an immediate function call
+ .pipe(wrapUmd({ src: 'src/umd.js'}))
+ .pipe(header(headerLong, { pkg: pkg }))
+ .pipe(trim({ leading: false }))
+ .pipe(chmod(0o644))
+ .pipe(gulp.dest('dist'))
+ .pipe(size({ showFiles: true, title: 'Full' }))
+})
+
+/**
+ * uglify the file and show the size of the result
+ * add the license info
+ * show the gzipped file size
+ */
+gulp.task('minify', ['unify'], function() {
+ return gulp.src('dist/svg.js')
+ .pipe(uglify())
+ .pipe(rename({ suffix:'.min' }))
+ .pipe(size({ showFiles: true, title: 'Minified' }))
+ .pipe(header(headerShort, { pkg: pkg }))
+ .pipe(chmod(0o644))
+ .pipe(gulp.dest('dist'))
+ .pipe(size({ showFiles: true, gzip: true, title: 'Gzipped' }))
+})
+
+
+gulp.task('default', ['clean', 'unify', 'minify'])
\ No newline at end of file
diff --git a/node_modules/svg.js/package.json b/node_modules/svg.js/package.json
new file mode 100644
index 0000000..dd5f2ce
--- /dev/null
+++ b/node_modules/svg.js/package.json
@@ -0,0 +1,83 @@
+{
+ "name": "svg.js",
+ "version": "2.7.1",
+ "description": "A lightweight library for manipulating and animating SVG.",
+ "url": "https://svgdotjs.github.io/",
+ "homepage": "https://svgdotjs.github.io/",
+ "keywords": [
+ "svg",
+ "vector",
+ "graphics",
+ "animation"
+ ],
+ "author": "Wout Fierens ",
+ "main": "dist/svg.js",
+ "jam": {
+ "include": [
+ "dist/svg.js",
+ "README.md",
+ "LICENSE.txt"
+ ]
+ },
+ "maintainers": [
+ {
+ "name": "Wout Fierens",
+ "email": "wout@mick-wout.com",
+ "web": "https://svgdotjs.github.io/"
+ },
+ {
+ "name": "Ulrich-Matthias Schäfer",
+ "email": "ulima.ums@googlemail.com"
+ },
+ {
+ "name": "Rémi Tétreault",
+ "web": "https://github.com/RmiTtro"
+ },
+ {
+ "name": "Jon Ege Ronnenberg",
+ "email": "jon@svgjs.com",
+ "url": "https://keybase.io/dotnetcarpenter"
+ }
+ ],
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://www.opensource.org/licenses/mit-license.php"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/svgdotjs/svg.js.git"
+ },
+ "github": "https://github.com/svgdotjs/svg.js",
+ "license": "MIT",
+ "typings": "./svg.js.d.ts",
+ "scripts": {
+ "build": "gulp",
+ "build:test": "gulp unify",
+ "test": "karma start .config/karma.conf.js --single-run",
+ "test:quick": "karma start .config/karma.quick.js"
+ },
+ "devDependencies": {
+ "coveralls": "^2.13.1",
+ "del": "^2.2.0",
+ "gulp": "^3.9.1",
+ "gulp-chmod": "^2.0.0",
+ "gulp-cli": "^1.3.0",
+ "gulp-concat": "^2.3.3",
+ "gulp-header": "^1.0.5",
+ "gulp-rename": "^1.2.2",
+ "gulp-size": "^2.1.0",
+ "gulp-trimlines": "^1.0.0",
+ "gulp-uglify": "^2.1.2",
+ "gulp-wrap": "^0.13.0",
+ "jasmine-core": "^2.6.2",
+ "karma": "^1.7.0",
+ "karma-coverage": "^1.1.1",
+ "karma-firefox-launcher": "^1.0.1",
+ "karma-jasmine": "^1.1.0",
+ "karma-phantomjs-launcher": "^1.0.4",
+ "request": "^2.81.0",
+ "svgdom": "latest"
+ }
+}
diff --git a/node_modules/svg.js/spec/SpecRunner.html b/node_modules/svg.js/spec/SpecRunner.html
new file mode 100644
index 0000000..0895a88
--- /dev/null
+++ b/node_modules/svg.js/spec/SpecRunner.html
@@ -0,0 +1,104 @@
+
+
+
+
+ SVG.js - Jasmine Spec Runner v2.6.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Some description
+
+
+
+
+
+
+
+
+
+
+ A
+ B
+ C
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/node_modules/svg.js/spec/fixtures/fixture.css b/node_modules/svg.js/spec/fixtures/fixture.css
new file mode 100644
index 0000000..e72e421
--- /dev/null
+++ b/node_modules/svg.js/spec/fixtures/fixture.css
@@ -0,0 +1,6 @@
+#drawing {
+ width: 500px;
+ height: 500px;
+ position: fixed;
+ z-index: -1;
+}
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/fixtures/fixture.svg b/node_modules/svg.js/spec/fixtures/fixture.svg
new file mode 100644
index 0000000..5154267
--- /dev/null
+++ b/node_modules/svg.js/spec/fixtures/fixture.svg
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+ Some description
+
+
+
+
+
+
+
+
+
+
+ A
+ B
+ C
+
+
+
+
diff --git a/node_modules/svg.js/spec/fixtures/pixel.png b/node_modules/svg.js/spec/fixtures/pixel.png
new file mode 100644
index 0000000..818c71d
Binary files /dev/null and b/node_modules/svg.js/spec/fixtures/pixel.png differ
diff --git a/node_modules/svg.js/spec/lib/jasmine-2.6.0/boot.js b/node_modules/svg.js/spec/lib/jasmine-2.6.0/boot.js
new file mode 100644
index 0000000..d9b5a80
--- /dev/null
+++ b/node_modules/svg.js/spec/lib/jasmine-2.6.0/boot.js
@@ -0,0 +1,133 @@
+/**
+ Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
+
+ If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
+
+ The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
+
+ [jasmine-gem]: http://github.com/pivotal/jasmine-gem
+ */
+
+(function() {
+
+ /**
+ * ## Require & Instantiate
+ *
+ * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
+ */
+ window.jasmine = jasmineRequire.core(jasmineRequire);
+
+ /**
+ * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
+ */
+ jasmineRequire.html(jasmine);
+
+ /**
+ * Create the Jasmine environment. This is used to run all specs in a project.
+ */
+ var env = jasmine.getEnv();
+
+ /**
+ * ## The Global Interface
+ *
+ * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
+ */
+ var jasmineInterface = jasmineRequire.interface(jasmine, env);
+
+ /**
+ * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
+ */
+ extend(window, jasmineInterface);
+
+ /**
+ * ## Runner Parameters
+ *
+ * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
+ */
+
+ var queryString = new jasmine.QueryString({
+ getWindowLocation: function() { return window.location; }
+ });
+
+ var filterSpecs = !!queryString.getParam("spec");
+
+ var catchingExceptions = queryString.getParam("catch");
+ env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
+
+ var throwingExpectationFailures = queryString.getParam("throwFailures");
+ env.throwOnExpectationFailure(throwingExpectationFailures);
+
+ var random = queryString.getParam("random");
+ env.randomizeTests(random);
+
+ var seed = queryString.getParam("seed");
+ if (seed) {
+ env.seed(seed);
+ }
+
+ /**
+ * ## Reporters
+ * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
+ */
+ var htmlReporter = new jasmine.HtmlReporter({
+ env: env,
+ onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); },
+ onThrowExpectationsClick: function() { queryString.navigateWithNewParam("throwFailures", !env.throwingExpectationFailures()); },
+ onRandomClick: function() { queryString.navigateWithNewParam("random", !env.randomTests()); },
+ addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); },
+ getContainer: function() { return document.body; },
+ createElement: function() { return document.createElement.apply(document, arguments); },
+ createTextNode: function() { return document.createTextNode.apply(document, arguments); },
+ timer: new jasmine.Timer(),
+ filterSpecs: filterSpecs
+ });
+
+ /**
+ * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
+ */
+ env.addReporter(jasmineInterface.jsApiReporter);
+ env.addReporter(htmlReporter);
+
+ /**
+ * Filter which specs will be run by matching the start of the full name against the `spec` query param.
+ */
+ var specFilter = new jasmine.HtmlSpecFilter({
+ filterString: function() { return queryString.getParam("spec"); }
+ });
+
+ env.specFilter = function(spec) {
+ return specFilter.matches(spec.getFullName());
+ };
+
+ /**
+ * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
+ */
+ window.setTimeout = window.setTimeout;
+ window.setInterval = window.setInterval;
+ window.clearTimeout = window.clearTimeout;
+ window.clearInterval = window.clearInterval;
+
+ /**
+ * ## Execution
+ *
+ * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
+ */
+ var currentWindowOnload = window.onload;
+
+ window.onload = function() {
+ if (currentWindowOnload) {
+ currentWindowOnload();
+ }
+ htmlReporter.initialize();
+ env.execute();
+ };
+
+ /**
+ * Helper function for readability above.
+ */
+ function extend(destination, source) {
+ for (var property in source) destination[property] = source[property];
+ return destination;
+ }
+
+}());
diff --git a/node_modules/svg.js/spec/lib/jasmine-2.6.0/console.js b/node_modules/svg.js/spec/lib/jasmine-2.6.0/console.js
new file mode 100644
index 0000000..38ad952
--- /dev/null
+++ b/node_modules/svg.js/spec/lib/jasmine-2.6.0/console.js
@@ -0,0 +1,190 @@
+/*
+Copyright (c) 2008-2017 Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+function getJasmineRequireObj() {
+ if (typeof module !== 'undefined' && module.exports) {
+ return exports;
+ } else {
+ window.jasmineRequire = window.jasmineRequire || {};
+ return window.jasmineRequire;
+ }
+}
+
+getJasmineRequireObj().console = function(jRequire, j$) {
+ j$.ConsoleReporter = jRequire.ConsoleReporter();
+};
+
+getJasmineRequireObj().ConsoleReporter = function() {
+
+ var noopTimer = {
+ start: function(){},
+ elapsed: function(){ return 0; }
+ };
+
+ function ConsoleReporter(options) {
+ var print = options.print,
+ showColors = options.showColors || false,
+ onComplete = options.onComplete || function() {},
+ timer = options.timer || noopTimer,
+ specCount,
+ failureCount,
+ failedSpecs = [],
+ pendingCount,
+ ansi = {
+ green: '\x1B[32m',
+ red: '\x1B[31m',
+ yellow: '\x1B[33m',
+ none: '\x1B[0m'
+ },
+ failedSuites = [];
+
+ print('ConsoleReporter is deprecated and will be removed in a future version.');
+
+ this.jasmineStarted = function() {
+ specCount = 0;
+ failureCount = 0;
+ pendingCount = 0;
+ print('Started');
+ printNewline();
+ timer.start();
+ };
+
+ this.jasmineDone = function() {
+ printNewline();
+ for (var i = 0; i < failedSpecs.length; i++) {
+ specFailureDetails(failedSpecs[i]);
+ }
+
+ if(specCount > 0) {
+ printNewline();
+
+ var specCounts = specCount + ' ' + plural('spec', specCount) + ', ' +
+ failureCount + ' ' + plural('failure', failureCount);
+
+ if (pendingCount) {
+ specCounts += ', ' + pendingCount + ' pending ' + plural('spec', pendingCount);
+ }
+
+ print(specCounts);
+ } else {
+ print('No specs found');
+ }
+
+ printNewline();
+ var seconds = timer.elapsed() / 1000;
+ print('Finished in ' + seconds + ' ' + plural('second', seconds));
+ printNewline();
+
+ for(i = 0; i < failedSuites.length; i++) {
+ suiteFailureDetails(failedSuites[i]);
+ }
+
+ onComplete(failureCount === 0);
+ };
+
+ this.specDone = function(result) {
+ specCount++;
+
+ if (result.status == 'pending') {
+ pendingCount++;
+ print(colored('yellow', '*'));
+ return;
+ }
+
+ if (result.status == 'passed') {
+ print(colored('green', '.'));
+ return;
+ }
+
+ if (result.status == 'failed') {
+ failureCount++;
+ failedSpecs.push(result);
+ print(colored('red', 'F'));
+ }
+ };
+
+ this.suiteDone = function(result) {
+ if (result.failedExpectations && result.failedExpectations.length > 0) {
+ failureCount++;
+ failedSuites.push(result);
+ }
+ };
+
+ return this;
+
+ function printNewline() {
+ print('\n');
+ }
+
+ function colored(color, str) {
+ return showColors ? (ansi[color] + str + ansi.none) : str;
+ }
+
+ function plural(str, count) {
+ return count == 1 ? str : str + 's';
+ }
+
+ function repeat(thing, times) {
+ var arr = [];
+ for (var i = 0; i < times; i++) {
+ arr.push(thing);
+ }
+ return arr;
+ }
+
+ function indent(str, spaces) {
+ var lines = (str || '').split('\n');
+ var newArr = [];
+ for (var i = 0; i < lines.length; i++) {
+ newArr.push(repeat(' ', spaces).join('') + lines[i]);
+ }
+ return newArr.join('\n');
+ }
+
+ function specFailureDetails(result) {
+ printNewline();
+ print(result.fullName);
+
+ for (var i = 0; i < result.failedExpectations.length; i++) {
+ var failedExpectation = result.failedExpectations[i];
+ printNewline();
+ print(indent(failedExpectation.message, 2));
+ print(indent(failedExpectation.stack, 2));
+ }
+
+ printNewline();
+ }
+
+ function suiteFailureDetails(result) {
+ for (var i = 0; i < result.failedExpectations.length; i++) {
+ printNewline();
+ print(colored('red', 'An error was thrown in an afterAll'));
+ printNewline();
+ print(colored('red', 'AfterAll ' + result.failedExpectations[i].message));
+
+ }
+ printNewline();
+ }
+ }
+
+ return ConsoleReporter;
+};
diff --git a/node_modules/svg.js/spec/lib/jasmine-2.6.0/jasmine-html.js b/node_modules/svg.js/spec/lib/jasmine-2.6.0/jasmine-html.js
new file mode 100644
index 0000000..90407cc
--- /dev/null
+++ b/node_modules/svg.js/spec/lib/jasmine-2.6.0/jasmine-html.js
@@ -0,0 +1,499 @@
+/*
+Copyright (c) 2008-2017 Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+jasmineRequire.html = function(j$) {
+ j$.ResultsNode = jasmineRequire.ResultsNode();
+ j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
+ j$.QueryString = jasmineRequire.QueryString();
+ j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
+};
+
+jasmineRequire.HtmlReporter = function(j$) {
+
+ var noopTimer = {
+ start: function() {},
+ elapsed: function() { return 0; }
+ };
+
+ function HtmlReporter(options) {
+ var env = options.env || {},
+ getContainer = options.getContainer,
+ createElement = options.createElement,
+ createTextNode = options.createTextNode,
+ onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {},
+ onThrowExpectationsClick = options.onThrowExpectationsClick || function() {},
+ onRandomClick = options.onRandomClick || function() {},
+ addToExistingQueryString = options.addToExistingQueryString || defaultQueryString,
+ filterSpecs = options.filterSpecs,
+ timer = options.timer || noopTimer,
+ results = [],
+ specsExecuted = 0,
+ failureCount = 0,
+ pendingSpecCount = 0,
+ htmlReporterMain,
+ symbols,
+ failedSuites = [];
+
+ this.initialize = function() {
+ clearPrior();
+ htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'},
+ createDom('div', {className: 'jasmine-banner'},
+ createDom('a', {className: 'jasmine-title', href: 'http://jasmine.github.io/', target: '_blank'}),
+ createDom('span', {className: 'jasmine-version'}, j$.version)
+ ),
+ createDom('ul', {className: 'jasmine-symbol-summary'}),
+ createDom('div', {className: 'jasmine-alert'}),
+ createDom('div', {className: 'jasmine-results'},
+ createDom('div', {className: 'jasmine-failures'})
+ )
+ );
+ getContainer().appendChild(htmlReporterMain);
+ };
+
+ var totalSpecsDefined;
+ this.jasmineStarted = function(options) {
+ totalSpecsDefined = options.totalSpecsDefined || 0;
+ timer.start();
+ };
+
+ var summary = createDom('div', {className: 'jasmine-summary'});
+
+ var topResults = new j$.ResultsNode({}, '', null),
+ currentParent = topResults;
+
+ this.suiteStarted = function(result) {
+ currentParent.addChild(result, 'suite');
+ currentParent = currentParent.last();
+ };
+
+ this.suiteDone = function(result) {
+ if (result.status == 'failed') {
+ failedSuites.push(result);
+ }
+
+ if (currentParent == topResults) {
+ return;
+ }
+
+ currentParent = currentParent.parent;
+ };
+
+ this.specStarted = function(result) {
+ currentParent.addChild(result, 'spec');
+ };
+
+ var failures = [];
+ this.specDone = function(result) {
+ if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') {
+ console.error('Spec \'' + result.fullName + '\' has no expectations.');
+ }
+
+ if (result.status != 'disabled') {
+ specsExecuted++;
+ }
+
+ if (!symbols){
+ symbols = find('.jasmine-symbol-summary');
+ }
+
+ symbols.appendChild(createDom('li', {
+ className: noExpectations(result) ? 'jasmine-empty' : 'jasmine-' + result.status,
+ id: 'spec_' + result.id,
+ title: result.fullName
+ }
+ ));
+
+ if (result.status == 'failed') {
+ failureCount++;
+
+ var failure =
+ createDom('div', {className: 'jasmine-spec-detail jasmine-failed'},
+ createDom('div', {className: 'jasmine-description'},
+ createDom('a', {title: result.fullName, href: specHref(result)}, result.fullName)
+ ),
+ createDom('div', {className: 'jasmine-messages'})
+ );
+ var messages = failure.childNodes[1];
+
+ for (var i = 0; i < result.failedExpectations.length; i++) {
+ var expectation = result.failedExpectations[i];
+ messages.appendChild(createDom('div', {className: 'jasmine-result-message'}, expectation.message));
+ messages.appendChild(createDom('div', {className: 'jasmine-stack-trace'}, expectation.stack));
+ }
+
+ failures.push(failure);
+ }
+
+ if (result.status == 'pending') {
+ pendingSpecCount++;
+ }
+ };
+
+ this.jasmineDone = function(doneResult) {
+ var banner = find('.jasmine-banner');
+ var alert = find('.jasmine-alert');
+ var order = doneResult && doneResult.order;
+ alert.appendChild(createDom('span', {className: 'jasmine-duration'}, 'finished in ' + timer.elapsed() / 1000 + 's'));
+
+ banner.appendChild(
+ createDom('div', { className: 'jasmine-run-options' },
+ createDom('span', { className: 'jasmine-trigger' }, 'Options'),
+ createDom('div', { className: 'jasmine-payload' },
+ createDom('div', { className: 'jasmine-exceptions' },
+ createDom('input', {
+ className: 'jasmine-raise',
+ id: 'jasmine-raise-exceptions',
+ type: 'checkbox'
+ }),
+ createDom('label', { className: 'jasmine-label', 'for': 'jasmine-raise-exceptions' }, 'raise exceptions')),
+ createDom('div', { className: 'jasmine-throw-failures' },
+ createDom('input', {
+ className: 'jasmine-throw',
+ id: 'jasmine-throw-failures',
+ type: 'checkbox'
+ }),
+ createDom('label', { className: 'jasmine-label', 'for': 'jasmine-throw-failures' }, 'stop spec on expectation failure')),
+ createDom('div', { className: 'jasmine-random-order' },
+ createDom('input', {
+ className: 'jasmine-random',
+ id: 'jasmine-random-order',
+ type: 'checkbox'
+ }),
+ createDom('label', { className: 'jasmine-label', 'for': 'jasmine-random-order' }, 'run tests in random order'))
+ )
+ ));
+
+ var raiseCheckbox = find('#jasmine-raise-exceptions');
+
+ raiseCheckbox.checked = !env.catchingExceptions();
+ raiseCheckbox.onclick = onRaiseExceptionsClick;
+
+ var throwCheckbox = find('#jasmine-throw-failures');
+ throwCheckbox.checked = env.throwingExpectationFailures();
+ throwCheckbox.onclick = onThrowExpectationsClick;
+
+ var randomCheckbox = find('#jasmine-random-order');
+ randomCheckbox.checked = env.randomTests();
+ randomCheckbox.onclick = onRandomClick;
+
+ var optionsMenu = find('.jasmine-run-options'),
+ optionsTrigger = optionsMenu.querySelector('.jasmine-trigger'),
+ optionsPayload = optionsMenu.querySelector('.jasmine-payload'),
+ isOpen = /\bjasmine-open\b/;
+
+ optionsTrigger.onclick = function() {
+ if (isOpen.test(optionsPayload.className)) {
+ optionsPayload.className = optionsPayload.className.replace(isOpen, '');
+ } else {
+ optionsPayload.className += ' jasmine-open';
+ }
+ };
+
+ if (specsExecuted < totalSpecsDefined) {
+ var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all';
+ var skippedLink = order && order.random ? '?random=true' : '?';
+ alert.appendChild(
+ createDom('span', {className: 'jasmine-bar jasmine-skipped'},
+ createDom('a', {href: skippedLink, title: 'Run all specs'}, skippedMessage)
+ )
+ );
+ }
+ var statusBarMessage = '';
+ var statusBarClassName = 'jasmine-bar ';
+
+ if (totalSpecsDefined > 0) {
+ statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount);
+ if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); }
+ statusBarClassName += (failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed';
+ } else {
+ statusBarClassName += 'jasmine-skipped';
+ statusBarMessage += 'No specs found';
+ }
+
+ var seedBar;
+ if (order && order.random) {
+ seedBar = createDom('span', {className: 'jasmine-seed-bar'},
+ ', randomized with seed ',
+ createDom('a', {title: 'randomized with seed ' + order.seed, href: seedHref(order.seed)}, order.seed)
+ );
+ }
+
+ alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage, seedBar));
+
+ var errorBarClassName = 'jasmine-bar jasmine-errored';
+ var errorBarMessagePrefix = 'AfterAll ';
+
+ for(var i = 0; i < failedSuites.length; i++) {
+ var failedSuite = failedSuites[i];
+ for(var j = 0; j < failedSuite.failedExpectations.length; j++) {
+ alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessagePrefix + failedSuite.failedExpectations[j].message));
+ }
+ }
+
+ var globalFailures = (doneResult && doneResult.failedExpectations) || [];
+ for(i = 0; i < globalFailures.length; i++) {
+ var failure = globalFailures[i];
+ alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessagePrefix + failure.message));
+ }
+
+ var results = find('.jasmine-results');
+ results.appendChild(summary);
+
+ summaryList(topResults, summary);
+
+ function summaryList(resultsTree, domParent) {
+ var specListNode;
+ for (var i = 0; i < resultsTree.children.length; i++) {
+ var resultNode = resultsTree.children[i];
+ if (filterSpecs && !hasActiveSpec(resultNode)) {
+ continue;
+ }
+ if (resultNode.type == 'suite') {
+ var suiteListNode = createDom('ul', {className: 'jasmine-suite', id: 'suite-' + resultNode.result.id},
+ createDom('li', {className: 'jasmine-suite-detail'},
+ createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description)
+ )
+ );
+
+ summaryList(resultNode, suiteListNode);
+ domParent.appendChild(suiteListNode);
+ }
+ if (resultNode.type == 'spec') {
+ if (domParent.getAttribute('class') != 'jasmine-specs') {
+ specListNode = createDom('ul', {className: 'jasmine-specs'});
+ domParent.appendChild(specListNode);
+ }
+ var specDescription = resultNode.result.description;
+ if(noExpectations(resultNode.result)) {
+ specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
+ }
+ if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') {
+ specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
+ }
+ specListNode.appendChild(
+ createDom('li', {
+ className: 'jasmine-' + resultNode.result.status,
+ id: 'spec-' + resultNode.result.id
+ },
+ createDom('a', {href: specHref(resultNode.result)}, specDescription)
+ )
+ );
+ }
+ }
+ }
+
+ if (failures.length) {
+ alert.appendChild(
+ createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-spec-list'},
+ createDom('span', {}, 'Spec List | '),
+ createDom('a', {className: 'jasmine-failures-menu', href: '#'}, 'Failures')));
+ alert.appendChild(
+ createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-failure-list'},
+ createDom('a', {className: 'jasmine-spec-list-menu', href: '#'}, 'Spec List'),
+ createDom('span', {}, ' | Failures ')));
+
+ find('.jasmine-failures-menu').onclick = function() {
+ setMenuModeTo('jasmine-failure-list');
+ };
+ find('.jasmine-spec-list-menu').onclick = function() {
+ setMenuModeTo('jasmine-spec-list');
+ };
+
+ setMenuModeTo('jasmine-failure-list');
+
+ var failureNode = find('.jasmine-failures');
+ for (i = 0; i < failures.length; i++) {
+ failureNode.appendChild(failures[i]);
+ }
+ }
+ };
+
+ return this;
+
+ function find(selector) {
+ return getContainer().querySelector('.jasmine_html-reporter ' + selector);
+ }
+
+ function clearPrior() {
+ // return the reporter
+ var oldReporter = find('');
+
+ if(oldReporter) {
+ getContainer().removeChild(oldReporter);
+ }
+ }
+
+ function createDom(type, attrs, childrenVarArgs) {
+ var el = createElement(type);
+
+ for (var i = 2; i < arguments.length; i++) {
+ var child = arguments[i];
+
+ if (typeof child === 'string') {
+ el.appendChild(createTextNode(child));
+ } else {
+ if (child) {
+ el.appendChild(child);
+ }
+ }
+ }
+
+ for (var attr in attrs) {
+ if (attr == 'className') {
+ el[attr] = attrs[attr];
+ } else {
+ el.setAttribute(attr, attrs[attr]);
+ }
+ }
+
+ return el;
+ }
+
+ function pluralize(singular, count) {
+ var word = (count == 1 ? singular : singular + 's');
+
+ return '' + count + ' ' + word;
+ }
+
+ function specHref(result) {
+ return addToExistingQueryString('spec', result.fullName);
+ }
+
+ function seedHref(seed) {
+ return addToExistingQueryString('seed', seed);
+ }
+
+ function defaultQueryString(key, value) {
+ return '?' + key + '=' + value;
+ }
+
+ function setMenuModeTo(mode) {
+ htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode);
+ }
+
+ function noExpectations(result) {
+ return (result.failedExpectations.length + result.passedExpectations.length) === 0 &&
+ result.status === 'passed';
+ }
+
+ function hasActiveSpec(resultNode) {
+ if (resultNode.type == 'spec' && resultNode.result.status != 'disabled') {
+ return true;
+ }
+
+ if (resultNode.type == 'suite') {
+ for (var i = 0, j = resultNode.children.length; i < j; i++) {
+ if (hasActiveSpec(resultNode.children[i])) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return HtmlReporter;
+};
+
+jasmineRequire.HtmlSpecFilter = function() {
+ function HtmlSpecFilter(options) {
+ var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
+ var filterPattern = new RegExp(filterString);
+
+ this.matches = function(specName) {
+ return filterPattern.test(specName);
+ };
+ }
+
+ return HtmlSpecFilter;
+};
+
+jasmineRequire.ResultsNode = function() {
+ function ResultsNode(result, type, parent) {
+ this.result = result;
+ this.type = type;
+ this.parent = parent;
+
+ this.children = [];
+
+ this.addChild = function(result, type) {
+ this.children.push(new ResultsNode(result, type, this));
+ };
+
+ this.last = function() {
+ return this.children[this.children.length - 1];
+ };
+ }
+
+ return ResultsNode;
+};
+
+jasmineRequire.QueryString = function() {
+ function QueryString(options) {
+
+ this.navigateWithNewParam = function(key, value) {
+ options.getWindowLocation().search = this.fullStringWithNewParam(key, value);
+ };
+
+ this.fullStringWithNewParam = function(key, value) {
+ var paramMap = queryStringToParamMap();
+ paramMap[key] = value;
+ return toQueryString(paramMap);
+ };
+
+ this.getParam = function(key) {
+ return queryStringToParamMap()[key];
+ };
+
+ return this;
+
+ function toQueryString(paramMap) {
+ var qStrPairs = [];
+ for (var prop in paramMap) {
+ qStrPairs.push(encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]));
+ }
+ return '?' + qStrPairs.join('&');
+ }
+
+ function queryStringToParamMap() {
+ var paramStr = options.getWindowLocation().search.substring(1),
+ params = [],
+ paramMap = {};
+
+ if (paramStr.length > 0) {
+ params = paramStr.split('&');
+ for (var i = 0; i < params.length; i++) {
+ var p = params[i].split('=');
+ var value = decodeURIComponent(p[1]);
+ if (value === 'true' || value === 'false') {
+ value = JSON.parse(value);
+ }
+ paramMap[decodeURIComponent(p[0])] = value;
+ }
+ }
+
+ return paramMap;
+ }
+
+ }
+
+ return QueryString;
+};
diff --git a/node_modules/svg.js/spec/lib/jasmine-2.6.0/jasmine.css b/node_modules/svg.js/spec/lib/jasmine-2.6.0/jasmine.css
new file mode 100644
index 0000000..6319982
--- /dev/null
+++ b/node_modules/svg.js/spec/lib/jasmine-2.6.0/jasmine.css
@@ -0,0 +1,58 @@
+body { overflow-y: scroll; }
+
+.jasmine_html-reporter { background-color: #eee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333; }
+.jasmine_html-reporter a { text-decoration: none; }
+.jasmine_html-reporter a:hover { text-decoration: underline; }
+.jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; }
+.jasmine_html-reporter .jasmine-banner, .jasmine_html-reporter .jasmine-symbol-summary, .jasmine_html-reporter .jasmine-summary, .jasmine_html-reporter .jasmine-result-message, .jasmine_html-reporter .jasmine-spec .jasmine-description, .jasmine_html-reporter .jasmine-spec-detail .jasmine-description, .jasmine_html-reporter .jasmine-alert .jasmine-bar, .jasmine_html-reporter .jasmine-stack-trace { padding-left: 9px; padding-right: 9px; }
+.jasmine_html-reporter .jasmine-banner { position: relative; }
+.jasmine_html-reporter .jasmine-banner .jasmine-title { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==') no-repeat; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=') no-repeat, none; -moz-background-size: 100%; -o-background-size: 100%; -webkit-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; }
+.jasmine_html-reporter .jasmine-banner .jasmine-version { margin-left: 14px; position: relative; top: 6px; }
+.jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; }
+.jasmine_html-reporter .jasmine-version { color: #aaa; }
+.jasmine_html-reporter .jasmine-banner { margin-top: 14px; }
+.jasmine_html-reporter .jasmine-duration { color: #fff; float: right; line-height: 28px; padding-right: 9px; }
+.jasmine_html-reporter .jasmine-symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; }
+.jasmine_html-reporter .jasmine-symbol-summary li { display: inline-block; height: 10px; width: 14px; font-size: 16px; }
+.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed { font-size: 14px; }
+.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed:before { color: #007069; content: "\02022"; }
+.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed { line-height: 9px; }
+.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed:before { color: #ca3a11; content: "\d7"; font-weight: bold; margin-left: -1px; }
+.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-disabled { font-size: 14px; }
+.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-disabled:before { color: #bababa; content: "\02022"; }
+.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending { line-height: 17px; }
+.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending:before { color: #ba9d37; content: "*"; }
+.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty { font-size: 14px; }
+.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty:before { color: #ba9d37; content: "\02022"; }
+.jasmine_html-reporter .jasmine-run-options { float: right; margin-right: 5px; border: 1px solid #8a4182; color: #8a4182; position: relative; line-height: 20px; }
+.jasmine_html-reporter .jasmine-run-options .jasmine-trigger { cursor: pointer; padding: 8px 16px; }
+.jasmine_html-reporter .jasmine-run-options .jasmine-payload { position: absolute; display: none; right: -1px; border: 1px solid #8a4182; background-color: #eee; white-space: nowrap; padding: 4px 8px; }
+.jasmine_html-reporter .jasmine-run-options .jasmine-payload.jasmine-open { display: block; }
+.jasmine_html-reporter .jasmine-bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
+.jasmine_html-reporter .jasmine-bar.jasmine-failed { background-color: #ca3a11; }
+.jasmine_html-reporter .jasmine-bar.jasmine-passed { background-color: #007069; }
+.jasmine_html-reporter .jasmine-bar.jasmine-skipped { background-color: #bababa; }
+.jasmine_html-reporter .jasmine-bar.jasmine-errored { background-color: #ca3a11; }
+.jasmine_html-reporter .jasmine-bar.jasmine-menu { background-color: #fff; color: #aaa; }
+.jasmine_html-reporter .jasmine-bar.jasmine-menu a { color: #333; }
+.jasmine_html-reporter .jasmine-bar a { color: white; }
+.jasmine_html-reporter.jasmine-spec-list .jasmine-bar.jasmine-menu.jasmine-failure-list, .jasmine_html-reporter.jasmine-spec-list .jasmine-results .jasmine-failures { display: none; }
+.jasmine_html-reporter.jasmine-failure-list .jasmine-bar.jasmine-menu.jasmine-spec-list, .jasmine_html-reporter.jasmine-failure-list .jasmine-summary { display: none; }
+.jasmine_html-reporter .jasmine-results { margin-top: 14px; }
+.jasmine_html-reporter .jasmine-summary { margin-top: 14px; }
+.jasmine_html-reporter .jasmine-summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; }
+.jasmine_html-reporter .jasmine-summary ul.jasmine-suite { margin-top: 7px; margin-bottom: 7px; }
+.jasmine_html-reporter .jasmine-summary li.jasmine-passed a { color: #007069; }
+.jasmine_html-reporter .jasmine-summary li.jasmine-failed a { color: #ca3a11; }
+.jasmine_html-reporter .jasmine-summary li.jasmine-empty a { color: #ba9d37; }
+.jasmine_html-reporter .jasmine-summary li.jasmine-pending a { color: #ba9d37; }
+.jasmine_html-reporter .jasmine-summary li.jasmine-disabled a { color: #bababa; }
+.jasmine_html-reporter .jasmine-description + .jasmine-suite { margin-top: 0; }
+.jasmine_html-reporter .jasmine-suite { margin-top: 14px; }
+.jasmine_html-reporter .jasmine-suite a { color: #333; }
+.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail { margin-bottom: 28px; }
+.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description { background-color: #ca3a11; }
+.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description a { color: white; }
+.jasmine_html-reporter .jasmine-result-message { padding-top: 14px; color: #333; white-space: pre; }
+.jasmine_html-reporter .jasmine-result-message span.jasmine-result { display: block; }
+.jasmine_html-reporter .jasmine-stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666; border: 1px solid #ddd; background: white; white-space: pre; }
diff --git a/node_modules/svg.js/spec/lib/jasmine-2.6.0/jasmine.js b/node_modules/svg.js/spec/lib/jasmine-2.6.0/jasmine.js
new file mode 100644
index 0000000..57b5afe
--- /dev/null
+++ b/node_modules/svg.js/spec/lib/jasmine-2.6.0/jasmine.js
@@ -0,0 +1,4943 @@
+/*
+Copyright (c) 2008-2017 Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+var getJasmineRequireObj = (function (jasmineGlobal) {
+ var jasmineRequire;
+
+ if (typeof module !== 'undefined' && module.exports && typeof exports !== 'undefined') {
+ if (typeof global !== 'undefined') {
+ jasmineGlobal = global;
+ } else {
+ jasmineGlobal = {};
+ }
+ jasmineRequire = exports;
+ } else {
+ if (typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]') {
+ jasmineGlobal = window;
+ }
+ jasmineRequire = jasmineGlobal.jasmineRequire = jasmineGlobal.jasmineRequire || {};
+ }
+
+ function getJasmineRequire() {
+ return jasmineRequire;
+ }
+
+ getJasmineRequire().core = function(jRequire) {
+ var j$ = {};
+
+ jRequire.base(j$, jasmineGlobal);
+ j$.util = jRequire.util();
+ j$.errors = jRequire.errors();
+ j$.formatErrorMsg = jRequire.formatErrorMsg();
+ j$.Any = jRequire.Any(j$);
+ j$.Anything = jRequire.Anything(j$);
+ j$.CallTracker = jRequire.CallTracker(j$);
+ j$.MockDate = jRequire.MockDate();
+ j$.getClearStack = jRequire.clearStack(j$);
+ j$.Clock = jRequire.Clock();
+ j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler();
+ j$.Env = jRequire.Env(j$);
+ j$.ExceptionFormatter = jRequire.ExceptionFormatter();
+ j$.Expectation = jRequire.Expectation();
+ j$.buildExpectationResult = jRequire.buildExpectationResult();
+ j$.JsApiReporter = jRequire.JsApiReporter();
+ j$.matchersUtil = jRequire.matchersUtil(j$);
+ j$.ObjectContaining = jRequire.ObjectContaining(j$);
+ j$.ArrayContaining = jRequire.ArrayContaining(j$);
+ j$.pp = jRequire.pp(j$);
+ j$.QueueRunner = jRequire.QueueRunner(j$);
+ j$.ReportDispatcher = jRequire.ReportDispatcher();
+ j$.Spec = jRequire.Spec(j$);
+ j$.Spy = jRequire.Spy(j$);
+ j$.SpyRegistry = jRequire.SpyRegistry(j$);
+ j$.SpyStrategy = jRequire.SpyStrategy(j$);
+ j$.StringMatching = jRequire.StringMatching(j$);
+ j$.Suite = jRequire.Suite(j$);
+ j$.Timer = jRequire.Timer();
+ j$.TreeProcessor = jRequire.TreeProcessor();
+ j$.version = jRequire.version();
+ j$.Order = jRequire.Order();
+ j$.DiffBuilder = jRequire.DiffBuilder(j$);
+ j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$);
+ j$.ObjectPath = jRequire.ObjectPath(j$);
+ j$.GlobalErrors = jRequire.GlobalErrors(j$);
+
+ j$.matchers = jRequire.requireMatchers(jRequire, j$);
+
+ return j$;
+ };
+
+ return getJasmineRequire;
+})(this);
+
+getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
+ var availableMatchers = [
+ 'toBe',
+ 'toBeCloseTo',
+ 'toBeDefined',
+ 'toBeFalsy',
+ 'toBeGreaterThan',
+ 'toBeGreaterThanOrEqual',
+ 'toBeLessThan',
+ 'toBeLessThanOrEqual',
+ 'toBeNaN',
+ 'toBeNegativeInfinity',
+ 'toBeNull',
+ 'toBePositiveInfinity',
+ 'toBeTruthy',
+ 'toBeUndefined',
+ 'toContain',
+ 'toEqual',
+ 'toHaveBeenCalled',
+ 'toHaveBeenCalledBefore',
+ 'toHaveBeenCalledTimes',
+ 'toHaveBeenCalledWith',
+ 'toMatch',
+ 'toThrow',
+ 'toThrowError'
+ ],
+ matchers = {};
+
+ for (var i = 0; i < availableMatchers.length; i++) {
+ var name = availableMatchers[i];
+ matchers[name] = jRequire[name](j$);
+ }
+
+ return matchers;
+};
+
+getJasmineRequireObj().base = function(j$, jasmineGlobal) {
+ j$.unimplementedMethod_ = function() {
+ throw new Error('unimplemented method');
+ };
+
+ /**
+ * Maximum object depth the pretty printer will print to.
+ * Set this to a lower value to speed up pretty printing if you have large objects.
+ * @name jasmine.MAX_PRETTY_PRINT_DEPTH
+ */
+ j$.MAX_PRETTY_PRINT_DEPTH = 40;
+ /**
+ * Maximum number of array elements to display when pretty printing objects.
+ * Elements past this number will be ellipised.
+ * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH
+ */
+ j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 100;
+ /**
+ * Default number of milliseconds Jasmine will wait for an asynchronous spec to complete.
+ * @name jasmine.DEFAULT_TIMEOUT_INTERVAL
+ */
+ j$.DEFAULT_TIMEOUT_INTERVAL = 5000;
+
+ j$.getGlobal = function() {
+ return jasmineGlobal;
+ };
+
+ /**
+ * Get the currently booted Jasmine Environment.
+ *
+ * @name jasmine.getEnv
+ * @function
+ * @return {Env}
+ */
+ j$.getEnv = function(options) {
+ var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options);
+ //jasmine. singletons in here (setTimeout blah blah).
+ return env;
+ };
+
+ j$.isArray_ = function(value) {
+ return j$.isA_('Array', value);
+ };
+
+ j$.isObject_ = function(value) {
+ return !j$.util.isUndefined(value) && value !== null && j$.isA_('Object', value);
+ };
+
+ j$.isString_ = function(value) {
+ return j$.isA_('String', value);
+ };
+
+ j$.isNumber_ = function(value) {
+ return j$.isA_('Number', value);
+ };
+
+ j$.isFunction_ = function(value) {
+ return j$.isA_('Function', value);
+ };
+
+ j$.isA_ = function(typeName, value) {
+ return j$.getType_(value) === '[object ' + typeName + ']';
+ };
+
+ j$.getType_ = function(value) {
+ return Object.prototype.toString.apply(value);
+ };
+
+ j$.isDomNode = function(obj) {
+ return obj.nodeType > 0;
+ };
+
+ j$.fnNameFor = function(func) {
+ if (func.name) {
+ return func.name;
+ }
+
+ var matches = func.toString().match(/^\s*function\s*(\w*)\s*\(/);
+ return matches ? matches[1] : '';
+ };
+
+ /**
+ * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
+ * that will succeed if the actual value being compared is an instance of the specified class/constructor.
+ * @name jasmine.any
+ * @function
+ * @param {Constructor} clazz - The constructor to check against.
+ */
+ j$.any = function(clazz) {
+ return new j$.Any(clazz);
+ };
+
+ /**
+ * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
+ * that will succeed if the actual value being compared is not `null` and not `undefined`.
+ * @name jasmine.anything
+ * @function
+ */
+ j$.anything = function() {
+ return new j$.Anything();
+ };
+
+ /**
+ * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
+ * that will succeed if the actual value being compared contains at least the keys and values.
+ * @name jasmine.objectContaining
+ * @function
+ * @param {Object} sample - The subset of properties that _must_ be in the actual.
+ */
+ j$.objectContaining = function(sample) {
+ return new j$.ObjectContaining(sample);
+ };
+
+ /**
+ * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
+ * that will succeed if the actual value is a `String` that matches the `RegExp` or `String`.
+ * @name jasmine.stringMatching
+ * @function
+ * @param {RegExp|String} expected
+ */
+ j$.stringMatching = function(expected) {
+ return new j$.StringMatching(expected);
+ };
+
+ /**
+ * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
+ * that will succeed if the actual value is an `Array` that contains at least the elements in the sample.
+ * @name jasmine.arrayContaining
+ * @function
+ * @param {Array} sample
+ */
+ j$.arrayContaining = function(sample) {
+ return new j$.ArrayContaining(sample);
+ };
+
+ /**
+ * Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it.
+ * @name jasmine.createSpy
+ * @function
+ * @param {String} [name] - Name to give the spy. This will be displayed in failure messages.
+ * @param {Function} [originalFn] - Function to act as the real implementation.
+ * @return {Spy}
+ */
+ j$.createSpy = function(name, originalFn) {
+ return j$.Spy(name, originalFn);
+ };
+
+ j$.isSpy = function(putativeSpy) {
+ if (!putativeSpy) {
+ return false;
+ }
+ return putativeSpy.and instanceof j$.SpyStrategy &&
+ putativeSpy.calls instanceof j$.CallTracker;
+ };
+
+ /**
+ * Create an object with multiple {@link Spy}s as its members.
+ * @name jasmine.createSpyObj
+ * @function
+ * @param {String} [baseName] - Base name for the spies in the object.
+ * @param {String[]|Object} methodNames - Array of method names to create spies for, or Object whose keys will be method names and values the {@link Spy#and#returnValue|returnValue}.
+ * @return {Object}
+ */
+ j$.createSpyObj = function(baseName, methodNames) {
+ var baseNameIsCollection = j$.isObject_(baseName) || j$.isArray_(baseName);
+
+ if (baseNameIsCollection && j$.util.isUndefined(methodNames)) {
+ methodNames = baseName;
+ baseName = 'unknown';
+ }
+
+ var obj = {};
+ var spiesWereSet = false;
+
+ if (j$.isArray_(methodNames)) {
+ for (var i = 0; i < methodNames.length; i++) {
+ obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]);
+ spiesWereSet = true;
+ }
+ } else if (j$.isObject_(methodNames)) {
+ for (var key in methodNames) {
+ if (methodNames.hasOwnProperty(key)) {
+ obj[key] = j$.createSpy(baseName + '.' + key);
+ obj[key].and.returnValue(methodNames[key]);
+ spiesWereSet = true;
+ }
+ }
+ }
+
+ if (!spiesWereSet) {
+ throw 'createSpyObj requires a non-empty array or object of method names to create spies for';
+ }
+
+ return obj;
+ };
+};
+
+getJasmineRequireObj().util = function() {
+
+ var util = {};
+
+ util.inherit = function(childClass, parentClass) {
+ var Subclass = function() {
+ };
+ Subclass.prototype = parentClass.prototype;
+ childClass.prototype = new Subclass();
+ };
+
+ util.htmlEscape = function(str) {
+ if (!str) {
+ return str;
+ }
+ return str.replace(/&/g, '&')
+ .replace(//g, '>');
+ };
+
+ util.argsToArray = function(args) {
+ var arrayOfArgs = [];
+ for (var i = 0; i < args.length; i++) {
+ arrayOfArgs.push(args[i]);
+ }
+ return arrayOfArgs;
+ };
+
+ util.isUndefined = function(obj) {
+ return obj === void 0;
+ };
+
+ util.arrayContains = function(array, search) {
+ var i = array.length;
+ while (i--) {
+ if (array[i] === search) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ util.clone = function(obj) {
+ if (Object.prototype.toString.apply(obj) === '[object Array]') {
+ return obj.slice();
+ }
+
+ var cloned = {};
+ for (var prop in obj) {
+ if (obj.hasOwnProperty(prop)) {
+ cloned[prop] = obj[prop];
+ }
+ }
+
+ return cloned;
+ };
+
+ util.getPropertyDescriptor = function(obj, methodName) {
+ var descriptor,
+ proto = obj;
+
+ do {
+ descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
+ proto = Object.getPrototypeOf(proto);
+ } while (!descriptor && proto);
+
+ return descriptor;
+ };
+
+ util.objectDifference = function(obj, toRemove) {
+ var diff = {};
+
+ for (var key in obj) {
+ if (util.has(obj, key) && !util.has(toRemove, key)) {
+ diff[key] = obj[key];
+ }
+ }
+
+ return diff;
+ };
+
+ util.has = function(obj, key) {
+ return Object.prototype.hasOwnProperty.call(obj, key);
+ };
+
+ return util;
+};
+
+getJasmineRequireObj().Spec = function(j$) {
+ function Spec(attrs) {
+ this.expectationFactory = attrs.expectationFactory;
+ this.resultCallback = attrs.resultCallback || function() {};
+ this.id = attrs.id;
+ this.description = attrs.description || '';
+ this.queueableFn = attrs.queueableFn;
+ this.beforeAndAfterFns = attrs.beforeAndAfterFns || function() { return {befores: [], afters: []}; };
+ this.userContext = attrs.userContext || function() { return {}; };
+ this.onStart = attrs.onStart || function() {};
+ this.getSpecName = attrs.getSpecName || function() { return ''; };
+ this.expectationResultFactory = attrs.expectationResultFactory || function() { };
+ this.queueRunnerFactory = attrs.queueRunnerFactory || function() {};
+ this.catchingExceptions = attrs.catchingExceptions || function() { return true; };
+ this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
+
+ if (!this.queueableFn.fn) {
+ this.pend();
+ }
+
+ this.result = {
+ id: this.id,
+ description: this.description,
+ fullName: this.getFullName(),
+ failedExpectations: [],
+ passedExpectations: [],
+ pendingReason: ''
+ };
+ }
+
+ Spec.prototype.addExpectationResult = function(passed, data, isError) {
+ var expectationResult = this.expectationResultFactory(data);
+ if (passed) {
+ this.result.passedExpectations.push(expectationResult);
+ } else {
+ this.result.failedExpectations.push(expectationResult);
+
+ if (this.throwOnExpectationFailure && !isError) {
+ throw new j$.errors.ExpectationFailed();
+ }
+ }
+ };
+
+ Spec.prototype.expect = function(actual) {
+ return this.expectationFactory(actual, this);
+ };
+
+ Spec.prototype.execute = function(onComplete, enabled) {
+ var self = this;
+
+ this.onStart(this);
+
+ if (!this.isExecutable() || this.markedPending || enabled === false) {
+ complete(enabled);
+ return;
+ }
+
+ var fns = this.beforeAndAfterFns();
+ var allFns = fns.befores.concat(this.queueableFn).concat(fns.afters);
+
+ this.queueRunnerFactory({
+ queueableFns: allFns,
+ onException: function() { self.onException.apply(self, arguments); },
+ onComplete: complete,
+ userContext: this.userContext()
+ });
+
+ function complete(enabledAgain) {
+ self.result.status = self.status(enabledAgain);
+ self.resultCallback(self.result);
+
+ if (onComplete) {
+ onComplete();
+ }
+ }
+ };
+
+ Spec.prototype.onException = function onException(e) {
+ if (Spec.isPendingSpecException(e)) {
+ this.pend(extractCustomPendingMessage(e));
+ return;
+ }
+
+ if (e instanceof j$.errors.ExpectationFailed) {
+ return;
+ }
+
+ this.addExpectationResult(false, {
+ matcherName: '',
+ passed: false,
+ expected: '',
+ actual: '',
+ error: e
+ }, true);
+ };
+
+ Spec.prototype.disable = function() {
+ this.disabled = true;
+ };
+
+ Spec.prototype.pend = function(message) {
+ this.markedPending = true;
+ if (message) {
+ this.result.pendingReason = message;
+ }
+ };
+
+ Spec.prototype.getResult = function() {
+ this.result.status = this.status();
+ return this.result;
+ };
+
+ Spec.prototype.status = function(enabled) {
+ if (this.disabled || enabled === false) {
+ return 'disabled';
+ }
+
+ if (this.markedPending) {
+ return 'pending';
+ }
+
+ if (this.result.failedExpectations.length > 0) {
+ return 'failed';
+ } else {
+ return 'passed';
+ }
+ };
+
+ Spec.prototype.isExecutable = function() {
+ return !this.disabled;
+ };
+
+ Spec.prototype.getFullName = function() {
+ return this.getSpecName(this);
+ };
+
+ var extractCustomPendingMessage = function(e) {
+ var fullMessage = e.toString(),
+ boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage),
+ boilerplateEnd = boilerplateStart + Spec.pendingSpecExceptionMessage.length;
+
+ return fullMessage.substr(boilerplateEnd);
+ };
+
+ Spec.pendingSpecExceptionMessage = '=> marked Pending';
+
+ Spec.isPendingSpecException = function(e) {
+ return !!(e && e.toString && e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1);
+ };
+
+ return Spec;
+};
+
+if (typeof window == void 0 && typeof exports == 'object') {
+ exports.Spec = jasmineRequire.Spec;
+}
+
+/*jshint bitwise: false*/
+
+getJasmineRequireObj().Order = function() {
+ function Order(options) {
+ this.random = 'random' in options ? options.random : true;
+ var seed = this.seed = options.seed || generateSeed();
+ this.sort = this.random ? randomOrder : naturalOrder;
+
+ function naturalOrder(items) {
+ return items;
+ }
+
+ function randomOrder(items) {
+ var copy = items.slice();
+ copy.sort(function(a, b) {
+ return jenkinsHash(seed + a.id) - jenkinsHash(seed + b.id);
+ });
+ return copy;
+ }
+
+ function generateSeed() {
+ return String(Math.random()).slice(-5);
+ }
+
+ // Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function
+ // used to get a different output when the key changes slighly.
+ // We use your return to sort the children randomly in a consistent way when
+ // used in conjunction with a seed
+
+ function jenkinsHash(key) {
+ var hash, i;
+ for(hash = i = 0; i < key.length; ++i) {
+ hash += key.charCodeAt(i);
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
+ }
+
+ }
+
+ return Order;
+};
+
+getJasmineRequireObj().Env = function(j$) {
+ /**
+ * _Note:_ Do not construct this directly, Jasmine will make one during booting.
+ * @name Env
+ * @classdesc The Jasmine environment
+ * @constructor
+ */
+ function Env(options) {
+ options = options || {};
+
+ var self = this;
+ var global = options.global || j$.getGlobal();
+
+ var totalSpecsDefined = 0;
+
+ var catchExceptions = true;
+
+ var realSetTimeout = j$.getGlobal().setTimeout;
+ var realClearTimeout = j$.getGlobal().clearTimeout;
+ var clearStack = j$.getClearStack(j$.getGlobal());
+ this.clock = new j$.Clock(global, function () { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global));
+
+ var runnableResources = {};
+
+ var currentSpec = null;
+ var currentlyExecutingSuites = [];
+ var currentDeclarationSuite = null;
+ var throwOnExpectationFailure = false;
+ var random = false;
+ var seed = null;
+
+ var currentSuite = function() {
+ return currentlyExecutingSuites[currentlyExecutingSuites.length - 1];
+ };
+
+ var currentRunnable = function() {
+ return currentSpec || currentSuite();
+ };
+
+ var reporter = new j$.ReportDispatcher([
+ 'jasmineStarted',
+ 'jasmineDone',
+ 'suiteStarted',
+ 'suiteDone',
+ 'specStarted',
+ 'specDone'
+ ]);
+
+ var globalErrors = new j$.GlobalErrors();
+
+ this.specFilter = function() {
+ return true;
+ };
+
+ this.addCustomEqualityTester = function(tester) {
+ if(!currentRunnable()) {
+ throw new Error('Custom Equalities must be added in a before function or a spec');
+ }
+ runnableResources[currentRunnable().id].customEqualityTesters.push(tester);
+ };
+
+ this.addMatchers = function(matchersToAdd) {
+ if(!currentRunnable()) {
+ throw new Error('Matchers must be added in a before function or a spec');
+ }
+ var customMatchers = runnableResources[currentRunnable().id].customMatchers;
+ for (var matcherName in matchersToAdd) {
+ customMatchers[matcherName] = matchersToAdd[matcherName];
+ }
+ };
+
+ j$.Expectation.addCoreMatchers(j$.matchers);
+
+ var nextSpecId = 0;
+ var getNextSpecId = function() {
+ return 'spec' + nextSpecId++;
+ };
+
+ var nextSuiteId = 0;
+ var getNextSuiteId = function() {
+ return 'suite' + nextSuiteId++;
+ };
+
+ var expectationFactory = function(actual, spec) {
+ return j$.Expectation.Factory({
+ util: j$.matchersUtil,
+ customEqualityTesters: runnableResources[spec.id].customEqualityTesters,
+ customMatchers: runnableResources[spec.id].customMatchers,
+ actual: actual,
+ addExpectationResult: addExpectationResult
+ });
+
+ function addExpectationResult(passed, result) {
+ return spec.addExpectationResult(passed, result);
+ }
+ };
+
+ var defaultResourcesForRunnable = function(id, parentRunnableId) {
+ var resources = {spies: [], customEqualityTesters: [], customMatchers: {}};
+
+ if(runnableResources[parentRunnableId]){
+ resources.customEqualityTesters = j$.util.clone(runnableResources[parentRunnableId].customEqualityTesters);
+ resources.customMatchers = j$.util.clone(runnableResources[parentRunnableId].customMatchers);
+ }
+
+ runnableResources[id] = resources;
+ };
+
+ var clearResourcesForRunnable = function(id) {
+ spyRegistry.clearSpies();
+ delete runnableResources[id];
+ };
+
+ var beforeAndAfterFns = function(suite) {
+ return function() {
+ var befores = [],
+ afters = [];
+
+ while(suite) {
+ befores = befores.concat(suite.beforeFns);
+ afters = afters.concat(suite.afterFns);
+
+ suite = suite.parentSuite;
+ }
+
+ return {
+ befores: befores.reverse(),
+ afters: afters
+ };
+ };
+ };
+
+ var getSpecName = function(spec, suite) {
+ var fullName = [spec.description],
+ suiteFullName = suite.getFullName();
+
+ if (suiteFullName !== '') {
+ fullName.unshift(suiteFullName);
+ }
+ return fullName.join(' ');
+ };
+
+ // TODO: we may just be able to pass in the fn instead of wrapping here
+ var buildExpectationResult = j$.buildExpectationResult,
+ exceptionFormatter = new j$.ExceptionFormatter(),
+ expectationResultFactory = function(attrs) {
+ attrs.messageFormatter = exceptionFormatter.message;
+ attrs.stackFormatter = exceptionFormatter.stack;
+
+ return buildExpectationResult(attrs);
+ };
+
+ // TODO: fix this naming, and here's where the value comes in
+ this.catchExceptions = function(value) {
+ catchExceptions = !!value;
+ return catchExceptions;
+ };
+
+ this.catchingExceptions = function() {
+ return catchExceptions;
+ };
+
+ var maximumSpecCallbackDepth = 20;
+ var currentSpecCallbackDepth = 0;
+
+ var catchException = function(e) {
+ return j$.Spec.isPendingSpecException(e) || catchExceptions;
+ };
+
+ this.throwOnExpectationFailure = function(value) {
+ throwOnExpectationFailure = !!value;
+ };
+
+ this.throwingExpectationFailures = function() {
+ return throwOnExpectationFailure;
+ };
+
+ this.randomizeTests = function(value) {
+ random = !!value;
+ };
+
+ this.randomTests = function() {
+ return random;
+ };
+
+ this.seed = function(value) {
+ if (value) {
+ seed = value;
+ }
+ return seed;
+ };
+
+ var queueRunnerFactory = function(options) {
+ options.catchException = catchException;
+ options.clearStack = options.clearStack || clearStack;
+ options.timeout = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout};
+ options.fail = self.fail;
+ options.globalErrors = globalErrors;
+
+ new j$.QueueRunner(options).execute();
+ };
+
+ var topSuite = new j$.Suite({
+ env: this,
+ id: getNextSuiteId(),
+ description: 'Jasmine__TopLevel__Suite',
+ expectationFactory: expectationFactory,
+ expectationResultFactory: expectationResultFactory
+ });
+ defaultResourcesForRunnable(topSuite.id);
+ currentDeclarationSuite = topSuite;
+
+ this.topSuite = function() {
+ return topSuite;
+ };
+
+ this.execute = function(runnablesToRun) {
+ if(!runnablesToRun) {
+ if (focusedRunnables.length) {
+ runnablesToRun = focusedRunnables;
+ } else {
+ runnablesToRun = [topSuite.id];
+ }
+ }
+
+ var order = new j$.Order({
+ random: random,
+ seed: seed
+ });
+
+ var processor = new j$.TreeProcessor({
+ tree: topSuite,
+ runnableIds: runnablesToRun,
+ queueRunnerFactory: queueRunnerFactory,
+ nodeStart: function(suite) {
+ currentlyExecutingSuites.push(suite);
+ defaultResourcesForRunnable(suite.id, suite.parentSuite.id);
+ reporter.suiteStarted(suite.result);
+ },
+ nodeComplete: function(suite, result) {
+ if (!suite.markedPending) {
+ clearResourcesForRunnable(suite.id);
+ }
+ currentlyExecutingSuites.pop();
+ reporter.suiteDone(result);
+ },
+ orderChildren: function(node) {
+ return order.sort(node.children);
+ }
+ });
+
+ if(!processor.processTree().valid) {
+ throw new Error('Invalid order: would cause a beforeAll or afterAll to be run multiple times');
+ }
+
+ reporter.jasmineStarted({
+ totalSpecsDefined: totalSpecsDefined
+ });
+
+ currentlyExecutingSuites.push(topSuite);
+
+ globalErrors.install();
+ processor.execute(function() {
+ clearResourcesForRunnable(topSuite.id);
+ currentlyExecutingSuites.pop();
+ globalErrors.uninstall();
+
+ reporter.jasmineDone({
+ order: order,
+ failedExpectations: topSuite.result.failedExpectations
+ });
+ });
+ };
+
+ /**
+ * Add a custom reporter to the Jasmine environment.
+ * @name Env#addReporter
+ * @function
+ * @see custom_reporter
+ */
+ this.addReporter = function(reporterToAdd) {
+ reporter.addReporter(reporterToAdd);
+ };
+
+ this.provideFallbackReporter = function(reporterToAdd) {
+ reporter.provideFallbackReporter(reporterToAdd);
+ };
+
+ this.clearReporters = function() {
+ reporter.clearReporters();
+ };
+
+ var spyRegistry = new j$.SpyRegistry({currentSpies: function() {
+ if(!currentRunnable()) {
+ throw new Error('Spies must be created in a before function or a spec');
+ }
+ return runnableResources[currentRunnable().id].spies;
+ }});
+
+ this.allowRespy = function(allow){
+ spyRegistry.allowRespy(allow);
+ };
+
+ this.spyOn = function() {
+ return spyRegistry.spyOn.apply(spyRegistry, arguments);
+ };
+
+ this.spyOnProperty = function() {
+ return spyRegistry.spyOnProperty.apply(spyRegistry, arguments);
+ };
+
+ var ensureIsFunction = function(fn, caller) {
+ if (!j$.isFunction_(fn)) {
+ throw new Error(caller + ' expects a function argument; received ' + j$.getType_(fn));
+ }
+ };
+
+ var suiteFactory = function(description) {
+ var suite = new j$.Suite({
+ env: self,
+ id: getNextSuiteId(),
+ description: description,
+ parentSuite: currentDeclarationSuite,
+ expectationFactory: expectationFactory,
+ expectationResultFactory: expectationResultFactory,
+ throwOnExpectationFailure: throwOnExpectationFailure
+ });
+
+ return suite;
+ };
+
+ this.describe = function(description, specDefinitions) {
+ ensureIsFunction(specDefinitions, 'describe');
+ var suite = suiteFactory(description);
+ if (specDefinitions.length > 0) {
+ throw new Error('describe does not expect any arguments');
+ }
+ if (currentDeclarationSuite.markedPending) {
+ suite.pend();
+ }
+ addSpecsToSuite(suite, specDefinitions);
+ return suite;
+ };
+
+ this.xdescribe = function(description, specDefinitions) {
+ ensureIsFunction(specDefinitions, 'xdescribe');
+ var suite = suiteFactory(description);
+ suite.pend();
+ addSpecsToSuite(suite, specDefinitions);
+ return suite;
+ };
+
+ var focusedRunnables = [];
+
+ this.fdescribe = function(description, specDefinitions) {
+ ensureIsFunction(specDefinitions, 'fdescribe');
+ var suite = suiteFactory(description);
+ suite.isFocused = true;
+
+ focusedRunnables.push(suite.id);
+ unfocusAncestor();
+ addSpecsToSuite(suite, specDefinitions);
+
+ return suite;
+ };
+
+ function addSpecsToSuite(suite, specDefinitions) {
+ var parentSuite = currentDeclarationSuite;
+ parentSuite.addChild(suite);
+ currentDeclarationSuite = suite;
+
+ var declarationError = null;
+ try {
+ specDefinitions.call(suite);
+ } catch (e) {
+ declarationError = e;
+ }
+
+ if (declarationError) {
+ self.it('encountered a declaration exception', function() {
+ throw declarationError;
+ });
+ }
+
+ currentDeclarationSuite = parentSuite;
+ }
+
+ function findFocusedAncestor(suite) {
+ while (suite) {
+ if (suite.isFocused) {
+ return suite.id;
+ }
+ suite = suite.parentSuite;
+ }
+
+ return null;
+ }
+
+ function unfocusAncestor() {
+ var focusedAncestor = findFocusedAncestor(currentDeclarationSuite);
+ if (focusedAncestor) {
+ for (var i = 0; i < focusedRunnables.length; i++) {
+ if (focusedRunnables[i] === focusedAncestor) {
+ focusedRunnables.splice(i, 1);
+ break;
+ }
+ }
+ }
+ }
+
+ var specFactory = function(description, fn, suite, timeout) {
+ totalSpecsDefined++;
+ var spec = new j$.Spec({
+ id: getNextSpecId(),
+ beforeAndAfterFns: beforeAndAfterFns(suite),
+ expectationFactory: expectationFactory,
+ resultCallback: specResultCallback,
+ getSpecName: function(spec) {
+ return getSpecName(spec, suite);
+ },
+ onStart: specStarted,
+ description: description,
+ expectationResultFactory: expectationResultFactory,
+ queueRunnerFactory: queueRunnerFactory,
+ userContext: function() { return suite.clonedSharedUserContext(); },
+ queueableFn: {
+ fn: fn,
+ timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
+ },
+ throwOnExpectationFailure: throwOnExpectationFailure
+ });
+
+ if (!self.specFilter(spec)) {
+ spec.disable();
+ }
+
+ return spec;
+
+ function specResultCallback(result) {
+ clearResourcesForRunnable(spec.id);
+ currentSpec = null;
+ reporter.specDone(result);
+ }
+
+ function specStarted(spec) {
+ currentSpec = spec;
+ defaultResourcesForRunnable(spec.id, suite.id);
+ reporter.specStarted(spec.result);
+ }
+ };
+
+ this.it = function(description, fn, timeout) {
+ // it() sometimes doesn't have a fn argument, so only check the type if
+ // it's given.
+ if (arguments.length > 1) {
+ ensureIsFunction(fn, 'it');
+ }
+ var spec = specFactory(description, fn, currentDeclarationSuite, timeout);
+ if (currentDeclarationSuite.markedPending) {
+ spec.pend();
+ }
+ currentDeclarationSuite.addChild(spec);
+ return spec;
+ };
+
+ this.xit = function(description, fn, timeout) {
+ // xit(), like it(), doesn't always have a fn argument, so only check the
+ // type when needed.
+ if (arguments.length > 1) {
+ ensureIsFunction(fn, 'xit');
+ }
+ var spec = this.it.apply(this, arguments);
+ spec.pend('Temporarily disabled with xit');
+ return spec;
+ };
+
+ this.fit = function(description, fn, timeout){
+ ensureIsFunction(fn, 'fit');
+ var spec = specFactory(description, fn, currentDeclarationSuite, timeout);
+ currentDeclarationSuite.addChild(spec);
+ focusedRunnables.push(spec.id);
+ unfocusAncestor();
+ return spec;
+ };
+
+ this.expect = function(actual) {
+ if (!currentRunnable()) {
+ throw new Error('\'expect\' was used when there was no current spec, this could be because an asynchronous test timed out');
+ }
+
+ return currentRunnable().expect(actual);
+ };
+
+ this.beforeEach = function(beforeEachFunction, timeout) {
+ ensureIsFunction(beforeEachFunction, 'beforeEach');
+ currentDeclarationSuite.beforeEach({
+ fn: beforeEachFunction,
+ timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
+ });
+ };
+
+ this.beforeAll = function(beforeAllFunction, timeout) {
+ ensureIsFunction(beforeAllFunction, 'beforeAll');
+ currentDeclarationSuite.beforeAll({
+ fn: beforeAllFunction,
+ timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
+ });
+ };
+
+ this.afterEach = function(afterEachFunction, timeout) {
+ ensureIsFunction(afterEachFunction, 'afterEach');
+ currentDeclarationSuite.afterEach({
+ fn: afterEachFunction,
+ timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
+ });
+ };
+
+ this.afterAll = function(afterAllFunction, timeout) {
+ ensureIsFunction(afterAllFunction, 'afterAll');
+ currentDeclarationSuite.afterAll({
+ fn: afterAllFunction,
+ timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
+ });
+ };
+
+ this.pending = function(message) {
+ var fullMessage = j$.Spec.pendingSpecExceptionMessage;
+ if(message) {
+ fullMessage += message;
+ }
+ throw fullMessage;
+ };
+
+ this.fail = function(error) {
+ if (!currentRunnable()) {
+ throw new Error('\'fail\' was used when there was no current spec, this could be because an asynchronous test timed out');
+ }
+
+ var message = 'Failed';
+ if (error) {
+ message += ': ';
+ if (error.message) {
+ message += error.message;
+ } else if (jasmine.isString_(error)) {
+ message += error;
+ } else {
+ // pretty print all kind of objects. This includes arrays.
+ message += jasmine.pp(error);
+ }
+ }
+
+ currentRunnable().addExpectationResult(false, {
+ matcherName: '',
+ passed: false,
+ expected: '',
+ actual: '',
+ message: message,
+ error: error && error.message ? error : null
+ });
+ };
+ }
+
+ return Env;
+};
+
+getJasmineRequireObj().JsApiReporter = function() {
+
+ var noopTimer = {
+ start: function(){},
+ elapsed: function(){ return 0; }
+ };
+
+ /**
+ * _Note:_ Do not construct this directly, use the global `jsApiReporter` to retrieve the instantiated object.
+ *
+ * @name jsApiReporter
+ * @classdesc Reporter added by default in `boot.js` to record results for retrieval in javascript code.
+ * @class
+ */
+ function JsApiReporter(options) {
+ var timer = options.timer || noopTimer,
+ status = 'loaded';
+
+ this.started = false;
+ this.finished = false;
+ this.runDetails = {};
+
+ this.jasmineStarted = function() {
+ this.started = true;
+ status = 'started';
+ timer.start();
+ };
+
+ var executionTime;
+
+ this.jasmineDone = function(runDetails) {
+ this.finished = true;
+ this.runDetails = runDetails;
+ executionTime = timer.elapsed();
+ status = 'done';
+ };
+
+ /**
+ * Get the current status for the Jasmine environment.
+ * @name jsApiReporter#status
+ * @function
+ * @return {String} - One of `loaded`, `started`, or `done`
+ */
+ this.status = function() {
+ return status;
+ };
+
+ var suites = [],
+ suites_hash = {};
+
+ this.suiteStarted = function(result) {
+ suites_hash[result.id] = result;
+ };
+
+ this.suiteDone = function(result) {
+ storeSuite(result);
+ };
+
+ /**
+ * Get the results for a set of suites.
+ *
+ * Retrievable in slices for easier serialization.
+ * @name jsApiReporter#suiteResults
+ * @function
+ * @param {Number} index - The position in the suites list to start from.
+ * @param {Number} length - Maximum number of suite results to return.
+ * @return {Object[]}
+ */
+ this.suiteResults = function(index, length) {
+ return suites.slice(index, index + length);
+ };
+
+ function storeSuite(result) {
+ suites.push(result);
+ suites_hash[result.id] = result;
+ }
+
+ /**
+ * Get all of the suites in a single object, with their `id` as the key.
+ * @name jsApiReporter#suites
+ * @function
+ * @return {Object}
+ */
+ this.suites = function() {
+ return suites_hash;
+ };
+
+ var specs = [];
+
+ this.specDone = function(result) {
+ specs.push(result);
+ };
+
+ /**
+ * Get the results for a set of specs.
+ *
+ * Retrievable in slices for easier serialization.
+ * @name jsApiReporter#specResults
+ * @function
+ * @param {Number} index - The position in the specs list to start from.
+ * @param {Number} length - Maximum number of specs results to return.
+ * @return {Object[]}
+ */
+ this.specResults = function(index, length) {
+ return specs.slice(index, index + length);
+ };
+
+ /**
+ * Get all spec results.
+ * @name jsApiReporter#specs
+ * @function
+ * @return {Object[]}
+ */
+ this.specs = function() {
+ return specs;
+ };
+
+ /**
+ * Get the number of milliseconds it took for the full Jasmine suite to run.
+ * @name jsApiReporter#executionTime
+ * @function
+ * @return {Number}
+ */
+ this.executionTime = function() {
+ return executionTime;
+ };
+
+ }
+
+ return JsApiReporter;
+};
+
+getJasmineRequireObj().Any = function(j$) {
+
+ function Any(expectedObject) {
+ if (typeof expectedObject === 'undefined') {
+ throw new TypeError(
+ 'jasmine.any() expects to be passed a constructor function. ' +
+ 'Please pass one or use jasmine.anything() to match any object.'
+ );
+ }
+ this.expectedObject = expectedObject;
+ }
+
+ Any.prototype.asymmetricMatch = function(other) {
+ if (this.expectedObject == String) {
+ return typeof other == 'string' || other instanceof String;
+ }
+
+ if (this.expectedObject == Number) {
+ return typeof other == 'number' || other instanceof Number;
+ }
+
+ if (this.expectedObject == Function) {
+ return typeof other == 'function' || other instanceof Function;
+ }
+
+ if (this.expectedObject == Object) {
+ return typeof other == 'object';
+ }
+
+ if (this.expectedObject == Boolean) {
+ return typeof other == 'boolean';
+ }
+
+ return other instanceof this.expectedObject;
+ };
+
+ Any.prototype.jasmineToString = function() {
+ return '';
+ };
+
+ return Any;
+};
+
+getJasmineRequireObj().Anything = function(j$) {
+
+ function Anything() {}
+
+ Anything.prototype.asymmetricMatch = function(other) {
+ return !j$.util.isUndefined(other) && other !== null;
+ };
+
+ Anything.prototype.jasmineToString = function() {
+ return '';
+ };
+
+ return Anything;
+};
+
+getJasmineRequireObj().ArrayContaining = function(j$) {
+ function ArrayContaining(sample) {
+ this.sample = sample;
+ }
+
+ ArrayContaining.prototype.asymmetricMatch = function(other, customTesters) {
+ var className = Object.prototype.toString.call(this.sample);
+ if (className !== '[object Array]') { throw new Error('You must provide an array to arrayContaining, not \'' + this.sample + '\'.'); }
+
+ for (var i = 0; i < this.sample.length; i++) {
+ var item = this.sample[i];
+ if (!j$.matchersUtil.contains(other, item, customTesters)) {
+ return false;
+ }
+ }
+
+ return true;
+ };
+
+ ArrayContaining.prototype.jasmineToString = function () {
+ return '';
+ };
+
+ return ArrayContaining;
+};
+
+getJasmineRequireObj().ObjectContaining = function(j$) {
+
+ function ObjectContaining(sample) {
+ this.sample = sample;
+ }
+
+ function getPrototype(obj) {
+ if (Object.getPrototypeOf) {
+ return Object.getPrototypeOf(obj);
+ }
+
+ if (obj.constructor.prototype == obj) {
+ return null;
+ }
+
+ return obj.constructor.prototype;
+ }
+
+ function hasProperty(obj, property) {
+ if (!obj) {
+ return false;
+ }
+
+ if (Object.prototype.hasOwnProperty.call(obj, property)) {
+ return true;
+ }
+
+ return hasProperty(getPrototype(obj), property);
+ }
+
+ ObjectContaining.prototype.asymmetricMatch = function(other, customTesters) {
+ if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); }
+
+ for (var property in this.sample) {
+ if (!hasProperty(other, property) ||
+ !j$.matchersUtil.equals(this.sample[property], other[property], customTesters)) {
+ return false;
+ }
+ }
+
+ return true;
+ };
+
+ ObjectContaining.prototype.jasmineToString = function() {
+ return '';
+ };
+
+ return ObjectContaining;
+};
+
+getJasmineRequireObj().StringMatching = function(j$) {
+
+ function StringMatching(expected) {
+ if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) {
+ throw new Error('Expected is not a String or a RegExp');
+ }
+
+ this.regexp = new RegExp(expected);
+ }
+
+ StringMatching.prototype.asymmetricMatch = function(other) {
+ return this.regexp.test(other);
+ };
+
+ StringMatching.prototype.jasmineToString = function() {
+ return '';
+ };
+
+ return StringMatching;
+};
+
+getJasmineRequireObj().CallTracker = function(j$) {
+
+ /**
+ * @namespace Spy#calls
+ */
+ function CallTracker() {
+ var calls = [];
+ var opts = {};
+
+ function argCloner(context) {
+ var clonedArgs = [];
+ var argsAsArray = j$.util.argsToArray(context.args);
+ for(var i = 0; i < argsAsArray.length; i++) {
+ if(Object.prototype.toString.apply(argsAsArray[i]).match(/^\[object/)) {
+ clonedArgs.push(j$.util.clone(argsAsArray[i]));
+ } else {
+ clonedArgs.push(argsAsArray[i]);
+ }
+ }
+ context.args = clonedArgs;
+ }
+
+ this.track = function(context) {
+ if(opts.cloneArgs) {
+ argCloner(context);
+ }
+ calls.push(context);
+ };
+
+ /**
+ * Check whether this spy has been invoked.
+ * @name Spy#calls#any
+ * @function
+ * @return {Boolean}
+ */
+ this.any = function() {
+ return !!calls.length;
+ };
+
+ /**
+ * Get the number of invocations of this spy.
+ * @name Spy#calls#count
+ * @function
+ * @return {Integer}
+ */
+ this.count = function() {
+ return calls.length;
+ };
+
+ /**
+ * Get the arguments that were passed to a specific invocation of this spy.
+ * @name Spy#calls#argsFor
+ * @function
+ * @param {Integer} index The 0-based invocation index.
+ * @return {Array}
+ */
+ this.argsFor = function(index) {
+ var call = calls[index];
+ return call ? call.args : [];
+ };
+
+ /**
+ * Get the raw calls array for this spy.
+ * @name Spy#calls#all
+ * @function
+ * @return {Spy.callData[]}
+ */
+ this.all = function() {
+ return calls;
+ };
+
+ /**
+ * Get all of the arguments for each invocation of this spy in the order they were received.
+ * @name Spy#calls#allArgs
+ * @function
+ * @return {Array}
+ */
+ this.allArgs = function() {
+ var callArgs = [];
+ for(var i = 0; i < calls.length; i++){
+ callArgs.push(calls[i].args);
+ }
+
+ return callArgs;
+ };
+
+ /**
+ * Get the first invocation of this spy.
+ * @name Spy#calls#first
+ * @function
+ * @return {ObjecSpy.callData}
+ */
+ this.first = function() {
+ return calls[0];
+ };
+
+ /**
+ * Get the most recent invocation of this spy.
+ * @name Spy#calls#mostRecent
+ * @function
+ * @return {ObjecSpy.callData}
+ */
+ this.mostRecent = function() {
+ return calls[calls.length - 1];
+ };
+
+ /**
+ * Reset this spy as if it has never been called.
+ * @name Spy#calls#reset
+ * @function
+ */
+ this.reset = function() {
+ calls = [];
+ };
+
+ /**
+ * Set this spy to do a shallow clone of arguments passed to each invocation.
+ * @name Spy#calls#saveArgumentsByValue
+ * @function
+ */
+ this.saveArgumentsByValue = function() {
+ opts.cloneArgs = true;
+ };
+
+ }
+
+ return CallTracker;
+};
+
+getJasmineRequireObj().clearStack = function(j$) {
+ function messageChannelImpl(global) {
+ var channel = new global.MessageChannel(),
+ head = {},
+ tail = head;
+
+ channel.port1.onmessage = function() {
+ head = head.next;
+ var task = head.task;
+ delete head.task;
+ task();
+ };
+
+ return function clearStack(fn) {
+ tail = tail.next = { task: fn };
+ channel.port2.postMessage(0);
+ };
+ }
+
+ function getClearStack(global) {
+ if (global && global.process && j$.isFunction_(global.process.nextTick)) {
+ return global.process.nextTick;
+ } else if (j$.isFunction_(global.setImmediate)) {
+ var realSetImmediate = global.setImmediate;
+ return function(fn) {
+ realSetImmediate(fn);
+ };
+ } else if (!j$.util.isUndefined(global.MessageChannel)) {
+ return messageChannelImpl(global);
+ } else {
+ var realSetTimeout = global.setTimeout;
+ return function clearStack(fn) {
+ Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
+ };
+ }
+ }
+
+ return getClearStack;
+};
+
+getJasmineRequireObj().Clock = function() {
+ /**
+ * _Note:_ Do not construct this directly, Jasmine will make one during booting. You can get the current clock with {@link jasmine.clock}.
+ * @class Clock
+ * @classdesc Jasmine's mock clock is used when testing time dependent code.
+ */
+ function Clock(global, delayedFunctionSchedulerFactory, mockDate) {
+ var self = this,
+ realTimingFunctions = {
+ setTimeout: global.setTimeout,
+ clearTimeout: global.clearTimeout,
+ setInterval: global.setInterval,
+ clearInterval: global.clearInterval
+ },
+ fakeTimingFunctions = {
+ setTimeout: setTimeout,
+ clearTimeout: clearTimeout,
+ setInterval: setInterval,
+ clearInterval: clearInterval
+ },
+ installed = false,
+ delayedFunctionScheduler,
+ timer;
+
+
+ /**
+ * Install the mock clock over the built-in methods.
+ * @name Clock#install
+ * @function
+ * @return {Clock}
+ */
+ self.install = function() {
+ if(!originalTimingFunctionsIntact()) {
+ throw new Error('Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?');
+ }
+ replace(global, fakeTimingFunctions);
+ timer = fakeTimingFunctions;
+ delayedFunctionScheduler = delayedFunctionSchedulerFactory();
+ installed = true;
+
+ return self;
+ };
+
+ /**
+ * Uninstall the mock clock, returning the built-in methods to their places.
+ * @name Clock#uninstall
+ * @function
+ */
+ self.uninstall = function() {
+ delayedFunctionScheduler = null;
+ mockDate.uninstall();
+ replace(global, realTimingFunctions);
+
+ timer = realTimingFunctions;
+ installed = false;
+ };
+
+ /**
+ * Execute a function with a mocked Clock
+ *
+ * The clock will be {@link Clock#install|install}ed before the function is called and {@link Clock#uninstall|uninstall}ed in a `finally` after the function completes.
+ * @name Clock#withMock
+ * @function
+ * @param {closure} Function The function to be called.
+ */
+ self.withMock = function(closure) {
+ this.install();
+ try {
+ closure();
+ } finally {
+ this.uninstall();
+ }
+ };
+
+ /**
+ * Instruct the installed Clock to also mock the date returned by `new Date()`
+ * @name Clock#mockDate
+ * @function
+ * @param {Date} [initialDate=now] The `Date` to provide.
+ */
+ self.mockDate = function(initialDate) {
+ mockDate.install(initialDate);
+ };
+
+ self.setTimeout = function(fn, delay, params) {
+ if (legacyIE()) {
+ if (arguments.length > 2) {
+ throw new Error('IE < 9 cannot support extra params to setTimeout without a polyfill');
+ }
+ return timer.setTimeout(fn, delay);
+ }
+ return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]);
+ };
+
+ self.setInterval = function(fn, delay, params) {
+ if (legacyIE()) {
+ if (arguments.length > 2) {
+ throw new Error('IE < 9 cannot support extra params to setInterval without a polyfill');
+ }
+ return timer.setInterval(fn, delay);
+ }
+ return Function.prototype.apply.apply(timer.setInterval, [global, arguments]);
+ };
+
+ self.clearTimeout = function(id) {
+ return Function.prototype.call.apply(timer.clearTimeout, [global, id]);
+ };
+
+ self.clearInterval = function(id) {
+ return Function.prototype.call.apply(timer.clearInterval, [global, id]);
+ };
+
+ /**
+ * Tick the Clock forward, running any enqueued timeouts along the way
+ * @name Clock#tick
+ * @function
+ * @param {int} millis The number of milliseconds to tick.
+ */
+ self.tick = function(millis) {
+ if (installed) {
+ delayedFunctionScheduler.tick(millis, function(millis) { mockDate.tick(millis); });
+ } else {
+ throw new Error('Mock clock is not installed, use jasmine.clock().install()');
+ }
+ };
+
+ return self;
+
+ function originalTimingFunctionsIntact() {
+ return global.setTimeout === realTimingFunctions.setTimeout &&
+ global.clearTimeout === realTimingFunctions.clearTimeout &&
+ global.setInterval === realTimingFunctions.setInterval &&
+ global.clearInterval === realTimingFunctions.clearInterval;
+ }
+
+ function legacyIE() {
+ //if these methods are polyfilled, apply will be present
+ return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply;
+ }
+
+ function replace(dest, source) {
+ for (var prop in source) {
+ dest[prop] = source[prop];
+ }
+ }
+
+ function setTimeout(fn, delay) {
+ return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2));
+ }
+
+ function clearTimeout(id) {
+ return delayedFunctionScheduler.removeFunctionWithId(id);
+ }
+
+ function setInterval(fn, interval) {
+ return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true);
+ }
+
+ function clearInterval(id) {
+ return delayedFunctionScheduler.removeFunctionWithId(id);
+ }
+
+ function argSlice(argsObj, n) {
+ return Array.prototype.slice.call(argsObj, n);
+ }
+ }
+
+ return Clock;
+};
+
+getJasmineRequireObj().DelayedFunctionScheduler = function() {
+ function DelayedFunctionScheduler() {
+ var self = this;
+ var scheduledLookup = [];
+ var scheduledFunctions = {};
+ var currentTime = 0;
+ var delayedFnCount = 0;
+
+ self.tick = function(millis, tickDate) {
+ millis = millis || 0;
+ var endTime = currentTime + millis;
+
+ runScheduledFunctions(endTime, tickDate);
+ currentTime = endTime;
+ };
+
+ self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) {
+ var f;
+ if (typeof(funcToCall) === 'string') {
+ /* jshint evil: true */
+ f = function() { return eval(funcToCall); };
+ /* jshint evil: false */
+ } else {
+ f = funcToCall;
+ }
+
+ millis = millis || 0;
+ timeoutKey = timeoutKey || ++delayedFnCount;
+ runAtMillis = runAtMillis || (currentTime + millis);
+
+ var funcToSchedule = {
+ runAtMillis: runAtMillis,
+ funcToCall: f,
+ recurring: recurring,
+ params: params,
+ timeoutKey: timeoutKey,
+ millis: millis
+ };
+
+ if (runAtMillis in scheduledFunctions) {
+ scheduledFunctions[runAtMillis].push(funcToSchedule);
+ } else {
+ scheduledFunctions[runAtMillis] = [funcToSchedule];
+ scheduledLookup.push(runAtMillis);
+ scheduledLookup.sort(function (a, b) {
+ return a - b;
+ });
+ }
+
+ return timeoutKey;
+ };
+
+ self.removeFunctionWithId = function(timeoutKey) {
+ for (var runAtMillis in scheduledFunctions) {
+ var funcs = scheduledFunctions[runAtMillis];
+ var i = indexOfFirstToPass(funcs, function (func) {
+ return func.timeoutKey === timeoutKey;
+ });
+
+ if (i > -1) {
+ if (funcs.length === 1) {
+ delete scheduledFunctions[runAtMillis];
+ deleteFromLookup(runAtMillis);
+ } else {
+ funcs.splice(i, 1);
+ }
+
+ // intervals get rescheduled when executed, so there's never more
+ // than a single scheduled function with a given timeoutKey
+ break;
+ }
+ }
+ };
+
+ return self;
+
+ function indexOfFirstToPass(array, testFn) {
+ var index = -1;
+
+ for (var i = 0; i < array.length; ++i) {
+ if (testFn(array[i])) {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+ }
+
+ function deleteFromLookup(key) {
+ var value = Number(key);
+ var i = indexOfFirstToPass(scheduledLookup, function (millis) {
+ return millis === value;
+ });
+
+ if (i > -1) {
+ scheduledLookup.splice(i, 1);
+ }
+ }
+
+ function reschedule(scheduledFn) {
+ self.scheduleFunction(scheduledFn.funcToCall,
+ scheduledFn.millis,
+ scheduledFn.params,
+ true,
+ scheduledFn.timeoutKey,
+ scheduledFn.runAtMillis + scheduledFn.millis);
+ }
+
+ function forEachFunction(funcsToRun, callback) {
+ for (var i = 0; i < funcsToRun.length; ++i) {
+ callback(funcsToRun[i]);
+ }
+ }
+
+ function runScheduledFunctions(endTime, tickDate) {
+ tickDate = tickDate || function() {};
+ if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) {
+ tickDate(endTime - currentTime);
+ return;
+ }
+
+ do {
+ var newCurrentTime = scheduledLookup.shift();
+ tickDate(newCurrentTime - currentTime);
+
+ currentTime = newCurrentTime;
+
+ var funcsToRun = scheduledFunctions[currentTime];
+ delete scheduledFunctions[currentTime];
+
+ forEachFunction(funcsToRun, function(funcToRun) {
+ if (funcToRun.recurring) {
+ reschedule(funcToRun);
+ }
+ });
+
+ forEachFunction(funcsToRun, function(funcToRun) {
+ funcToRun.funcToCall.apply(null, funcToRun.params || []);
+ });
+ } while (scheduledLookup.length > 0 &&
+ // checking first if we're out of time prevents setTimeout(0)
+ // scheduled in a funcToRun from forcing an extra iteration
+ currentTime !== endTime &&
+ scheduledLookup[0] <= endTime);
+
+ // ran out of functions to call, but still time left on the clock
+ if (currentTime !== endTime) {
+ tickDate(endTime - currentTime);
+ }
+ }
+ }
+
+ return DelayedFunctionScheduler;
+};
+
+getJasmineRequireObj().errors = function() {
+ function ExpectationFailed() {}
+
+ ExpectationFailed.prototype = new Error();
+ ExpectationFailed.prototype.constructor = ExpectationFailed;
+
+ return {
+ ExpectationFailed: ExpectationFailed
+ };
+};
+getJasmineRequireObj().ExceptionFormatter = function() {
+ function ExceptionFormatter() {
+ this.message = function(error) {
+ var message = '';
+
+ if (error.name && error.message) {
+ message += error.name + ': ' + error.message;
+ } else {
+ message += error.toString() + ' thrown';
+ }
+
+ if (error.fileName || error.sourceURL) {
+ message += ' in ' + (error.fileName || error.sourceURL);
+ }
+
+ if (error.line || error.lineNumber) {
+ message += ' (line ' + (error.line || error.lineNumber) + ')';
+ }
+
+ return message;
+ };
+
+ this.stack = function(error) {
+ return error ? error.stack : null;
+ };
+ }
+
+ return ExceptionFormatter;
+};
+
+getJasmineRequireObj().Expectation = function() {
+
+ /**
+ * Matchers that come with Jasmine out of the box.
+ * @namespace matchers
+ */
+ function Expectation(options) {
+ this.util = options.util || { buildFailureMessage: function() {} };
+ this.customEqualityTesters = options.customEqualityTesters || [];
+ this.actual = options.actual;
+ this.addExpectationResult = options.addExpectationResult || function(){};
+ this.isNot = options.isNot;
+
+ var customMatchers = options.customMatchers || {};
+ for (var matcherName in customMatchers) {
+ this[matcherName] = Expectation.prototype.wrapCompare(matcherName, customMatchers[matcherName]);
+ }
+ }
+
+ Expectation.prototype.wrapCompare = function(name, matcherFactory) {
+ return function() {
+ var args = Array.prototype.slice.call(arguments, 0),
+ expected = args.slice(0),
+ message = '';
+
+ args.unshift(this.actual);
+
+ var matcher = matcherFactory(this.util, this.customEqualityTesters),
+ matcherCompare = matcher.compare;
+
+ function defaultNegativeCompare() {
+ var result = matcher.compare.apply(null, args);
+ result.pass = !result.pass;
+ return result;
+ }
+
+ if (this.isNot) {
+ matcherCompare = matcher.negativeCompare || defaultNegativeCompare;
+ }
+
+ var result = matcherCompare.apply(null, args);
+
+ if (!result.pass) {
+ if (!result.message) {
+ args.unshift(this.isNot);
+ args.unshift(name);
+ message = this.util.buildFailureMessage.apply(null, args);
+ } else {
+ if (Object.prototype.toString.apply(result.message) === '[object Function]') {
+ message = result.message();
+ } else {
+ message = result.message;
+ }
+ }
+ }
+
+ if (expected.length == 1) {
+ expected = expected[0];
+ }
+
+ // TODO: how many of these params are needed?
+ this.addExpectationResult(
+ result.pass,
+ {
+ matcherName: name,
+ passed: result.pass,
+ message: message,
+ error: result.error,
+ actual: this.actual,
+ expected: expected // TODO: this may need to be arrayified/sliced
+ }
+ );
+ };
+ };
+
+ Expectation.addCoreMatchers = function(matchers) {
+ var prototype = Expectation.prototype;
+ for (var matcherName in matchers) {
+ var matcher = matchers[matcherName];
+ prototype[matcherName] = prototype.wrapCompare(matcherName, matcher);
+ }
+ };
+
+ Expectation.Factory = function(options) {
+ options = options || {};
+
+ var expect = new Expectation(options);
+
+ // TODO: this would be nice as its own Object - NegativeExpectation
+ // TODO: copy instead of mutate options
+ options.isNot = true;
+ expect.not = new Expectation(options);
+
+ return expect;
+ };
+
+ return Expectation;
+};
+
+//TODO: expectation result may make more sense as a presentation of an expectation.
+getJasmineRequireObj().buildExpectationResult = function() {
+ function buildExpectationResult(options) {
+ var messageFormatter = options.messageFormatter || function() {},
+ stackFormatter = options.stackFormatter || function() {};
+
+ var result = {
+ matcherName: options.matcherName,
+ message: message(),
+ stack: stack(),
+ passed: options.passed
+ };
+
+ if(!result.passed) {
+ result.expected = options.expected;
+ result.actual = options.actual;
+ }
+
+ return result;
+
+ function message() {
+ if (options.passed) {
+ return 'Passed.';
+ } else if (options.message) {
+ return options.message;
+ } else if (options.error) {
+ return messageFormatter(options.error);
+ }
+ return '';
+ }
+
+ function stack() {
+ if (options.passed) {
+ return '';
+ }
+
+ var error = options.error;
+ if (!error) {
+ try {
+ throw new Error(message());
+ } catch (e) {
+ error = e;
+ }
+ }
+ return stackFormatter(error);
+ }
+ }
+
+ return buildExpectationResult;
+};
+
+getJasmineRequireObj().formatErrorMsg = function() {
+ function generateErrorMsg(domain, usage) {
+ var usageDefinition = usage ? '\nUsage: ' + usage : '';
+
+ return function errorMsg(msg) {
+ return domain + ' : ' + msg + usageDefinition;
+ };
+ }
+
+ return generateErrorMsg;
+};
+
+getJasmineRequireObj().GlobalErrors = function(j$) {
+ function GlobalErrors(global) {
+ var handlers = [];
+ global = global || j$.getGlobal();
+
+ var onerror = function onerror() {
+ var handler = handlers[handlers.length - 1];
+ handler.apply(null, Array.prototype.slice.call(arguments, 0));
+ };
+
+ this.uninstall = function noop() {};
+
+ this.install = function install() {
+ if (global.process && j$.isFunction_(global.process.on)) {
+ var originalHandlers = global.process.listeners('uncaughtException');
+ global.process.removeAllListeners('uncaughtException');
+ global.process.on('uncaughtException', onerror);
+
+ this.uninstall = function uninstall() {
+ global.process.removeListener('uncaughtException', onerror);
+ for (var i = 0; i < originalHandlers.length; i++) {
+ global.process.on('uncaughtException', originalHandlers[i]);
+ }
+ };
+ } else {
+ var originalHandler = global.onerror;
+ global.onerror = onerror;
+
+ this.uninstall = function uninstall() {
+ global.onerror = originalHandler;
+ };
+ }
+ };
+
+ this.pushListener = function pushListener(listener) {
+ handlers.push(listener);
+ };
+
+ this.popListener = function popListener() {
+ handlers.pop();
+ };
+ }
+
+ return GlobalErrors;
+};
+
+getJasmineRequireObj().DiffBuilder = function(j$) {
+ return function DiffBuilder() {
+ var path = new j$.ObjectPath(),
+ mismatches = [];
+
+ return {
+ record: function (actual, expected, formatter) {
+ formatter = formatter || defaultFormatter;
+ mismatches.push(formatter(actual, expected, path));
+ },
+
+ getMessage: function () {
+ return mismatches.join('\n');
+ },
+
+ withPath: function (pathComponent, block) {
+ var oldPath = path;
+ path = path.add(pathComponent);
+ block();
+ path = oldPath;
+ }
+ };
+
+ function defaultFormatter (actual, expected, path) {
+ return 'Expected ' +
+ path + (path.depth() ? ' = ' : '') +
+ j$.pp(actual) +
+ ' to equal ' +
+ j$.pp(expected) +
+ '.';
+ }
+ };
+};
+
+getJasmineRequireObj().matchersUtil = function(j$) {
+ // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter?
+
+ return {
+ equals: equals,
+
+ contains: function(haystack, needle, customTesters) {
+ customTesters = customTesters || [];
+
+ if ((Object.prototype.toString.apply(haystack) === '[object Set]')) {
+ return haystack.has(needle);
+ }
+
+ if ((Object.prototype.toString.apply(haystack) === '[object Array]') ||
+ (!!haystack && !haystack.indexOf))
+ {
+ for (var i = 0; i < haystack.length; i++) {
+ if (equals(haystack[i], needle, customTesters)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ return !!haystack && haystack.indexOf(needle) >= 0;
+ },
+
+ buildFailureMessage: function() {
+ var args = Array.prototype.slice.call(arguments, 0),
+ matcherName = args[0],
+ isNot = args[1],
+ actual = args[2],
+ expected = args.slice(3),
+ englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
+
+ var message = 'Expected ' +
+ j$.pp(actual) +
+ (isNot ? ' not ' : ' ') +
+ englishyPredicate;
+
+ if (expected.length > 0) {
+ for (var i = 0; i < expected.length; i++) {
+ if (i > 0) {
+ message += ',';
+ }
+ message += ' ' + j$.pp(expected[i]);
+ }
+ }
+
+ return message + '.';
+ }
+ };
+
+ function isAsymmetric(obj) {
+ return obj && j$.isA_('Function', obj.asymmetricMatch);
+ }
+
+ function asymmetricMatch(a, b, customTesters, diffBuilder) {
+ var asymmetricA = isAsymmetric(a),
+ asymmetricB = isAsymmetric(b),
+ result;
+
+ if (asymmetricA && asymmetricB) {
+ return undefined;
+ }
+
+ if (asymmetricA) {
+ result = a.asymmetricMatch(b, customTesters);
+ diffBuilder.record(a, b);
+ return result;
+ }
+
+ if (asymmetricB) {
+ result = b.asymmetricMatch(a, customTesters);
+ diffBuilder.record(a, b);
+ return result;
+ }
+ }
+
+ function equals(a, b, customTesters, diffBuilder) {
+ customTesters = customTesters || [];
+ diffBuilder = diffBuilder || j$.NullDiffBuilder();
+
+ return eq(a, b, [], [], customTesters, diffBuilder);
+ }
+
+ // Equality function lovingly adapted from isEqual in
+ // [Underscore](http://underscorejs.org)
+ function eq(a, b, aStack, bStack, customTesters, diffBuilder) {
+ var result = true, i;
+
+ var asymmetricResult = asymmetricMatch(a, b, customTesters, diffBuilder);
+ if (!j$.util.isUndefined(asymmetricResult)) {
+ return asymmetricResult;
+ }
+
+ for (i = 0; i < customTesters.length; i++) {
+ var customTesterResult = customTesters[i](a, b);
+ if (!j$.util.isUndefined(customTesterResult)) {
+ if (!customTesterResult) {
+ diffBuilder.record(a, b);
+ }
+ return customTesterResult;
+ }
+ }
+
+ if (a instanceof Error && b instanceof Error) {
+ result = a.message == b.message;
+ if (!result) {
+ diffBuilder.record(a, b);
+ }
+ return result;
+ }
+
+ // Identical objects are equal. `0 === -0`, but they aren't identical.
+ // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
+ if (a === b) {
+ result = a !== 0 || 1 / a == 1 / b;
+ if (!result) {
+ diffBuilder.record(a, b);
+ }
+ return result;
+ }
+ // A strict comparison is necessary because `null == undefined`.
+ if (a === null || b === null) {
+ result = a === b;
+ if (!result) {
+ diffBuilder.record(a, b);
+ }
+ return result;
+ }
+ var className = Object.prototype.toString.call(a);
+ if (className != Object.prototype.toString.call(b)) {
+ diffBuilder.record(a, b);
+ return false;
+ }
+ switch (className) {
+ // Strings, numbers, dates, and booleans are compared by value.
+ case '[object String]':
+ // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
+ // equivalent to `new String("5")`.
+ result = a == String(b);
+ if (!result) {
+ diffBuilder.record(a, b);
+ }
+ return result;
+ case '[object Number]':
+ // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
+ // other numeric values.
+ result = a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b);
+ if (!result) {
+ diffBuilder.record(a, b);
+ }
+ return result;
+ case '[object Date]':
+ case '[object Boolean]':
+ // Coerce dates and booleans to numeric primitive values. Dates are compared by their
+ // millisecond representations. Note that invalid dates with millisecond representations
+ // of `NaN` are not equivalent.
+ result = +a == +b;
+ if (!result) {
+ diffBuilder.record(a, b);
+ }
+ return result;
+ // RegExps are compared by their source patterns and flags.
+ case '[object RegExp]':
+ return a.source == b.source &&
+ a.global == b.global &&
+ a.multiline == b.multiline &&
+ a.ignoreCase == b.ignoreCase;
+ }
+ if (typeof a != 'object' || typeof b != 'object') {
+ diffBuilder.record(a, b);
+ return false;
+ }
+
+ var aIsDomNode = j$.isDomNode(a);
+ var bIsDomNode = j$.isDomNode(b);
+ if (aIsDomNode && bIsDomNode) {
+ // At first try to use DOM3 method isEqualNode
+ if (a.isEqualNode) {
+ result = a.isEqualNode(b);
+ if (!result) {
+ diffBuilder.record(a, b);
+ }
+ return result;
+ }
+ // IE8 doesn't support isEqualNode, try to use outerHTML && innerText
+ var aIsElement = a instanceof Element;
+ var bIsElement = b instanceof Element;
+ if (aIsElement && bIsElement) {
+ result = a.outerHTML == b.outerHTML;
+ if (!result) {
+ diffBuilder.record(a, b);
+ }
+ return result;
+ }
+ if (aIsElement || bIsElement) {
+ diffBuilder.record(a, b);
+ return false;
+ }
+ result = a.innerText == b.innerText && a.textContent == b.textContent;
+ if (!result) {
+ diffBuilder.record(a, b);
+ }
+ return result;
+ }
+ if (aIsDomNode || bIsDomNode) {
+ diffBuilder.record(a, b);
+ return false;
+ }
+
+ // Assume equality for cyclic structures. The algorithm for detecting cyclic
+ // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
+ var length = aStack.length;
+ while (length--) {
+ // Linear search. Performance is inversely proportional to the number of
+ // unique nested structures.
+ if (aStack[length] == a) { return bStack[length] == b; }
+ }
+ // Add the first object to the stack of traversed objects.
+ aStack.push(a);
+ bStack.push(b);
+ var size = 0;
+ // Recursively compare objects and arrays.
+ // Compare array lengths to determine if a deep comparison is necessary.
+ if (className == '[object Array]') {
+ size = a.length;
+ if (size !== b.length) {
+ diffBuilder.record(a, b);
+ return false;
+ }
+
+ for (i = 0; i < size; i++) {
+ diffBuilder.withPath(i, function() {
+ result = eq(a[i], b[i], aStack, bStack, customTesters, diffBuilder) && result;
+ });
+ }
+ if (!result) {
+ return false;
+ }
+ } else if (className == '[object Set]') {
+ if (a.size != b.size) {
+ diffBuilder.record(a, b);
+ return false;
+ }
+ var iterA = a.values(), iterB = b.values();
+ var valA, valB;
+ do {
+ valA = iterA.next();
+ valB = iterB.next();
+ if (!eq(valA.value, valB.value, aStack, bStack, customTesters, j$.NullDiffBuilder())) {
+ diffBuilder.record(a, b);
+ return false;
+ }
+ } while (!valA.done && !valB.done);
+ } else {
+
+ // Objects with different constructors are not equivalent, but `Object`s
+ // or `Array`s from different frames are.
+ var aCtor = a.constructor, bCtor = b.constructor;
+ if (aCtor !== bCtor &&
+ isFunction(aCtor) && isFunction(bCtor) &&
+ a instanceof aCtor && b instanceof bCtor &&
+ !(aCtor instanceof aCtor && bCtor instanceof bCtor)) {
+
+ diffBuilder.record(a, b, constructorsAreDifferentFormatter);
+ return false;
+ }
+ }
+
+ // Deep compare objects.
+ var aKeys = keys(a, className == '[object Array]'), key;
+ size = aKeys.length;
+
+ // Ensure that both objects contain the same number of properties before comparing deep equality.
+ if (keys(b, className == '[object Array]').length !== size) {
+ diffBuilder.record(a, b, objectKeysAreDifferentFormatter);
+ return false;
+ }
+
+ for (i = 0; i < size; i++) {
+ key = aKeys[i];
+ // Deep compare each member
+ if (!j$.util.has(b, key)) {
+ diffBuilder.record(a, b, objectKeysAreDifferentFormatter);
+ result = false;
+ continue;
+ }
+
+ diffBuilder.withPath(key, function() {
+ if(!eq(a[key], b[key], aStack, bStack, customTesters, diffBuilder)) {
+ result = false;
+ }
+ });
+ }
+
+ if (!result) {
+ return false;
+ }
+
+ // Remove the first object from the stack of traversed objects.
+ aStack.pop();
+ bStack.pop();
+
+ return result;
+ }
+
+ function keys(obj, isArray) {
+ var allKeys = Object.keys ? Object.keys(obj) :
+ (function(o) {
+ var keys = [];
+ for (var key in o) {
+ if (j$.util.has(o, key)) {
+ keys.push(key);
+ }
+ }
+ return keys;
+ })(obj);
+
+ if (!isArray) {
+ return allKeys;
+ }
+
+ if (allKeys.length === 0) {
+ return allKeys;
+ }
+
+ var extraKeys = [];
+ for (var i in allKeys) {
+ if (!allKeys[i].match(/^[0-9]+$/)) {
+ extraKeys.push(allKeys[i]);
+ }
+ }
+
+ return extraKeys;
+ }
+
+ function has(obj, key) {
+ return Object.prototype.hasOwnProperty.call(obj, key);
+ }
+
+ function isFunction(obj) {
+ return typeof obj === 'function';
+ }
+
+ function objectKeysAreDifferentFormatter(actual, expected, path) {
+ var missingProperties = j$.util.objectDifference(expected, actual),
+ extraProperties = j$.util.objectDifference(actual, expected),
+ missingPropertiesMessage = formatKeyValuePairs(missingProperties),
+ extraPropertiesMessage = formatKeyValuePairs(extraProperties),
+ messages = [];
+
+ if (!path.depth()) {
+ path = 'object';
+ }
+
+ if (missingPropertiesMessage.length) {
+ messages.push('Expected ' + path + ' to have properties' + missingPropertiesMessage);
+ }
+
+ if (extraPropertiesMessage.length) {
+ messages.push('Expected ' + path + ' not to have properties' + extraPropertiesMessage);
+ }
+
+ return messages.join('\n');
+ }
+
+ function constructorsAreDifferentFormatter(actual, expected, path) {
+ if (!path.depth()) {
+ path = 'object';
+ }
+
+ return 'Expected ' +
+ path + ' to be a kind of ' +
+ j$.fnNameFor(expected.constructor) +
+ ', but was ' + j$.pp(actual) + '.';
+ }
+
+ function formatKeyValuePairs(obj) {
+ var formatted = '';
+ for (var key in obj) {
+ formatted += '\n ' + key + ': ' + j$.pp(obj[key]);
+ }
+ return formatted;
+ }
+};
+
+getJasmineRequireObj().NullDiffBuilder = function(j$) {
+ return function() {
+ return {
+ withPath: function(_, block) {
+ block();
+ },
+ record: function() {}
+ };
+ };
+};
+
+getJasmineRequireObj().ObjectPath = function(j$) {
+ function ObjectPath(components) {
+ this.components = components || [];
+ }
+
+ ObjectPath.prototype.toString = function() {
+ if (this.components.length) {
+ return '$' + map(this.components, formatPropertyAccess).join('');
+ } else {
+ return '';
+ }
+ };
+
+ ObjectPath.prototype.add = function(component) {
+ return new ObjectPath(this.components.concat([component]));
+ };
+
+ ObjectPath.prototype.depth = function() {
+ return this.components.length;
+ };
+
+ function formatPropertyAccess(prop) {
+ if (typeof prop === 'number') {
+ return '[' + prop + ']';
+ }
+
+ if (isValidIdentifier(prop)) {
+ return '.' + prop;
+ }
+
+ return '[\'' + prop + '\']';
+ }
+
+ function map(array, fn) {
+ var results = [];
+ for (var i = 0; i < array.length; i++) {
+ results.push(fn(array[i]));
+ }
+ return results;
+ }
+
+ function isValidIdentifier(string) {
+ return /^[A-Za-z\$_][A-Za-z0-9\$_]*$/.test(string);
+ }
+
+ return ObjectPath;
+};
+
+getJasmineRequireObj().toBe = function() {
+ /**
+ * {@link expect} the actual value to be `===` to the expected value.
+ * @function
+ * @name matchers#toBe
+ * @param {Object} expected - The expected value to compare against.
+ * @example
+ * expect(thing).toBe(realThing);
+ */
+ function toBe() {
+ return {
+ compare: function(actual, expected) {
+ return {
+ pass: actual === expected
+ };
+ }
+ };
+ }
+
+ return toBe;
+};
+
+getJasmineRequireObj().toBeCloseTo = function() {
+ /**
+ * {@link expect} the actual value to be within a specified precision of the expected value.
+ * @function
+ * @name matchers#toBeCloseTo
+ * @param {Object} expected - The expected value to compare against.
+ * @param {Number} [precision=2] - The number of decimal points to check.
+ * @example
+ * expect(number).toBeCloseTo(42.2, 3);
+ */
+ function toBeCloseTo() {
+ return {
+ compare: function(actual, expected, precision) {
+ if (precision !== 0) {
+ precision = precision || 2;
+ }
+
+ return {
+ pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2)
+ };
+ }
+ };
+ }
+
+ return toBeCloseTo;
+};
+
+getJasmineRequireObj().toBeDefined = function() {
+ /**
+ * {@link expect} the actual value to be defined. (Not `undefined`)
+ * @function
+ * @name matchers#toBeDefined
+ * @example
+ * expect(result).toBeDefined();
+ */
+ function toBeDefined() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: (void 0 !== actual)
+ };
+ }
+ };
+ }
+
+ return toBeDefined;
+};
+
+getJasmineRequireObj().toBeFalsy = function() {
+ /**
+ * {@link expect} the actual value to be falsy
+ * @function
+ * @name matchers#toBeFalsy
+ * @example
+ * expect(result).toBeFalsy();
+ */
+ function toBeFalsy() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: !!!actual
+ };
+ }
+ };
+ }
+
+ return toBeFalsy;
+};
+
+getJasmineRequireObj().toBeGreaterThan = function() {
+ /**
+ * {@link expect} the actual value to be greater than the expected value.
+ * @function
+ * @name matchers#toBeGreaterThan
+ * @param {Number} expected - The value to compare against.
+ * @example
+ * expect(result).toBeGreaterThan(3);
+ */
+ function toBeGreaterThan() {
+ return {
+ compare: function(actual, expected) {
+ return {
+ pass: actual > expected
+ };
+ }
+ };
+ }
+
+ return toBeGreaterThan;
+};
+
+
+getJasmineRequireObj().toBeGreaterThanOrEqual = function() {
+ /**
+ * {@link expect} the actual value to be greater than or equal to the expected value.
+ * @function
+ * @name matchers#toBeGreaterThanOrEqual
+ * @param {Number} expected - The expected value to compare against.
+ * @example
+ * expect(result).toBeGreaterThanOrEqual(25);
+ */
+ function toBeGreaterThanOrEqual() {
+ return {
+ compare: function(actual, expected) {
+ return {
+ pass: actual >= expected
+ };
+ }
+ };
+ }
+
+ return toBeGreaterThanOrEqual;
+};
+
+getJasmineRequireObj().toBeLessThan = function() {
+ /**
+ * {@link expect} the actual value to be less than the expected value.
+ * @function
+ * @name matchers#toBeLessThan
+ * @param {Number} expected - The expected value to compare against.
+ * @example
+ * expect(result).toBeLessThan(0);
+ */
+ function toBeLessThan() {
+ return {
+
+ compare: function(actual, expected) {
+ return {
+ pass: actual < expected
+ };
+ }
+ };
+ }
+
+ return toBeLessThan;
+};
+
+getJasmineRequireObj().toBeLessThanOrEqual = function() {
+ /**
+ * {@link expect} the actual value to be less than or equal to the expected value.
+ * @function
+ * @name matchers#toBeLessThanOrEqual
+ * @param {Number} expected - The expected value to compare against.
+ * @example
+ * expect(result).toBeLessThanOrEqual(123);
+ */
+ function toBeLessThanOrEqual() {
+ return {
+
+ compare: function(actual, expected) {
+ return {
+ pass: actual <= expected
+ };
+ }
+ };
+ }
+
+ return toBeLessThanOrEqual;
+};
+
+getJasmineRequireObj().toBeNaN = function(j$) {
+ /**
+ * {@link expect} the actual value to be `NaN` (Not a Number).
+ * @function
+ * @name matchers#toBeNaN
+ * @example
+ * expect(thing).toBeNaN();
+ */
+ function toBeNaN() {
+ return {
+ compare: function(actual) {
+ var result = {
+ pass: (actual !== actual)
+ };
+
+ if (result.pass) {
+ result.message = 'Expected actual not to be NaN.';
+ } else {
+ result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be NaN.'; };
+ }
+
+ return result;
+ }
+ };
+ }
+
+ return toBeNaN;
+};
+
+getJasmineRequireObj().toBeNegativeInfinity = function(j$) {
+ /**
+ * {@link expect} the actual value to be `-Infinity` (-infinity).
+ * @function
+ * @name matchers#toBeNegativeInfinity
+ * @example
+ * expect(thing).toBeNegativeInfinity();
+ */
+ function toBeNegativeInfinity() {
+ return {
+ compare: function(actual) {
+ var result = {
+ pass: (actual === Number.NEGATIVE_INFINITY)
+ };
+
+ if (result.pass) {
+ result.message = 'Expected actual to be -Infinity.';
+ } else {
+ result.message = function() { return 'Expected ' + j$.pp(actual) + ' not to be -Infinity.'; };
+ }
+
+ return result;
+ }
+ };
+ }
+
+ return toBeNegativeInfinity;
+};
+
+getJasmineRequireObj().toBeNull = function() {
+ /**
+ * {@link expect} the actual value to be `null`.
+ * @function
+ * @name matchers#toBeNull
+ * @example
+ * expect(result).toBeNull();
+ */
+ function toBeNull() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: actual === null
+ };
+ }
+ };
+ }
+
+ return toBeNull;
+};
+
+getJasmineRequireObj().toBePositiveInfinity = function(j$) {
+ /**
+ * {@link expect} the actual value to be `Infinity` (infinity).
+ * @function
+ * @name matchers#toBePositiveInfinity
+ * @example
+ * expect(thing).toBePositiveInfinity();
+ */
+ function toBePositiveInfinity() {
+ return {
+ compare: function(actual) {
+ var result = {
+ pass: (actual === Number.POSITIVE_INFINITY)
+ };
+
+ if (result.pass) {
+ result.message = 'Expected actual to be Infinity.';
+ } else {
+ result.message = function() { return 'Expected ' + j$.pp(actual) + ' not to be Infinity.'; };
+ }
+
+ return result;
+ }
+ };
+ }
+
+ return toBePositiveInfinity;
+};
+
+getJasmineRequireObj().toBeTruthy = function() {
+ /**
+ * {@link expect} the actual value to be truthy.
+ * @function
+ * @name matchers#toBeTruthy
+ * @example
+ * expect(thing).toBeTruthy();
+ */
+ function toBeTruthy() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: !!actual
+ };
+ }
+ };
+ }
+
+ return toBeTruthy;
+};
+
+getJasmineRequireObj().toBeUndefined = function() {
+ /**
+ * {@link expect} the actual value to be `undefined`.
+ * @function
+ * @name matchers#toBeUndefined
+ * @example
+ * expect(result).toBeUndefined():
+ */
+ function toBeUndefined() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: void 0 === actual
+ };
+ }
+ };
+ }
+
+ return toBeUndefined;
+};
+
+getJasmineRequireObj().toContain = function() {
+ /**
+ * {@link expect} the actual value to contain a specific value.
+ * @function
+ * @name matchers#toContain
+ * @param {Object} expected - The value to look for.
+ * @example
+ * expect(array).toContain(anElement);
+ * expect(string).toContain(substring);
+ */
+ function toContain(util, customEqualityTesters) {
+ customEqualityTesters = customEqualityTesters || [];
+
+ return {
+ compare: function(actual, expected) {
+
+ return {
+ pass: util.contains(actual, expected, customEqualityTesters)
+ };
+ }
+ };
+ }
+
+ return toContain;
+};
+
+getJasmineRequireObj().toEqual = function(j$) {
+ /**
+ * {@link expect} the actual value to be equal to the expected, using deep equality comparison.
+ * @function
+ * @name matchers#toEqual
+ * @param {Object} expected - Expected value
+ * @example
+ * expect(bigObject).toEqual({"foo": ['bar', 'baz']});
+ */
+ function toEqual(util, customEqualityTesters) {
+ customEqualityTesters = customEqualityTesters || [];
+
+ return {
+ compare: function(actual, expected) {
+ var result = {
+ pass: false
+ },
+ diffBuilder = j$.DiffBuilder();
+
+ result.pass = util.equals(actual, expected, customEqualityTesters, diffBuilder);
+
+ // TODO: only set error message if test fails
+ result.message = diffBuilder.getMessage();
+
+ return result;
+ }
+ };
+ }
+
+ return toEqual;
+};
+
+getJasmineRequireObj().toHaveBeenCalled = function(j$) {
+
+ var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalled()');
+
+ /**
+ * {@link expect} the actual (a {@link Spy}) to have been called.
+ * @function
+ * @name matchers#toHaveBeenCalled
+ * @example
+ * expect(mySpy).toHaveBeenCalled();
+ * expect(mySpy).not.toHaveBeenCalled();
+ */
+ function toHaveBeenCalled() {
+ return {
+ compare: function(actual) {
+ var result = {};
+
+ if (!j$.isSpy(actual)) {
+ throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.'));
+ }
+
+ if (arguments.length > 1) {
+ throw new Error(getErrorMsg('Does not take arguments, use toHaveBeenCalledWith'));
+ }
+
+ result.pass = actual.calls.any();
+
+ result.message = result.pass ?
+ 'Expected spy ' + actual.and.identity() + ' not to have been called.' :
+ 'Expected spy ' + actual.and.identity() + ' to have been called.';
+
+ return result;
+ }
+ };
+ }
+
+ return toHaveBeenCalled;
+};
+
+getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) {
+
+ var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledBefore()');
+
+ /**
+ * {@link expect} the actual value (a {@link Spy}) to have been called before another {@link Spy}.
+ * @function
+ * @name matchers#toHaveBeenCalledBefore
+ * @param {Spy} expected - {@link Spy} that should have been called after the `actual` {@link Spy}.
+ * @example
+ * expect(mySpy).toHaveBeenCalledBefore(otherSpy);
+ */
+ function toHaveBeenCalledBefore() {
+ return {
+ compare: function(firstSpy, latterSpy) {
+ if (!j$.isSpy(firstSpy)) {
+ throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(firstSpy) + '.'));
+ }
+ if (!j$.isSpy(latterSpy)) {
+ throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(latterSpy) + '.'));
+ }
+
+ var result = { pass: false };
+
+ if (!firstSpy.calls.count()) {
+ result.message = 'Expected spy ' + firstSpy.and.identity() + ' to have been called.';
+ return result;
+ }
+ if (!latterSpy.calls.count()) {
+ result.message = 'Expected spy ' + latterSpy.and.identity() + ' to have been called.';
+ return result;
+ }
+
+ var latest1stSpyCall = firstSpy.calls.mostRecent().invocationOrder;
+ var first2ndSpyCall = latterSpy.calls.first().invocationOrder;
+
+ result.pass = latest1stSpyCall < first2ndSpyCall;
+
+ if (result.pass) {
+ result.message = 'Expected spy ' + firstSpy.and.identity() + ' to not have been called before spy ' + latterSpy.and.identity() + ', but it was';
+ } else {
+ var first1stSpyCall = firstSpy.calls.first().invocationOrder;
+ var latest2ndSpyCall = latterSpy.calls.mostRecent().invocationOrder;
+
+ if(first1stSpyCall < first2ndSpyCall) {
+ result.message = 'Expected latest call to spy ' + firstSpy.and.identity() + ' to have been called before first call to spy ' + latterSpy.and.identity() + ' (no interleaved calls)';
+ } else if (latest2ndSpyCall > latest1stSpyCall) {
+ result.message = 'Expected first call to spy ' + latterSpy.and.identity() + ' to have been called after latest call to spy ' + firstSpy.and.identity() + ' (no interleaved calls)';
+ } else {
+ result.message = 'Expected spy ' + firstSpy.and.identity() + ' to have been called before spy ' + latterSpy.and.identity();
+ }
+ }
+
+ return result;
+ }
+ };
+ }
+
+ return toHaveBeenCalledBefore;
+};
+
+getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) {
+
+ var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledTimes()');
+
+ /**
+ * {@link expect} the actual (a {@link Spy}) to have been called the specified number of times.
+ * @function
+ * @name matchers#toHaveBeenCalledTimes
+ * @param {Number} expected - The number of invocations to look for.
+ * @example
+ * expect(mySpy).toHaveBeenCalledTimes(3);
+ */
+ function toHaveBeenCalledTimes() {
+ return {
+ compare: function(actual, expected) {
+ if (!j$.isSpy(actual)) {
+ throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.'));
+ }
+
+ var args = Array.prototype.slice.call(arguments, 0),
+ result = { pass: false };
+
+ if (!j$.isNumber_(expected)){
+ throw new Error(getErrorMsg('The expected times failed is a required argument and must be a number.'));
+ }
+
+ actual = args[0];
+ var calls = actual.calls.count();
+ var timesMessage = expected === 1 ? 'once' : expected + ' times';
+ result.pass = calls === expected;
+ result.message = result.pass ?
+ 'Expected spy ' + actual.and.identity() + ' not to have been called ' + timesMessage + '. It was called ' + calls + ' times.' :
+ 'Expected spy ' + actual.and.identity() + ' to have been called ' + timesMessage + '. It was called ' + calls + ' times.';
+ return result;
+ }
+ };
+ }
+
+ return toHaveBeenCalledTimes;
+};
+
+getJasmineRequireObj().toHaveBeenCalledWith = function(j$) {
+
+ var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledWith(...arguments)');
+
+ /**
+ * {@link expect} the actual (a {@link Spy}) to have been called with particular arguments at least once.
+ * @function
+ * @name matchers#toHaveBeenCalledWith
+ * @param {...Object} - The arguments to look for
+ * @example
+ * expect(mySpy).toHaveBeenCalledWith('foo', 'bar', 2);
+ */
+ function toHaveBeenCalledWith(util, customEqualityTesters) {
+ return {
+ compare: function() {
+ var args = Array.prototype.slice.call(arguments, 0),
+ actual = args[0],
+ expectedArgs = args.slice(1),
+ result = { pass: false };
+
+ if (!j$.isSpy(actual)) {
+ throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.'));
+ }
+
+ if (!actual.calls.any()) {
+ result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but it was never called.'; };
+ return result;
+ }
+
+ if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) {
+ result.pass = true;
+ result.message = function() { return 'Expected spy ' + actual.and.identity() + ' not to have been called with ' + j$.pp(expectedArgs) + ' but it was.'; };
+ } else {
+ result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but actual calls were ' + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + '.'; };
+ }
+
+ return result;
+ }
+ };
+ }
+
+ return toHaveBeenCalledWith;
+};
+
+getJasmineRequireObj().toMatch = function(j$) {
+
+ var getErrorMsg = j$.formatErrorMsg('', 'expect().toMatch( || )');
+
+ /**
+ * {@link expect} the actual value to match a regular expression
+ * @function
+ * @name matchers#toMatch
+ * @param {RegExp|String} expected - Value to look for in the string.
+ * @example
+ * expect("my string").toMatch(/string$/);
+ * expect("other string").toMatch("her");
+ */
+ function toMatch() {
+ return {
+ compare: function(actual, expected) {
+ if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) {
+ throw new Error(getErrorMsg('Expected is not a String or a RegExp'));
+ }
+
+ var regexp = new RegExp(expected);
+
+ return {
+ pass: regexp.test(actual)
+ };
+ }
+ };
+ }
+
+ return toMatch;
+};
+
+getJasmineRequireObj().toThrow = function(j$) {
+
+ var getErrorMsg = j$.formatErrorMsg('', 'expect(function() {}).toThrow()');
+
+ /**
+ * {@link expect} a function to `throw` something.
+ * @function
+ * @name matchers#toThrow
+ * @param {Object} [expected] - Value that should be thrown. If not provided, simply the fact that something was thrown will be checked.
+ * @example
+ * expect(function() { return 'things'; }).toThrow('foo');
+ * expect(function() { return 'stuff'; }).toThrow();
+ */
+ function toThrow(util) {
+ return {
+ compare: function(actual, expected) {
+ var result = { pass: false },
+ threw = false,
+ thrown;
+
+ if (typeof actual != 'function') {
+ throw new Error(getErrorMsg('Actual is not a Function'));
+ }
+
+ try {
+ actual();
+ } catch (e) {
+ threw = true;
+ thrown = e;
+ }
+
+ if (!threw) {
+ result.message = 'Expected function to throw an exception.';
+ return result;
+ }
+
+ if (arguments.length == 1) {
+ result.pass = true;
+ result.message = function() { return 'Expected function not to throw, but it threw ' + j$.pp(thrown) + '.'; };
+
+ return result;
+ }
+
+ if (util.equals(thrown, expected)) {
+ result.pass = true;
+ result.message = function() { return 'Expected function not to throw ' + j$.pp(expected) + '.'; };
+ } else {
+ result.message = function() { return 'Expected function to throw ' + j$.pp(expected) + ', but it threw ' + j$.pp(thrown) + '.'; };
+ }
+
+ return result;
+ }
+ };
+ }
+
+ return toThrow;
+};
+
+getJasmineRequireObj().toThrowError = function(j$) {
+
+ var getErrorMsg = j$.formatErrorMsg('', 'expect(function() {}).toThrowError(, )');
+
+ /**
+ * {@link expect} a function to `throw` an `Error`.
+ * @function
+ * @name matchers#toThrowError
+ * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used.
+ * @param {RegExp|String} [message] - The message that should be set on the thrown `Error`
+ * @example
+ * expect(function() { return 'things'; }).toThrowError(MyCustomError, 'message');
+ * expect(function() { return 'things'; }).toThrowError(MyCustomError, /bar/);
+ * expect(function() { return 'stuff'; }).toThrowError(MyCustomError);
+ * expect(function() { return 'other'; }).toThrowError(/foo/);
+ * expect(function() { return 'other'; }).toThrowError();
+ */
+ function toThrowError () {
+ return {
+ compare: function(actual) {
+ var threw = false,
+ pass = {pass: true},
+ fail = {pass: false},
+ thrown;
+
+ if (typeof actual != 'function') {
+ throw new Error(getErrorMsg('Actual is not a Function'));
+ }
+
+ var errorMatcher = getMatcher.apply(null, arguments);
+
+ try {
+ actual();
+ } catch (e) {
+ threw = true;
+ thrown = e;
+ }
+
+ if (!threw) {
+ fail.message = 'Expected function to throw an Error.';
+ return fail;
+ }
+
+ // Get Error constructor of thrown
+ if (!isErrorObject(thrown)) {
+ fail.message = function() { return 'Expected function to throw an Error, but it threw ' + j$.pp(thrown) + '.'; };
+ return fail;
+ }
+
+ if (errorMatcher.hasNoSpecifics()) {
+ pass.message = 'Expected function not to throw an Error, but it threw ' + j$.fnNameFor(thrown) + '.';
+ return pass;
+ }
+
+ if (errorMatcher.matches(thrown)) {
+ pass.message = function() {
+ return 'Expected function not to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() + '.';
+ };
+ return pass;
+ } else {
+ fail.message = function() {
+ return 'Expected function to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() +
+ ', but it threw ' + errorMatcher.thrownDescription(thrown) + '.';
+ };
+ return fail;
+ }
+ }
+ };
+
+ function getMatcher() {
+ var expected = null,
+ errorType = null;
+
+ if (arguments.length == 2) {
+ expected = arguments[1];
+ if (isAnErrorType(expected)) {
+ errorType = expected;
+ expected = null;
+ }
+ } else if (arguments.length > 2) {
+ errorType = arguments[1];
+ expected = arguments[2];
+ if (!isAnErrorType(errorType)) {
+ throw new Error(getErrorMsg('Expected error type is not an Error.'));
+ }
+ }
+
+ if (expected && !isStringOrRegExp(expected)) {
+ if (errorType) {
+ throw new Error(getErrorMsg('Expected error message is not a string or RegExp.'));
+ } else {
+ throw new Error(getErrorMsg('Expected is not an Error, string, or RegExp.'));
+ }
+ }
+
+ function messageMatch(message) {
+ if (typeof expected == 'string') {
+ return expected == message;
+ } else {
+ return expected.test(message);
+ }
+ }
+
+ return {
+ errorTypeDescription: errorType ? j$.fnNameFor(errorType) : 'an exception',
+ thrownDescription: function(thrown) {
+ var thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception',
+ thrownMessage = '';
+
+ if (expected) {
+ thrownMessage = ' with message ' + j$.pp(thrown.message);
+ }
+
+ return thrownName + thrownMessage;
+ },
+ messageDescription: function() {
+ if (expected === null) {
+ return '';
+ } else if (expected instanceof RegExp) {
+ return ' with a message matching ' + j$.pp(expected);
+ } else {
+ return ' with message ' + j$.pp(expected);
+ }
+ },
+ hasNoSpecifics: function() {
+ return expected === null && errorType === null;
+ },
+ matches: function(error) {
+ return (errorType === null || error instanceof errorType) &&
+ (expected === null || messageMatch(error.message));
+ }
+ };
+ }
+
+ function isStringOrRegExp(potential) {
+ return potential instanceof RegExp || (typeof potential == 'string');
+ }
+
+ function isAnErrorType(type) {
+ if (typeof type !== 'function') {
+ return false;
+ }
+
+ var Surrogate = function() {};
+ Surrogate.prototype = type.prototype;
+ return isErrorObject(new Surrogate());
+ }
+
+ function isErrorObject(thrown) {
+ if (thrown instanceof Error) {
+ return true;
+ }
+ if (thrown && thrown.constructor && thrown.constructor.constructor &&
+ (thrown instanceof (thrown.constructor.constructor('return this')()).Error)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ return toThrowError;
+};
+
+getJasmineRequireObj().MockDate = function() {
+ function MockDate(global) {
+ var self = this;
+ var currentTime = 0;
+
+ if (!global || !global.Date) {
+ self.install = function() {};
+ self.tick = function() {};
+ self.uninstall = function() {};
+ return self;
+ }
+
+ var GlobalDate = global.Date;
+
+ self.install = function(mockDate) {
+ if (mockDate instanceof GlobalDate) {
+ currentTime = mockDate.getTime();
+ } else {
+ currentTime = new GlobalDate().getTime();
+ }
+
+ global.Date = FakeDate;
+ };
+
+ self.tick = function(millis) {
+ millis = millis || 0;
+ currentTime = currentTime + millis;
+ };
+
+ self.uninstall = function() {
+ currentTime = 0;
+ global.Date = GlobalDate;
+ };
+
+ createDateProperties();
+
+ return self;
+
+ function FakeDate() {
+ switch(arguments.length) {
+ case 0:
+ return new GlobalDate(currentTime);
+ case 1:
+ return new GlobalDate(arguments[0]);
+ case 2:
+ return new GlobalDate(arguments[0], arguments[1]);
+ case 3:
+ return new GlobalDate(arguments[0], arguments[1], arguments[2]);
+ case 4:
+ return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3]);
+ case 5:
+ return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
+ arguments[4]);
+ case 6:
+ return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
+ arguments[4], arguments[5]);
+ default:
+ return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
+ arguments[4], arguments[5], arguments[6]);
+ }
+ }
+
+ function createDateProperties() {
+ FakeDate.prototype = GlobalDate.prototype;
+
+ FakeDate.now = function() {
+ if (GlobalDate.now) {
+ return currentTime;
+ } else {
+ throw new Error('Browser does not support Date.now()');
+ }
+ };
+
+ FakeDate.toSource = GlobalDate.toSource;
+ FakeDate.toString = GlobalDate.toString;
+ FakeDate.parse = GlobalDate.parse;
+ FakeDate.UTC = GlobalDate.UTC;
+ }
+ }
+
+ return MockDate;
+};
+
+getJasmineRequireObj().pp = function(j$) {
+
+ function PrettyPrinter() {
+ this.ppNestLevel_ = 0;
+ this.seen = [];
+ }
+
+ function hasCustomToString(value) {
+ // value.toString !== Object.prototype.toString if value has no custom toString but is from another context (e.g.
+ // iframe, web worker)
+ return value.toString !== Object.prototype.toString && (value.toString() !== Object.prototype.toString.call(value));
+ }
+
+ PrettyPrinter.prototype.format = function(value) {
+ this.ppNestLevel_++;
+ try {
+ if (j$.util.isUndefined(value)) {
+ this.emitScalar('undefined');
+ } else if (value === null) {
+ this.emitScalar('null');
+ } else if (value === 0 && 1/value === -Infinity) {
+ this.emitScalar('-0');
+ } else if (value === j$.getGlobal()) {
+ this.emitScalar('');
+ } else if (value.jasmineToString) {
+ this.emitScalar(value.jasmineToString());
+ } else if (typeof value === 'string') {
+ this.emitString(value);
+ } else if (j$.isSpy(value)) {
+ this.emitScalar('spy on ' + value.and.identity());
+ } else if (value instanceof RegExp) {
+ this.emitScalar(value.toString());
+ } else if (typeof value === 'function') {
+ this.emitScalar('Function');
+ } else if (typeof value.nodeType === 'number') {
+ this.emitScalar('HTMLNode');
+ } else if (value instanceof Date) {
+ this.emitScalar('Date(' + value + ')');
+ } else if (value.toString && value.toString() == '[object Set]') {
+ this.emitSet(value);
+ } else if (value.toString && typeof value === 'object' && !j$.isArray_(value) && hasCustomToString(value)) {
+ this.emitScalar(value.toString());
+ } else if (j$.util.arrayContains(this.seen, value)) {
+ this.emitScalar('');
+ } else if (j$.isArray_(value) || j$.isA_('Object', value)) {
+ this.seen.push(value);
+ if (j$.isArray_(value)) {
+ this.emitArray(value);
+ } else {
+ this.emitObject(value);
+ }
+ this.seen.pop();
+ } else {
+ this.emitScalar(value.toString());
+ }
+ } finally {
+ this.ppNestLevel_--;
+ }
+ };
+
+ PrettyPrinter.prototype.iterateObject = function(obj, fn) {
+ for (var property in obj) {
+ if (!Object.prototype.hasOwnProperty.call(obj, property)) { continue; }
+ fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) &&
+ obj.__lookupGetter__(property) !== null) : false);
+ }
+ };
+
+ PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_;
+ PrettyPrinter.prototype.emitSet = j$.unimplementedMethod_;
+ PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_;
+ PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_;
+ PrettyPrinter.prototype.emitString = j$.unimplementedMethod_;
+
+ function StringPrettyPrinter() {
+ PrettyPrinter.call(this);
+
+ this.string = '';
+ }
+
+ j$.util.inherit(StringPrettyPrinter, PrettyPrinter);
+
+ StringPrettyPrinter.prototype.emitScalar = function(value) {
+ this.append(value);
+ };
+
+ StringPrettyPrinter.prototype.emitString = function(value) {
+ this.append('\'' + value + '\'');
+ };
+
+ StringPrettyPrinter.prototype.emitArray = function(array) {
+ if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
+ this.append('Array');
+ return;
+ }
+ var length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
+ this.append('[ ');
+ for (var i = 0; i < length; i++) {
+ if (i > 0) {
+ this.append(', ');
+ }
+ this.format(array[i]);
+ }
+ if(array.length > length){
+ this.append(', ...');
+ }
+
+ var self = this;
+ var first = array.length === 0;
+ this.iterateObject(array, function(property, isGetter) {
+ if (property.match(/^\d+$/)) {
+ return;
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ self.append(', ');
+ }
+
+ self.formatProperty(array, property, isGetter);
+ });
+
+ this.append(' ]');
+ };
+
+ StringPrettyPrinter.prototype.emitSet = function(set) {
+ if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
+ this.append('Set');
+ return;
+ }
+ this.append('Set( ');
+ var size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
+ var iter = set.values();
+ for (var i = 0; i < size; i++) {
+ if (i > 0) {
+ this.append(', ');
+ }
+ this.format(iter.next().value);
+ }
+ if (set.size > size){
+ this.append(', ...');
+ }
+ this.append(' )');
+ };
+
+ StringPrettyPrinter.prototype.emitObject = function(obj) {
+ var ctor = obj.constructor,
+ constructorName;
+
+ constructorName = typeof ctor === 'function' && obj instanceof ctor ?
+ j$.fnNameFor(obj.constructor) :
+ 'null';
+
+ this.append(constructorName);
+
+ if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
+ return;
+ }
+
+ var self = this;
+ this.append('({ ');
+ var first = true;
+
+ this.iterateObject(obj, function(property, isGetter) {
+ if (first) {
+ first = false;
+ } else {
+ self.append(', ');
+ }
+
+ self.formatProperty(obj, property, isGetter);
+ });
+
+ this.append(' })');
+ };
+
+ StringPrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) {
+ this.append(property);
+ this.append(': ');
+ if (isGetter) {
+ this.append('');
+ } else {
+ this.format(obj[property]);
+ }
+ };
+
+ StringPrettyPrinter.prototype.append = function(value) {
+ this.string += value;
+ };
+
+ return function(value) {
+ var stringPrettyPrinter = new StringPrettyPrinter();
+ stringPrettyPrinter.format(value);
+ return stringPrettyPrinter.string;
+ };
+};
+
+getJasmineRequireObj().QueueRunner = function(j$) {
+
+ function once(fn) {
+ var called = false;
+ return function() {
+ if (!called) {
+ called = true;
+ fn();
+ }
+ return null;
+ };
+ }
+
+ function QueueRunner(attrs) {
+ this.queueableFns = attrs.queueableFns || [];
+ this.onComplete = attrs.onComplete || function() {};
+ this.clearStack = attrs.clearStack || function(fn) {fn();};
+ this.onException = attrs.onException || function() {};
+ this.catchException = attrs.catchException || function() { return true; };
+ this.userContext = attrs.userContext || {};
+ this.timeout = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout};
+ this.fail = attrs.fail || function() {};
+ this.globalErrors = attrs.globalErrors || { pushListener: function() {}, popListener: function() {} };
+ }
+
+ QueueRunner.prototype.execute = function() {
+ this.run(this.queueableFns, 0);
+ };
+
+ QueueRunner.prototype.run = function(queueableFns, recursiveIndex) {
+ var length = queueableFns.length,
+ self = this,
+ iterativeIndex;
+
+
+ for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) {
+ var queueableFn = queueableFns[iterativeIndex];
+ if (queueableFn.fn.length > 0) {
+ attemptAsync(queueableFn);
+ return;
+ } else {
+ attemptSync(queueableFn);
+ }
+ }
+
+ this.clearStack(this.onComplete);
+
+ function attemptSync(queueableFn) {
+ try {
+ queueableFn.fn.call(self.userContext);
+ } catch (e) {
+ handleException(e, queueableFn);
+ }
+ }
+
+ function attemptAsync(queueableFn) {
+ var clearTimeout = function () {
+ Function.prototype.apply.apply(self.timeout.clearTimeout, [j$.getGlobal(), [timeoutId]]);
+ },
+ handleError = function(error) {
+ onException(error);
+ next();
+ },
+ next = once(function () {
+ clearTimeout(timeoutId);
+ self.globalErrors.popListener(handleError);
+ self.run(queueableFns, iterativeIndex + 1);
+ }),
+ timeoutId;
+
+ next.fail = function() {
+ self.fail.apply(null, arguments);
+ next();
+ };
+
+ self.globalErrors.pushListener(handleError);
+
+ if (queueableFn.timeout) {
+ timeoutId = Function.prototype.apply.apply(self.timeout.setTimeout, [j$.getGlobal(), [function() {
+ var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.');
+ onException(error);
+ next();
+ }, queueableFn.timeout()]]);
+ }
+
+ try {
+ queueableFn.fn.call(self.userContext, next);
+ } catch (e) {
+ handleException(e, queueableFn);
+ next();
+ }
+ }
+
+ function onException(e) {
+ self.onException(e);
+ }
+
+ function handleException(e, queueableFn) {
+ onException(e);
+ if (!self.catchException(e)) {
+ //TODO: set a var when we catch an exception and
+ //use a finally block to close the loop in a nice way..
+ throw e;
+ }
+ }
+ };
+
+ return QueueRunner;
+};
+
+getJasmineRequireObj().ReportDispatcher = function() {
+ function ReportDispatcher(methods) {
+
+ var dispatchedMethods = methods || [];
+
+ for (var i = 0; i < dispatchedMethods.length; i++) {
+ var method = dispatchedMethods[i];
+ this[method] = (function(m) {
+ return function() {
+ dispatch(m, arguments);
+ };
+ }(method));
+ }
+
+ var reporters = [];
+ var fallbackReporter = null;
+
+ this.addReporter = function(reporter) {
+ reporters.push(reporter);
+ };
+
+ this.provideFallbackReporter = function(reporter) {
+ fallbackReporter = reporter;
+ };
+
+ this.clearReporters = function() {
+ reporters = [];
+ };
+
+ return this;
+
+ function dispatch(method, args) {
+ if (reporters.length === 0 && fallbackReporter !== null) {
+ reporters.push(fallbackReporter);
+ }
+ for (var i = 0; i < reporters.length; i++) {
+ var reporter = reporters[i];
+ if (reporter[method]) {
+ reporter[method].apply(reporter, args);
+ }
+ }
+ }
+ }
+
+ return ReportDispatcher;
+};
+
+
+getJasmineRequireObj().interface = function(jasmine, env) {
+ var jasmineInterface = {
+ /**
+ * Create a group of specs (often called a suite).
+ *
+ * Calls to `describe` can be nested within other calls to compose your suite as a tree.
+ * @name describe
+ * @function
+ * @global
+ * @param {String} description Textual description of the group
+ * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites a specs
+ */
+ describe: function(description, specDefinitions) {
+ return env.describe(description, specDefinitions);
+ },
+
+ /**
+ * A temporarily disabled [`describe`]{@link describe}
+ *
+ * Specs within an `xdescribe` will be marked pending and not executed
+ * @name xdescribe
+ * @function
+ * @global
+ * @param {String} description Textual description of the group
+ * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites a specs
+ */
+ xdescribe: function(description, specDefinitions) {
+ return env.xdescribe(description, specDefinitions);
+ },
+
+ /**
+ * A focused [`describe`]{@link describe}
+ *
+ * If suites or specs are focused, only those that are focused will be executed
+ * @see fit
+ * @name fdescribe
+ * @function
+ * @global
+ * @param {String} description Textual description of the group
+ * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites a specs
+ */
+ fdescribe: function(description, specDefinitions) {
+ return env.fdescribe(description, specDefinitions);
+ },
+
+ /**
+ * Define a single spec. A spec should contain one or more {@link expect|expectations} that test the state of the code.
+ *
+ * A spec whose expectations all succeed will be passing and a spec with any failures will fail.
+ * @name it
+ * @function
+ * @global
+ * @param {String} description Textual description of what this spec is checking
+ * @param {Function} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`.
+ * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec.
+ */
+ it: function() {
+ return env.it.apply(env, arguments);
+ },
+
+ /**
+ * A temporarily disabled [`it`]{@link it}
+ *
+ * The spec will report as `pending` and will not be executed.
+ * @name xit
+ * @function
+ * @global
+ * @param {String} description Textual description of what this spec is checking.
+ * @param {Function} [testFunction] Function that contains the code of your test. Will not be executed.
+ */
+ xit: function() {
+ return env.xit.apply(env, arguments);
+ },
+
+ /**
+ * A focused [`it`]{@link it}
+ *
+ * If suites or specs are focused, only those that are focused will be executed.
+ * @name fit
+ * @function
+ * @global
+ * @param {String} description Textual description of what this spec is checking.
+ * @param {Function} testFunction Function that contains the code of your test.
+ * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec.
+ */
+ fit: function() {
+ return env.fit.apply(env, arguments);
+ },
+
+ /**
+ * Run some shared setup before each of the specs in the {@link describe} in which it is called.
+ * @name beforeEach
+ * @function
+ * @global
+ * @param {Function} [function] Function that contains the code to setup your specs.
+ * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach.
+ */
+ beforeEach: function() {
+ return env.beforeEach.apply(env, arguments);
+ },
+
+ /**
+ * Run some shared teardown after each of the specs in the {@link describe} in which it is called.
+ * @name afterEach
+ * @function
+ * @global
+ * @param {Function} [function] Function that contains the code to teardown your specs.
+ * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach.
+ */
+ afterEach: function() {
+ return env.afterEach.apply(env, arguments);
+ },
+
+ /**
+ * Run some shared setup once before all of the specs in the {@link describe} are run.
+ *
+ * _Note:_ Be careful, sharing the setup from a beforeAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail.
+ * @name beforeAll
+ * @function
+ * @global
+ * @param {Function} [function] Function that contains the code to setup your specs.
+ * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll.
+ */
+ beforeAll: function() {
+ return env.beforeAll.apply(env, arguments);
+ },
+
+ /**
+ * Run some shared teardown once before all of the specs in the {@link describe} are run.
+ *
+ * _Note:_ Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail.
+ * @name afterAll
+ * @function
+ * @global
+ * @param {Function} [function] Function that contains the code to teardown your specs.
+ * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll.
+ */
+ afterAll: function() {
+ return env.afterAll.apply(env, arguments);
+ },
+
+ /**
+ * Create an expectation for a spec.
+ * @name expect
+ * @function
+ * @global
+ * @param {Object} actual - Actual computed value to test expectations against.
+ * @return {matchers}
+ */
+ expect: function(actual) {
+ return env.expect(actual);
+ },
+
+ /**
+ * Mark a spec as pending, expectation results will be ignored.
+ * @name pending
+ * @function
+ * @global
+ * @param {String} [message] - Reason the spec is pending.
+ */
+ pending: function() {
+ return env.pending.apply(env, arguments);
+ },
+
+ /**
+ * Explicitly mark a spec as failed.
+ * @name fail
+ * @function
+ * @global
+ * @param {String|Error} [error] - Reason for the failure.
+ */
+ fail: function() {
+ return env.fail.apply(env, arguments);
+ },
+
+ /**
+ * Install a spy onto an existing object.
+ * @name spyOn
+ * @function
+ * @global
+ * @param {Object} obj - The object upon which to install the {@link Spy}.
+ * @param {String} methodName - The name of the method to replace with a {@link Spy}.
+ * @returns {Spy}
+ */
+ spyOn: function(obj, methodName) {
+ return env.spyOn(obj, methodName);
+ },
+
+ /**
+ * Install a spy on a property onto an existing object.
+ * @name spyOnProperty
+ * @function
+ * @global
+ * @param {Object} obj - The object upon which to install the {@link Spy}
+ * @param {String} propertyName - The name of the property to replace with a {@link Spy}.
+ * @param {String} [accessType=get] - The access type (get|set) of the property to {@link Spy} on.
+ * @returns {Spy}
+ */
+ spyOnProperty: function(obj, methodName, accessType) {
+ return env.spyOnProperty(obj, methodName, accessType);
+ },
+
+ jsApiReporter: new jasmine.JsApiReporter({
+ timer: new jasmine.Timer()
+ }),
+
+ /**
+ * @namespace jasmine
+ */
+ jasmine: jasmine
+ };
+
+ /**
+ * Add a custom equality tester for the current scope of specs.
+ *
+ * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}.
+ * @name jasmine.addCustomEqualityTester
+ * @function
+ * @param {Function} tester - A function which takes two arguments to compare and returns a `true` or `false` comparison result if it knows how to compare them, and `undefined` otherwise.
+ * @see custom_equality
+ */
+ jasmine.addCustomEqualityTester = function(tester) {
+ env.addCustomEqualityTester(tester);
+ };
+
+ /**
+ * Add custom matchers for the current scope of specs.
+ *
+ * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}.
+ * @name jasmine.addMatchers
+ * @function
+ * @param {Object} matchers - Keys from this object will be the new matcher names.
+ * @see custom_matcher
+ */
+ jasmine.addMatchers = function(matchers) {
+ return env.addMatchers(matchers);
+ };
+
+ /**
+ * Get the currently booted mock {Clock} for this Jasmine environment.
+ * @name jasmine.clock
+ * @function
+ * @returns {Clock}
+ */
+ jasmine.clock = function() {
+ return env.clock;
+ };
+
+ return jasmineInterface;
+};
+
+getJasmineRequireObj().Spy = function (j$) {
+
+ var nextOrder = (function() {
+ var order = 0;
+
+ return function() {
+ return order++;
+ };
+ })();
+
+ /**
+ * _Note:_ Do not construct this directly, use {@link spyOn}, {@link spyOnProperty}, {@link jasmine.createSpy}, or {@link jasmine.createSpyObj}
+ * @constructor
+ * @name Spy
+ */
+ function Spy(name, originalFn) {
+ var args = buildArgs(),
+ /*`eval` is the only option to preserve both this and context:
+ - former is needed to work as expected with methods,
+ - latter is needed to access real spy function and allows to reduce eval'ed code to absolute minimum
+ More explanation here (look at comments): http://www.bennadel.com/blog/1909-javascript-function-constructor-does-not-create-a-closure.htm
+ */
+ /* jshint evil: true */
+ wrapper = eval('(0, function (' + args + ') { return spy.apply(this, Array.prototype.slice.call(arguments)); })'),
+ /* jshint evil: false */
+ spyStrategy = new j$.SpyStrategy({
+ name: name,
+ fn: originalFn,
+ getSpy: function () {
+ return wrapper;
+ }
+ }),
+ callTracker = new j$.CallTracker(),
+ spy = function () {
+ /**
+ * @name Spy.callData
+ * @property {object} object - `this` context for the invocation.
+ * @property {number} invocationOrder - Order of the invocation.
+ * @property {Array} args - The arguments passed for this invocation.
+ */
+ var callData = {
+ object: this,
+ invocationOrder: nextOrder(),
+ args: Array.prototype.slice.apply(arguments)
+ };
+
+ callTracker.track(callData);
+ var returnValue = spyStrategy.exec.apply(this, arguments);
+ callData.returnValue = returnValue;
+
+ return returnValue;
+ };
+
+ function buildArgs() {
+ var args = [];
+
+ while (originalFn instanceof Function && args.length < originalFn.length) {
+ args.push('arg' + args.length);
+ }
+
+ return args.join(', ');
+ }
+
+ for (var prop in originalFn) {
+ if (prop === 'and' || prop === 'calls') {
+ throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon');
+ }
+
+ wrapper[prop] = originalFn[prop];
+ }
+
+ wrapper.and = spyStrategy;
+ wrapper.calls = callTracker;
+
+ return wrapper;
+ }
+
+ return Spy;
+};
+
+getJasmineRequireObj().SpyRegistry = function(j$) {
+
+ var getErrorMsg = j$.formatErrorMsg('', 'spyOn(, )');
+
+ function SpyRegistry(options) {
+ options = options || {};
+ var currentSpies = options.currentSpies || function() { return []; };
+
+ this.allowRespy = function(allow){
+ this.respy = allow;
+ };
+
+ this.spyOn = function(obj, methodName) {
+
+ if (j$.util.isUndefined(obj) || obj === null) {
+ throw new Error(getErrorMsg('could not find an object to spy upon for ' + methodName + '()'));
+ }
+
+ if (j$.util.isUndefined(methodName) || methodName === null) {
+ throw new Error(getErrorMsg('No method name supplied'));
+ }
+
+ if (j$.util.isUndefined(obj[methodName])) {
+ throw new Error(getErrorMsg(methodName + '() method does not exist'));
+ }
+
+ if (obj[methodName] && j$.isSpy(obj[methodName]) ) {
+ if ( !!this.respy ){
+ return obj[methodName];
+ }else {
+ throw new Error(getErrorMsg(methodName + ' has already been spied upon'));
+ }
+ }
+
+ var descriptor;
+ try {
+ descriptor = Object.getOwnPropertyDescriptor(obj, methodName);
+ } catch(e) {
+ // IE 8 doesn't support `definePropery` on non-DOM nodes
+ }
+
+ if (descriptor && !(descriptor.writable || descriptor.set)) {
+ throw new Error(getErrorMsg(methodName + ' is not declared writable or has no setter'));
+ }
+
+ var originalMethod = obj[methodName],
+ spiedMethod = j$.createSpy(methodName, originalMethod),
+ restoreStrategy;
+
+ if (Object.prototype.hasOwnProperty.call(obj, methodName)) {
+ restoreStrategy = function() {
+ obj[methodName] = originalMethod;
+ };
+ } else {
+ restoreStrategy = function() {
+ if (!delete obj[methodName]) {
+ obj[methodName] = originalMethod;
+ }
+ };
+ }
+
+ currentSpies().push({
+ restoreObjectToOriginalState: restoreStrategy
+ });
+
+ obj[methodName] = spiedMethod;
+
+ return spiedMethod;
+ };
+
+ this.spyOnProperty = function (obj, propertyName, accessType) {
+ accessType = accessType || 'get';
+
+ if (j$.util.isUndefined(obj)) {
+ throw new Error('spyOn could not find an object to spy upon for ' + propertyName + '');
+ }
+
+ if (j$.util.isUndefined(propertyName)) {
+ throw new Error('No property name supplied');
+ }
+
+ var descriptor;
+ try {
+ descriptor = j$.util.getPropertyDescriptor(obj, propertyName);
+ } catch(e) {
+ // IE 8 doesn't support `definePropery` on non-DOM nodes
+ }
+
+ if (!descriptor) {
+ throw new Error(propertyName + ' property does not exist');
+ }
+
+ if (!descriptor.configurable) {
+ throw new Error(propertyName + ' is not declared configurable');
+ }
+
+ if(!descriptor[accessType]) {
+ throw new Error('Property ' + propertyName + ' does not have access type ' + accessType);
+ }
+
+ if (j$.isSpy(descriptor[accessType])) {
+ //TODO?: should this return the current spy? Downside: may cause user confusion about spy state
+ throw new Error(propertyName + ' has already been spied upon');
+ }
+
+ var originalDescriptor = j$.util.clone(descriptor),
+ spy = j$.createSpy(propertyName, descriptor[accessType]),
+ restoreStrategy;
+
+ if (Object.prototype.hasOwnProperty.call(obj, propertyName)) {
+ restoreStrategy = function() {
+ Object.defineProperty(obj, propertyName, originalDescriptor);
+ };
+ } else {
+ restoreStrategy = function() {
+ delete obj[propertyName];
+ };
+ }
+
+ currentSpies().push({
+ restoreObjectToOriginalState: restoreStrategy
+ });
+
+ descriptor[accessType] = spy;
+
+ Object.defineProperty(obj, propertyName, descriptor);
+
+ return spy;
+ };
+
+ this.clearSpies = function() {
+ var spies = currentSpies();
+ for (var i = spies.length - 1; i >= 0; i--) {
+ var spyEntry = spies[i];
+ spyEntry.restoreObjectToOriginalState();
+ }
+ };
+ }
+
+ return SpyRegistry;
+};
+
+getJasmineRequireObj().SpyStrategy = function(j$) {
+
+ /**
+ * @namespace Spy#and
+ */
+ function SpyStrategy(options) {
+ options = options || {};
+
+ var identity = options.name || 'unknown',
+ originalFn = options.fn || function() {},
+ getSpy = options.getSpy || function() {},
+ plan = function() {};
+
+ /**
+ * Return the identifying information for the spy.
+ * @name Spy#and#identity
+ * @function
+ * @returns {String}
+ */
+ this.identity = function() {
+ return identity;
+ };
+
+ /**
+ * Execute the current spy strategy.
+ * @name Spy#and#exec
+ * @function
+ */
+ this.exec = function() {
+ return plan.apply(this, arguments);
+ };
+
+ /**
+ * Tell the spy to call through to the real implementation when invoked.
+ * @name Spy#and#callThrough
+ * @function
+ */
+ this.callThrough = function() {
+ plan = originalFn;
+ return getSpy();
+ };
+
+ /**
+ * Tell the spy to return the value when invoked.
+ * @name Spy#and#returnValue
+ * @function
+ * @param {*} value The value to return.
+ */
+ this.returnValue = function(value) {
+ plan = function() {
+ return value;
+ };
+ return getSpy();
+ };
+
+ /**
+ * Tell the spy to return one of the specified values (sequentially) each time the spy is invoked.
+ * @name Spy#and#returnValues
+ * @function
+ * @param {...*} values - Values to be returned on subsequent calls to the spy.
+ */
+ this.returnValues = function() {
+ var values = Array.prototype.slice.call(arguments);
+ plan = function () {
+ return values.shift();
+ };
+ return getSpy();
+ };
+
+ /**
+ * Tell the spy to throw an error when invoked.
+ * @name Spy#and#throwError
+ * @function
+ * @param {Error|String} something Thing to throw
+ */
+ this.throwError = function(something) {
+ var error = (something instanceof Error) ? something : new Error(something);
+ plan = function() {
+ throw error;
+ };
+ return getSpy();
+ };
+
+ /**
+ * Tell the spy to call a fake implementation when invoked.
+ * @name Spy#and#callFake
+ * @function
+ * @param {Function} fn The function to invoke with the passed parameters.
+ */
+ this.callFake = function(fn) {
+ if(!j$.isFunction_(fn)) {
+ throw new Error('Argument passed to callFake should be a function, got ' + fn);
+ }
+ plan = fn;
+ return getSpy();
+ };
+
+ /**
+ * Tell the spy to do nothing when invoked. This is the default.
+ * @name Spy#and#stub
+ * @function
+ */
+ this.stub = function(fn) {
+ plan = function() {};
+ return getSpy();
+ };
+ }
+
+ return SpyStrategy;
+};
+
+getJasmineRequireObj().Suite = function(j$) {
+ function Suite(attrs) {
+ this.env = attrs.env;
+ this.id = attrs.id;
+ this.parentSuite = attrs.parentSuite;
+ this.description = attrs.description;
+ this.expectationFactory = attrs.expectationFactory;
+ this.expectationResultFactory = attrs.expectationResultFactory;
+ this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
+
+ this.beforeFns = [];
+ this.afterFns = [];
+ this.beforeAllFns = [];
+ this.afterAllFns = [];
+
+ this.children = [];
+
+ this.result = {
+ id: this.id,
+ description: this.description,
+ fullName: this.getFullName(),
+ failedExpectations: []
+ };
+ }
+
+ Suite.prototype.expect = function(actual) {
+ return this.expectationFactory(actual, this);
+ };
+
+ Suite.prototype.getFullName = function() {
+ var fullName = [];
+ for (var parentSuite = this; parentSuite; parentSuite = parentSuite.parentSuite) {
+ if (parentSuite.parentSuite) {
+ fullName.unshift(parentSuite.description);
+ }
+ }
+ return fullName.join(' ');
+ };
+
+ Suite.prototype.pend = function() {
+ this.markedPending = true;
+ };
+
+ Suite.prototype.beforeEach = function(fn) {
+ this.beforeFns.unshift(fn);
+ };
+
+ Suite.prototype.beforeAll = function(fn) {
+ this.beforeAllFns.push(fn);
+ };
+
+ Suite.prototype.afterEach = function(fn) {
+ this.afterFns.unshift(fn);
+ };
+
+ Suite.prototype.afterAll = function(fn) {
+ this.afterAllFns.unshift(fn);
+ };
+
+ Suite.prototype.addChild = function(child) {
+ this.children.push(child);
+ };
+
+ Suite.prototype.status = function() {
+ if (this.markedPending) {
+ return 'pending';
+ }
+
+ if (this.result.failedExpectations.length > 0) {
+ return 'failed';
+ } else {
+ return 'finished';
+ }
+ };
+
+ Suite.prototype.isExecutable = function() {
+ return !this.markedPending;
+ };
+
+ Suite.prototype.canBeReentered = function() {
+ return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0;
+ };
+
+ Suite.prototype.getResult = function() {
+ this.result.status = this.status();
+ return this.result;
+ };
+
+ Suite.prototype.sharedUserContext = function() {
+ if (!this.sharedContext) {
+ this.sharedContext = this.parentSuite ? clone(this.parentSuite.sharedUserContext()) : {};
+ }
+
+ return this.sharedContext;
+ };
+
+ Suite.prototype.clonedSharedUserContext = function() {
+ return clone(this.sharedUserContext());
+ };
+
+ Suite.prototype.onException = function() {
+ if (arguments[0] instanceof j$.errors.ExpectationFailed) {
+ return;
+ }
+
+ if(isAfterAll(this.children)) {
+ var data = {
+ matcherName: '',
+ passed: false,
+ expected: '',
+ actual: '',
+ error: arguments[0]
+ };
+ this.result.failedExpectations.push(this.expectationResultFactory(data));
+ } else {
+ for (var i = 0; i < this.children.length; i++) {
+ var child = this.children[i];
+ child.onException.apply(child, arguments);
+ }
+ }
+ };
+
+ Suite.prototype.addExpectationResult = function () {
+ if(isAfterAll(this.children) && isFailure(arguments)){
+ var data = arguments[1];
+ this.result.failedExpectations.push(this.expectationResultFactory(data));
+ if(this.throwOnExpectationFailure) {
+ throw new j$.errors.ExpectationFailed();
+ }
+ } else {
+ for (var i = 0; i < this.children.length; i++) {
+ var child = this.children[i];
+ try {
+ child.addExpectationResult.apply(child, arguments);
+ } catch(e) {
+ // keep going
+ }
+ }
+ }
+ };
+
+ function isAfterAll(children) {
+ return children && children[0].result.status;
+ }
+
+ function isFailure(args) {
+ return !args[0];
+ }
+
+ function clone(obj) {
+ var clonedObj = {};
+ for (var prop in obj) {
+ if (obj.hasOwnProperty(prop)) {
+ clonedObj[prop] = obj[prop];
+ }
+ }
+
+ return clonedObj;
+ }
+
+ return Suite;
+};
+
+if (typeof window == void 0 && typeof exports == 'object') {
+ exports.Suite = jasmineRequire.Suite;
+}
+
+getJasmineRequireObj().Timer = function() {
+ var defaultNow = (function(Date) {
+ return function() { return new Date().getTime(); };
+ })(Date);
+
+ function Timer(options) {
+ options = options || {};
+
+ var now = options.now || defaultNow,
+ startTime;
+
+ this.start = function() {
+ startTime = now();
+ };
+
+ this.elapsed = function() {
+ return now() - startTime;
+ };
+ }
+
+ return Timer;
+};
+
+getJasmineRequireObj().TreeProcessor = function() {
+ function TreeProcessor(attrs) {
+ var tree = attrs.tree,
+ runnableIds = attrs.runnableIds,
+ queueRunnerFactory = attrs.queueRunnerFactory,
+ nodeStart = attrs.nodeStart || function() {},
+ nodeComplete = attrs.nodeComplete || function() {},
+ orderChildren = attrs.orderChildren || function(node) { return node.children; },
+ stats = { valid: true },
+ processed = false,
+ defaultMin = Infinity,
+ defaultMax = 1 - Infinity;
+
+ this.processTree = function() {
+ processNode(tree, false);
+ processed = true;
+ return stats;
+ };
+
+ this.execute = function(done) {
+ if (!processed) {
+ this.processTree();
+ }
+
+ if (!stats.valid) {
+ throw 'invalid order';
+ }
+
+ var childFns = wrapChildren(tree, 0);
+
+ queueRunnerFactory({
+ queueableFns: childFns,
+ userContext: tree.sharedUserContext(),
+ onException: function() {
+ tree.onException.apply(tree, arguments);
+ },
+ onComplete: done
+ });
+ };
+
+ function runnableIndex(id) {
+ for (var i = 0; i < runnableIds.length; i++) {
+ if (runnableIds[i] === id) {
+ return i;
+ }
+ }
+ }
+
+ function processNode(node, parentEnabled) {
+ var executableIndex = runnableIndex(node.id);
+
+ if (executableIndex !== undefined) {
+ parentEnabled = true;
+ }
+
+ parentEnabled = parentEnabled && node.isExecutable();
+
+ if (!node.children) {
+ stats[node.id] = {
+ executable: parentEnabled && node.isExecutable(),
+ segments: [{
+ index: 0,
+ owner: node,
+ nodes: [node],
+ min: startingMin(executableIndex),
+ max: startingMax(executableIndex)
+ }]
+ };
+ } else {
+ var hasExecutableChild = false;
+
+ var orderedChildren = orderChildren(node);
+
+ for (var i = 0; i < orderedChildren.length; i++) {
+ var child = orderedChildren[i];
+
+ processNode(child, parentEnabled);
+
+ if (!stats.valid) {
+ return;
+ }
+
+ var childStats = stats[child.id];
+
+ hasExecutableChild = hasExecutableChild || childStats.executable;
+ }
+
+ stats[node.id] = {
+ executable: hasExecutableChild
+ };
+
+ segmentChildren(node, orderedChildren, stats[node.id], executableIndex);
+
+ if (!node.canBeReentered() && stats[node.id].segments.length > 1) {
+ stats = { valid: false };
+ }
+ }
+ }
+
+ function startingMin(executableIndex) {
+ return executableIndex === undefined ? defaultMin : executableIndex;
+ }
+
+ function startingMax(executableIndex) {
+ return executableIndex === undefined ? defaultMax : executableIndex;
+ }
+
+ function segmentChildren(node, orderedChildren, nodeStats, executableIndex) {
+ var currentSegment = { index: 0, owner: node, nodes: [], min: startingMin(executableIndex), max: startingMax(executableIndex) },
+ result = [currentSegment],
+ lastMax = defaultMax,
+ orderedChildSegments = orderChildSegments(orderedChildren);
+
+ function isSegmentBoundary(minIndex) {
+ return lastMax !== defaultMax && minIndex !== defaultMin && lastMax < minIndex - 1;
+ }
+
+ for (var i = 0; i < orderedChildSegments.length; i++) {
+ var childSegment = orderedChildSegments[i],
+ maxIndex = childSegment.max,
+ minIndex = childSegment.min;
+
+ if (isSegmentBoundary(minIndex)) {
+ currentSegment = {index: result.length, owner: node, nodes: [], min: defaultMin, max: defaultMax};
+ result.push(currentSegment);
+ }
+
+ currentSegment.nodes.push(childSegment);
+ currentSegment.min = Math.min(currentSegment.min, minIndex);
+ currentSegment.max = Math.max(currentSegment.max, maxIndex);
+ lastMax = maxIndex;
+ }
+
+ nodeStats.segments = result;
+ }
+
+ function orderChildSegments(children) {
+ var specifiedOrder = [],
+ unspecifiedOrder = [];
+
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i],
+ segments = stats[child.id].segments;
+
+ for (var j = 0; j < segments.length; j++) {
+ var seg = segments[j];
+
+ if (seg.min === defaultMin) {
+ unspecifiedOrder.push(seg);
+ } else {
+ specifiedOrder.push(seg);
+ }
+ }
+ }
+
+ specifiedOrder.sort(function(a, b) {
+ return a.min - b.min;
+ });
+
+ return specifiedOrder.concat(unspecifiedOrder);
+ }
+
+ function executeNode(node, segmentNumber) {
+ if (node.children) {
+ return {
+ fn: function(done) {
+ nodeStart(node);
+
+ queueRunnerFactory({
+ onComplete: function() {
+ nodeComplete(node, node.getResult());
+ done();
+ },
+ queueableFns: wrapChildren(node, segmentNumber),
+ userContext: node.sharedUserContext(),
+ onException: function() {
+ node.onException.apply(node, arguments);
+ }
+ });
+ }
+ };
+ } else {
+ return {
+ fn: function(done) { node.execute(done, stats[node.id].executable); }
+ };
+ }
+ }
+
+ function wrapChildren(node, segmentNumber) {
+ var result = [],
+ segmentChildren = stats[node.id].segments[segmentNumber].nodes;
+
+ for (var i = 0; i < segmentChildren.length; i++) {
+ result.push(executeNode(segmentChildren[i].owner, segmentChildren[i].index));
+ }
+
+ if (!stats[node.id].executable) {
+ return result;
+ }
+
+ return node.beforeAllFns.concat(result).concat(node.afterAllFns);
+ }
+ }
+
+ return TreeProcessor;
+};
+
+getJasmineRequireObj().version = function() {
+ return '2.6.0';
+};
diff --git a/node_modules/svg.js/spec/lib/jasmine-2.6.0/jasmine_favicon.png b/node_modules/svg.js/spec/lib/jasmine-2.6.0/jasmine_favicon.png
new file mode 100644
index 0000000..3b84583
Binary files /dev/null and b/node_modules/svg.js/spec/lib/jasmine-2.6.0/jasmine_favicon.png differ
diff --git a/node_modules/svg.js/spec/spec/adopter.js b/node_modules/svg.js/spec/spec/adopter.js
new file mode 100644
index 0000000..a73bc35
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/adopter.js
@@ -0,0 +1,81 @@
+describe('Adopter', function() {
+ var path, polyline, polygon, linearGradient, radialGradient
+
+ beforeEach(function() {
+ path = SVG.get('lineAB')
+ polyline = SVG.get('inlineSVG').select('polyline').first()
+ polygon = SVG.get('inlineSVG').select('polygon').first()
+ linearGradient = SVG.get('inlineSVG').select('linearGradient').first()
+ radialGradient = SVG.get('inlineSVG').select('radialGradient').first()
+ })
+
+ describe('with SVG.Doc instance', function() {
+ it('adopts the main svg document when parent() method is called on first level children', function() {
+ expect(path.parent() instanceof SVG.Doc).toBeTruthy()
+ })
+ it('defines a xmlns attribute', function() {
+ expect(path.parent().node.getAttribute('xmlns')).toBe(SVG.ns)
+ })
+ it('defines a version attribute', function() {
+ expect(path.parent().node.getAttribute('version')).toBe('1.1')
+ })
+ it('defines a xmlns:xlink attribute', function() {
+ expect(path.parent().node.getAttribute('xmlns:xlink')).toBe(SVG.xlink)
+ })
+ it('initializes a defs node', function() {
+ expect(path.parent()._defs).toBe(path.parent().defs())
+ })
+ })
+
+ describe('with SVG.Path instance', function() {
+ it('adopts an exiting path element', function() {
+ expect(path instanceof SVG.Path).toBeTruthy()
+ })
+ it('modifies an adopted element', function() {
+ path.fill('#f06')
+ expect(path.node.getAttribute('fill')).toBe('#ff0066')
+ })
+ it('parses d attribute to SVG.PathArray', function() {
+ expect(path.array() instanceof SVG.PathArray).toBeTruthy()
+ })
+ })
+
+ describe('with SVG.Polyline instance', function() {
+ it('parses points attribute to SVG.PointArray', function() {
+ expect(polyline.array() instanceof SVG.PointArray).toBeTruthy()
+ })
+ })
+
+ describe('with SVG.Polygon instance', function() {
+ it('parses points attribute to SVG.PointArray', function() {
+ expect(polygon.array() instanceof SVG.PointArray).toBeTruthy()
+ })
+ })
+
+ describe('with linear SVG.Gradient instance', function() {
+ it('is instance of SVG.Gradient', function() {
+ expect(linearGradient instanceof SVG.Gradient).toBeTruthy()
+ })
+ it('has type of linear', function() {
+ expect(linearGradient.type).toBe('linearGradient') // actually it should be 'linear'. see #606
+ })
+ })
+
+ describe('with radial SVG.Gradient instance', function() {
+ it('is instance of SVG.Gradient', function() {
+ expect(radialGradient instanceof SVG.Gradient).toBeTruthy()
+ })
+ it('has type of radial', function() {
+ expect(radialGradient.type).toBe('radialGradient') // actually it should be 'radial'. see #606
+ })
+ })
+
+ describe('with node that has no matching svg.js class', function() {
+ it('wraps the node in the base SVG.Element class', function() {
+ var desc = SVG.get('inlineSVG').select('desc').first()
+ expect(desc instanceof SVG.Element).toBeTruthy()
+ })
+ })
+
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/spec/arrange.js b/node_modules/svg.js/spec/spec/arrange.js
new file mode 100644
index 0000000..3424b2a
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/arrange.js
@@ -0,0 +1,185 @@
+describe('Arrange', function() {
+ var e1, e2, e3
+
+ beforeEach(function() {
+ draw.clear()
+
+ e1 = draw.rect(100,100).move(10,10).attr('id', 'e1')
+ e2 = draw.ellipse(100,100).move(20,20).attr('id', 'e2')
+ e3 = draw.line(0,0,100,100).move(30,30).attr('id', 'e3')
+ })
+
+ describe('siblings()', function() {
+ it('returns all siblings of targeted element', function() {
+ expect(e1.siblings().length).toBe(3+parserInDoc)
+ expect(parser.concat([e1,e2,e3])).toEqual(e2.siblings())
+ })
+ })
+
+ describe('position()', function() {
+ it('returns the index position within it\'s parent', function() {
+ expect(e1.siblings().length).toBe(3+parserInDoc)
+ expect(e1.position()).toBe(0+parserInDoc)
+ expect(e2.position()).toBe(1+parserInDoc)
+ expect(e3.position()).toBe(2+parserInDoc)
+ })
+ })
+
+ describe('next()', function() {
+ it('returns the next sibling within the parent element', function() {
+ expect(e1.next()).toBe(e2)
+ expect(e2.next()).toBe(e3)
+ expect(e3.next()).toBe(undefined)
+ })
+ })
+
+ describe('previous()', function() {
+ it('returns the previous sibling within the parent element', function() {
+ expect(e1.previous()).toBe(parser[0])
+ expect(e2.previous()).toBe(e1)
+ expect(e3.previous()).toBe(e2)
+ })
+ })
+
+ describe('forward()', function() {
+ it('returns the element itself', function() {
+ expect(e1.forward()).toBe(e1)
+ })
+ it('moves the element one step forward within its parent', function() {
+ e1.forward()
+ expect(e1.position()).toBe(1+parserInDoc)
+ expect(e2.position()).toBe(0+parserInDoc)
+ expect(e3.position()).toBe(2+parserInDoc)
+ })
+ it('keeps the last element at the same position', function() {
+ e3.forward()
+ expect(e3.position()).toBe(2+parserInDoc)
+ })
+ it('keeps the defs on top of the stack', function() {
+ draw.defs()
+ e3.forward()
+ expect(draw.node.childNodes[2+parserInDoc]).toBe(e3.node)
+ expect(draw.node.childNodes[3+parserInDoc]).toBe(draw.defs().node)
+ })
+ })
+
+ describe('backward()', function() {
+ it('returns the element itself', function() {
+ if(parserInDoc){
+ expect(parser[0].backward()).toBe(parser[0])
+ }else{
+ expect(e1.backward()).toBe(e1)
+ }
+ })
+ it('moves the element one step backwards within its parent', function() {
+ e3.backward()
+ expect(e1.position()).toBe(0+parserInDoc)
+ expect(e2.position()).toBe(2+parserInDoc)
+ expect(e3.position()).toBe(1+parserInDoc)
+ })
+ it('keeps the first element at the same position', function() {
+ e3.backward()
+ expect(e1.position()).toBe(0+parserInDoc)
+ })
+ })
+
+ describe('front()', function() {
+ it('returns the element itself', function() {
+ expect(e3.front()).toBe(e3)
+ })
+ it('moves the element to the top of the stack within its parent', function() {
+ e1.front()
+ expect(e1.position()).toBe(2+parserInDoc)
+ expect(e2.position()).toBe(0+parserInDoc)
+ expect(e3.position()).toBe(1+parserInDoc)
+ })
+ it('keeps the last element at the same position', function() {
+ e3.front()
+ expect(e3.position()).toBe(2+parserInDoc)
+ })
+ it('keeps the defs on top of the stack', function() {
+ e1.front()
+ expect(draw.node.childNodes[2+parserInDoc]).toBe(e1.node)
+ expect(draw.node.childNodes[3+parserInDoc]).toBe(draw.defs().node)
+ })
+ })
+
+ describe('back()', function() {
+ it('returns the element itself', function() {
+ expect(e3.back()).toBe(e3)
+ })
+ it('moves the element to the bottom of the stack within its parent', function() {
+ e3.back()
+ expect(e1.position()).toBe(1+parserInDoc)
+ expect(e2.position()).toBe(2+parserInDoc)
+ expect(e3.position()).toBe(0)
+ })
+ it('keeps the first element at the same position', function() {
+ e1.back()
+ expect(e1.position()).toBe(0)
+ })
+ })
+
+ describe('before()', function() {
+ it('returns the targeted element itself', function() {
+ expect(e3.before(e1)).toBe(e3)
+ })
+ it('inserts a given element before the targeted element', function() {
+ e3.before(e1)
+ expect(e1.position()).toBe(1+parserInDoc)
+ expect(e2.position()).toBe(0+parserInDoc)
+ expect(e3.position()).toBe(2+parserInDoc)
+ })
+ it('moves elements between containers', function() {
+ var group = draw.group()
+ , e4 = group.rect(80,120)
+ , e5 = group.rect(80,120)
+ , e6 = group.rect(80,120)
+
+ e2.before(e5)
+ expect(e1.position()).toBe(0+parserInDoc)
+ expect(e2.position()).toBe(2+parserInDoc)
+ expect(e3.position()).toBe(3+parserInDoc)
+ expect(e5.position()).toBe(1+parserInDoc)
+ })
+ })
+
+ describe('after()', function() {
+ it('returns the targeted element itself', function() {
+ expect(e3.after(e1)).toBe(e3)
+ })
+ it('inserts a given element after the targeted element', function() {
+ e3.after(e1)
+ expect(e1.position()).toBe(2+parserInDoc)
+ expect(e2.position()).toBe(0+parserInDoc)
+ expect(e3.position()).toBe(1+parserInDoc)
+ })
+ it('moves elements between containers', function() {
+ var group = draw.group()
+ , e4 = group.rect(80,120)
+ , e5 = group.rect(80,120)
+ , e6 = group.rect(80,120)
+
+ e2.after(e5)
+ expect(e1.position()).toBe(0+parserInDoc)
+ expect(e2.position()).toBe(1+parserInDoc)
+ expect(e3.position()).toBe(3+parserInDoc)
+ expect(e5.position()).toBe(2+parserInDoc)
+ })
+ })
+
+})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/node_modules/svg.js/spec/spec/array.js b/node_modules/svg.js/spec/spec/array.js
new file mode 100644
index 0000000..bdf91ce
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/array.js
@@ -0,0 +1,420 @@
+describe('Array', function () {
+ var array, arr1, arr2
+
+ it('parses a matrix array correctly to string', function() {
+ array = new SVG.Array([ .343, .669, .119, 0, 0
+ , .249, -.626, .130, 0, 0
+ , .172, .334, .111, 0, 0
+ , .000, .000, .000, 1, -0 ])
+
+ expect(array + '').toBe('0.343 0.669 0.119 0 0 0.249 -0.626 0.13 0 0 0.172 0.334 0.111 0 0 0 0 0 1 0')
+ })
+ it('parses space seperated string and converts it to array', function() {
+ expect((new SVG.Array('1 2 3 4')).value).toEqual([1,2,3,4])
+ })
+ it('parses comma seperated string and converts it to array', function() {
+ expect((new SVG.Array('1,2,3,4')).value).toEqual([1,2,3,4])
+ })
+ describe('reverse()', function() {
+ it('reverses the array', function() {
+ array = new SVG.Array([1 ,2 ,3, 4, 5]).reverse()
+ expect(array.value).toEqual([5, 4, 3, 2, 1])
+ })
+ it('returns itself', function() {
+ array = new SVG.Array()
+ expect(array.reverse()).toBe(array)
+ })
+ })
+ describe('clone()', function() {
+ it('creates a deep clone of the array', function() {
+ array = new SVG.Array([1, 2, 3, 4, 5])
+
+ clone = array.clone()
+
+ expect(array).toEqual(clone)
+ expect(array).not.toBe(clone)
+
+ array = new SVG.Array([[1,2], [3, 4], [5]])
+ clone = array.clone()
+
+ expect(array).toEqual(array)
+ for(var i = 0, len = array.value.length; i; ++i){
+ expect(array[i]).not.toBe(clone[i])
+ }
+ })
+ it('also works with PointArray', function() {
+ array = new SVG.PointArray([1,2,3,4,5,6])
+ clone = array.clone()
+
+ expect(array).toEqual(clone)
+ expect(array).not.toBe(clone)
+
+ for(var i = 0, len = array.value.length; i; ++i){
+ expect(array[i]).not.toBe(clone[i])
+ }
+ })
+ it('also works with PathArray', function() {
+ array = new SVG.PathArray([['M',1,2],['L',3,4],['L',5,6]])
+ clone = array.clone()
+
+ expect(array).toEqual(clone)
+ expect(array).not.toBe(clone)
+
+ for(var i = 0, len = array.value.length; i; ++i){
+ expect(array[i]).not.toBe(clone[i])
+ }
+ })
+ })
+ describe('morph()', function() {
+ it('adds entries so that destination array has equal length', function() {
+
+ arr1 = new SVG.Array([1,2,3,4,5])
+ arr2 = new SVG.Array([1,2,3,4])
+
+ arr1.morph(arr2)
+
+ expect(arr1.destination.length).toBe(arr1.value.length)
+ })
+ it('does the same the other way round', function() {
+
+ arr1 = new SVG.Array([1,2,3,4])
+ arr2 = new SVG.Array([1,2,3,4,5])
+
+ arr1.morph(arr2)
+
+ expect(arr1.destination.length).toBe(arr1.value.length)
+ })
+ })
+ describe('settle()', function() {
+ it('cleans up any duplicate value', function() {
+ array = new SVG.Array([1,2,3,4,5,4,3,2,1])
+ expect(array.settle().sort()).toEqual([1,2,3,4,5].sort())
+ })
+ })
+ describe('at()', function() {
+ beforeEach(function() {
+ arr1 = new SVG.Array([1,2,3,4])
+ arr2 = new SVG.Array([2,3,4,5])
+ })
+
+ it('returns a new array instance', function() {
+ arr1.morph(arr2)
+
+ start = arr1.at(0)
+ end = arr1.at(1)
+
+ expect(start instanceof SVG.Array).toBeTruthy()
+ expect(start).not.toBe(arr1)
+
+ expect(end instanceof SVG.Array).toBeTruthy()
+ expect(end).not.toBe(arr2)
+ })
+ it('morphs all values of the array', function() {
+ arr1.morph(arr2)
+ expect(arr1.at(0.5).value).toEqual([1.5, 2.5, 3.5, 4.5])
+ })
+ it('returns itself if no destination was specified', function() {
+ expect(arr1.at(0.5)).toBe(arr1)
+ })
+ })
+})
+
+
+describe('PointArray', function () {
+ it('parses a string to a point array', function() {
+ var array = new SVG.PointArray('0,1 -.05,7.95 1000.0001,-200.222')
+
+ expect(array.valueOf()).toEqual([[0, 1], [-0.05, 7.95], [1000.0001, -200.222]])
+ })
+ it('parses a points array correctly to string', function() {
+ var array = new SVG.PointArray([[0,.15], [-100,-3.141592654], [50,100]])
+
+ expect(array + '').toBe('0,0.15 -100,-3.141592654 50,100')
+ })
+ it('parses a flat array of x/y coordinates to a point array', function() {
+ var array = new SVG.PointArray([1,4, 5,68, 12,24])
+
+ expect(array.value).toEqual([[1,4], [5,68], [12,24]])
+ })
+ it('parses an array of arrays correctly', function () {
+ var array = new SVG.PointArray([[1,4], [5,68], [12,24]])
+
+ expect(array.value).toEqual([[1,4], [5,68], [12,24]])
+ })
+ it('copies array if necessary', function () {
+ var arr = [[1,4], [5,68], [12,24]]
+ var array = new SVG.PointArray(arr)
+
+ expect(array.valueOf()).not.toBe(arr)
+ })
+ it('parses an array of point objects correctly', function () {
+ var array = new SVG.PointArray([{x:1,y:4}, {x:5,y:68}, {x:12,y:24}])
+
+ expect(array.value).toEqual([[1,4], [5,68], [12,24]])
+ })
+ it('parses points with space delimitered x/y coordinates', function() {
+ var array = new SVG.PointArray('221.08 191.79 0.46 191.79 0.46 63.92 63.8 0.46 284.46 0.46 284.46 128.37 221.08 191.79')
+
+ expect(array + '').toBe('221.08,191.79 0.46,191.79 0.46,63.92 63.8,0.46 284.46,0.46 284.46,128.37 221.08,191.79')
+ })
+ it('parses points with comma delimitered x/y coordinates', function() {
+ var array = new SVG.PointArray('221.08,191.79,0.46,191.79,0.46,63.92,63.8,0.46,284.46,0.46,284.46,128.37,221.08,191.79')
+
+ expect(array + '').toBe('221.08,191.79 0.46,191.79 0.46,63.92 63.8,0.46 284.46,0.46 284.46,128.37 221.08,191.79')
+ })
+ it('parses points with comma and space delimitered x/y coordinates', function() {
+ var array = new SVG.PointArray('221.08, 191.79, 0.46, 191.79, 0.46, 63.92, 63.8, 0.46, 284.46, 0.46, 284.46, 128.37, 221.08, 191.79')
+
+ expect(array + '').toBe('221.08,191.79 0.46,191.79 0.46,63.92 63.8,0.46 284.46,0.46 284.46,128.37 221.08,191.79')
+ })
+ it('parses points with space and comma delimitered x/y coordinates', function() {
+ var array = new SVG.PointArray('221.08 ,191.79 ,0.46 ,191.79 ,0.46 ,63.92 ,63.8 ,0.46 ,284.46 ,0.46 ,284.46 ,128.37 ,221.08 ,191.79')
+
+ expect(array + '').toBe('221.08,191.79 0.46,191.79 0.46,63.92 63.8,0.46 284.46,0.46 284.46,128.37 221.08,191.79')
+ })
+ it('parses points with redundant spaces at the end', function() {
+ var array = new SVG.PointArray('2176.6,1708.8 2176.4,1755.8 2245.8,1801.5 2297,1787.8 ')
+
+ expect(array + '').toBe('2176.6,1708.8 2176.4,1755.8 2245.8,1801.5 2297,1787.8')
+ })
+ it('parses points with space delimitered x/y coordinates - even with leading or trailing space', function() {
+ var array = new SVG.PointArray(' 1 2 3 4 ')
+
+ expect(array + '').toBe('1,2 3,4')
+ })
+ it('parses odd number of points with space delimitered x/y coordinates and silently remove the odd point', function() {
+ // this is according to spec: https://svgwg.org/svg2-draft/shapes.html#DataTypePoints
+
+ var array = new SVG.PointArray('1 2 3')
+
+ expect(array + '').toBe('1,2')
+ })
+ it('parses odd number of points in a flat array of x/y coordinates and silently remove the odd point', function() {
+ // this is according to spec: https://svgwg.org/svg2-draft/shapes.html#DataTypePoints
+
+ var array = new SVG.PointArray([1, 2, 3])
+
+ expect(array.value).toEqual([[1,2]])
+ })
+
+ describe('size()', function() {
+ it('correctly sizes the points over the whole area', function() {
+ var array = new SVG.PointArray([10, 10, 20, 20, 30, 30])
+ expect(array.size(60, 60).valueOf()).toEqual([[10,10], [40, 40], [70, 70]])
+ })
+
+ it('let coordinates untouched when width/height is zero', function() {
+ var array = new SVG.PointArray([10, 10, 10, 20, 10, 30])
+ expect(array.size(60, 60).valueOf()).toEqual([[10,10], [10, 40], [10, 70]])
+
+ array = new SVG.PointArray([10, 10, 20, 10, 30, 10])
+ expect(array.size(60, 60).valueOf()).toEqual([[10,10], [40, 10], [70, 10]])
+ })
+
+ })
+
+ describe('at()', function() {
+ var arr1, arr2
+
+ beforeEach(function() {
+ arr1 = new SVG.PointArray([[1,2],[3,4]])
+ arr2 = new SVG.Array([[2,3],[4,5]])
+ })
+
+ it('returns a new array instance', function() {
+ arr1.morph(arr2)
+
+ start = arr1.at(0)
+ end = arr1.at(1)
+
+ expect(start instanceof SVG.PointArray).toBeTruthy()
+ expect(start).not.toBe(arr1)
+
+ expect(end instanceof SVG.PointArray).toBeTruthy()
+ expect(end).not.toBe(arr2)
+ })
+ it('morphs all values of the array', function() {
+ arr1.morph(arr2)
+ expect(arr1.at(0.5).value).toEqual([[1.5, 2.5], [3.5, 4.5]])
+ })
+ it('returns itself if no destination was specified', function() {
+ expect(arr1.at(0.5)).toBe(arr1)
+ })
+ })
+})
+
+describe('PathArray', function () {
+ var p1, p2, p3, p4, p5, p6, p7
+
+ beforeEach(function() {
+ p1 = new SVG.PathArray('m10 10 h 80 v 80 h -80 l 300 400 z')
+ p2 = new SVG.PathArray('m10 80 c 40 10 65 10 95 80 s 150 150 180 80 t 300 300 q 52 10 95 80 z')
+ p3 = new SVG.PathArray('m80 80 A 45 45, 0, 0, 0, 125 125 L 125 80 z')
+ p4 = new SVG.PathArray('M215.458,245.23c0,0,77.403,0,94.274,0S405,216.451,405,138.054S329.581,15,287.9,15c-41.68,0-139.924,0-170.688,0C86.45,15,15,60.65,15,134.084c0,73.434,96.259,112.137,114.122,112.137C146.984,246.221,215.458,245.23,215.458,245.23z')
+ p5 = new SVG.PathArray('M10 10-45-30.5.5 .89L2e-2.5.5.5-.5C.5.5.5.5.5.5L-3-4z')
+ p6 = new SVG.PathArray('m 0,0 0,3189 2209,0 0,-3189 -2209,0 z m 154,154 1901,0 0,2881 -1901,0 0,-2881 z')
+
+ })
+
+ it('converts to absolute values', function() {
+ expect(p1.toString()).toBe('M10 10H90V90H10L310 490Z ')
+ expect(p2.toString()).toBe('M10 80C50 90 75 90 105 160S255 310 285 240T585 540Q637 550 680 620Z ')
+ expect(p3.toString()).toBe('M80 80A45 45 0 0 0 125 125L125 80Z ')
+ expect(p4.toString()).toBe('M215.458 245.23C215.458 245.23 292.861 245.23 309.73199999999997 245.23S405 216.451 405 138.054S329.581 15 287.9 15C246.21999999999997 15 147.97599999999997 15 117.21199999999999 15C86.45 15 15 60.65 15 134.084C15 207.518 111.259 246.221 129.122 246.221C146.984 246.221 215.458 245.23 215.458 245.23Z ')
+ expect(p6.toString()).toBe('M0 0L0 3189L2209 3189L2209 0L0 0ZM154 154L2055 154L2055 3035L154 3035L154 154Z ')
+ })
+
+ it('parses difficult syntax correctly', function() {
+ expect(p5.toString()).toBe('M10 10L-45 -30.5L0.5 0.89L0.02 0.5L0.5 -0.5C0.5 0.5 0.5 0.5 0.5 0.5L-3 -4Z ')
+ })
+
+ it('parses flat arrays correctly', function() {
+ p6 = new SVG.PathArray([ 'M', 0, 0, 'L', 100, 100, 'z' ])
+ expect(p6.toString()).toBe('M0 0L100 100Z ')
+ })
+
+ it('parses nested arrays correctly', function() {
+ p7 = new SVG.PathArray([ ['M', 0, 0], ['L', 100, 100], ['z'] ])
+ expect(p7.toString()).toBe('M0 0L100 100Z ')
+ })
+
+ // this test is designed to cover a certain line but it doesnt work because of #608
+ it('returns the valueOf when PathArray is given', function() {
+ var p = new SVG.PathArray('m10 10 h 80 v 80 h -80 l 300 400 z')
+
+ expect((new SVG.PathArray(p)).value).toEqual(p.value)
+ })
+
+ it('can handle all formats which can be used', function() {
+ // when no command is specified after move, line is used automatically (specs say so)
+ expect(new SVG.PathArray('M10 10 80 80 30 30 Z').toString()).toBe('M10 10L80 80L30 30Z ')
+
+ // parsing can handle 0.5.3.3.2 stuff
+ expect(new SVG.PathArray('M10 10L.5.5.3.3Z').toString()).toBe('M10 10L0.5 0.5L0.3 0.3Z ')
+ })
+
+ describe('move()', function() {
+ it('moves all points in a straight path', function() {
+ expect(p1.move(100,200).toString()).toBe('M100 200H180V280H100L400 680Z ')
+ })
+ it('moves all points in a curved path', function() {
+ expect(p2.move(100,200).toString()).toBe('M100 200C140 210 165 210 195 280S345 430 375 360T675 660Q727 670 770 740Z ')
+ })
+ it('moves all points in a arc path', function() {
+ expect(p3.move(100,200).toString()).toBe('M100 200A45 45 0 0 0 145 245L145 200Z ')
+ })
+ })
+
+ describe('size()', function() {
+ it('resizes all points in a straight path', function() {
+ expect(p1.size(600,200).toString()).toBe('M10 10H170V43.333333333333336H10L610 210Z ')
+ })
+ it('resizes all points in a curved path', function() {
+ expect(p2.size(600,200).toString()).toBe('M10 80C45.82089552238806 83.70370370370371 68.2089552238806 83.70370370370371 95.07462686567165 109.62962962962963S229.40298507462686 165.1851851851852 256.2686567164179 139.25925925925927T524.9253731343283 250.37037037037038Q571.4925373134329 254.07407407407408 610 280Z ')
+ })
+ it('resizes all points in a arc path', function() {
+ var expected = [
+ ['M', 80, 80],
+ ['A', 600, 200, 0, 0, 0, 680, 280],
+ ['L', 680, 80],
+ ['Z']
+ ]
+
+ var toBeTested = p3.size(600,200).value
+ for(var i in toBeTested) {
+ expect(toBeTested[i].shift().toUpperCase()).toBe(expected[i].shift().toUpperCase())
+ for(var j in toBeTested[i]) {
+ expect(toBeTested[i][j]).toBeCloseTo(expected[i][j])
+ }
+ }
+ })
+ })
+
+ describe('equalCommands()', function() {
+ it('return true if the passed path array use the same commands', function() {
+ var pathArray1 = new SVG.PathArray('m -1500,-478 a 292,195 0 0 1 262,205 l -565,319 c 0,0 -134,-374 51,-251 185,122 251,-273 251,-273 z')
+ , pathArray2 = new SVG.PathArray('m -680, 527 a 292,195 0 0 1 262,205 l -565,319 c 0,0 -134,-374 51,-251 185,122 251,-273 251,-273 z')
+
+ expect(pathArray1.equalCommands(pathArray2)).toBe(true)
+ })
+ it('return false if the passed path array does not use the same commands', function() {
+ var pathArray1 = new SVG.PathArray('m -1500,-478 a 292,195 0 0 1 262,205 l -565,319 c 0,0 -134,-374 51,-251 185,122 251,-273 251,-273 z')
+ , pathArray2 = new SVG.PathArray('m - 663, 521 c 147,178 118,-25 245,210 l -565,319 c 0,0 -134,-374 51,-251 185,122 268,-278 268,-278 z')
+
+ expect(pathArray1.equalCommands(pathArray2)).toBe(false)
+ })
+ })
+
+ describe('morph()', function() {
+ it('should set the attribute destination to the passed path array when it have the same comands as this path array', function() {
+ var pathArray1 = new SVG.PathArray('m -1500,-478 a 292,195 0 0 1 262,205 l -565,319 c 0,0 -134,-374 51,-251 185,122 251,-273 251,-273 z')
+ , pathArray2 = new SVG.PathArray('m -680, 527 a 292,195 0 0 1 262,205 l -565,319 c 0,0 -134,-374 51,-251 185,122 251,-273 251,-273 z')
+
+ pathArray1.morph(pathArray2)
+ expect(pathArray1.destination).toEqual(pathArray2)
+ })
+ it('should set the attribute destination to null when the passed path array does not have the same comands as this path array', function() {
+ var pathArray1 = new SVG.PathArray('m -1500,-478 a 292,195 0 0 1 262,205 l -565,319 c 0,0 -134,-374 51,-251 185,122 251,-273 251,-273 z')
+ , pathArray2 = new SVG.PathArray('m - 663, 521 c 147,178 118,-25 245,210 l -565,319 c 0,0 -134,-374 51,-251 185,122 268,-278 268,-278 z')
+
+ pathArray1.morph(pathArray2)
+ expect(pathArray1.destination).toBeNull()
+ })
+ })
+
+ describe('at()', function() {
+ it('returns a morphed path array at a given position', function() {
+ var pathArray1 = new SVG.PathArray("M 63 25 A 15 15 0 0 1 73 40 A 15 15 0 0 1 61 53 C 49 36 50 59 50 59 L 33 55 Z")
+ , pathArray2 = new SVG.PathArray("M 132 40 A 15 15 0 0 1 141 54 A 15 15 0 0 1 130 67 C 118 51 119 73 119 73 L 103 69 Z")
+ , morphedPathArray = pathArray1.morph(pathArray2).at(0.5)
+ , sourceArray = pathArray1.value, destinationArray = pathArray1.destination.value
+ , morphedArray = morphedPathArray.value
+ , i, il, j, jl
+
+ expect(morphedArray.length).toBe(sourceArray.length)
+
+ // For all the commands
+ for(i = 0, il = sourceArray.length; i < il; i++) {
+ // Expect the current command to be the same
+ expect(morphedArray[i][0]).toBe(sourceArray[i][0])
+ expect(morphedArray[i].length).toBe(sourceArray[i].length)
+
+ // For all the parameters of the current command
+ for(j = 1, jl = sourceArray[i].length; j < jl; j++) {
+ expect(morphedArray[i][j]).toBe((sourceArray[i][j] + destinationArray[i][j]) / 2)
+ }
+ }
+ })
+ it('should interpolate flags and booleans as fractions between zero and one, with any non-zero value considered to be a value of one/true', function() {
+ // Only the Elliptical arc command use flags, it has the following form:
+ // A rx ry x-axis-rotation large-arc-flag sweep-flag x y
+ var pathArray1 = new SVG.PathArray('M 13 13 A 25 37 0 0 1 43 25')
+ , pathArray2 = new SVG.PathArray('M 101 55 A 25 37 0 1 0 130 67')
+ , morphedPathArray
+
+ pathArray1.morph(pathArray2)
+
+ // The morphedPathArray.value contain 2 commands: [['M', ...], ['A', ...]]
+ // Elliptical arc command in a path array followed by corresponding indexes:
+ // ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
+ // 0 1 2 3 4 5 6 7
+ morphedPathArray = pathArray1.at(0)
+ expect(morphedPathArray.value[1][4]).toBe(0)
+ expect(morphedPathArray.value[1][5]).toBe(1)
+
+ morphedPathArray = pathArray1.at(0.5)
+ expect(morphedPathArray.value[1][4]).toBe(1)
+ expect(morphedPathArray.value[1][5]).toBe(1)
+
+ morphedPathArray = pathArray1.at(1)
+ expect(morphedPathArray.value[1][4]).toBe(1)
+ expect(morphedPathArray.value[1][5]).toBe(0)
+ })
+ it('return itself if the destination attribute is null', function(){
+ var pathArray = new SVG.PathArray('M 13 13 A 25 37 0 0 1 43 25')
+ pathArray.destination = null
+ expect(pathArray.at(0.45)).toBe(pathArray)
+ })
+ })
+
+})
diff --git a/node_modules/svg.js/spec/spec/bare.js b/node_modules/svg.js/spec/spec/bare.js
new file mode 100644
index 0000000..0488bbd
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/bare.js
@@ -0,0 +1,41 @@
+describe('Bare', function() {
+
+ describe('element()', function() {
+ var element
+
+ beforeEach(function() {
+ element = draw.element('rect')
+ })
+
+ it('creates an instance of SVG.Bare', function() {
+ expect(element instanceof SVG.Bare).toBeTruthy()
+ })
+ it('creates element in called parent', function() {
+ expect(element.parent()).toBe(draw)
+ })
+ it('inherits from given parent', function() {
+ expect(draw.element('g', SVG.Container).rect).toBeTruthy()
+ expect(draw.element('g', SVG.Container).group).toBeTruthy()
+ })
+ })
+
+ describe('words()', function() {
+ it('inserts plain text in a node', function() {
+ var element = draw.element('title').words('These are some words.').id(null)
+ var result = element.svg()
+ expect(
+ result == 'These are some words. '
+ || result == 'These are some words. '
+ ).toBe(true)
+ })
+ it('removes all nodes before adding words', function() {
+ var element = draw.element('title').words('These are some words.').id(null)
+ element.words('These are some words.')
+ var result = element.svg()
+ expect(
+ result == 'These are some words. '
+ || result == 'These are some words. '
+ ).toBe(true)
+ })
+ })
+})
diff --git a/node_modules/svg.js/spec/spec/boxes.js b/node_modules/svg.js/spec/spec/boxes.js
new file mode 100644
index 0000000..b3c9685
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/boxes.js
@@ -0,0 +1,233 @@
+describe('Box', function() {
+ it('creates a new instance without passing anything', function() {
+ var box = new SVG.Box
+
+ expect(box instanceof SVG.Box).toBe(true)
+ expect(box).toEqual(jasmine.objectContaining({
+ x:0, y:0, cx:0, cy:0, width:0, height:0
+ }))
+ })
+
+ it('creates a new instance with 4 arguments given', function() {
+ var box = new SVG.Box(10, 20, 100, 50)
+
+ expect(box instanceof SVG.Box).toBe(true)
+ expect(box).toEqual(jasmine.objectContaining({
+ x:10, y:20, cx:60, cy:45, width:100, height:50
+ }))
+ })
+
+ it('creates a new instance with object given', function() {
+ var box = new SVG.Box({x:10, y:20, width: 100, height:50})
+
+ expect(box instanceof SVG.Box).toBe(true)
+ expect(box).toEqual(jasmine.objectContaining({
+ x:10, y:20, cx:60, cy:45, width:100, height:50
+ }))
+ })
+
+ describe('merge()', function() {
+ it('merges various bounding boxes', function() {
+ var box1 = new SVG.Box(50, 50, 100, 100)
+ var box2 = new SVG.Box(300, 400, 100, 100)
+ var box3 = new SVG.Box(500, 100, 100, 100)
+ var merged = box1.merge(box2).merge(box3)
+
+ expect(merged).toEqual(jasmine.objectContaining({
+ x: 50, y: 50, cx: 325, cy: 275, width: 550, height: 450
+ }))
+ })
+ it('returns a new instance', function() {
+ var box1 = new SVG.Box(50, 50, 100, 100)
+ var box2 = new SVG.Box(300, 400, 100, 100)
+ var merged = box1.merge(box2)
+ expect(box1).not.toBe(merged)
+ expect(box2).not.toBe(merged)
+
+ expect(merged instanceof SVG.Box).toBe(true)
+ })
+ })
+
+ describe('transform()', function() {
+ it('transforms the box with given matrix', function() {
+ var box1 = new SVG.Box(50, 50, 100, 100).transform(new SVG.Matrix(1,0,0,1,20,20))
+ var box2 = new SVG.Box(50, 50, 100, 100).transform(new SVG.Matrix(2,0,0,2,0,0))
+ var box3 = new SVG.Box(-200, -200, 100, 100).transform(new SVG.Matrix(1,0,0,1,-20,-20))
+
+ expect(box1).toEqual(jasmine.objectContaining({
+ x: 70, y: 70, cx: 120, cy: 120, width: 100, height: 100
+ }))
+
+ expect(box2).toEqual(jasmine.objectContaining({
+ x: 100, y: 100, cx: 200, cy: 200, width: 200, height: 200
+ }))
+
+ expect(box3).toEqual(jasmine.objectContaining({
+ x: -220, y: -220, cx: -170, cy: -170, width: 100, height: 100
+ }))
+ })
+ })
+})
+
+describe('BBox', function() {
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ it('creates a new instance from an element', function() {
+ var rect = draw.rect(100, 100).move(100, 25)
+ var box = new SVG.BBox(rect)
+
+ expect(box).toEqual(jasmine.objectContaining({
+ x: 100, y: 25, cx: 150, cy: 75, width: 100, height: 100
+ }))
+ })
+
+ describe('merge()', function() {
+ it('returns an instance of SVG.BBox', function() {
+ var box1 = new SVG.BBox(50, 50, 100, 100)
+ var box2 = new SVG.BBox(300, 400, 100, 100)
+ var merged = box1.merge(box2)
+
+ expect(merged instanceof SVG.BBox).toBe(true)
+ })
+ })
+
+})
+
+describe('TBox', function() {
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ it('should map to RBox and be removed in 3.x', function() {
+ var rect = draw.rect(100, 100).move(100, 25).stroke({width:0})
+ var tbox = rect.tbox()
+
+ expect(tbox.x).toBe(100)
+ expect(tbox.y).toBeCloseTo(25)
+
+ rect.transform({ scale: 1.5 })
+ tbox = rect.tbox()
+ expect(tbox.x).toBe(75)
+ expect(tbox.y).toBe(0)
+
+ rect.transform({ skewX: 5 })
+ tbox = rect.tbox()
+ expect(tbox.x|0).toBe(68)
+ expect(tbox.y|0).toBe(0)
+ })
+
+})
+
+describe('RBox', function() {
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ it('creates a new instance from an element', function() {
+ var rect = draw.rect(100, 100).move(100, 25).stroke({width:0})
+ var box = new SVG.RBox(rect).transform(rect.doc().screenCTM().inverse()).addOffset()
+ expect(window.roundBox(box)).toEqual(jasmine.objectContaining({
+ x: 100, y: 25, cx: 150, cy: 75, width: 100, height: 100
+ }))
+ })
+
+ describe('merge()', function() {
+ it('returns an instance of SVG.RBox', function() {
+ var box1 = new SVG.RBox(50, 50, 100, 100)
+ var box2 = new SVG.RBox(300, 400, 100, 100)
+ var merged = box1.merge(box2)
+
+ expect(merged instanceof SVG.RBox).toBe(true)
+ })
+ })
+})
+
+describe('Boxes', function() {
+ var rect, nested, offset
+
+ beforeEach(function() {
+ offset = draw.screenCTM()
+ draw.viewbox(100,100, 200, 200)
+ nested = draw.nested().size(200, 200).move(100,100).viewbox(0, 0, 100, 100)
+ rect = nested.rect(50, 180).stroke({width:0}).move(25, 90).scale(2, 0, 0).transform({x:10, y:10}, true)
+ })
+ afterEach(function() {
+ draw.clear().attr('viewBox', null)
+ })
+
+ describe('bbox()', function() {
+ it('returns an instance of SVG.BBox', function() {
+ expect(rect.bbox() instanceof SVG.BBox).toBeTruthy()
+ })
+ it('matches the size of the target element, ignoring transformations', function() {
+ var box = rect.bbox()
+
+ expect(box).toEqual(jasmine.objectContaining({
+ x: 25, y: 90, cx: 50, cy: 180, width: 50, height: 180
+ }))
+ })
+ it('returns a box even if the element is not in the dom', function() {
+ var line = new SVG.Line().plot(0, 0, 50, 50)
+ var box = line.bbox()
+
+ expect(box).toEqual(jasmine.objectContaining({
+ x: 0, y: 0, width: 50, height: 50
+ }))
+
+ expect('Should not result into infinite loop').toBe('Should not result into infinite loop')
+ })
+ it('returns a box even if the element is not in the dom and invisible', function() {
+ var line = new SVG.Line().plot(0, 0, 50, 50).hide()
+ var box = line.bbox()
+
+ expect(box).toEqual(jasmine.objectContaining({
+ x: 0, y: 0, width: 50, height: 50
+ }))
+
+ expect('Should not result into infinite loop').toBe('Should not result into infinite loop')
+ })
+ })
+
+ describe('rbox()', function() {
+ it('returns an instance of SVG.RBox', function() {
+ expect(rect.rbox() instanceof SVG.RBox).toBeTruthy()
+ })
+
+ it('returns the elements box in absolute screen coordinates by default', function() {
+ var box = rect.rbox()
+
+ expect(window.roundBox(box)).toEqual(jasmine.objectContaining(window.roundBox({
+ x: 70 + offset.e, y: 200 + offset.f, width: 100, height: 360
+ })))
+
+ })
+
+ it('returns the elements box in coordinates of given element (doc)', function() {
+ var box = rect.rbox(draw)
+
+ expect(window.roundBox(box)).toEqual(jasmine.objectContaining({
+ x: 240, y: 500, width: 200, height: 720
+ }))
+ })
+
+ it('returns the elements box in coordinates of given element (nested)', function() {
+ var box = rect.rbox(nested)
+
+ expect(window.roundBox(box)).toEqual(jasmine.objectContaining({
+ x: 70, y: 200, width: 100, height: 360
+ }))
+ })
+ })
+
+})
+
+
+
+
+
+
diff --git a/node_modules/svg.js/spec/spec/circle.js b/node_modules/svg.js/spec/spec/circle.js
new file mode 100644
index 0000000..fc66e0c
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/circle.js
@@ -0,0 +1,177 @@
+describe('Circle', function() {
+ var circle
+
+ beforeEach(function() {
+ circle = draw.circle(240)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ describe('x()', function() {
+ it('returns the value of x without an argument', function() {
+ expect(circle.x()).toBe(0)
+ })
+ it('sets the value of x with the first argument', function() {
+ circle.x(123)
+ var box = circle.bbox()
+ expect(box.x).toBeCloseTo(123)
+ })
+ })
+
+ describe('y()', function() {
+ it('returns the value of y without an argument', function() {
+ expect(circle.y()).toBe(0)
+ })
+ it('sets the value of cy with the first argument', function() {
+ circle.y(345)
+ var box = circle.bbox()
+ expect(box.y).toBe(345)
+ })
+ })
+
+ describe('cx()', function() {
+ it('returns the value of cx without an argument', function() {
+ expect(circle.cx()).toBe(120)
+ })
+ it('sets the value of cx with the first argument', function() {
+ circle.cx(123)
+ var box = circle.bbox()
+ expect(box.cx).toBe(123)
+ })
+ })
+
+ describe('cy()', function() {
+ it('returns the value of cy without an argument', function() {
+ expect(circle.cy()).toBe(120)
+ })
+ it('sets the value of cy with the first argument', function() {
+ circle.cy(345)
+ var box = circle.bbox()
+ expect(box.cy).toBe(345)
+ })
+ })
+
+ describe('radius()', function() {
+ it('sets the r attribute with the first argument', function() {
+ circle.radius(10)
+ expect(circle.node.getAttribute('r')).toBe('10')
+ })
+ })
+
+ describe('rx()', function() {
+ it('sets the r attribute with the first argument', function() {
+ circle.rx(11)
+ expect(circle.node.getAttribute('r')).toBe('11')
+ })
+ it('gets the r attribute without and argument', function() {
+ circle.rx()
+ expect(circle.node.getAttribute('r')).toBe('120')
+ })
+ })
+
+ describe('ry()', function() {
+ it('sets the r attribute with the first argument', function() {
+ circle.ry(12)
+ expect(circle.node.getAttribute('r')).toBe('12')
+ })
+ it('gets the r attribute without and argument', function() {
+ circle.ry()
+ expect(circle.node.getAttribute('r')).toBe('120')
+ })
+ })
+
+ describe('move()', function() {
+ it('sets the x and y position', function() {
+ circle.move(123, 456)
+ var box = circle.bbox()
+ expect(box.x).toBeCloseTo(123)
+ expect(box.y).toBe(456)
+ })
+ })
+
+ describe('dx()', function() {
+ it('moves the x positon of the element relative to the current position', function() {
+ circle.move(50, 60)
+ circle.dx(100)
+ expect(circle.node.getAttribute('cx')).toBe('270')
+ })
+ })
+
+ describe('dy()', function() {
+ it('moves the y positon of the element relative to the current position', function() {
+ circle.move(50, 60)
+ circle.dy(120)
+ expect(circle.node.getAttribute('cy')).toBe('300')
+ })
+ })
+
+ describe('dmove()', function() {
+ it('moves the x and y positon of the element relative to the current position', function() {
+ circle.move(50,60)
+ circle.dmove(80, 25)
+ expect(circle.node.getAttribute('cx')).toBe('250')
+ expect(circle.node.getAttribute('cy')).toBe('205')
+ })
+ })
+
+ describe('center()', function() {
+ it('sets the cx and cy position', function() {
+ circle.center(321,567)
+ var box = circle.bbox()
+ expect(box.cx).toBe(321)
+ expect(box.cy).toBe(567)
+ })
+ })
+
+ describe('width()', function() {
+ it('sets the width and height of the element', function() {
+ circle.width(82)
+ expect(circle.node.getAttribute('r')).toBe('41')
+ })
+ it('gets the width and height of the element if the argument is null', function() {
+ expect((circle.width() / 2).toString()).toBe(circle.node.getAttribute('r'))
+ })
+ })
+
+ describe('height()', function() {
+ it('sets the height and width of the element', function() {
+ circle.height(1236)
+ expect(circle.node.getAttribute('r')).toBe('618')
+ })
+ it('gets the height and width of the element if the argument is null', function() {
+ expect((circle.height() / 2).toString()).toBe(circle.node.getAttribute('r'))
+ })
+ })
+
+ describe('size()', function() {
+ it('defines the r of the element', function() {
+ circle.size(987)
+ expect(circle.node.getAttribute('r')).toBe((987 / 2).toString())
+ })
+ })
+
+ describe('scale()', function() {
+ it('should scale the element universally with one argument', function() {
+ var box = circle.scale(2).rbox()
+
+ expect(box.width).toBe(circle.attr('r') * 2 * 2)
+ expect(box.height).toBe(circle.attr('r') * 2 * 2)
+ })
+ it('should scale the element over individual x and y axes with two arguments', function() {
+ var box = circle.scale(2, 3.5).rbox()
+
+ expect(box.width).toBe(circle.attr('r') * 2 * 2)
+ expect(box.height).toBe(circle.attr('r') * 2 * 3.5)
+ })
+ })
+
+ describe('translate()', function() {
+ it('sets the translation of an element', function() {
+ circle.transform({ x: 12, y: 12 })
+ expect(window.matrixStringToArray(circle.node.getAttribute('transform'))).toEqual([1,0,0,1,12,12])
+ })
+ })
+
+})
diff --git a/node_modules/svg.js/spec/spec/clip.js b/node_modules/svg.js/spec/spec/clip.js
new file mode 100644
index 0000000..c641c4c
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/clip.js
@@ -0,0 +1,62 @@
+describe('ClipPath', function() {
+ var rect, circle
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ circle = draw.circle(100).move(50, 50)
+ rect.clipWith(circle)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ it('moves the clipping element to a new clip node', function() {
+ expect(circle.parent() instanceof SVG.ClipPath).toBe(true)
+ })
+
+ it('creates the clip node in the defs node', function() {
+ expect(circle.parent().parent()).toBe(draw.defs())
+ })
+
+ it('sets the "clip-path" attribute on the cliped element with the clip id', function() {
+ expect(rect.attr('clip-path')).toBe('url("#' + circle.parent().attr('id') + '")')
+ })
+
+ it('references the clip element in the masked element', function() {
+ expect(rect.clipper).toBe(circle.parent())
+ })
+
+ it('references the clipped element in the clipPath target list', function() {
+ expect(rect.clipper.targets.indexOf(rect) > -1).toBe(true)
+ })
+
+ it('reuses clip element when clip was given', function() {
+ var clip = rect.clipper
+ expect(draw.rect(100,100).clipWith(clip).clipper).toBe(clip)
+ })
+
+ it('unclips all clipped elements when being removed', function() {
+ rect.clipper.remove()
+ expect(rect.attr('clip-path')).toBe(undefined)
+ })
+
+ describe('unclip()', function() {
+
+ it('clears the "clip-path" attribute on the clipped element', function() {
+ rect.unclip()
+ expect(rect.attr('clip-path')).toBe(undefined)
+ })
+
+ it('removes the reference to the clipping element', function() {
+ rect.unclip()
+ expect(rect.clipper).toBe(undefined)
+ })
+
+ it('returns the clipPath element', function() {
+ expect(rect.unclip()).toBe(rect)
+ })
+
+ })
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/spec/color.js b/node_modules/svg.js/spec/spec/color.js
new file mode 100644
index 0000000..1e86544
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/color.js
@@ -0,0 +1,86 @@
+describe('Color', function() {
+ var color
+
+ beforeEach(function() {
+ color = new SVG.Color({ r: 0, g: 102, b: 255 })
+ })
+
+ it('correclty parses a rgb string', function() {
+ color = new SVG.Color('rgb(255,0,128)')
+ expect(color.r).toBe(255)
+ expect(color.g).toBe(0)
+ expect(color.b).toBe(128)
+ })
+
+ it('correclty parses a 3 digit hex string', function() {
+ color = new SVG.Color('#f06')
+ expect(color.r).toBe(255)
+ expect(color.g).toBe(0)
+ expect(color.b).toBe(102)
+ })
+
+ it('correclty parses a 6 digit hex string', function() {
+ color = new SVG.Color('#0066ff')
+ expect(color.r).toBe(0)
+ expect(color.g).toBe(102)
+ expect(color.b).toBe(255)
+ })
+
+ describe('toHex()', function() {
+ it('returns a hex color', function() {
+ expect(color.toHex()).toBe('#0066ff')
+ })
+ })
+
+ describe('toRgb()', function() {
+ it('returns a rgb string color', function() {
+ expect(color.toRgb()).toBe('rgb(0,102,255)')
+ })
+ })
+
+ describe('brightness()', function() {
+ it('returns the percieved brightness value of a color', function() {
+ expect(color.brightness()).toBe(0.346)
+ })
+ })
+
+ describe('morph()', function() {
+ it('prepares the color for morphing', function() {
+ var destination = new SVG.Color
+ color.morph(destination)
+ expect(color.destination).toEqual(destination)
+ })
+ })
+
+ describe('at()', function() {
+ it('morphes color to a given position', function() {
+ var destination = new SVG.Color
+ var morphed = color.morph(destination).at(0.5)
+ expect(morphed.r).toBe(0)
+ expect(morphed.g).toBe(51)
+ expect(morphed.b).toBe(127)
+ })
+
+ it('morphes color to 1 with higher values', function() {
+ var destination = new SVG.Color('#fff')
+ var morphed = color.morph(destination).at(2)
+ expect(morphed.r).toBe(255)
+ expect(morphed.g).toBe(255)
+ expect(morphed.b).toBe(255)
+ })
+
+ it('morphes color to 0 with lower values', function() {
+ var destination = new SVG.Color('#fff')
+ var morphed = color.morph(destination).at(-3)
+ expect(morphed.r).toBe(0)
+ expect(morphed.g).toBe(102)
+ expect(morphed.b).toBe(255)
+ })
+
+ it('returns itself when no destination specified', function() {
+ expect(color.at(0.5)).toBe(color)
+ })
+ })
+
+})
+
diff --git a/node_modules/svg.js/spec/spec/container.js b/node_modules/svg.js/spec/spec/container.js
new file mode 100644
index 0000000..b9fabe7
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/container.js
@@ -0,0 +1,362 @@
+describe('Container', function() {
+
+ beforeEach(function() {
+ draw.clear()
+ })
+
+ describe('rect()', function() {
+ it('should increase children by 1', function() {
+ var initial = draw.children().length
+ draw.rect(100,100)
+ expect(draw.children().length).toBe(initial + 1)
+ })
+ it('should create a rect', function() {
+ expect(draw.rect(100,100).type).toBe('rect')
+ })
+ it('should create an instance of SVG.Rect', function() {
+ expect(draw.rect(100,100) instanceof SVG.Rect).toBe(true)
+ })
+ it('should be an instance of SVG.Shape', function() {
+ expect(draw.rect(100,100) instanceof SVG.Shape).toBe(true)
+ })
+ it('should be an instance of SVG.Element', function() {
+ expect(draw.rect(100,100) instanceof SVG.Element).toBe(true)
+ })
+ })
+
+ describe('ellipse()', function() {
+ it('should increase children by 1', function() {
+ var initial = draw.children().length
+ draw.ellipse(100,100)
+ expect(draw.children().length).toBe(initial + 1)
+ })
+ it('should create an ellipse', function() {
+ expect(draw.ellipse(100,100).type).toBe('ellipse')
+ })
+ it('should create an instance of SVG.Ellipse', function() {
+ expect(draw.ellipse(100,100) instanceof SVG.Ellipse).toBe(true)
+ })
+ it('should be an instance of SVG.Shape', function() {
+ expect(draw.ellipse(100,100) instanceof SVG.Shape).toBe(true)
+ })
+ it('should be an instance of SVG.Element', function() {
+ expect(draw.ellipse(100,100) instanceof SVG.Element).toBe(true)
+ })
+ })
+
+ describe('circle()', function() {
+ it('should increase children by 1', function() {
+ var initial = draw.children().length
+ draw.circle(100)
+ expect(draw.children().length).toBe(initial + 1)
+ })
+ it('should create an circle', function() {
+ expect(draw.circle(100).type).toBe('circle')
+ })
+ it('should create an instance of SVG.Circle', function() {
+ expect(draw.circle(100) instanceof SVG.Circle).toBe(true)
+ })
+ it('should be an instance of SVG.Shape', function() {
+ expect(draw.circle(100) instanceof SVG.Shape).toBe(true)
+ })
+ it('should be an instance of SVG.Element', function() {
+ expect(draw.circle(100) instanceof SVG.Element).toBe(true)
+ })
+ })
+
+ describe('line()', function() {
+ it('should increase children by 1', function() {
+ var initial = draw.children().length
+ draw.line(0,100,100,0)
+ expect(draw.children().length).toBe(initial + 1)
+ })
+ it('should create a line', function() {
+ expect(draw.line(0,100,100,0).type).toBe('line')
+ })
+ it('should create an instance of SVG.Line', function() {
+ expect(draw.line(0,100,100,0) instanceof SVG.Line).toBe(true)
+ })
+ it('should be an instance of SVG.Shape', function() {
+ expect(draw.line(0,100,100,0) instanceof SVG.Shape).toBe(true)
+ })
+ it('should be an instance of SVG.Element', function() {
+ expect(draw.line(0,100,100,0) instanceof SVG.Element).toBe(true)
+ })
+ })
+
+ describe('polyline()', function() {
+ it('should increase children by 1', function() {
+ var initial = draw.children().length
+ draw.polyline('0,0 100,0 100,100 0,100')
+ expect(draw.children().length).toBe(initial + 1)
+ })
+ it('should create a polyline', function() {
+ expect(draw.polyline('0,0 100,0 100,100 0,100').type).toBe('polyline')
+ })
+ it('should be an instance of SVG.Polyline', function() {
+ expect(draw.polyline('0,0 100,0 100,100 0,100') instanceof SVG.Polyline).toBe(true)
+ })
+ it('should be an instance of SVG.Shape', function() {
+ expect(draw.polyline('0,0 100,0 100,100 0,100') instanceof SVG.Shape).toBe(true)
+ })
+ it('should be an instance of SVG.Element', function() {
+ expect(draw.polyline('0,0 100,0 100,100 0,100') instanceof SVG.Element).toBe(true)
+ })
+ })
+
+ describe('polygon()', function() {
+ it('should increase children by 1', function() {
+ var initial = draw.children().length
+ draw.polygon('0,0 100,0 100,100 0,100')
+ expect(draw.children().length).toBe(initial + 1)
+ })
+ it('should create a polygon', function() {
+ expect(draw.polygon('0,0 100,0 100,100 0,100').type).toBe('polygon')
+ })
+ it('should be an instance of SVG.Polygon', function() {
+ expect(draw.polygon('0,0 100,0 100,100 0,100') instanceof SVG.Polygon).toBe(true)
+ })
+ it('should be an instance of SVG.Shape', function() {
+ expect(draw.polygon('0,0 100,0 100,100 0,100') instanceof SVG.Shape).toBe(true)
+ })
+ it('should be an instance of SVG.Element', function() {
+ expect(draw.polygon('0,0 100,0 100,100 0,100') instanceof SVG.Element).toBe(true)
+ })
+ })
+
+ describe('path()', function() {
+ it('should increase children by 1', function() {
+ var initial = draw.children().length
+ draw.path(svgPath)
+ expect(draw.children().length).toBe(initial + 1)
+ })
+ it('should create a path', function() {
+ expect(draw.path(svgPath).type).toBe('path')
+ })
+ it('should be an instance of SVG.Path', function() {
+ expect(draw.path(svgPath) instanceof SVG.Path).toBe(true)
+ })
+ it('should be an instance of SVG.Shape', function() {
+ expect(draw.path(svgPath) instanceof SVG.Shape).toBe(true)
+ })
+ it('should be an instance of SVG.Element', function() {
+ expect(draw.path(svgPath) instanceof SVG.Element).toBe(true)
+ })
+ })
+
+ describe('image()', function() {
+ it('should increase children by 1', function() {
+ var initial = draw.children().length
+ draw.image(imageUrl, 100, 100)
+ expect(draw.children().length).toBe(initial + 1)
+ })
+ it('should create a rect', function() {
+ expect(draw.image(imageUrl, 100, 100).type).toBe('image')
+ })
+ it('should create an instance of SVG.Rect', function() {
+ expect(draw.image(imageUrl, 100, 100) instanceof SVG.Image).toBe(true)
+ })
+ it('should be an instance of SVG.Shape', function() {
+ expect(draw.image(imageUrl, 100, 100) instanceof SVG.Shape).toBe(true)
+ })
+ it('should be an instance of SVG.Element', function() {
+ expect(draw.image(imageUrl, 100, 100) instanceof SVG.Element).toBe(true)
+ })
+ })
+
+ describe('text()', function() {
+ it('increases children by 1', function() {
+ var initial = draw.children().length
+ draw.text(loremIpsum)
+ expect(draw.children().length).toBe(initial + 1)
+ })
+ it('creates a text element', function() {
+ expect(draw.text(loremIpsum).type).toBe('text')
+ })
+ it('creates an instance of SVG.Rect', function() {
+ expect(draw.text(loremIpsum) instanceof SVG.Text).toBe(true)
+ })
+ it('is an instance of SVG.Shape', function() {
+ expect(draw.text(loremIpsum) instanceof SVG.Shape).toBe(true)
+ })
+ it('is an instance of SVG.Element', function() {
+ expect(draw.text(loremIpsum) instanceof SVG.Element).toBe(true)
+ })
+ })
+
+ describe('plain()', function() {
+ it('increases children by 1', function() {
+ var initial = draw.children().length
+ draw.plain(loremIpsum)
+ expect(draw.children().length).toBe(initial + 1)
+ })
+ it('creates a plain element', function() {
+ expect(draw.plain(loremIpsum).type).toBe('text')
+ })
+ it('creates an instance of SVG.Rect', function() {
+ expect(draw.plain(loremIpsum) instanceof SVG.Text).toBe(true)
+ })
+ it('is an instance of SVG.Shape', function() {
+ expect(draw.plain(loremIpsum) instanceof SVG.Shape).toBe(true)
+ })
+ it('is an instance of SVG.Element', function() {
+ expect(draw.plain(loremIpsum) instanceof SVG.Element).toBe(true)
+ })
+ })
+
+ describe('clear()', function() {
+ it('removes all children except the parser if present', function() {
+ draw.rect(100,100)
+ draw.clear()
+ expect(draw.children().length).toBe(parserInDoc)
+ })
+ it('creates a new defs node', function() {
+ var oldDefs = draw.defs()
+ draw.rect(100,100).maskWith(draw.circle(100, 100))
+ draw.clear()
+ expect(draw.defs()).not.toBe(oldDefs)
+ })
+ it('clears all children in the defs node', function() {
+ draw.rect(100,100).maskWith(draw.circle(100, 100))
+ draw.clear()
+ expect(draw.defs().children().length).toBe(0)
+ })
+ })
+
+ describe('each()', function() {
+ it('should iterate over all children', function() {
+ var children = []
+
+ draw.rect(100,100)
+ draw.ellipse(100, 100)
+ draw.polygon()
+
+ draw.each(function() {
+ children.push(this.type)
+ })
+ expect(children).toEqual((parserInDoc ? [parser[0].type] : []).concat(['rect', 'ellipse', 'polygon']))
+ })
+ it('should only include the its own children', function() {
+ var children = []
+ , group = draw.group()
+
+ draw.rect(100,200)
+ draw.circle(300)
+
+ group.rect(100,100)
+ group.ellipse(100, 100)
+ group.polygon()
+
+ group.each(function() {
+ children.push(this)
+ })
+
+ expect(children).toEqual(group.children())
+ })
+ it('should traverse recursively when set to deep', function() {
+ var children = []
+ , group = draw.group()
+
+ draw.rect(100,200)
+ draw.circle(300)
+
+ group.rect(100,100)
+ group.ellipse(100, 100)
+ group.polygon()
+
+ draw.each(function() {
+ children.push(this)
+ }, true)
+
+ expect(children.length).toEqual(draw.children().length + group.children().length + (parserInDoc ? parser[0].children().length : 0))
+ })
+ })
+
+ describe('get()', function() {
+ it('gets an element at a given index', function() {
+ draw.clear()
+ var rect = draw.rect(100,100)
+ var circle = draw.circle(100)
+ var line = draw.line(0,0,100,100)
+ expect(draw.get(0+parserInDoc)).toBe(rect)
+ expect(draw.get(1+parserInDoc)).toBe(circle)
+ expect(draw.get(2+parserInDoc)).toBe(line)
+ expect(draw.get(3+parserInDoc)).toBeNull()
+ })
+ })
+
+ describe('first()', function() {
+ it('gets the first child', function() {
+ draw.clear()
+ var rect = draw.rect(100,100)
+ var circle = draw.circle(100)
+ var line = draw.line(0,0,100,100)
+ expect(draw.first()).toBe(parserInDoc ? parser[0] : rect)
+ })
+ })
+
+ describe('last()', function() {
+ it('gets the last child', function() {
+ draw.clear()
+ var rect = draw.rect(100,100)
+ var circle = draw.circle(100)
+ var line = draw.line(0,0,100,100)
+ expect(draw.last()).toBe(line)
+ })
+ })
+
+ describe('has()', function() {
+ it('determines if a given element is a child of the parent', function() {
+ var rect = draw.rect(100,100)
+ var circle = draw.circle(100)
+ var group = draw.group()
+ var line = group.line(0,0,100,100)
+ expect(draw.has(rect)).toBe(true)
+ expect(draw.has(circle)).toBe(true)
+ expect(draw.has(group)).toBe(true)
+ expect(draw.has(line)).toBe(false)
+ expect(group.has(line)).toBe(true)
+ })
+ })
+
+ describe('index()', function() {
+ it('determines the index of given element', function() {
+ var rect = draw.rect(100,100)
+ var circle = draw.circle(100)
+ var group = draw.group()
+ var line = group.line(0,0,100,100)
+ expect(draw.index(rect)).toBe(0+parserInDoc)
+ expect(draw.index(circle)).toBe(1+parserInDoc)
+ expect(draw.index(group)).toBe(2+parserInDoc)
+ expect(draw.index(line)).toBe(-1)
+ expect(group.index(line)).toBe(0)
+ })
+ })
+
+ describe('parent()', function() {
+ it('returns the parent element instance', function() {
+ var rect = draw.rect(100,100)
+ expect(rect.parent()).toBe(rect.node.parentNode.instance)
+ })
+ })
+
+ describe('defs()', function() {
+ it('returns the defs from the svg', function() {
+ var g = draw.group()
+ expect(g.defs()).toBe(draw.doc().defs())
+ expect(g.defs() instanceof SVG.Defs).toBeTruthy()
+ })
+ })
+
+})
+
+
+
+
+
+
+
+
+
+
+
diff --git a/node_modules/svg.js/spec/spec/defs.js b/node_modules/svg.js/spec/spec/defs.js
new file mode 100644
index 0000000..5e5da08
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/defs.js
@@ -0,0 +1,12 @@
+describe('Defs', function() {
+ var defs
+
+ beforeEach(function() {
+ defs = draw.defs()
+ })
+
+ it('creates an instance of SVG.Defs', function() {
+ expect(defs instanceof SVG.Defs).toBeTruthy()
+ })
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/spec/doc.js b/node_modules/svg.js/spec/spec/doc.js
new file mode 100644
index 0000000..1b47717
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/doc.js
@@ -0,0 +1,74 @@
+describe('Doc', function() {
+
+ describe('create()', function(){
+ it('doenst alter size when adopting width SVG()', function() {
+ var svg = SVG('inlineSVG')
+ expect(svg.width()).toBe(0)
+ expect(svg.height()).toBe(0)
+ })
+ })
+
+ it('is an instance of SVG.Container', function() {
+ expect(draw instanceof SVG.Container).toBe(true)
+ })
+
+ it('is an instance of SVG.Doc', function() {
+ expect(draw instanceof SVG.Doc).toBe(true)
+ })
+
+ it('returns itself as Doc', function() {
+ expect(draw.doc()).toBe(draw)
+ })
+
+ it('has a defs element', function() {
+ expect(draw.defs() instanceof SVG.Defs).toBe(true)
+ })
+
+ describe('defs()', function() {
+ it('returns defs element', function(){
+ expect(draw.defs()).toBe(draw._defs)
+ })
+ it('references parent node', function(){
+ expect(draw.defs().parent()).toBe(draw)
+ })
+ })
+
+ describe('remove()', function() {
+ it('removes the doc from the dom only if doc is not root element', function() {
+ var cnt = window.document.querySelectorAll('svg').length
+ draw.remove()
+ if(parserInDoc){
+ expect(window.document.querySelectorAll('svg').length).toBe(cnt)
+ }else{
+ expect(window.document.querySelectorAll('svg').length).toBe(cnt-1)
+ }
+
+ draw = SVG(drawing).size(100,100);
+ expect(window.document.querySelectorAll('svg').length).toBe(cnt)
+ })
+ })
+
+ describe('clone()', function () {
+ it('clones the doc and inserts the clone after the doc', function () {
+ var clone = draw.clone()
+
+ expect(draw.node.nextSibling).toBe(clone.node)
+ clone.remove()
+ })
+ it('clones the doc and inserts the clone in the provided parent', function () {
+ var el = document.createElement('div')
+ var clone = draw.clone(el)
+
+ expect(clone.node.parentNode).toBe(el)
+ })
+ })
+
+ describe('parent()', function () {
+ it('returns null if nodeName is document-fragment', function() {
+ var fragment = document.createDocumentFragment();
+ var svgFrag = new SVG(fragment);
+ expect(svgFrag.parent()).toBe(null);
+ })
+ })
+
+})
diff --git a/node_modules/svg.js/spec/spec/easing.js b/node_modules/svg.js/spec/spec/easing.js
new file mode 100644
index 0000000..04690ac
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/easing.js
@@ -0,0 +1,22 @@
+describe('SVG.easing', function() {
+ var easedValues = {
+ '-':0.5,
+ '<>':0.5,
+ '>':0.7071,
+ '<':0.2929,
+ }
+
+ ;['-', '<>', '<', '>'].forEach(function(el) {
+ describe(el, function() {
+ it('is 0 at 0', function() {
+ expect(SVG.easing[el](0)).toBe(0)
+ })
+ it('is 1 at 1', function() {
+ expect(Math.round(SVG.easing[el](1)*1000)/1000).toBe(1) // we need to round cause for some reason at some point 1==0.999999999
+ })
+ it('is eased at 0.5', function() {
+ expect(SVG.easing[el](0.5)).toBeCloseTo(easedValues[el])
+ })
+ })
+ })
+})
diff --git a/node_modules/svg.js/spec/spec/element.js b/node_modules/svg.js/spec/spec/element.js
new file mode 100644
index 0000000..a5b3ba1
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/element.js
@@ -0,0 +1,1001 @@
+describe('Element', function() {
+
+ beforeEach(function() {
+ draw.clear()
+ draw.attr('viewBox', null)
+ })
+
+ it('should create a circular reference on the node', function() {
+ var rect = draw.rect(100,100)
+ expect(rect.node.instance).toBe(rect)
+ })
+
+ describe('native()', function() {
+ it('returns the node reference', function() {
+ var rect = draw.rect(100,100)
+ expect(rect.native()).toBe(rect.node)
+ })
+ })
+
+ describe('attr()', function() {
+ var rect
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ })
+
+ afterEach(function() {
+ rect.remove()
+ draw.defs().select('pattern').each(function() { this.remove() })
+ })
+
+ it('sets one attribute when two arguments are given', function() {
+ rect.attr('fill', '#ff0066')
+ expect(rect.node.getAttribute('fill')).toBe('#ff0066')
+ })
+ it('sets various attributes when an object is given', function() {
+ rect.attr({ fill: '#00ff66', stroke: '#ff2233', 'stroke-width': 10 })
+ expect(rect.node.getAttribute('fill')).toBe('#00ff66')
+ expect(rect.node.getAttribute('stroke')).toBe('#ff2233')
+ expect(rect.node.getAttribute('stroke-width')).toBe('10')
+ })
+ it('gets the value of the string value given as first argument', function() {
+ rect.attr('fill', '#ff0066')
+ expect(rect.attr('fill')).toEqual('#ff0066')
+ })
+ it('gets an object with all attributes without any arguments', function() {
+ rect.attr({ fill: '#00ff66', stroke: '#ff2233' })
+ var attr = rect.attr()
+ expect(attr.fill).toBe('#00ff66')
+ expect(attr.stroke).toBe('#ff2233')
+ })
+ it('removes an attribute if the second argument is explicitly set to null', function() {
+ rect.attr('stroke-width', 10)
+ expect(rect.node.getAttribute('stroke-width')).toBe('10')
+ rect.attr('stroke-width', null)
+ expect(rect.node.getAttribute('stroke-width')).toBe(null)
+ })
+ it('correctly parses numeric values as a getter', function() {
+ rect.attr('stroke-width', 11)
+ expect(rect.node.getAttribute('stroke-width')).toBe('11')
+ expect(rect.attr('stroke-width')).toBe(11)
+ })
+ it('correctly parses negative numeric values as a getter', function() {
+ rect.attr('x', -120)
+ expect(rect.node.getAttribute('x')).toBe('-120')
+ expect(rect.attr('x')).toBe(-120)
+ })
+ it('falls back on default values if attribute is not present', function() {
+ expect(rect.attr('stroke-linejoin')).toBe('miter')
+ })
+ it('gets the "style" attribute as a string', function() {
+ rect.style('cursor', 'pointer')
+ expect(rect.node.style.cursor).toBe('pointer')
+ })
+ it('sets the style attribute correctly', function() {
+ rect.attr('style', 'cursor:move;')
+ expect(rect.node.style.cursor).toBe('move')
+ })
+ it('acts as a global getter when no arguments are given', function() {
+ rect.fill('#ff0066')
+ expect(rect.attr().fill).toBe('#ff0066')
+ })
+ it('correctly parses numeric values as a global getter', function() {
+ rect.stroke({ width: 20 })
+ expect(rect.attr()['stroke-width']).toBe(20)
+ })
+ it('correctly parses negative numeric values as a global getter', function() {
+ rect.x(-30)
+ expect(rect.attr().x).toBe(-30)
+ })
+ it('leaves unit values alone as a global getter', function() {
+ rect.attr('x', '69%')
+ expect(rect.attr().x).toBe('69%')
+ })
+ it('creates an image in defs when image path is specified for fill', function() {
+ rect.attr('fill', imageUrl)
+ expect(draw.defs().select('pattern').length()).toBe(1)
+ expect(draw.defs().select('pattern image').length()).toBe(1)
+ expect(draw.defs().select('pattern image').first().src).toBe(imageUrl)
+ })
+ it('creates pattern in defs when image object is specified for fill', function() {
+ rect.attr('fill', new SVG.Image().load(imageUrl))
+ expect(draw.defs().select('pattern').length()).toBe(1)
+ expect(draw.defs().select('pattern image').length()).toBe(1)
+ expect(draw.defs().select('pattern image').first().src).toBe(imageUrl)
+ })
+ it('correctly creates SVG.Array if array given', function() {
+ rect.attr('something', [2,3,4])
+ expect(rect.attr('something')).toBe('2 3 4')
+ })
+ it('redirects to the leading() method when setting leading', function() {
+ var text = draw.text(loremIpsum)
+ spyOn(text, 'leading')
+
+ text.attr('leading', 2)
+ expect(text.leading).toHaveBeenCalled()
+ text.remove()
+ })
+ })
+
+ describe('id()', function() {
+ var rect
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ })
+
+ it('gets the value if the id attribute without an argument', function() {
+ expect(rect.id()).toBe(rect.attr('id'))
+ })
+ it('sets the value of the id', function() {
+ rect.id('new_id')
+ expect(rect.attr('id')).toBe('new_id')
+ })
+ })
+
+ describe('style()', function() {
+ it('sets the style with key and value arguments', function() {
+ var rect = draw.rect(100,100).style('cursor', 'crosshair')
+ expect(window.stripped(rect.node.style.cssText)).toBe('cursor:crosshair')
+ })
+ it('sets multiple styles with an object as the first argument', function() {
+ var rect = draw.rect(100,100).style({ cursor: 'help', display: 'block' })
+ expect(window.stripped(rect.node.style.cssText)).toMatch(/cursor:help/)
+ expect(window.stripped(rect.node.style.cssText)).toMatch(/display:block/)
+ expect(window.stripped(rect.node.style.cssText).length).toBe(('display:block;cursor:help').length)
+ })
+ it('sets multiple styles with a css string as the first argument', function() {
+ var rect = draw.rect(100,100).style('cursor: help; display: block;')
+ expect(window.stripped(rect.node.style.cssText)).toMatch(/cursor:help/)
+ expect(window.stripped(rect.node.style.cssText)).toMatch(/display:block/)
+ expect(window.stripped(rect.node.style.cssText).length).toBe(('display:block;cursor:help').length)
+ })
+ it('gets a style with a string key as the fists argument', function() {
+ var rect = draw.rect(100,100).style({ cursor: 'progress', display: 'block' })
+ expect(rect.style('cursor')).toBe('progress')
+ })
+ it('gets the full css string with no argument', function() {
+ var rect = draw.rect(100,100).style({ cursor: 's-resize', display: 'none' })
+ expect(window.stripped(rect.style())).toMatch(/display:none/)
+ expect(window.stripped(rect.style())).toMatch(/cursor:s-resize/)
+ expect(window.stripped(rect.style()).length).toBe(('cursor:s-resize;display:none').length)
+ })
+ it('removes a style if the value is an empty string', function() {
+ var rect = draw.rect(100,100).style({ cursor: 'n-resize', display: '' })
+ expect(window.stripped(rect.style())).toBe('cursor:n-resize')
+ })
+ it('removes a style if the value explicitly set to null', function() {
+ var rect = draw.rect(100,100).style('cursor', 'w-resize')
+ expect(window.stripped(rect.style())).toBe('cursor:w-resize')
+ rect.style('cursor', null)
+ expect(rect.style()).toBe('')
+ })
+ })
+
+ describe('transform()', function() {
+ var rect, ctm
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ })
+
+ it('gets the current transformations', function() {
+ expect(rect.transform()).toEqual(new SVG.Matrix(rect).extract())
+ })
+ it('sets the translation of and element', function() {
+ rect.transform({ x: 10, y: 11 })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([1,0,0,1,10,11])
+ })
+ it('performs an absolute translation', function() {
+ rect.transform({ x: 10, y: 11 }).transform({ x: 20, y: 21 })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([1,0,0,1,20,21])
+ })
+ it('performs a relative translation when relative is set to true', function() {
+ rect.transform({ x: 10, y: 11 }).transform({ x: 20, y: 21, relative: true })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([1,0,0,1,30,32])
+ })
+ it('performs a relative translation with relative flag', function() {
+ rect.transform({ x: 10, y: 11 }).transform({ x: 20, y: 21 }, true)
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([1,0,0,1,30,32])
+ })
+ it('sets the scaleX and scaleY of an element', function() {
+ rect.transform({ scaleX: 0.5, scaleY: 2 })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([0.5,0,0,2,25,-50])
+ })
+ it('performs a uniform scale with scale given', function() {
+ rect.transform({ scale: 3 })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([3,0,0,3,-100,-100])
+ })
+ it('also works with only skaleX', function() {
+ rect.transform({ scaleX: 3 })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([3,0,0,1,-100,0])
+ })
+ it('also works with only skaleY', function() {
+ rect.transform({ scaleY: 3 })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([1,0,0,3,0,-100])
+ })
+
+ it('performs an absolute scale by default', function() {
+ rect.transform({ scale: 3 }).transform({ scale: 0.5 })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([0.5,0,0,0.5,25,25])
+ })
+ it('performs a relative scale with a relative flag', function() {
+ rect.transform({ scaleX: 0.5, scaleY: 2 }).transform({ scaleX: 3, scaleY: 4 }, true)
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([1.5,0,0,8,-25,-350])
+ })
+ it('sets the skewX of an element with center on the element', function() {
+ ctm = rect.transform({ skewX: 10 }).ctm()
+ expect(ctm.a).toBe(1)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBeCloseTo(0.17632698070846498)
+ expect(ctm.d).toBe(1)
+ expect(ctm.e).toBeCloseTo(-8.81634903542325)
+ expect(ctm.f).toBe(0)
+ })
+ it('sets the skewX of an element with given center', function() {
+ ctm = rect.transform({ skewX: 10, cx: 0, cy: 0 }).ctm()
+ expect(ctm.a).toBe(1)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBeCloseTo(0.17632698070846498)
+ expect(ctm.d).toBe(1)
+ expect(ctm.e).toBe(0)
+ expect(ctm.f).toBe(0)
+ })
+ it('sets the skewY of an element', function() {
+ ctm = rect.transform({ skewY: -10, cx: 0, cy: 0 }).ctm()
+ expect(ctm.a).toBe(1)
+ expect(ctm.b).toBeCloseTo(-0.17632698070846498)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(1)
+ expect(ctm.e).toBe(0)
+ expect(ctm.f).toBe(0)
+ })
+ it('sets the skewX and skewY of an element', function() {
+ ctm = rect.transform({ skewX: 10, skewY: -10, cx: 0, cy: 0 }).ctm()
+ expect(ctm.a).toBe(1)
+ expect(ctm.b).toBeCloseTo(-0.17632698070846498)
+ expect(ctm.c).toBeCloseTo(0.17632698070846498)
+ expect(ctm.d).toBe(1)
+ expect(ctm.e).toBe(0)
+ expect(ctm.f).toBe(0)
+ })
+ it('performs a uniform skew with skew given', function() {
+ ctm = rect.transform({ skew: 5, cx: 0, cy: 0 }).ctm()
+ expect(ctm.a).toBe(1)
+ expect(ctm.b).toBeCloseTo(0.08748866352592401)
+ expect(ctm.c).toBeCloseTo(0.08748866352592401)
+ expect(ctm.d).toBe(1)
+ expect(ctm.e).toBe(0)
+ expect(ctm.f).toBe(0)
+ })
+ it('rotates the element around its centre if no rotation point is given', function() {
+ ctm = rect.center(100, 100).transform({ rotation: 45 }).ctm()
+ expect(ctm.a).toBeCloseTo(0.7071068286895752)
+ expect(ctm.b).toBeCloseTo(0.7071068286895752)
+ expect(ctm.c).toBeCloseTo(-0.7071068286895752)
+ expect(ctm.d).toBeCloseTo(0.7071068286895752)
+ expect(ctm.e).toBeCloseTo(100)
+ expect(ctm.f).toBeCloseTo(-41.421356201171875)
+ expect(rect.transform('rotation')).toBe(45)
+ })
+ it('rotates the element around the given rotation point', function() {
+ ctm = rect.transform({ rotation: 55, cx: 80, cy:2 }).ctm()
+ expect(ctm.a).toBeCloseTo(0.5735765099525452)
+ expect(ctm.b).toBeCloseTo(0.8191521167755127)
+ expect(ctm.c).toBeCloseTo(-0.8191521167755127)
+ expect(ctm.d).toBeCloseTo(0.5735765099525452)
+ expect(ctm.e).toBeCloseTo(35.75218963623047)
+ expect(ctm.f).toBeCloseTo(-64.67931365966797)
+ })
+ it('transforms element using a matrix', function() {
+ rect.transform({ a: 0.5, c: 0.5 })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([0.5,0,0.5,1,0,0])
+ })
+ it('transforms relative using a matrix', function() {
+ rect.transform({ a: 0.5, c: 0.5 }).transform(new SVG.Matrix({ e: 20, f: 20 }), true)
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([0.5,0,0.5,1,20,20])
+ })
+ it('flips the element on x axis', function() {
+ rect.transform({ flip: 'x' })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([-1,0,0,1,100,0])
+ })
+ it('flips the element on x axis with offset', function() {
+ rect.transform({ flip: 'x', offset: 20 })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([-1,0,0,1,40,0])
+ })
+ it('flips the element on y axis with offset', function() {
+ rect.transform({ flip: 'y', offset: 20 })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([1,0,0,-1,0,40])
+ })
+ it('flips the element on both axis with offset', function() {
+ rect.transform({ flip: 'both', offset: 20 })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([-1,0,0,-1,40,40])
+ })
+ it('flips the element on both axis', function() {
+ rect.transform({ flip: 'both' })
+ expect(window.matrixStringToArray(rect.node.getAttribute('transform'))).toEqual([-1,0,0,-1,100,100])
+ })
+ })
+
+ describe('untransform()', function() {
+ var circle
+
+ beforeEach(function() {
+ circle = draw.circle(100).translate(50, 100)
+ })
+
+ it('removes the transform attribute', function() {
+ expect(window.matrixStringToArray(circle.node.getAttribute('transform'))).toEqual([1,0,0,1,50,100])
+ circle.untransform()
+ expect(circle.node.getAttribute('transform')).toBeNull()
+ })
+ it('resets the current transform matix', function() {
+ expect(circle.ctm()).toEqual(new SVG.Matrix(1,0,0,1,50,100))
+ circle.untransform()
+ expect(circle.ctm()).toEqual(new SVG.Matrix)
+ })
+ })
+
+ describe('matrixify', function() {
+ var rect
+
+ beforeEach(function() {
+ rect = draw.rect(100, 100)
+ })
+
+ it('allow individual transform definitions to be separated by whitespace', function(){
+ // One space
+ rect.attr('transform', 'translate(20) translate(20)')
+ expect(rect.matrixify().toString()).toBe('matrix(1,0,0,1,40,0)')
+
+ // More than one space
+ rect.attr('transform', 'translate(20) translate(-60)')
+ expect(rect.matrixify().toString()).toBe('matrix(1,0,0,1,-40,0)')
+ })
+
+ it('allow individual transform definitions to be separated by a comma', function(){
+ rect.attr('transform', 'translate(20,16),translate(20)')
+ expect(rect.matrixify().toString()).toBe('matrix(1,0,0,1,40,16)')
+ })
+
+ it('allow individual transform definitions to be separated by whitespace and a comma', function(){
+ // Spaces before the comma
+ rect.attr('transform', 'translate(20,16) ,translate(20)')
+ expect(rect.matrixify().toString()).toBe('matrix(1,0,0,1,40,16)')
+
+ // Spaces after the comma
+ rect.attr('transform', 'translate(12), translate(10,14)')
+ expect(rect.matrixify().toString()).toBe('matrix(1,0,0,1,22,14)')
+
+ // Spaces before and after the comma
+ rect.attr('transform', 'translate(24,14) , translate(36,6)')
+ expect(rect.matrixify().toString()).toBe('matrix(1,0,0,1,60,20)')
+ })
+ })
+
+ describe('toParent()', function() {
+ var nested, g1, g2, rect1
+
+ beforeEach(function() {
+ nested = draw.nested()
+ g1 = nested.group().translate(20, 20)
+ g2 = g1.group().translate(100, 100)
+ rect1 = g2.rect(100,100).scale(2)
+ rect2 = nested.rect(100,100).scale(0.5)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ it('returns itself when given parent and it is the same', function() {
+ expect(g2.toParent(g2)).toBe(g2)
+ })
+
+ it('moves the element to other parent while maintaining the same visal representation', function() {
+ expect(rect1.toParent(nested).transform()).toEqual(jasmine.objectContaining({
+ a:2, b:0, c:0, d:2, e:70, f:70
+ }))
+ expect(rect1.parent()).toEqual(nested)
+ expect(rect2.toParent(g2).transform()).toEqual(jasmine.objectContaining({
+ a:0.5, b:0, c:0, d:0.5, e:-95, f:-95
+ }))
+ expect(rect2.parent()).toEqual(g2)
+ })
+ })
+
+ describe('toDoc()', function() {
+ var nested, g1, g2, rect
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ spyOn(rect, 'toParent')
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ it('redirects to toParent(doc)', function() {
+ rect.toDoc()
+ expect(rect.toParent).toHaveBeenCalledWith(rect.doc())
+ })
+ })
+
+ describe('ungroup()', function() {
+ var nested, g1, g2, rect1
+
+ beforeEach(function() {
+ draw.defs()
+ nested = draw.nested()
+ g1 = nested.group().translate(20, 20)
+ g2 = g1.group().translate(100, 100)
+ rect1 = g2.rect(100,100).scale(2)
+ rect2 = g1.rect(100,100).scale(0.5)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ it('returns itself when depths is 0 or this is SVG.Defs', function() {
+ expect(draw.defs().ungroup()).toBe(draw.defs())
+ expect(g1.ungroup(null, 0)).toBe(g1)
+ })
+
+ it('breaks up all container and move the elements to the parent', function() {
+ g1.ungroup()
+ expect(rect1.parent()).toBe(nested)
+ expect(rect2.parent()).toBe(nested)
+
+ expect(g1.node.parentNode).toBeFalsy()
+ expect(g2.node.parentNode).toBeFalsy()
+
+ expect(rect1.transform()).toEqual(jasmine.objectContaining({
+ a:2, b:0, c:0, d:2, e:70, f:70
+ }))
+ expect(rect2.transform()).toEqual(jasmine.objectContaining({
+ a:0.5, b:0, c:0, d:0.5, e:45, f:45
+ }))
+ })
+
+ it('ungroups everything to the doc root when called on SVG.Doc / does not ungroup defs/parser', function() {
+ draw.ungroup()
+ expect(rect1.parent()).toBe(draw)
+ expect(rect2.parent()).toBe(draw)
+
+ expect(g1.node.parentNode).toBeFalsy()
+ expect(g1.node.parentNode).toBeFalsy()
+ expect(nested.node.parentNode).toBeFalsy()
+
+ expect(rect1.transform()).toEqual(jasmine.objectContaining({
+ a:2, b:0, c:0, d:2, e:70, f:70
+ }))
+ expect(rect2.transform()).toEqual(jasmine.objectContaining({
+ a:0.5, b:0, c:0, d:0.5, e:45, f:45
+ }))
+
+ expect(draw.children().length).toBe(3+parserInDoc) // 2 * rect + defs
+ })
+ })
+
+ describe('flatten()', function() {
+ it('redirects the call to ungroup()', function() {
+ spyOn(draw, 'ungroup')
+ draw.flatten()
+ expect(draw.ungroup).toHaveBeenCalled()
+ })
+ })
+
+ describe('ctm()', function() {
+ var rect
+
+ beforeEach(function() {
+ rect = draw.rect(100, 100)
+ })
+
+ it('gets the current transform matrix of the element', function() {
+ rect.translate(10, 20)
+ expect(rect.ctm().toString()).toBe('matrix(1,0,0,1,10,20)')
+ })
+ it('returns an instance of SVG.Matrix', function() {
+ expect(rect.ctm() instanceof SVG.Matrix).toBeTruthy()
+ })
+ })
+
+ describe('data()', function() {
+ it('sets a data attribute and convert value to json', function() {
+ var rect = draw.rect(100,100).data('test', 'value')
+ expect(rect.node.getAttribute('data-test')).toBe('value')
+ })
+ it('sets a data attribute and not convert value to json if flagged raw', function() {
+ var rect = draw.rect(100,100).data('test', 'value', true)
+ expect(rect.node.getAttribute('data-test')).toBe('value')
+ })
+ it('sets multiple data attributes and convert values to json when an object is passed', function() {
+ var rect = draw.rect(100,100).data({
+ forbidden: 'fruit'
+ , multiple: {
+ values: 'in'
+ , an: 'object'
+ }
+ })
+ expect(rect.node.getAttribute('data-forbidden')).toBe('fruit')
+ expect(rect.node.getAttribute('data-multiple')).toEqual('{"values":"in","an":"object"}')
+ })
+ it('gets data value if only one argument is passed', function() {
+ var rect = draw.rect(100,100).data('test', 101)
+ expect(rect.data('test')).toBe(101)
+ })
+ it('gets the raw value when value is no valid json', function() {
+ var rect = draw.rect(100,100).data('test', '{["sd":12}]', true)
+ expect(rect.data('test')).toBe('{["sd":12}]')
+ })
+ it('removes data when null given', function() {
+ var rect = draw.rect(100,100).data('test', '{"sd":12}', true)
+ expect(rect.data('test', null).attr('data-test')).toBeFalsy()
+ })
+ it('maintains data type for a number', function() {
+ var rect = draw.rect(100,100).data('test', 101)
+ expect(typeof rect.data('test')).toBe('number')
+ })
+ it('maintains data type for an object', function() {
+ var rect = draw.rect(100,100).data('test', { string: 'value', array: [1,2,3] })
+ expect(typeof rect.data('test')).toBe('object')
+ expect(Array.isArray(rect.data('test').array)).toBe(true)
+ })
+ })
+
+ describe('remove()', function() {
+ it('removes an element and return it', function() {
+ var rect = draw.rect(100,100)
+ expect(rect.remove()).toBe(rect)
+ })
+ it('removes an element from its parent', function() {
+ var rect = draw.rect(100,100)
+ rect.remove()
+ expect(draw.has(rect)).toBe(false)
+ })
+ })
+
+ describe('addTo()', function() {
+ it('adds an element to a given parent and returns itself', function() {
+ var rect = draw.rect(100,100)
+ , group = draw.group()
+
+ expect(rect.addTo(group)).toBe(rect)
+ expect(rect.parent()).toBe(group)
+ })
+ })
+
+ describe('putIn()', function() {
+ it('adds an element to a given parent and returns parent', function() {
+ var rect = draw.rect(100,100)
+ , group = draw.group()
+
+ expect(rect.putIn(group)).toBe(group)
+ expect(rect.parent()).toBe(group)
+ })
+ })
+
+ describe('rbox()', function() {
+ it('returns an instance of SVG.RBox', function() {
+ var rect = draw.rect(100,100)
+ expect(rect.rbox() instanceof SVG.RBox).toBe(true)
+ })
+ it('returns the correct rectangular box', function() {
+ // stroke has to be set in order to get the correct result when calling getBoundingClientRect in IE11
+ var rect = draw.size(200, 150).viewbox(0, 0, 200, 150).rect(105, 210).move(2, 12).stroke({width:0})
+ var box = rect.rbox(draw)
+ expect(box.x).toBeCloseTo(2)
+ expect(box.y).toBeCloseTo(12)
+ expect(box.cx).toBeCloseTo(54.5)
+ expect(box.cy).toBeCloseTo(117)
+ expect(box.width).toBeCloseTo(105)
+ expect(box.height).toBeCloseTo(210)
+ })
+ })
+
+ describe('doc()', function() {
+ it('returns the parent document', function() {
+ var rect = draw.rect(100,100)
+ expect(rect.doc()).toBe(draw)
+ })
+ })
+
+ describe('parent()', function() {
+ it('contains the parent svg', function() {
+ var rect = draw.rect(100,100)
+ expect(rect.parent()).toBe(draw)
+ })
+ it('contains the parent group when in a group', function() {
+ var group = draw.group()
+ , rect = group.rect(100,100)
+ expect(rect.parent()).toBe(group)
+ })
+ it('contains the parent which matches type', function() {
+ var group = draw.group()
+ , rect = group.rect(100,100)
+ expect(rect.parent(SVG.Doc)).toBe(draw)
+ })
+ it('contains the parent which matches selector', function() {
+ var group1 = draw.group().addClass('test')
+ , group2 = group1.group()
+ , rect = group2.rect(100,100)
+ expect(rect.parent('.test')).toBe(group1)
+ })
+ it('returns null if element is detached', function() {
+ expect(new SVG.Rect().parent()).toBe(null)
+ })
+ it('returns null if elements parents are detached', function() {
+ expect(new SVG.Rect().addTo(new SVG.G()).parent('svg')).toBe(null)
+ })
+ it('works on detachd documents', function() {
+ var g = new SVG.G()
+ expect(new SVG.Rect().addTo(g).parent()).toBe(g)
+ })
+ it('returns null if nodeName is document-fragment', function() {
+ var fragment = document.createDocumentFragment();
+ var svgFrag = new SVG(fragment);
+ expect(svgFrag.parent()).toBe(null);
+ })
+ })
+
+ describe('parents()', function() {
+ it('returns array of parent up to but not including the dom element filtered by type', function() {
+ var group1 = draw.group().addClass('test')
+ , group2 = group1.group()
+ , rect = group2.rect(100,100)
+
+ expect(rect.parents('.test')[0]).toBe(group1)
+ expect(rect.parents(SVG.G)[0]).toBe(group2)
+ expect(rect.parents(SVG.G)[1]).toBe(group1)
+ expect(rect.parents().length).toBe(3)
+ })
+ })
+
+ describe('clone()', function() {
+ var rect, group, circle
+
+ beforeEach(function() {
+ rect = draw.rect(100,100).center(321,567).fill('#f06')
+ group = draw.group().add(rect)
+ circle = group.circle(100)
+ })
+
+ it('makes an exact copy of the element', function() {
+ clone = rect.clone()
+ expect(clone.attr('id', null).attr()).toEqual(rect.attr('id', null).attr())
+ })
+ it('assigns a new id to the cloned element', function() {
+ clone = rect.clone()
+ expect(clone.attr('id')).not.toBe(rect.attr('id'))
+ })
+ it('copies all child nodes as well', function() {
+ clone = group.clone()
+ expect(clone.children().length).toBe(group.children().length)
+ })
+ it('assigns a new id to cloned child elements', function() {
+ clone = group.clone()
+ expect(clone.attr('id')).not.toEqual(group.attr('id'))
+ expect(clone.get(0).attr('id')).not.toBe(group.get(0).attr('id'))
+ expect(clone.get(1).attr('id')).not.toBe(group.get(1).attr('id'))
+ })
+ it('inserts the clone after the cloned element', function() {
+ clone = rect.clone()
+ expect(rect.next()).toBe(clone)
+ })
+ it('inserts the clone in the specified parent', function() {
+ var g = draw.group()
+ clone = rect.clone(g)
+ expect(g.get(0)).toBe(clone)
+ })
+ it('deep copies over dom data', function() {
+ group.dom = {'foo':'bar'}
+ rect.dom = {'foo':'baz'}
+ clone = group.clone()
+ expect(clone.dom.foo).toBe('bar')
+ expect(clone.get(0).dom.foo).toBe('baz')
+ })
+ })
+
+ describe('toString()', function() {
+ it('returns the element id', function() {
+ var rect = draw.rect(100,100).center(321,567).fill('#f06')
+ expect(rect + '').toBe(rect.attr('id'))
+ })
+ })
+
+ describe('replace()', function() {
+ it('replaces the original element by another given element', function() {
+ var rect = draw.rect(100,100).center(321,567).fill('#f06')
+ var circle = draw.circle(200)
+ var rectIndex = draw.children().indexOf(rect)
+
+ rect.replace(circle)
+
+ expect(rectIndex).toBe(draw.children().indexOf(circle))
+ })
+ it('removes the original element', function() {
+ var rect = draw.rect(100,100).center(321,567).fill('#f06')
+
+ rect.replace(draw.circle(200))
+
+ expect(draw.has(rect)).toBe(false)
+ })
+ it('returns the new element', function() {
+ var circle = draw.circle(200)
+ var element = draw.rect(100,100).center(321,567).fill('#f06').replace(circle)
+
+ expect(element).toBe(circle)
+ })
+ })
+
+ describe('classes()', function() {
+ it('returns an array of classes on the node', function() {
+ var element = draw.rect(100,100)
+ element.node.setAttribute('class', 'one two')
+ expect(element.classes()).toEqual(['one', 'two'])
+ })
+ })
+
+ describe('hasClass()', function() {
+ it('returns true if the node has the class', function() {
+ var element = draw.rect(100,100)
+ element.node.setAttribute('class', 'one')
+ expect(element.hasClass('one')).toBeTruthy()
+ })
+
+ it('returns false if the node does not have the class', function() {
+ var element = draw.rect(100,100)
+ element.node.setAttribute('class', 'one')
+ expect(element.hasClass('two')).toBeFalsy()
+ })
+ })
+
+ describe('addClass()', function() {
+ it('adds the class to the node', function() {
+ var element = draw.rect(100,100)
+ element.addClass('one')
+ expect(element.hasClass('one')).toBeTruthy()
+ })
+
+ it('does not add duplicate classes', function() {
+ var element = draw.rect(100,100)
+ element.addClass('one')
+ element.addClass('one')
+ expect(element.node.getAttribute('class')).toEqual('one')
+ })
+
+ it('returns the svg instance', function() {
+ var element = draw.rect(100,100)
+ expect(element.addClass('one')).toEqual(element)
+ })
+ })
+
+ describe('removeClass()', function() {
+ it('removes the class from the node when the class exists', function() {
+ var element = draw.rect(100,100)
+ element.addClass('one')
+ element.removeClass('one')
+ expect(element.hasClass('one')).toBeFalsy()
+ })
+
+ it('does nothing when the class does not exist', function() {
+ var element = draw.rect(100,100)
+ element.removeClass('one')
+ expect(element.hasClass('one')).toBeFalsy()
+ })
+
+ it('returns the element', function() {
+ var element = draw.rect(100,100)
+ expect(element.removeClass('one')).toEqual(element)
+ })
+ })
+
+ describe('toggleClass()', function() {
+ it('adds the class when it does not already exist', function(){
+ var element = draw.rect(100,100)
+ element.toggleClass('one')
+ expect(element.hasClass('one')).toBeTruthy()
+ })
+ it('removes the class when it already exists', function(){
+ var element = draw.rect(100,100)
+ element.addClass('one')
+ element.toggleClass('one')
+ expect(element.hasClass('one')).toBeFalsy()
+ })
+ it('returns the svg instance', function() {
+ var element = draw.rect(100,100)
+ expect(element.toggleClass('one')).toEqual(element)
+ })
+ })
+
+ describe('reference()', function() {
+ it('gets a referenced element from a given attribute', function() {
+ var rect = draw.defs().rect(100, 100)
+ , use = draw.use(rect)
+ , mark = draw.marker(10, 10)
+ , path = draw.path(svgPath).marker('end', mark)
+
+ expect(use.reference('href')).toBe(rect)
+ expect(path.reference('marker-end')).toBe(mark)
+ })
+
+ it('returns null if reference not found', function() {
+ var rect = draw.rect(100, 100)
+
+ expect(rect.reference('href')).toBe(null)
+ })
+ })
+
+ describe('svg()', function() {
+ describe('without an argument', function() {
+ it('returns full raw svg when called on the main svg doc', function() {
+ draw.size(100,100).rect(100,100).id(null)
+ draw.circle(100).fill('#f06').id(null)
+
+ var toBeTested = draw.svg()
+
+ // Test for different browsers namely Firefox and Chrome
+ expect(
+ // IE
+ toBeTested === ' '
+
+ // Firefox
+ || toBeTested === ' '
+
+ // svgdom
+ || toBeTested === ' '
+ ).toBeTruthy()
+
+ })
+ it('returns partial raw svg when called on a sub group', function() {
+ var group = draw.group().id(null)
+ group.rect(100,100).id(null)
+ group.circle(100).fill('#f06').id(null)
+
+ var toBeTested = group.svg()
+
+ expect(
+ toBeTested === ' '
+ || toBeTested === ' '
+ || toBeTested === ' '
+ ).toBeTruthy()
+ })
+ it('returns a single element when called on an element', function() {
+ var group = draw.group().id(null)
+ group.rect(100,100).id(null)
+ var circle = group.circle(100).fill('#f06').id(null)
+ var toBeTested = circle.svg()
+
+ expect(
+ toBeTested === ' '
+ || toBeTested === ' '
+ || toBeTested === ' '
+ ).toBeTruthy()
+ })
+ })
+ describe('with raw svg given', function() {
+ it('imports a full svg document', function() {
+ draw.svg(' ')
+
+ expect(draw.get(0+parserInDoc).type).toBe('svg')
+ expect(draw.get(0+parserInDoc).children().length).toBe(2)
+ expect(draw.get(0+parserInDoc).get(0).type).toBe('rect')
+ expect(draw.get(0+parserInDoc).get(1).type).toBe('circle')
+ expect(draw.get(0+parserInDoc).get(1).attr('fill')).toBe('#ff0066')
+ })
+ it('imports partial svg content', function() {
+ draw.svg(' ')
+ expect(draw.get(0+parserInDoc).type).toBe('g')
+ expect(draw.get(0+parserInDoc).get(0).type).toBe('rect')
+ expect(draw.get(0+parserInDoc).get(1).type).toBe('circle')
+ expect(draw.get(0+parserInDoc).get(1).attr('fill')).toBe('#ff0066')
+ })
+ it('does not import on single elements, even with an argument it acts as a getter', function() {
+ var rect = draw.rect(100,100).id(null)
+ , result = rect.svg('')
+
+ expect(
+ result === ' '
+ || result === ' '
+ || result === ' '
+ ).toBeTruthy()
+ })
+ })
+ })
+
+ describe('writeDataToDom()', function() {
+ it('set all properties in el.dom to the svgjs:data attribute', function(){
+ var rect = draw.rect(100,100)
+ rect.dom.foo = 'bar'
+ rect.dom.number = new SVG.Number('3px')
+
+ rect.writeDataToDom()
+
+ expect(rect.attr('svgjs:data')).toBe('{"foo":"bar","number":"3px"}')
+ })
+ it('recursively dumps the data', function() {
+ var g = draw.group()
+ rect = g.rect(100,100)
+ g.dom.foo = 'bar'
+ rect.dom.number = new SVG.Number('3px')
+
+ g.writeDataToDom()
+
+ expect(g.attr('svgjs:data')).toBe('{"foo":"bar"}')
+ expect(rect.attr('svgjs:data')).toBe('{"number":"3px"}')
+ })
+ it('uses lines() instead of each() when dealing with text', function() {
+ var text = draw.text('Hello\nWorld')
+ text.writeDataToDom()
+ expect(text.attr('svgjs:data')).toBe('{"leading":"1.3"}')
+ expect(text.lines().first().attr('svgjs:data')).toBe('{"newLined":true}')
+ })
+ })
+
+ describe('setData()', function() {
+ it('read all data from the svgjs:data attribute and assign it to el.dom', function(){
+ var rect = draw.rect(100,100)
+
+ rect.attr('svgjs:data', '{"foo":"bar","number":"3px"}')
+ rect.setData(JSON.parse(rect.attr('svgjs:data')))
+
+ expect(rect.dom.foo).toBe('bar')
+ expect(rect.dom.number).toBe('3px')
+ })
+ })
+
+ describe('point()', function() {
+ it('creates a point from screen coordinates transformed in the elements space', function(){
+ var rect = draw.rect(100,100)
+
+ var m = draw.node.getScreenCTM()
+ // alert([m.a, m.a, m.c, m.d, m.e, m.f].join(', '))
+
+ var translation = {x: m.e, y: m.f}
+ var pos = {x: 2, y:5}
+
+ expect(rect.point(pos.x, pos.y).x).toBeCloseTo(pos.x - translation.x)
+ expect(rect.point(pos.x, pos.y).y).toBeCloseTo(pos.y - translation.y)
+ })
+ })
+
+ describe('inside()', function() {
+ it('checks whether the given point inside the bounding box of the element', function() {
+ var rect = draw.rect(100,100)
+ expect(rect.inside(50,50)).toBeTruthy()
+ expect(rect.inside(150,150)).toBeFalsy()
+ })
+ })
+ describe('show()', function() {
+ it('sets display property to ""', function() {
+ var rect = draw.rect(100,100).show()
+ expect(rect.style('display')).toBe('')
+ })
+ })
+ describe('hide()', function() {
+ it('sets display property to none', function() {
+ var rect = draw.rect(100,100).hide()
+ expect(rect.style('display')).toBe('none')
+ })
+ })
+ describe('visible()', function() {
+ it('checks if element is hidden or not', function() {
+ var rect = draw.rect(100,100).hide()
+ expect(rect.visible()).toBeFalsy()
+ rect.show()
+ expect(rect.visible()).toBeTruthy()
+ })
+ })
+ describe('is()', function() {
+ it('checks if element is instance of a certain kind', function() {
+ var rect = draw.rect(100,100)
+ expect(rect.is(SVG.Rect)).toBeTruthy()
+ expect(rect.is(SVG.Element)).toBeTruthy()
+ expect(rect.is(SVG.Parent)).toBeFalsy()
+ })
+ })
+})
diff --git a/node_modules/svg.js/spec/spec/ellipse.js b/node_modules/svg.js/spec/spec/ellipse.js
new file mode 100644
index 0000000..f6aa271
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/ellipse.js
@@ -0,0 +1,187 @@
+describe('Ellipse', function() {
+ var ellipse
+
+ beforeEach(function() {
+ ellipse = draw.ellipse(240,90)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ describe('x()', function() {
+ it('returns the value of x without an argument', function() {
+ expect(ellipse.x()).toBe(0)
+ })
+ it('sets the value of x with the first argument', function() {
+ ellipse.x(123)
+ var box = ellipse.bbox()
+ expect(box.x).toBeCloseTo(123)
+ })
+ })
+
+ describe('y()', function() {
+ it('returns the value of y without an argument', function() {
+ expect(ellipse.y()).toBe(0)
+ })
+ it('sets the value of cy with the first argument', function() {
+ ellipse.y(345)
+ var box = ellipse.bbox()
+ expect(box.y).toBe(345)
+ })
+ })
+
+ describe('cx()', function() {
+ it('returns the value of cx without an argument', function() {
+ expect(ellipse.cx()).toBe(120)
+ })
+ it('sets the value of cx with the first argument', function() {
+ ellipse.cx(123)
+ var box = ellipse.bbox()
+ expect(box.cx).toBe(123)
+ })
+ })
+
+ describe('cy()', function() {
+ it('returns the value of cy without an argument', function() {
+ expect(ellipse.cy()).toBe(45)
+ })
+ it('sets the value of cy with the first argument', function() {
+ ellipse.cy(345)
+ var box = ellipse.bbox()
+ expect(box.cy).toBe(345)
+ })
+ })
+
+ describe('radius()', function() {
+ it('sets the rx and ry', function() {
+ ellipse.radius(10, 20)
+ expect(ellipse.node.getAttribute('rx')).toBe('10')
+ expect(ellipse.node.getAttribute('ry')).toBe('20')
+ })
+ it('sets the rx and ry if only rx given', function() {
+ ellipse.radius(30)
+ expect(ellipse.node.getAttribute('rx')).toBe('30')
+ expect(ellipse.node.getAttribute('ry')).toBe('30')
+ })
+ it('sets the rx and ry value correctly when given 0', function() {
+ ellipse.radius(11, 0)
+ expect(ellipse.node.getAttribute('rx')).toBe('11')
+ expect(ellipse.node.getAttribute('ry')).toBe('0')
+ })
+ })
+
+ describe('move()', function() {
+ it('sets the x and y position', function() {
+ ellipse.move(123, 456)
+ var box = ellipse.bbox()
+ expect(box.x).toBeCloseTo(123)
+ expect(box.y).toBeCloseTo(456)
+ })
+ })
+
+ describe('dx()', function() {
+ it('moves the x positon of the element relative to the current position', function() {
+ ellipse.move(50, 60)
+ ellipse.dx(100)
+ expect(ellipse.node.getAttribute('cx')).toBe('270')
+ })
+ })
+
+ describe('dy()', function() {
+ it('moves the y positon of the element relative to the current position', function() {
+ ellipse.move(50, 60)
+ ellipse.dy(120)
+ expect(ellipse.node.getAttribute('cy')).toBe('225')
+ })
+ })
+
+ describe('dmove()', function() {
+ it('moves the x and y positon of the element relative to the current position', function() {
+ ellipse.move(50,60)
+ ellipse.dmove(80, 25)
+ expect(ellipse.node.getAttribute('cx')).toBe('250')
+ expect(ellipse.node.getAttribute('cy')).toBe('130')
+ })
+ })
+
+ describe('center()', function() {
+ it('sets the cx and cy position', function() {
+ ellipse.center(321,567)
+ var box = ellipse.bbox()
+ expect(box.cx).toBe(321)
+ expect(box.cy).toBe(567)
+ })
+ })
+
+ describe('width()', function() {
+ it('sets the width of the element', function() {
+ ellipse.width(82)
+ expect(ellipse.node.getAttribute('rx')).toBe('41')
+ })
+ it('gets the width of the element if the argument is null', function() {
+ expect((ellipse.width() / 2).toString()).toBe(ellipse.node.getAttribute('rx'))
+ })
+ })
+
+ describe('height()', function() {
+ it('sets the height of the element', function() {
+ ellipse.height(1236)
+ expect(ellipse.node.getAttribute('ry')).toBe('618')
+ })
+ it('gets the height of the element if the argument is null', function() {
+ expect((ellipse.height() / 2).toString()).toBe(ellipse.node.getAttribute('ry'))
+ })
+ })
+
+ describe('size()', function() {
+ it('defines the rx and ry of the element', function() {
+ ellipse.size(987,654)
+ expect(ellipse.node.getAttribute('rx')).toBe((987 / 2).toString())
+ expect(ellipse.node.getAttribute('ry')).toBe((654 / 2).toString())
+ })
+ it('defines the width and height proportionally with only the width value given', function() {
+ var box = ellipse.bbox()
+ ellipse.size(500)
+ expect(ellipse.width()).toBe(500)
+ expect(ellipse.width() / ellipse.height()).toBe(box.width / box.height)
+ })
+ it('defines the width and height proportionally with only the height value given', function() {
+ var box = ellipse.bbox()
+ ellipse.size(null, 525)
+ expect(ellipse.height()).toBe(525)
+ expect(ellipse.width() / ellipse.height()).toBe(box.width / box.height)
+ })
+ })
+
+ describe('scale()', function() {
+ it('should scale the element universally with one argument', function() {
+ var box = ellipse.scale(2).rbox()
+
+ expect(box.width).toBe(ellipse.attr('rx') * 2 * 2)
+ expect(box.height).toBe(ellipse.attr('ry') * 2 * 2)
+ })
+ it('should scale the element over individual x and y axes with two arguments', function() {
+ var box = ellipse.scale(2, 3.5).rbox()
+
+ expect(box.width).toBe(ellipse.attr('rx') * 2 * 2)
+ expect(box.height).toBe(ellipse.attr('ry') * 2 * 3.5)
+ })
+ })
+
+ describe('translate()', function() {
+ it('sets the translation of an element', function() {
+ ellipse.transform({ x: 12, y: 12 })
+ expect(ellipse.node.getAttribute('transform')).toBe('matrix(1,0,0,1,12,12)')
+ })
+ })
+
+})
+
+
+
+
+
+
+
+
diff --git a/node_modules/svg.js/spec/spec/event.js b/node_modules/svg.js/spec/spec/event.js
new file mode 100644
index 0000000..1fad4aa
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/event.js
@@ -0,0 +1,262 @@
+describe('Event', function() {
+ var rect, context
+ , toast = null
+ , fruitsInDetail = null,
+ action = function(e) {
+ toast = 'ready'
+ context = this
+ fruitsInDetail = e.detail || null
+ }
+
+ beforeEach(function() {
+ rect = draw.rect(100, 100)
+ spyOn(SVG,'on').and.callThrough()
+ })
+
+ afterEach(function() {
+ toast = context = null
+ })
+
+ if (!this.isTouchDevice) {
+ [ 'click'
+ , 'dblclick'
+ , 'mousedown'
+ , 'mouseup'
+ , 'mouseover'
+ , 'mouseout'
+ , 'mousemove'
+ , 'mouseenter'
+ , 'mouseleave'
+ ].forEach(function(event) {
+ describe(event+'()', function() {
+ it('calls `on()` with '+event+' as event', function() {
+ rect[event](action)
+ expect(SVG.on).toHaveBeenCalledWith(rect, event, action)
+ })
+ })
+ })
+ } else {
+ [ 'touchstart'
+ , 'touchmove'
+ , 'touchleave'
+ , 'touchend'
+ , 'touchcancel'
+ ].forEach(function(event) {
+ describe(event+'()', function() {
+ it('calls `on()` with '+event+' as event', function() {
+ rect[event](action)
+ expect(SVG.on).toHaveBeenCalledWith(rect, event, action)
+ })
+ })
+ })
+ }
+
+ describe('on()', function() {
+
+ it('attaches an event to the element', function() {
+ rect.on('event', action).fire('event')
+ expect(toast).toBe('ready')
+ })
+ it('attaches an event to a non svg element', function() {
+ var el = document.createElement('div')
+ SVG.on(el, 'event', action)
+ el.dispatchEvent(new window.CustomEvent('event'))
+ expect(toast).toBe('ready')
+ SVG.off(el, 'event', action)
+ })
+ it('attaches multiple handlers on different element', function() {
+ var rect2 = draw.rect(100, 100)
+ var rect3 = draw.rect(100, 100)
+
+ rect.on('event', action)
+ rect2.on('event', action)
+ rect3.on('event', function(){ butter = 'melting' })
+ rect3.on('event', action)
+
+ expect(Object.keys(rect._events['event']['*']).length).toBe(1) // 1 listener on rect
+ expect(Object.keys(rect2._events['event']['*']).length).toBe(1) // 1 listener on rect2
+ expect(Object.keys(rect3._events['event']['*']).length).toBe(2) // 2 listener on rect3
+ })
+ it('attaches a handler to a namespaced event', function(){
+ var rect2 = draw.rect(100, 100)
+ var rect3 = draw.rect(100, 100)
+
+ rect.on('event.namespace1', action)
+ rect2.on('event.namespace2', action)
+ rect3.on('event.namespace3', function(){ butter = 'melting' })
+ rect3.on('event', action)
+
+ expect(rect._events['event']['*']).toBeUndefined() // no global listener on rect
+ expect(Object.keys(rect._events['event']['namespace1']).length).toBe( 1) // 1 namespaced listener on rect
+ expect(Object.keys(rect2._events['event']['namespace2']).length).toBe(1) // 1 namespaced listener on rect2
+ expect(Object.keys(rect3._events['event']['*']).length).toBe(1) // 1 gobal listener on rect3
+ expect(Object.keys(rect3._events['event']['namespace3']).length).toBe(1) // 1 namespaced listener on rect3
+ })
+ it('applies the element as context', function() {
+ rect.on('event', action).fire('event')
+ expect(context).toBe(rect)
+ })
+ it('applies given object as context', function() {
+ rect.on('event', action, this).fire('event')
+ expect(context).toBe(this)
+ })
+ it('stores the listener for future reference', function() {
+ rect.on('event', action)
+ expect(rect._events['event']['*'][action._svgjsListenerId]).not.toBeUndefined()
+ })
+ it('returns the called element', function() {
+ expect(rect.on('event', action)).toBe(rect)
+ })
+ })
+
+ describe('off()', function() {
+ var butter = null
+
+ beforeEach(function() {
+ butter = null
+ })
+
+ it('detaches a specific event listener, all other still working', function() {
+ rect2 = draw.rect(100,100)
+ rect3 = draw.rect(100,100)
+
+ rect.on('event', action)
+ rect2.on('event', action)
+ rect3.on('event', function(){ butter = 'melting' })
+
+ rect.off('event', action)
+
+ expect(Object.keys(rect._events['event']['*']).length).toBe(0)
+
+ rect.fire('event')
+ expect(toast).toBeNull()
+
+ rect2.fire('event')
+ expect(toast).toBe('ready')
+
+ rect3.fire('event')
+ expect(butter).toBe('melting')
+
+ expect(rect._events['event']['*'][action]).toBeUndefined()
+ })
+ it('detaches a specific namespaced event listener, all other still working', function() {
+ rect2 = draw.rect(100,100)
+ rect3 = draw.rect(100,100)
+
+ rect.on('event.namespace', action)
+ rect2.on('event.namespace', action)
+ rect3.on('event.namespace', function(){ butter = 'melting' })
+
+ rect.off('event.namespace', action)
+
+ expect(Object.keys(rect._events['event']['namespace']).length).toBe(0)
+ expect(Object.keys(rect2._events['event']['namespace']).length).toBe(1)
+
+ rect.fire('event')
+ expect(toast).toBeNull()
+
+ rect2.fire('event')
+ expect(toast).toBe('ready')
+
+ rect3.fire('event')
+ expect(butter).toBe('melting')
+
+ expect(rect._events['event']['namespace'][action]).toBeUndefined()
+ })
+ it('detaches all listeners for a specific namespace', function() {
+ rect.on('event', action)
+ rect.on('event.namespace', function() { butter = 'melting'; })
+ rect.off('.namespace')
+
+ rect.fire('event')
+ expect(toast).toBe('ready')
+ expect(butter).toBeNull()
+ })
+ it('detaches all listeners for an event without a listener given', function() {
+ rect.on('event', action)
+ rect.on('event.namespace', function() { butter = 'melting'; })
+ rect.off('event')
+
+ rect.fire('event')
+ expect(toast).toBeNull()
+ expect(butter).toBeNull()
+ expect(rect._events['event']).toBeUndefined()
+ })
+ it('detaches all listeners without an argument', function() {
+ rect.on('event', action)
+ rect.on('click', function() { butter = 'melting' })
+ rect.off()
+ rect.fire('event')
+ rect.fire('click')
+ expect(toast).toBeNull()
+ expect(butter).toBeNull()
+ expect(Object.keys(rect._events).length).toBe(0)
+ })
+ it('returns the called element', function() {
+ expect(rect.off('event', action)).toBe(rect)
+ })
+ it('does not throw when event is removed which was already removed with a global off', function() {
+ var undefined
+
+ rect.on('event', action)
+ rect.off()
+ try{
+ rect.off('event')
+ }catch(e){
+ expect('Should not error out').toBe(true)
+ }
+
+ expect(Object.keys(rect._events).length).toBe(0)
+ })
+ })
+
+ describe('fire()', function() {
+
+ beforeEach(function() {
+ rect.on('event', action)
+ })
+
+ it('fires an event for the element', function() {
+ expect(toast).toBeNull()
+ rect.fire('event')
+ expect(toast).toBe('ready')
+ expect(fruitsInDetail).toBe(null)
+ })
+ it('returns the called element', function() {
+ expect(rect.fire('event')).toBe(rect)
+ })
+ it('fires event with additional data', function() {
+ expect(fruitsInDetail).toBeNull()
+ rect.fire('event', {apple:1})
+ expect(fruitsInDetail).not.toBe(null)
+ expect(fruitsInDetail.apple).toBe(1)
+ })
+ it('fires my own event', function() {
+ toast = null
+ rect.fire(new window.CustomEvent('event'))
+ expect(toast).toBe('ready')
+ })
+ it('makes the event cancelable', function() {
+ rect.on('event', function(e) {
+ e.preventDefault()
+ })
+ rect.fire('event')
+ expect(rect._event.defaultPrevented).toBe(true)
+ })
+ })
+
+ describe('event()', function() {
+ it('returns null when no event was fired', function() {
+ expect(rect.event()).toBe(null)
+ })
+ it('returns the last fired event', function() {
+ var event = new window.CustomEvent('foo')
+ rect.fire(event)
+ expect(rect.event()).toBe(event)
+
+ event = new window.CustomEvent('bar')
+ rect.fire(event)
+ expect(rect.event()).toBe(event)
+ })
+ })
+})
diff --git a/node_modules/svg.js/spec/spec/fx.js b/node_modules/svg.js/spec/spec/fx.js
new file mode 100644
index 0000000..f8f7d69
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/fx.js
@@ -0,0 +1,2952 @@
+describe('FX', function() {
+ var rect, fx, undefined;
+
+ beforeEach(function() {
+ rect = draw.rect(100,100).move(100,100)
+ fx = rect.animate(500)
+
+ jasmine.clock().install()
+ jasmine.clock().mockDate() // This freeze the Date
+ })
+
+ afterEach(function() {
+ jasmine.clock().uninstall()
+
+ fx.stop(false, true)
+ })
+
+
+ it('creates an instance of SVG.FX and sets parameter', function() {
+ expect(fx instanceof SVG.FX).toBe(true)
+ expect(fx._target).toBe(rect)
+ expect(fx.absPos).toBe(0)
+ expect(fx.pos).toBe(0)
+ expect(fx.lastPos).toBe(0)
+ expect(fx.paused).toBe(false)
+ expect(fx.active).toBe(false)
+ expect(fx._speed).toBe(1)
+ expect(fx.situations).toEqual([])
+ expect(fx.situation.init).toBe(false)
+ expect(fx.situation.reversed).toBe(false)
+ expect(fx.situation.duration).toBe(500)
+ expect(fx.situation.delay).toBe(0)
+ expect(fx.situation.loops).toBe(false)
+ expect(fx.situation.loop).toBe(0)
+ expect(fx.situation.animations).toEqual({})
+ expect(fx.situation.attrs).toEqual({})
+ expect(fx.situation.styles).toEqual({})
+ expect(fx.situation.transforms).toEqual([])
+ expect(fx.situation.once).toEqual({})
+ })
+
+ describe('animate()', function () {
+ it('set duration, ease and delay of the new situation to their default value when they are not passed', function() {
+ var defaultDuration = 1000
+ , defaultEase = SVG.easing['-']
+ , defaultDelay = 0
+ , lastSituation = fx.animate().last()
+
+ expect(lastSituation.duration).toBe(defaultDuration)
+ expect(lastSituation.ease).toBe(defaultEase)
+ expect(lastSituation.delay).toBe(defaultDelay)
+ })
+
+ it('use the passed values to set duration, ease and delay of the new situation', function() {
+ var duration = 14502
+ , ease = '>'
+ , delay = 450
+ , lastSituation = fx.animate(duration, ease, delay).last()
+
+ expect(lastSituation.duration).toBe(duration)
+ expect(lastSituation.ease).toBe(SVG.easing[ease])
+ expect(lastSituation.delay).toBe(delay)
+ })
+
+ it('allow duration, ease and delay to be passed in an object', function() {
+ var o = {
+ duration: 7892
+ , ease: '<'
+ , delay: 1145
+ }
+ , lastSituation = fx.animate(o).last()
+
+ expect(lastSituation.duration).toBe(o.duration)
+ expect(lastSituation.ease).toBe(SVG.easing[o.ease])
+ expect(lastSituation.delay).toBe(o.delay)
+ })
+
+ it('allow ease to be a custom function', function () {
+ var customEase = function() {}
+ , lastSituation = fx.animate({ease: customEase}).last()
+
+ expect(lastSituation.ease).toBe(customEase)
+ })
+ })
+
+ describe('target()', function(){
+ it('returns the current fx object with no argument given', function(){
+ expect(fx.target()).toBe(rect)
+ })
+
+ it('changes the target of the animation when parameter given', function(){
+ var c = draw.circle(5)
+ expect(fx.target(c).target()).toBe(c)
+ })
+ })
+
+
+ describe('timeToAbsPos()', function() {
+ it('converts a timestamp to an absolute progress', function() {
+ expect(fx.timeToAbsPos( fx.situation.start + fx.situation.duration*0.5 )).toBe(0.5)
+ })
+
+ it('should take speed into consideration', function() {
+ var spd
+
+ spd = 4
+ fx.speed(spd)
+ expect(fx.timeToAbsPos( fx.situation.start + (fx.situation.duration/spd)*0.5 )).toBe(0.5)
+
+ spd = 0.5
+ fx.speed(spd)
+ expect(fx.timeToAbsPos( fx.situation.start + (fx.situation.duration/spd)*0.25 )).toBe(0.25)
+ })
+ })
+
+
+ describe('absPosToTime()', function() {
+ it('converts an absolute progress to a timestamp', function() {
+ expect(fx.absPosToTime(0.5)).toBe( fx.situation.start + fx.situation.duration*0.5 )
+ })
+
+ it('should take speed into consideration', function() {
+ var spd
+
+ spd = 4
+ fx.speed(spd)
+ expect(fx.absPosToTime(0.5)).toBe( fx.situation.start + (fx.situation.duration/spd)*0.5 )
+
+ spd = 0.5
+ fx.speed(spd)
+ expect(fx.absPosToTime(0.25)).toBe( fx.situation.start + (fx.situation.duration/spd)*0.25 )
+ })
+ })
+
+
+ describe('atStart()', function () {
+ it('sets the animation at the start', function() {
+ // When the animation is running forward, the start position is 0
+ fx.pos = 0.5
+ expect(fx.atStart().pos).toBe(0)
+
+ // When the animation is running backward, the start position is 1
+ fx.pos = 0.5
+ expect(fx.reverse(true).atStart().pos).toBe(1)
+ })
+
+ it('sets the animation at the start, before any loops', function() {
+ fx.loop(true)
+
+ // When the animation is running forward, the start position is 0
+ fx.at(3.7, true)
+ expect(fx.absPos).toBe(3.7)
+ expect(fx.pos).toBeCloseTo(0.7)
+ expect(fx.situation.loop).toBe(3)
+
+ fx.atStart()
+ expect(fx.absPos).toBe(0)
+ expect(fx.pos).toBe(0)
+ expect(fx.situation.loop).toBe(0)
+
+ // When the animation is running backward, the start position is 1
+ fx.reverse(true).at(2.14, true)
+ expect(fx.absPos).toBe(2.14)
+ expect(fx.pos).toBeCloseTo(1 - 0.14)
+ expect(fx.situation.loop).toBe(2)
+ expect(fx.situation.reversed).toBe(true)
+
+ fx.atStart()
+ expect(fx.absPos).toBe(0)
+ expect(fx.pos).toBe(1)
+ expect(fx.situation.loop).toBe(0)
+ expect(fx.situation.reversed).toBe(true)
+ })
+
+ it('sets the animation at the start, before any loops when reversing is true', function() {
+ fx.loop(true, true) // Set reversing to true
+
+ // When the animation is running forward, the start position is 0
+ fx.at(11.21, true)
+ expect(fx.absPos).toBe(11.21)
+ expect(fx.pos).toBeCloseTo(1 - 0.21)
+ expect(fx.situation.loop).toBe(11)
+ expect(fx.situation.reversed).toBe(true)
+
+ fx.atStart()
+ expect(fx.absPos).toBe(0)
+ expect(fx.pos).toBe(0)
+ expect(fx.situation.loop).toBe(0)
+ expect(fx.situation.reversed).toBe(false)
+
+ // When the animation is running backward, the start position is 1
+ fx.reverse(true).at(14.10, true)
+ expect(fx.absPos).toBe(14.10)
+ expect(fx.pos).toBeCloseTo(1 - 0.10)
+ expect(fx.situation.loop).toBe(14)
+ expect(fx.situation.reversed).toBe(true)
+
+ fx.atStart()
+ expect(fx.absPos).toBe(0)
+ expect(fx.pos).toBe(1)
+ expect(fx.situation.loop).toBe(0)
+ expect(fx.situation.reversed).toBe(true)
+ })
+ })
+
+
+ describe('atEnd()', function () {
+ it('sets the animation at the end', function() {
+ // When the animation is running forward, the end position is 1
+ fx.pos = 0.5
+ expect(fx.atEnd().pos).toBe(1)
+ expect(fx.situation).toBeNull()
+
+ // Recreate an animation since the other one was ended
+ fx.animate()
+
+ // When the animation is running backward, the end position is 0
+ fx.pos = 0.5
+ expect(fx.reverse(true).atEnd().pos).toBe(0)
+ expect(fx.situation).toBeNull()
+ })
+
+ it('sets the animation at the end, after all loops', function() {
+ var loops
+
+ // When the animation is running forward, the end position is 1
+ loops = 12
+ fx.loop(loops).start().step()
+ expect(fx.absPos).toBe(0)
+ expect(fx.pos).toBe(0)
+ expect(fx.active).toBe(true)
+ expect(fx.situation.loop).toBe(0)
+ expect(fx.situation.loops).toBe(loops)
+
+ fx.atEnd()
+ expect(fx.absPos).toBe(loops)
+ expect(fx.pos).toBe(1)
+ expect(fx.active).toBe(false)
+ expect(fx.situation).toBeNull()
+
+ // Recreate an animation since the other one was ended
+ fx.animate()
+
+
+ // When the animation is running backward, the end position is 0
+ loops = 21
+ fx.reverse(true).loop(loops).start().step()
+ expect(fx.absPos).toBe(0)
+ expect(fx.pos).toBe(1)
+ expect(fx.active).toBe(true)
+ expect(fx.situation.loop).toBe(0)
+ expect(fx.situation.loops).toBe(loops)
+ expect(fx.situation.reversed).toBe(true)
+
+ fx.atEnd()
+ expect(fx.absPos).toBe(loops)
+ expect(fx.pos).toBe(0)
+ expect(fx.active).toBe(false)
+ expect(fx.situation).toBeNull()
+ })
+
+ it('sets the animation at the end, after all loops when reversing is true', function() {
+ var loops
+
+ // When reversing is true, the end position is 0 when loops is even and
+ // 1 when loops is odd
+
+ // The animation is running forward
+ loops = 6
+ fx.loop(loops, true).start().step()
+ expect(fx.absPos).toBe(0)
+ expect(fx.pos).toBe(0)
+ expect(fx.active).toBe(true)
+ expect(fx.situation.loop).toBe(0)
+ expect(fx.situation.loops).toBe(loops)
+ expect(fx.situation.reversed).toBe(false)
+
+ fx.atEnd()
+ expect(fx.absPos).toBe(loops)
+ expect(fx.pos).toBe(0) // End position is 0 because loops is even
+ expect(fx.active).toBe(false)
+ expect(fx.situation).toBeNull()
+
+ // Recreate an animation since the other one was ended
+ fx.animate()
+
+ // When reversing is true and the animation is running backward,
+ // the end position is 1 when loops is even and 0 when loops is odd
+
+ // The animation is running backward
+ loops = 3
+ fx.reverse(true).loop(loops, true).start().step()
+ expect(fx.absPos).toBe(0)
+ expect(fx.pos).toBe(1)
+ expect(fx.active).toBe(true)
+ expect(fx.situation.loop).toBe(0)
+ expect(fx.situation.loops).toBe(loops)
+ expect(fx.situation.reversed).toBe(true)
+
+ fx.atEnd()
+ expect(fx.absPos).toBe(loops)
+ expect(fx.pos).toBe(0) // End position is 0 because loops is odd
+ expect(fx.active).toBe(false)
+ expect(fx.situation).toBeNull()
+ })
+
+ it('sets the animation at the end of the current iteration when in an infinite loop', function () {
+ // When the animation is running forward, the end position is 1
+ fx.loop(true).start().step()
+ expect(fx.absPos).toBe(0)
+ expect(fx.pos).toBe(0)
+ expect(fx.active).toBe(true)
+ expect(fx.situation.loop).toBe(0)
+ expect(fx.situation.loops).toBe(true)
+
+ // Should be halfway through iteration 10
+ jasmine.clock().tick(500 * 10 + 250)
+ fx.step()
+ expect(fx.absPos).toBe(10.5)
+ expect(fx.pos).toBe(0.5)
+ expect(fx.active).toBe(true)
+ expect(fx.situation.loop).toBe(10)
+ expect(fx.situation.loops).toBe(true)
+
+ fx.atEnd()
+ expect(fx.absPos).toBe(11)
+ expect(fx.pos).toBe(1)
+ expect(fx.active).toBe(false)
+ expect(fx.situation).toBeNull()
+
+ // Recreate an animation since the other one was ended
+ fx.animate(500)
+
+ // When the animation is running backward, the end position is 0
+ fx.reverse(true).loop(true).start().step()
+ expect(fx.absPos).toBe(0)
+ expect(fx.pos).toBe(1)
+ expect(fx.active).toBe(true)
+ expect(fx.situation.loop).toBe(0)
+ expect(fx.situation.loops).toBe(true)
+ expect(fx.situation.reversed).toBe(true)
+
+ // Should be halfway through iteration 21
+ jasmine.clock().tick(500 * 21 + 250)
+ fx.step()
+ expect(fx.absPos).toBe(21.5)
+ expect(fx.pos).toBe(0.5)
+ expect(fx.active).toBe(true)
+ expect(fx.situation.loop).toBe(21)
+ expect(fx.situation.loops).toBe(true)
+
+ fx.atEnd()
+ expect(fx.absPos).toBe(22)
+ expect(fx.pos).toBe(0)
+ expect(fx.active).toBe(false)
+ expect(fx.situation).toBeNull()
+ })
+
+
+ it('sets the animation at the end of the current iteration when in an infinite loop and reversing is true', function () {
+ // When reversing is true, the end position is 1 when ending on an even
+ // iteration and 0 when ending on an odd iteration as illustrated below:
+
+ // 0 Iteration 1
+ // |--------------0------------->|
+ // |<-------------1--------------|
+ // |--------------2------------->|
+ // |<-------------3--------------|
+ // ...
+
+
+ // The animation is running forward
+ fx.loop(true, true).start().step()
+ expect(fx.absPos).toBe(0)
+ expect(fx.pos).toBe(0)
+ expect(fx.active).toBe(true)
+ expect(fx.situation.loop).toBe(0)
+ expect(fx.situation.loops).toBe(true)
+
+ // Should be halfway through iteration 11
+ jasmine.clock().tick(500 * 11 + 250)
+ fx.step()
+ expect(fx.absPos).toBe(11.5)
+ expect(fx.pos).toBe(0.5)
+ expect(fx.active).toBe(true)
+ expect(fx.situation.loop).toBe(11)
+ expect(fx.situation.loops).toBe(true)
+
+ fx.atEnd()
+ expect(fx.absPos).toBe(12)
+ expect(fx.pos).toBe(0) // End position is 0 because ended on a odd iteration
+ expect(fx.active).toBe(false)
+ expect(fx.situation).toBeNull()
+
+ // Recreate an animation since the other one was ended
+ fx.animate(500)
+
+ // When reversing is true and the animation is running backward,
+ // the end position is 0 when ending on an even iteration and
+ // 1 when ending on an odd iteration as illustrated below:
+
+ // 0 Iteration 1
+ // |<-------------0--------------|
+ // |--------------1------------->|
+ // |<-------------2--------------|
+ // |--------------3------------->|
+ // ...
+
+ // The animation is running backward
+ fx.reverse(true).loop(true).start().step()
+ expect(fx.absPos).toBe(0)
+ expect(fx.pos).toBe(1)
+ expect(fx.active).toBe(true)
+ expect(fx.situation.loop).toBe(0)
+ expect(fx.situation.loops).toBe(true)
+ expect(fx.situation.reversed).toBe(true)
+
+ // Should be halfway through iteration 42
+ jasmine.clock().tick(500 * 42 + 250)
+ fx.step()
+ expect(fx.absPos).toBe(42.5)
+ expect(fx.pos).toBe(0.5)
+ expect(fx.active).toBe(true)
+ expect(fx.situation.loop).toBe(42)
+ expect(fx.situation.loops).toBe(true)
+
+ fx.atEnd()
+ expect(fx.absPos).toBe(43)
+ expect(fx.pos).toBe(0) // End position is 0 because ended on an even iteration
+ expect(fx.active).toBe(false)
+ expect(fx.situation).toBeNull()
+ })
+ })
+
+
+ describe('at()', function() {
+ it('sets the progress to the specified position', function() {
+ var pos
+
+ // Animation running forward
+ pos = 0.5
+ expect(fx.at(pos).pos).toBe(pos)
+ expect(fx.situation.start).toBe(+new Date - fx.situation.duration * pos)
+
+ // Animation running backward
+ pos = 0.4
+ expect(fx.reverse(true).at(pos).pos).toBe(pos)
+ expect(fx.situation.start).toBe(+new Date - fx.situation.duration * (1-pos))
+ })
+
+ it('should convert a position to an absolute position', function () {
+ var pos, loop, absPos
+
+ fx.loop(true)
+
+ // Animation running forward
+ pos = 0.7
+ loop = 4
+ absPos = pos+loop
+ fx.situation.loop = loop
+ expect(fx.at(pos).absPos).toBe(absPos)
+ expect(fx.situation.start).toBe(+new Date - fx.situation.duration * absPos)
+
+ // Animation running backward
+ pos = 0.23
+ loop = 9
+ absPos = (1-pos)+loop
+ fx.situation.loop = loop
+ fx.situation.reversed = true
+ expect(fx.at(pos).absPos).toBe(absPos)
+ expect(fx.situation.start).toBe(+new Date - fx.situation.duration * absPos)
+
+ })
+
+ it('should end the animation when the end position is passed', function() {
+ var pos
+
+ fx.start()
+ expect(fx.active).toBe(true)
+ expect(fx.situation).not.toBeNull()
+
+ // When running forward, the end position is 1
+ pos = 1
+ expect(fx.at(pos).pos).toBe(pos)
+ expect(fx.active).toBe(false)
+ expect(fx.situation).toBeNull()
+
+ // Recreate an animation since the other one was ended
+ fx.animate().start()
+ expect(fx.active).toBe(true)
+ expect(fx.situation).not.toBeNull()
+
+ // When running backward, the end position is 0
+ pos = 0
+ expect(fx.reverse(true).at(pos).pos).toBe(pos)
+ expect(fx.active).toBe(false)
+ expect(fx.situation).toBeNull()
+ })
+
+ it('correct the passed position when it is out of [0,1] and the animation is not looping', function () {
+ var pos
+
+ pos = -0.7
+ expect(fx.at(pos).pos).toBe(0)
+
+ pos = 1.3
+ expect(fx.at(pos).pos).toBe(1)
+
+ // Recreate an animation since the other one was ended
+ fx.animate()
+
+ // Should work even when animation is running backward
+ pos = 1.3
+ expect(fx.reverse(true).at(pos).pos).toBe(1)
+
+ pos = -0.7
+ expect(fx.reverse(true).at(pos).pos).toBe(0)
+ })
+
+ it('should, when the animation is looping and the passed position is out of [0,1], use the integer part of postion to update the loop counter and set position to its fractional part', function(){
+ var loop, pos, posFrac, posInt
+
+ // Without the reverse flag
+ fx.loop(10)
+ expect(fx.situation.loops).toBe(10)
+ expect(fx.situation.loop).toBe(loop = 0)
+
+ pos = 1.3
+ posFrac = pos % 1
+ posInt = pos - posFrac
+ expect(fx.at(pos).pos).toBeCloseTo(posFrac)
+ expect(fx.situation.loop).toBe(loop += posInt)
+
+ pos = 7.723
+ posFrac = pos % 1
+ posInt = pos - posFrac
+ expect(fx.at(pos).pos).toBeCloseTo(posFrac)
+ expect(fx.situation.loop).toBe(loop += posInt)
+
+ // In this case, pos is above the remaining number of loops, so we expect
+ // the position to be set to 1 and the animation to be ended
+ pos = 4.3
+ posFrac = pos % 1
+ posInt = pos - posFrac
+ expect(fx.at(pos).pos).toBe(1)
+ expect(fx.situation).toBeNull()
+
+ // Recreate an animation since the other one was ended
+ fx.animate()
+
+ // With the reverse flag, the position is reversed each time loop is odd
+ fx.loop(10, true)
+ expect(fx.situation.loops).toBe(10)
+ expect(fx.situation.loop).toBe(loop = 0)
+ expect(fx.situation.reversed).toBe(false)
+
+ pos = 3.3
+ posFrac = pos % 1
+ posInt = pos - posFrac
+ expect(fx.at(pos).pos).toBeCloseTo(1-posFrac) // Animation is reversed because 0+3 is odd
+ expect(fx.situation.loop).toBe(loop += posInt)
+ expect(fx.situation.reversed).toBe(true)
+
+ // When the passed position is below 0, the integer part of position is
+ // substracted from 1, so, in this case, -0.6 has 1 as is integer part
+ // This is necessary so we can add something to the loop counter
+ pos = -0.645
+ posFrac = (1-pos) % 1
+ posInt = (1-pos) - posFrac
+ expect(fx.at(pos).pos).toBeCloseTo(posFrac)
+ expect(fx.situation.loop).toBe(loop += posInt)
+ expect(fx.situation.reversed).toBe(false)
+
+ // In this case, pos is above the remaining number of loop, so we expect
+ // the position to be set to 0 (since we end reversed) and the animation to
+ // be ended
+ pos = 7.2
+ posFrac = pos % 1
+ posInt = pos - posFrac
+ expect(fx.at(pos).pos).toBe(0)
+ expect(fx.situation).toBeNull()
+ })
+
+ it('should, when the animation is in a infinite loop and the passed position is out of [0,1], use the integer part of postion to update the loop counter and set position to its fractional part', function(){
+ var loop, pos, posFrac, posInt
+
+ // Without the reverse flag
+ fx.loop(true)
+ expect(fx.situation.loops).toBe(true)
+ expect(fx.situation.loop).toBe(loop = 0)
+
+ pos = 10.34
+ posFrac = pos % 1
+ posInt = pos - posFrac
+ expect(fx.at(pos).pos).toBeCloseTo(posFrac)
+ expect(fx.situation.loop).toBe(loop += posInt)
+
+ // With the reverse flag, the position is reversed each time loop is odd
+ fx.loop(true, true)
+ expect(fx.situation.loops).toBe(true)
+ expect(fx.situation.loop).toBe(loop = 0)
+ expect(fx.situation.reversed).toBe(false)
+
+ pos = 3.3
+ posFrac = pos % 1
+ posInt = pos - posFrac
+ expect(fx.at(pos).pos).toBeCloseTo(1-posFrac) // Animation is reversed because 3+0 is odd
+ expect(fx.situation.loop).toBe(loop += posInt)
+ expect(fx.situation.reversed).toBe(true)
+
+ pos = -8.41
+ posFrac = (1-pos) % 1
+ posInt = (1-pos) - posFrac
+ expect(fx.at(pos).pos).toBeCloseTo(posFrac)
+ expect(fx.situation.loop).toBe(loop += posInt)
+ expect(fx.situation.reversed).toBe(false)
+ })
+
+ it('should take speed into consideration', function() {
+ var dur, spd
+
+ dur = fx.situation.duration
+
+ spd = 4
+ fx.speed(spd).at(0)
+ expect(fx.situation.finish-fx.situation.start).toBe(dur/spd)
+
+ spd = 5
+ fx.speed(spd).at(0.15)
+ expect(fx.situation.finish-fx.situation.start).toBe(dur/spd)
+
+ spd = 0.25
+ fx.speed(spd).at(0.75)
+ expect(fx.situation.finish-fx.situation.start).toBe(dur/spd)
+
+ spd = 0.5
+ fx.speed(spd).at(0.83)
+ expect(fx.situation.finish-fx.situation.start).toBe(dur/spd)
+ })
+
+ it('should consider the first parameter as an absolute position when the second parameter is true', function() {
+ var absPos
+
+ fx.loop(true)
+
+ absPos = 3.2
+ expect(fx.at(absPos, true).absPos).toBe(absPos)
+
+ absPos = -4.27
+ expect(fx.at(absPos, true).absPos).toBe(absPos)
+
+ absPos = 0
+ expect(fx.at(absPos, true).absPos).toBe(absPos)
+
+ absPos = 1
+ expect(fx.at(absPos, true).absPos).toBe(absPos)
+ })
+ })
+
+
+ describe('start()', function(){
+ it('starts the animation', function() {
+ fx.start()
+ expect(fx.active).toBe(true)
+
+ jasmine.clock().tick(200)
+ fx.step() // Call step to update the animation
+
+ expect(fx.pos).toBeGreaterThan(0)
+ })
+
+ it('should take speed into consideration', function() {
+ var dur = 500
+ , delay = 300
+ , spd = 4
+
+
+ fx.stop().animate(dur, '-', delay).speed(spd).start()
+ expect(fx.situation.finish - new Date).toBe(delay/spd + dur/spd)
+ })
+
+ it('should do the delay', function() {
+ fx.situation.delay = 1000
+ expect(fx.start().active).toBe(true)
+
+ jasmine.clock().tick(501)
+ fx.step() // Call step to update the animation
+ expect(fx.active).toBe(true)
+
+ jasmine.clock().tick(501)
+ fx.step() // Call step to update the animation
+ expect(fx.active).toBe(true)
+
+ jasmine.clock().tick(501)
+ fx.step() // Call step to update the animation
+ expect(fx.active).toBe(false)
+ })
+ })
+
+ describe('delay()', function() {
+ it('should push an empty situation with its duration attribute set to the duration of the delay', function() {
+ var delay = 8300
+ fx.delay(delay)
+ expect(fx.situations[0].duration).toBe(delay)
+ })
+ })
+
+
+ describe('pause()', function() {
+ it('pause the animation', function() {
+ expect(fx.pause().paused).toBe(true)
+ })
+ })
+
+ describe('play()', function() {
+ it('returns itself when animation not paused', function() {
+ expect(fx.paused).toBe(false)
+ expect(fx.play()).toBe(fx)
+ })
+
+ it('unpause the animation', function() {
+ var start = fx.start().pause().situation.start
+
+ jasmine.clock().tick(200)
+
+ expect(fx.situation.start).toBe(start)
+ expect(fx.play().paused).toBe(false)
+ expect(fx.situation.start).not.toBe(start)
+ })
+
+ it('should not change the position when the animation is unpaused while it is set to run backward', function(){
+ var pos = 0.4
+
+ expect(fx.reverse(true).at(pos).pause().play().pos).toBe(pos)
+ })
+
+ it('should be able to unpause the delay', function () {
+ fx.stop().animate(500, '-', 300).start().step()
+ expect(fx.pos).toBe(0)
+ expect(fx.absPos).toBeCloseTo(-0.6)
+
+ // At this point, we should have an animation of 500 ms with a delay of
+ // 300 ms that should be running.
+
+ jasmine.clock().tick(150)
+
+ // Should be halfway through the delay
+ fx.step()
+ expect(fx.pos).toBe(0)
+ expect(fx.absPos).toBe(-0.3)
+
+ expect(fx.pause().paused).toBe(true) // Pause the delay
+
+ jasmine.clock().tick(150)
+
+ // Unpause, should still be halfway through the delay
+ expect(fx.play().paused).toBe(false)
+ expect(fx.pos).toBe(0)
+ expect(fx.absPos).toBe(-0.3)
+
+ jasmine.clock().tick(150)
+
+ // Delay should be done
+ fx.step()
+ expect(fx.pos).toBe(0)
+ expect(fx.absPos).toBe(0)
+
+ jasmine.clock().tick(500)
+
+ // Animation and delay should be done
+ fx.step()
+ expect(fx.active).toBe(false)
+ expect(fx.pos).toBe(1)
+ expect(fx.absPos).toBe(1)
+ })
+ })
+
+
+ describe('speed()', function() {
+ it('set the speed of the animation', function(){
+ var dur, spd
+
+ dur = fx.situation.duration
+
+ spd = 2
+ fx.speed(spd)
+ expect(fx._speed).toBe(spd)
+ expect(fx.situation.finish-fx.situation.start).toBe(dur/spd)
+
+ spd = 0.5
+ fx.speed(spd)
+ expect(fx._speed).toBe(spd)
+ expect(fx.situation.finish-fx.situation.start).toBe(dur/spd)
+
+ spd = 2
+ fx.at(0.2).speed(spd)
+ expect(fx._speed).toBe(spd)
+ expect(fx.situation.finish-fx.situation.start).toBe(dur/spd)
+
+ spd = 1
+ fx.speed(spd)
+ expect(fx._speed).toBe(spd)
+ expect(fx.situation.finish-fx.situation.start).toBe(dur)
+ })
+
+ it('should not change the position when the animation is run backward', function(){
+ var pos = 0.4
+
+ expect(fx.reverse(true).at(pos).speed(2).pos).toBe(pos)
+ })
+
+ it('return the current speed with no argument given', function(){
+ var spd
+
+ spd = 2
+ fx._speed = spd
+ expect(fx.speed()).toBe(spd)
+
+ spd = 0.5
+ fx._speed = spd
+ expect(fx.speed()).toBe(spd)
+
+ spd = 1
+ fx._speed = spd
+ expect(fx.speed()).toBe(spd)
+ })
+
+ it('pause the animation when a speed of 0 is passed', function(){
+ var spd = fx._speed
+
+ expect(fx.speed(0)).toBe(fx)
+ expect(fx._speed).toBe(spd)
+ expect(fx.paused).toBe(true)
+ })
+
+ it('should affect all animations in the queue', function(){
+ fx.speed(2).animate(300)
+ expect(fx.situations.length).not.toBe(0)
+ expect(fx.pos).not.toBe(1)
+
+ // At this point, there should be 2 animations in the queue to be played:
+ // the one of 500ms that is added before every test and the one of 300ms
+ // we just added. Normally, it would take 800ms before both of these
+ // animations are done, but because we set the speed to 2, it should
+ // only take 400ms to do both animations.
+ fx.start().step()
+
+ jasmine.clock().tick(250)
+
+ // Should be playing the second animation
+ fx.step()
+ expect(fx.active).toBe(true)
+ expect(fx.situations.length).toBe(0)
+ expect(fx.pos).not.toBe(1)
+
+ jasmine.clock().tick(150) // 400ms have passed
+
+ // All animations should be done
+ fx.step()
+ expect(fx.active).toBe(false)
+ expect(fx.situations.length).toBe(0)
+ expect(fx.pos).toBe(1)
+ })
+
+ it('should affect the delay', function() {
+ fx.stop().animate(500, '-', 300).start().step()
+ expect(fx.pos).toBe(0)
+ expect(fx.absPos).toBeCloseTo(-0.6)
+
+ fx.speed(2)
+ expect(fx.pos).toBe(0)
+ expect(fx.absPos).toBeCloseTo(-0.6)
+
+ // At this point, we should have an animation of 500 ms with a delay of
+ // 300 ms that should be running. Normally, it would take 800 ms for the
+ // animation and its delay to complete, but because the speed is set to 2
+ // , it should only take 400ms
+
+ jasmine.clock().tick(75)
+
+ // Should be halfway through the delay
+ fx.step()
+ expect(fx.pos).toBe(0)
+ expect(fx.absPos).toBe(-0.3)
+
+ jasmine.clock().tick(75)
+
+ // Delay should be done
+ fx.step()
+ expect(fx.pos).toBe(0)
+ expect(fx.absPos).toBe(0)
+
+ jasmine.clock().tick(250)
+
+ // Animation and delay should be done
+ fx.step()
+ expect(fx.active).toBe(false)
+ expect(fx.pos).toBe(1)
+ expect(fx.absPos).toBe(1)
+ })
+ })
+
+
+ describe('reverse()', function() {
+ it('toggles the direction of the animation without a parameter', function() {
+ expect(fx.reverse().situation.reversed).toBe(true)
+ })
+ it('sets the direction to backwards with true given', function() {
+ expect(fx.reverse(true).situation.reversed).toBe(true)
+ })
+ it('sets the direction to forwards with false given', function() {
+ expect(fx.reverse(false).situation.reversed).toBe(false)
+ })
+ })
+
+
+ describe('queue()', function() {
+ it('can add a situation to the queue', function() {
+ var situation = new SVG.Situation({duration: 1000, delay: 0, ease: SVG.easing['-']})
+
+ fx.queue(situation)
+ expect(fx.situations[0]).toBe(situation)
+ })
+
+ it('can add a function to the queue', function() {
+ var f = function(){}
+
+ fx.queue(f)
+ expect(fx.situations[0]).toBe(f)
+ })
+
+ it('should set the situation attribute before pushing something in the situations queue', function(){
+ var situation = new SVG.Situation({duration: 1000, delay: 0, ease: SVG.easing['-']})
+
+ // Clear the animation that is created before each test
+ fx.stop()
+
+ expect(fx.situation).toBeNull()
+ expect(fx.situations.length).toBe(0)
+ fx.queue(situation)
+ expect(fx.situation).toBe(situation)
+ expect(fx.situations.length).toBe(0)
+ })
+ })
+
+
+ describe('dequeue()', function() {
+ it('should pull the next situtation from the queue', function() {
+ var situation = new SVG.Situation({duration: 1000, delay: 0, ease: SVG.easing['-']})
+
+ fx.queue(situation)
+ expect(fx.situtation).not.toBe(situation)
+ expect(fx.situations[0]).toBe(situation)
+
+ fx.dequeue()
+ expect(fx.situation).toBe(situation)
+ expect(fx.situations.length).toBe(0)
+ })
+
+ it('initialize the animation pulled from the queue to its start position', function() {
+ // When the animation is forward, the start position is 0
+ fx.animate()
+ fx.pos = 0.5
+ expect(fx.dequeue().pos).toBe(0)
+
+ // When the animation backward, the start position is 1
+ fx.animate().reverse(true)
+ fx.pos = 0.5
+ expect(fx.dequeue().pos).toBe(1)
+ })
+
+ it('when the first element of the queue is a function, it should execute it', function() {
+ var called = false
+
+ fx.queue(function(){
+ called = true
+ expect(this).toBe(fx)
+ this.dequeue()
+ }).dequeue()
+
+ expect(called).toBe(true)
+ })
+
+ it('should stop the currently running animation when there is one', function() {
+ fx.start()
+ expect(fx.active).toBe(true)
+ fx.queue(function() {
+ expect(this.active).toBe(false)
+ this.dequeue()
+ })
+ fx.dequeue()
+ })
+ })
+
+
+ describe('stop()', function() {
+ it('stops the animation immediately without a parameter', function() {
+ fx.animate(500).start()
+ expect(fx.stop().situation).toBeNull()
+ expect(fx.active).toBe(false)
+ expect(fx.situations.length).toBe(1)
+ })
+ it('stops the animation immediately and fullfill it if first parameter true', function() {
+ fx.animate(500).start()
+ expect(fx.stop(true).situation).toBeNull()
+ expect(fx.active).toBe(false)
+ expect(fx.pos).toBe(1)
+ expect(fx.situations.length).toBe(1)
+ })
+ it('stops the animation immediately and remove all items from queue when second parameter true', function() {
+ fx.animate(500).start()
+ expect(fx.stop(false, true).situation).toBeNull()
+ expect(fx.active).toBe(false)
+ expect(fx.situations.length).toBe(0)
+ })
+ })
+
+
+ describe('reset()', function() {
+ it('resets the element to the state it was when the current animation was started', function() {
+ var loops = 4
+ , situation = fx.situation
+
+ // These settings make the animations run backward
+ fx.situation.loop = 2
+ fx.situation.loops = loops
+ fx.situation.reversed = true
+ fx.pos = 0.5
+ fx.absPos = 2.5
+
+ fx.reset()
+
+ expect(fx.situation).toBe(situation)
+ expect(fx.situation.loops).toBe(loops)
+ expect(fx.situation.loop).toBe(0)
+ expect(fx.situation.reversed).toBe(true) // True because the animation is backward
+ expect(fx.pos).toBe(1)
+ expect(fx.absPos).toBe(0)
+ })
+ })
+
+
+ describe('finish()', function() {
+ it('finish the whole animation by fullfilling every single one', function() {
+ fx.animate(500)
+ expect(fx.finish().pos).toBe(1)
+ expect(fx.situations.length).toBe(0)
+ expect(fx.situation).toBeNull()
+ })
+ })
+
+
+ describe('progress()', function() {
+ it('returns the current position', function() {
+ expect(fx.progress()).toBe(0)
+ expect(fx.progress()).toBe(fx.pos)
+ })
+ it('returns the current position as eased value if fist argument is true', function() {
+ var anim = draw.rect(100,100).animate(500,'>').start()
+ expect(anim.progress(true)).toBe(0)
+
+ anim.at(0.25)
+ expect(anim.progress(true)).toBeCloseTo(anim.situation.ease(0.25))
+ })
+ })
+
+
+ describe('after()', function() {
+ it('adds a callback which is called when the current animation is finished', function() {
+ var called = false
+
+ fx.start().after(function(situation){
+ expect(fx.situation).toBe(situation)
+ expect(fx.pos).toBe(1)
+ called = true
+ })
+
+ jasmine.clock().tick(500)
+ fx.step()
+ expect(called).toBe(true)
+ })
+ })
+
+
+ describe('afterAll()', function() {
+ it('adds a callback which is called when all animations are finished', function() {
+ var called = false
+
+ fx.animate(150).animate(125).start().afterAll(function(){
+ expect(fx.pos).toBe(1)
+ expect(fx.situations.length).toBe(0)
+ called = true
+ })
+
+ expect(fx.situations.length).toBe(2)
+
+ // End of the first animation
+ jasmine.clock().tick(500)
+ fx.step()
+ expect(fx.situations.length).toBe(1)
+ expect(called).toBe(false)
+
+ // End of the second animation
+ jasmine.clock().tick(150)
+ fx.step()
+ expect(fx.situations.length).toBe(0)
+ expect(called).toBe(false)
+
+ // End of the third and last animation
+ jasmine.clock().tick(125)
+ fx.step()
+ expect(fx.situation).toBeNull()
+ expect(called).toBe(true)
+ })
+ })
+
+
+ describe('during()', function() {
+ it('adds a callback which is called on every animation step', function() {
+ var called = 0
+
+ fx.start().during(function(pos, morph, eased, situation){
+
+ expect(fx.situation).toBe(situation)
+
+ 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
+
+ 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() {
+
+ fx.finish()
+ rect.off('.fx')
+
+ fx.animate(500).start().animate(500)
+
+ var sit = null
+
+ var pos1 = false
+ var pos2 = false
+
+ fx.duringAll(function(pos, morph, eased, situation){
+
+ if(pos1){
+ pos1 = false
+ sit = situation
+ expect(this.fx.pos).toBeCloseTo(0.6)
+ }
+
+ if(pos2){
+ pos2 = null
+ expect(situation).not.toBe(sit)
+ expect(this.fx.pos).toBeCloseTo(0.75)
+ }
+ })
+
+ 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')
+ }
+ })
+ })
+
+
+ describe('once()', function() {
+ it('adds a callback which is called once at the specified position', function() {
+ var called = false
+
+ fx.start().once(0.5, function(pos, eased){
+ 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)
+ })
+
+ it('adds the callback on the last situation', function () {
+ var callback = function () {}
+
+ fx.animate(500).animate(500).once(0.5, callback)
+ expect(fx.situation.once['0.5']).toBeUndefined()
+ expect(fx.situations[0].once['0.5']).toBeUndefined()
+ expect(fx.situations[1].once['0.5']).toBe(callback)
+ })
+ })
+
+
+ describe('loop()', function() {
+ 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(0)
+ expect(fx.situation.loops).toBe(true)
+ expect(fx.pos).toBe(0)
+ expect(fx.absPos).toBe(0)
+
+ 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() {
+ var time = 850452, dur = fx.situation.duration
+
+ fx.loop(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().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() {
+ var time = 0, dur = fx.situation.duration
+
+ fx.loop(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().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() {
+ var time = 0, dur = fx.situation.duration
+
+ fx.loop(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().step()
+ jasmine.clock().tick(325)
+ time = 325
+
+ 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() {
+ var time = 0, dur = fx.situation.duration
+
+ fx.loop(2, 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).toBe(0)
+ expect(fx.absPos).toBe(0)
+
+ fx.start().step()
+ jasmine.clock().tick(325)
+ time = 325
+
+ 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(0)
+ expect(fx.situation.loops).toBe(5)
+ expect(fx.situation.reversing).toBe(false)
+
+ fx.animate().loop(3, true)
+ 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(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)
+ })
+ })
+
+
+ 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('should not throw an error when stop is called in a during callback', function () {
+ fx.move(100,100).start()
+ fx.during(function () {this.stop()})
+ expect(fx.step.bind(fx)).not.toThrow()
+ })
+
+ it('should not throw an error when finish is called in a during callback', function () {
+ fx.move(100,100).start()
+ fx.during(function () {this.finish()})
+ expect(fx.step.bind(fx)).not.toThrow()
+ })
+
+ it('should not set active to false if the afterAll callback add situations to the situations queue', function () {
+ fx.afterAll(function(){this.animate(500).move(0,0)})
+
+ jasmine.clock().tick(500)
+ fx.step()
+ expect(fx.active).toBe(true)
+ expect(fx.situation).not.toBeNull()
+ expect(fx.situations.length).toBe(0)
+
+ jasmine.clock().tick(500)
+ fx.step()
+ expect(fx.active).toBe(false)
+ expect(fx.situation).toBeNull()
+ expect(fx.situations.length).toBe(0)
+ })
+ })
+
+
+ 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)
+ called = true
+
+ })
+
+ 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() {
+ 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(){
+
+ var ctm = rect.ctm()
+ expect(ctm.a).toBeCloseTo(0.8)
+ expect(ctm.b).toBeCloseTo(0.4)
+ expect(ctm.c).toBeCloseTo(-0.15)
+ expect(ctm.d).toBeCloseTo(0.7)
+ expect(ctm.e).toBeCloseTo(90.3)
+ expect(ctm.f).toBeCloseTo(27.07)
+ called = true
+
+ })
+
+ 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)
+ })
+
+ it('animate a scale transform using the passed center point when there is already a transform in place', function(){
+ var ctm
+
+ // When no ceter point is passed to the method scale, it use the center of the element as the center point
+
+ rect.scale(2) // The transform in place
+
+ fx.scale(0.5)
+ jasmine.clock().tick(500) // Have the animation reach its end
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(0.5)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(0.5)
+ expect(ctm.e).toBe(75)
+ expect(ctm.f).toBe(75)
+ })
+
+ it('animate a flip(x) transform', function() {
+ var ctm
+
+ fx.transform({flip: 'x'}).start()
+
+ jasmine.clock().tick(125) // Have the animation be 1/4 of the way (not halfway as usual because of a bug in the node method getCTM on Firefox)
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(0.5)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(1)
+ expect(ctm.e).toBe(75)
+ expect(ctm.f).toBe(0)
+
+ jasmine.clock().tick(475) // Have the animation reach its end
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(-1)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(1)
+ expect(ctm.e).toBe(300)
+ expect(ctm.f).toBe(0)
+ })
+
+ it('animate a flip(x) transform with an offset', function() {
+ var ctm
+
+ fx.transform({flip: 'x', offset: 20}).start()
+
+ jasmine.clock().tick(125) // Have the animation be 1/4 of the way (not halfway as usual because of a bug in the node method getCTM on Firefox)
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(0.5)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(1)
+ expect(ctm.e).toBe(10)
+ expect(ctm.f).toBe(0)
+
+ jasmine.clock().tick(475) // Have the animation reach its end
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(-1)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(1)
+ expect(ctm.e).toBe(40)
+ expect(ctm.f).toBe(0)
+ })
+
+ it('animate a flip(y) transform', function() {
+ var ctm
+
+ fx.transform({flip: 'y'}).start()
+
+ jasmine.clock().tick(125) // Have the animation be 1/4 of the way (not halfway as usual because of a bug in the node method getCTM on Firefox)
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(1)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(0.5)
+ expect(ctm.e).toBe(0)
+ expect(ctm.f).toBe(75)
+
+ jasmine.clock().tick(475) // Have the animation reach its end
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(1)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(-1)
+ expect(ctm.e).toBe(0)
+ expect(ctm.f).toBe(300)
+ })
+
+ it('animate a flip(y) transform with an offset', function() {
+ var ctm
+
+ fx.transform({flip: 'y', offset: 20}).start()
+
+ jasmine.clock().tick(125) // Have the animation be 1/4 of the way (not halfway as usual because of a bug in the node method getCTM on Firefox)
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(1)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(0.5)
+ expect(ctm.e).toBe(0)
+ expect(ctm.f).toBe(10)
+
+ jasmine.clock().tick(475) // Have the animation reach its end
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(1)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(-1)
+ expect(ctm.e).toBe(0)
+ expect(ctm.f).toBe(40)
+ })
+
+ it('animate a flip() transform', function() {
+ var ctm
+
+ fx.transform({flip: 'both'}).start()
+
+ jasmine.clock().tick(125) // Have the animation be 1/4 of the way (not halfway as usual because of a bug in the node method getCTM on Firefox)
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(0.5)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(0.5)
+ expect(ctm.e).toBe(75)
+ expect(ctm.f).toBe(75)
+
+ jasmine.clock().tick(475) // Have the animation reach its end
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(-1)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(-1)
+ expect(ctm.e).toBe(300)
+ expect(ctm.f).toBe(300)
+ })
+
+ it('animate a flip() transform with an offset', function() {
+ var ctm
+
+ fx.transform({flip: 'both', offset: 20}).start()
+
+ jasmine.clock().tick(125) // Have the animation be 1/4 of the way (not halfway as usual because of a bug in the node method getCTM on Firefox)
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(0.5)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(0.5)
+ expect(ctm.e).toBe(10)
+ expect(ctm.f).toBe(10)
+
+ jasmine.clock().tick(475) // Have the animation reach its end
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(-1)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(-1)
+ expect(ctm.e).toBe(40)
+ expect(ctm.f).toBe(40)
+ })
+
+ it('animate relative matrix transform', function(){
+ var ctm
+
+ fx.transform(new SVG.Matrix().scale(2,0,0), true)
+
+ jasmine.clock().tick(250) // Have the animation be half way
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(1.5)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(1.5)
+ expect(ctm.e).toBe(0)
+ expect(ctm.f).toBe(0)
+
+ jasmine.clock().tick(250) // Have the animation reach its end
+ fx.step()
+
+ ctm = rect.ctm()
+ expect(ctm.a).toBe(2)
+ expect(ctm.b).toBe(0)
+ expect(ctm.c).toBe(0)
+ expect(ctm.d).toBe(2)
+ expect(ctm.e).toBe(0)
+ expect(ctm.f).toBe(0)
+ })
+
+ describe('when animating plots', function() {
+ it('should allow plot animations to be chained', function() {
+ var pathString1 = 'M10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80'
+ , pathString2 = 'M10 80 C 40 150, 65 150, 95 80 S 150 10, 180 80'
+ , path = draw.path(pathString1)
+ , morph
+
+ fx = path.animate(1000).plot(pathString2).animate(1000).plot(pathString1)
+ morph = new SVG.PathArray(pathString1).morph(pathString2)
+
+ fx.start()
+ expect(path.array()).toEqual(morph.at(0))
+
+ jasmine.clock().tick(500) // Have the first animation be half way
+ fx.step()
+ expect(path.array()).toEqual(morph.at(0.5))
+
+ jasmine.clock().tick(500) // Have the first animation reach its end
+ fx.step()
+ expect(path.array()).toEqual(morph.at(1))
+ morph = new SVG.PathArray(pathString2).morph(pathString1)
+ expect(path.array()).toEqual(morph.at(0))
+
+ jasmine.clock().tick(500) // Have the second animation be half way
+ fx.step()
+ expect(path.array()).toEqual(morph.at(0.5))
+
+ jasmine.clock().tick(500) // Have the second animation reach its end
+ fx.step()
+ expect(path.array()).toEqual(morph.at(1))
+ })
+
+ it('should allow plot to be called on a polyline', function() {
+ var startValue = [[0,0], [100,50], [50,100], [150,50], [200,50]]
+ , endValue = [[0,0], [100,50], [50,100], [150,50], [200,50], [250,100], [300,50], [350,50]]
+ , morph = new SVG.PointArray(startValue).morph(endValue)
+ , polyline = draw.polyline(startValue)
+
+ fx = polyline.animate(3000).plot(endValue)
+
+ fx.start()
+ expect(polyline.array()).toEqual(morph.at(0))
+
+ jasmine.clock().tick(1500) // Have the animation be half way
+ fx.step()
+ expect(polyline.array()).toEqual(morph.at(0.5))
+
+ jasmine.clock().tick(1500) // Have the animation reach its end
+ fx.step()
+ expect(polyline.array()).toEqual(morph.at(1))
+ })
+
+ it('should allow plot to be called on a polygon', function() {
+ var startValue = [[0,0], [100,50], [50,100], [150,50], [200,50]]
+ , endValue = [[0,0], [100,50], [50,100], [150,50], [200,50], [250,100], [300,50], [350,50]]
+ , morph = new SVG.PointArray(startValue).morph(endValue)
+ , polygon = draw.polygon(startValue)
+
+ fx = polygon.animate(3000).plot(endValue)
+
+ fx.start()
+ expect(polygon.array()).toEqual(morph.at(0))
+
+ jasmine.clock().tick(1500) // Have the animation be half way
+ fx.step()
+ expect(polygon.array()).toEqual(morph.at(0.5))
+
+ jasmine.clock().tick(1500) // Have the animation reach its end
+ fx.step()
+ expect(polygon.array()).toEqual(morph.at(1))
+ })
+
+ it('should allow plot to be called on a path', function() {
+ var startValue = new SVG.PathArray('M10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80')
+ , endValue = new SVG.PathArray('M10 80 C 40 150, 65 150, 95 80 S 150 10, 180 80')
+ , morph = new SVG.PathArray(startValue).morph(endValue)
+ , path = draw.path(startValue)
+
+ fx = path.animate(2000).plot(endValue)
+
+ fx.start()
+ expect(path.array()).toEqual(morph.at(0))
+
+ jasmine.clock().tick(1000) // Have the animation be half way
+ fx.step()
+ expect(path.array()).toEqual(morph.at(0.5))
+
+ jasmine.clock().tick(1000) // Have the animation reach its end
+ fx.step()
+ expect(path.array()).toEqual(morph.at(1))
+ })
+
+ it('should allow plot to be called on a textpath', function() {
+ var startValue = new SVG.PathArray('M10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80')
+ , endValue = new SVG.PathArray('M10 80 C 40 150, 65 150, 95 80 S 150 10, 180 80')
+ , morph = new SVG.PathArray(startValue).morph(endValue)
+
+ var text = draw.text(function(add) {
+ add.tspan("We go up and down, then we go down, then up again")
+ })
+
+ fx = text.path(startValue).animate(500).plot(endValue)
+
+ fx.start()
+ expect(text.array()).toEqual(morph.at(0))
+
+ jasmine.clock().tick(250) // Have the animation be half way
+ fx.step()
+ expect(text.array()).toEqual(morph.at(0.5))
+
+ jasmine.clock().tick(250) // Have the animation reach its end
+ fx.step()
+ expect(text.array()).toEqual(morph.at(1))
+ })
+
+ it('should allow plot to be called on a line', function() {
+ var startValue = '0,0 100,150'
+ , endValue = [[50,30], [120,250]]
+ , morph = new SVG.PointArray(startValue).morph(endValue)
+ , line = draw.line(startValue)
+
+ fx = line.animate(3000).plot(endValue)
+
+ fx.start()
+ expect(line.array()).toEqual(morph.at(0))
+
+ jasmine.clock().tick(1500) // Have the animation be half way
+ fx.step()
+ expect(line.array()).toEqual(morph.at(0.5))
+
+ jasmine.clock().tick(1500) // Have the animation reach its end
+ fx.step()
+ expect(line.array()).toEqual(morph.at(1))
+ })
+
+ it('should allow plot to be called with 4 parameters on a line', function () {
+ var startPointArray = new SVG.PointArray('0,0 100,150')
+ , endPointArray = new SVG.PointArray([[50,30], [120,250]])
+ , morph = new SVG.PointArray(startPointArray).morph(endPointArray)
+ , a
+
+ a = startPointArray.value
+ var line = draw.line(a[0][0], a[0][1], a[1][0], a[1][1])
+
+ a = endPointArray.value
+ fx = line.animate(3000).plot(a[0][0], a[0][1], a[1][0], a[1][1])
+
+ fx.start()
+ expect(line.array()).toEqual(morph.at(0))
+
+ jasmine.clock().tick(1500) // Have the animation be half way
+ fx.step()
+ expect(line.array()).toEqual(morph.at(0.5))
+
+ jasmine.clock().tick(1500) // Have the animation reach its end
+ fx.step()
+ expect(line.array()).toEqual(morph.at(1))
+ })
+ })
+
+
+ describe('when animating attributes', function() {
+ it('should be possible to animate numeric attributes', function () {
+ var startValue = 0
+ , endValue = 150
+ , morph = new SVG.Number(startValue).morph(endValue)
+
+ var text = draw.text(function(add) {
+ add.tspan('We go ')
+ add.tspan('up').fill('#f09').dy(-40)
+ add.tspan(', then we go down, then up again').dy(40)
+ })
+
+ var path = 'M 100 200 C 200 100 300 0 400 100 C 500 200 600 300 700 200 C 800 100 900 100 900 100'
+
+ text.path(path).font({ size: 42.5, family: 'Verdana' })
+
+ text.textPath().attr('startOffset', startValue)
+ fx = text.textPath().animate(1000).attr('startOffset', endValue)
+
+ fx.start()
+ expect(text.textPath().attr('startOffset')).toBe(morph.at(0).value)
+
+ jasmine.clock().tick(500) // Have the animation be half way
+ fx.step()
+ expect(text.textPath().attr('startOffset')).toBe(morph.at(0.5).value)
+
+ jasmine.clock().tick(500) // Have the animation reach its end
+ fx.step()
+ expect(text.textPath().attr('startOffset')).toBe(morph.at(1).value)
+ })
+
+ it('should be possible to animate non-numeric attributes', function () {
+ var startValue = 'butt'
+ , endValue = 'round'
+ , line = draw.line('0,0 100,150').attr('stroke-linecap', startValue)
+
+ fx = line.animate(3000).attr('stroke-linecap', endValue)
+
+ fx.start()
+ expect(line.attr('stroke-linecap')).toBe(startValue)
+
+ jasmine.clock().tick(1500) // Have the animation be half way
+ fx.step()
+ expect(line.attr('stroke-linecap')).toBe(startValue)
+
+ jasmine.clock().tick(1500) // Have the animation reach its end
+ fx.step()
+ expect(line.attr('stroke-linecap')).toBe(endValue)
+ })
+
+ it('should be possible to animate array attributes', function() {
+ var startValue = [10,5]
+ , endValue = [20,10]
+ , morph = new SVG.Array(startValue).morph(endValue)
+
+ rect.attr('stroke-dasharray', startValue)
+ fx.attr('stroke-dasharray', endValue)
+
+ fx.start()
+ expect(rect.attr('stroke-dasharray')).toBe(morph.at(0).toString())
+
+ jasmine.clock().tick(250) // Have the animation be half way
+ fx.step()
+ expect(rect.attr('stroke-dasharray')).toBe(morph.at(0.5).toString())
+
+ jasmine.clock().tick(250) // Have the animation reach its end
+ fx.step()
+ expect(rect.attr('stroke-dasharray')).toBe(morph.at(1).toString())
+ })
+
+
+ it('should be possible to animate path attribute', function() {
+ // use values from issue #847
+ var startValue = 'M 832 512 L 192 896 L 192 128 L 832 512 Z M 832 512'
+ , endValue = 'M 832 800 L 192 896 L 192 128 L 832 800 Z M 832 800'
+ , morph = new SVG.PathArray(startValue).morph(endValue)
+ , path = draw.path(startValue)
+
+ fx = path.animate(500).attr('d', endValue)
+
+ // I have to use clear() in the tests below since animating the
+ // d attribute directly does not clear the cache
+
+ fx.start()
+ expect(path.clear().array()).toEqual(morph.at(0))
+
+ jasmine.clock().tick(250) // Have the animation be half way
+ fx.step()
+ expect(path.clear().array()).toEqual(morph.at(0.5))
+
+ jasmine.clock().tick(250) // Have the animation reach its end
+ fx.step()
+ expect(path.clear().array()).toEqual(morph.at(1))
+ })
+
+ it('should be possible to animate color attributes by using SVG.Color', function() {
+ var startValue = 'rgb(42,251,100)'
+ , endValue = 'rgb(10,80,175)'
+ , morph = new SVG.Color(startValue).morph(endValue)
+
+ rect.attr('fill', startValue)
+ fx.attr('fill', endValue)
+
+ fx.start()
+ expect(rect.attr('fill')).toBe(morph.at(0).toString())
+
+ jasmine.clock().tick(250) // Have the animation be half way
+ fx.step()
+ expect(rect.attr('fill')).toBe(morph.at(0.5).toString())
+
+ jasmine.clock().tick(250) // Have the animation reach its end
+ fx.step()
+ expect(rect.attr('fill')).toBe(morph.at(1).toString())
+ })
+
+ it('should be possible to pass percentage strings to numeric attributes', function () {
+ var startValue = '0%'
+ , endValue = '80%'
+ , morph = new SVG.Number(startValue).morph(endValue)
+
+ var text = draw.text(function(add) {
+ add.tspan('We go ')
+ add.tspan('up').fill('#f09').dy(-40)
+ add.tspan(', then we go down, then up again').dy(40)
+ })
+
+ var path = 'M 100 200 C 200 100 300 0 400 100 C 500 200 600 300 700 200 C 800 100 900 100 900 100'
+
+ text.path(path).font({ size: 42.5, family: 'Verdana' })
+
+ text.textPath().attr('startOffset', startValue)
+ fx = text.textPath().animate(1000).attr('startOffset', endValue)
+
+ fx.start()
+ expect(text.textPath().attr('startOffset')).toBe(morph.at(0).toString())
+
+ jasmine.clock().tick(500) // Have the animation be half way
+ fx.step()
+ expect(text.textPath().attr('startOffset')).toBe(morph.at(0.5).toString())
+
+ jasmine.clock().tick(500) // Have the animation reach its end
+ fx.step()
+ expect(text.textPath().attr('startOffset')).toBe(morph.at(1).toString())
+ })
+
+ it('should allow 0 to be specified without unit', function () {
+ // This code snippet come from issue #552
+
+ var gradient = draw.gradient('linear', function(stop) {
+ s1 = stop.at(0, '#33235b')
+ s2 = stop.at(0.5, '#E97639')
+ s3 = stop.at(1, '#33235b')
+ })
+
+ var r1, r2;
+ var fill = draw.pattern('300%', '100%', function(add) {
+ r1 = add.rect('150%', '100%').fill(gradient)
+ r2 = add.rect('150%', '100%').fill(gradient)
+ });
+ fill.attr({patternUnits: 'userSpaceOnUse'})
+
+ r1.attr('x', 0).animate('0.5s').attr('x', '150%')
+ r2.attr('x', '-150%').animate('0.5s').attr('x', 0)
+
+ var text = draw.text('Manifesto').move('50%', '50%').fill(fill)
+ text.font({
+ size: 70
+ , anchor: 'middle'
+ , leading: 1
+ })
+
+ r1.fx.start()
+ r2.fx.start()
+
+ jasmine.clock().tick(250) // Have the animation be half way
+ r1.fx.step()
+ r2.fx.step()
+ expect(r1.attr('x')).toBe('75%')
+ expect(r2.attr('x')).toBe('-75%')
+
+ jasmine.clock().tick(250) // Have the animation reach its end
+ r1.fx.step()
+ r2.fx.step()
+ expect(r1.attr('x')).toBe('150%')
+ expect(r2.attr('x')).toBe('0%')
+ })
+ })
+
+
+ describe('when animating styles', function() {
+ it('should be possible to animate numeric styles', function () {
+ var startValue = 0
+ , endValue = 5
+ , morph = new SVG.Number(startValue).morph(endValue)
+
+ rect.style('stroke-width', startValue)
+ fx.style('stroke-width', endValue)
+
+ fx.start()
+ expect(rect.style('stroke-width')).toBe(morph.at(0).toString())
+
+ jasmine.clock().tick(250) // Have the animation be half way
+ fx.step()
+ expect(rect.style('stroke-width')).toBe(morph.at(0.5).toString())
+
+ jasmine.clock().tick(250) // Have the animation reach its end
+ fx.step()
+ expect(rect.style('stroke-width')).toBe(morph.at(1).toString())
+ })
+
+ it('should be possible to animate non-numeric styles', function () {
+ var startValue = 'butt'
+ , endValue = 'round'
+ , line = draw.line('0,0 100,150').style('stroke-linecap', startValue)
+
+ fx = line.animate(3000).style('stroke-linecap', endValue)
+
+ fx.start()
+ expect(line.style('stroke-linecap')).toBe(startValue)
+
+ jasmine.clock().tick(1500) // Have the animation be half way
+ fx.step()
+ expect(line.style('stroke-linecap')).toBe(startValue)
+
+ jasmine.clock().tick(1500) // Have the animation reach its end
+ fx.step()
+ expect(line.style('stroke-linecap')).toBe(endValue)
+ })
+
+ it('should be possible to animate array styles', function() {
+ var startValue = [10,5]
+ , endValue = [20,10]
+ , morph = new SVG.Array(startValue).morph(endValue)
+
+ rect.style('stroke-dasharray', startValue)
+ fx.style('stroke-dasharray', endValue)
+
+ fx.start()
+ expect(rect.style('stroke-dasharray')).toBe(morph.at(0).valueOf().join(", "))
+
+ jasmine.clock().tick(250) // Have the animation be half way
+ fx.step()
+ expect(rect.style('stroke-dasharray')).toBe(morph.at(0.5).valueOf().join(", "))
+
+ jasmine.clock().tick(250) // Have the animation reach its end
+ fx.step()
+ expect(rect.style('stroke-dasharray')).toBe(morph.at(1).valueOf().join(", "))
+ })
+
+ it('should be possible to animate color styles by using SVG.Color', function() {
+ var startValue = '#81DE01'
+ , endValue = '#B1835D'
+ , morph = new SVG.Color(startValue).morph(endValue)
+
+ rect.style('fill', startValue)
+ fx.style('fill', endValue)
+
+
+ fx.start()
+ // When setting a style color, it get saved as a rgb() string even if it was passed as an hex code
+ // The style rgb string has spaces while the one returned by SVG.Color do not as show bellow
+ // CSS: rgb(255, 255, 255) SVG.Color: rgb(255,255,255)
+ // The space in the style rbg string are removed so they can be equal
+ expect(rect.style('fill').replace(/\s+/g, '')).toBe(morph.at(0).toRgb())
+
+ jasmine.clock().tick(250) // Have the animation be half way
+ fx.step()
+ expect(rect.style('fill').replace(/ /g, '')).toBe(morph.at(0.5).toRgb())
+
+ jasmine.clock().tick(250) // Have the animation reach its end
+ fx.step()
+ expect(rect.style('fill').replace(/ /g, '')).toBe(morph.at(1).toRgb())
+ })
+
+ it('should be possible to pass percentage strings to numeric styles', function () {
+ var startValue = '0%'
+ , endValue = '5%'
+ , morph = new SVG.Number(startValue).morph(endValue)
+
+ rect.style('stroke-width', startValue)
+ fx.style('stroke-width', endValue)
+
+ fx.start()
+ expect(rect.style('stroke-width')).toBe(morph.at(0).toString())
+
+ jasmine.clock().tick(250) // Have the animation be half way
+ fx.step()
+ expect(rect.style('stroke-width')).toBe(morph.at(0.5).toString())
+
+ jasmine.clock().tick(250) // Have the animation reach its end
+ fx.step()
+ expect(rect.style('stroke-width')).toBe(morph.at(1).toString())
+ })
+
+ it('should allow 0 to be specified without a unit', function () {
+ var r1 = draw.rect(100,100).move(200,200)
+ , r2 = draw.rect(100,100).move(400,400)
+
+ r1.style('stroke-width', '100%').animate(500).style('stroke-width', 0)
+ r2.style('stroke-width', 0).animate(500).style('stroke-width', '100%')
+
+ r1.fx.start()
+ r2.fx.start()
+ expect(r1.style('stroke-width')).toBe('100%')
+ expect(r2.style('stroke-width')).toBe('0%')
+
+ jasmine.clock().tick(250) // Have the animation be half way
+ r1.fx.step()
+ r2.fx.step()
+ expect(r1.style('stroke-width')).toBe('50%')
+ expect(r2.style('stroke-width')).toBe('50%')
+
+ jasmine.clock().tick(250) // Have the animation reach its end
+ r1.fx.step()
+ r2.fx.step()
+ expect(r1.style('stroke-width')).toBe('0%')
+ expect(r2.style('stroke-width')).toBe('100%')
+ })
+ })
+
+
+ describe('add()', function() {
+ it('adds to animations obj by default', function() {
+ fx.add('x', new SVG.Number(20))
+ expect(fx.situation.animations.x.value).toBe(20)
+ })
+
+ it('adds to specified obj', function() {
+ fx.add('x', new SVG.Number(20), 'animations')
+ fx.add('x', new SVG.Number(20), 'attrs')
+ fx.add('x', new SVG.Number(20), 'styles')
+ expect(fx.situation.animations.x.value).toBe(20)
+ expect(fx.situation.attrs.x.value).toBe(20)
+ expect(fx.situation.styles.x.value).toBe(20)
+ })
+ })
+
+ describe('attr()', function() {
+ it('should allow an object to be passed', function() {
+ spyOn(fx, 'attr').and.callThrough()
+ fx.attr({
+ x: 20,
+ y: 20
+ })
+
+ expect(fx.attr).toHaveBeenCalledWith('x', 20)
+ expect(fx.attr).toHaveBeenCalledWith('y', 20)
+ })
+
+ it('should call add() with attrs as method', function() {
+ spyOn(fx, 'add')
+ fx.attr('x', 20)
+ expect(fx.add).toHaveBeenCalledWith('x', 20, 'attrs')
+ })
+ })
+
+ describe('style()', function() {
+ it('should allow an object to be passed', function() {
+ spyOn(fx, 'style').and.callThrough()
+ fx.style({
+ x: 20,
+ y: 20
+ })
+
+ expect(fx.style).toHaveBeenCalledWith('x', 20)
+ expect(fx.style).toHaveBeenCalledWith('y', 20)
+ })
+
+ it('should call add() with styles as method', function() {
+ spyOn(fx, 'add')
+ fx.style('x', 20)
+ expect(fx.add).toHaveBeenCalledWith('x', 20, 'styles')
+ })
+ })
+
+ describe('x() / y()', function() {
+ it('should add an entry to the animations obj', function() {
+ spyOn(fx, 'add')
+ fx.x(20)
+ fx.y(20)
+
+ expect(fx.add).toHaveBeenCalledWith('x', jasmine.objectContaining({value:20}))
+ expect(fx.add).toHaveBeenCalledWith('y', jasmine.objectContaining({value:20}))
+ })
+
+ it('allows relative move with relative flag set', function() {
+ spyOn(fx, 'add')
+ fx.x(20, true)
+ fx.y(20, true)
+
+ expect(fx.add).toHaveBeenCalledWith('x', jasmine.objectContaining({value:20, relative:true }))
+ expect(fx.add).toHaveBeenCalledWith('y', jasmine.objectContaining({value:20, relative:true }))
+ })
+
+ it('redirects to transform when target is a group', function() {
+ var group = draw.group()
+ , fx = group.animate(500)
+
+ spyOn(fx, 'transform')
+
+ fx.x(20)
+ fx.y(20)
+
+ expect(fx.transform).toHaveBeenCalledWith({x: 20}, undefined)
+ expect(fx.transform).toHaveBeenCalledWith({y: 20}, undefined)
+ })
+
+ it('redirects to transform when target is a group with relative flag set', function() {
+ var group = draw.group()
+ , fx = group.animate(500)
+
+ spyOn(fx, 'transform')
+
+ fx.x(20, true)
+ fx.y(20, true)
+
+ expect(fx.transform).toHaveBeenCalledWith({x: 20}, true)
+ expect(fx.transform).toHaveBeenCalledWith({y: 20}, true)
+ })
+ })
+
+ describe('cx() / cy()', function() {
+ it('should call add with method and argument', function() {
+ spyOn(fx, 'add')
+ fx.cx(20)
+ fx.cy(20)
+
+ expect(fx.add).toHaveBeenCalledWith('cx', jasmine.objectContaining({value:20}))
+ expect(fx.add).toHaveBeenCalledWith('cy', jasmine.objectContaining({value:20}))
+ })
+ })
+
+ describe('move()', function() {
+ it('should redirect call to x() and y()', function() {
+ spyOn(fx, 'x').and.callThrough()
+ spyOn(fx, 'y').and.callThrough()
+ fx.move(20, 20)
+
+ expect(fx.x).toHaveBeenCalledWith(20)
+ expect(fx.y).toHaveBeenCalledWith(20)
+ })
+ })
+
+ describe('center()', function() {
+ it('should redirect call to cx() and cy()', function() {
+ spyOn(fx, 'cx').and.callThrough()
+ spyOn(fx, 'cy').and.callThrough()
+ fx.center(20, 20)
+
+ expect(fx.cx).toHaveBeenCalledWith(20)
+ expect(fx.cy).toHaveBeenCalledWith(20)
+ })
+ })
+
+ describe('size()', function() {
+ it('should set font-size with attr() when called on a text', function() {
+ var text = draw.text('Hello World')
+ , fx = text.animate(500)
+
+ spyOn(fx, 'attr')
+ fx.size(20)
+ expect(fx.attr).toHaveBeenCalledWith('font-size', 20)
+ })
+
+ it('should set width and height with add()', function() {
+ spyOn(fx, 'add').and.callThrough()
+ fx.size(20, 20)
+
+ expect(fx.add).toHaveBeenCalledWith('width', jasmine.objectContaining({value:20}))
+ expect(fx.add).toHaveBeenCalledWith('height', jasmine.objectContaining({value:20}))
+ })
+
+ it('should calculate proportional size when only height or width is given', function() {
+ spyOn(fx, 'add').and.callThrough()
+ fx.size(40, null)
+ fx.size(null, 60)
+
+ expect(fx.add).toHaveBeenCalledWith('width', jasmine.objectContaining({value:40}))
+ expect(fx.add).toHaveBeenCalledWith('height', jasmine.objectContaining({value:40}))
+
+ expect(fx.add).toHaveBeenCalledWith('width', jasmine.objectContaining({value:60}))
+ expect(fx.add).toHaveBeenCalledWith('height', jasmine.objectContaining({value:60}))
+ })
+ })
+
+ describe('width()', function() {
+ it('should set width with add()', function() {
+ spyOn(fx, 'add').and.callThrough()
+ fx.width(20)
+ expect(fx.add).toHaveBeenCalledWith('width', jasmine.objectContaining({value:20}))
+ })
+
+ it('should animate the width attribute', function() {
+ fx.width(200)
+ expect(rect.width()).toBe(100)
+
+ jasmine.clock().tick(250)
+ fx.step()
+ expect(rect.width()).toBe(150)
+
+ jasmine.clock().tick(250)
+ fx.step()
+ expect(rect.width()).toBe(200)
+ })
+ })
+
+ describe('height()', function() {
+ it('should set height with add()', function() {
+ spyOn(fx, 'add').and.callThrough()
+ fx.height(20)
+ expect(fx.add).toHaveBeenCalledWith('height', jasmine.objectContaining({value:20}))
+ })
+
+ it('should animate the height attribute', function() {
+ fx.height(200)
+ expect(rect.height()).toBe(100)
+
+ jasmine.clock().tick(250)
+ fx.step()
+ expect(rect.height()).toBe(150)
+
+ jasmine.clock().tick(250)
+ fx.step()
+ expect(rect.height()).toBe(200)
+ })
+ })
+
+ describe('plot()', function() {
+ it('should call add with plot as method', function() {
+ var polyline = draw.polyline('10 10 20 20 30 10 50 20')
+ , fx = polyline.animate(500)
+
+ spyOn(fx, 'add')
+ fx.plot('5 5 30 29 40 19 12 30')
+ expect(fx.add).toHaveBeenCalledWith('plot', new SVG.PointArray('5 5 30 29 40 19 12 30'))
+ })
+
+ it('also accept parameter list', function() {
+ var line = draw.line('10 10 20 20')
+ , fx = line.animate(500)
+
+ spyOn(fx, 'add')
+ fx.plot(5, 5, 10, 10)
+ expect(fx.add).toHaveBeenCalledWith('plot', new SVG.PointArray([5, 5, 10, 10]))
+ })
+ })
+
+ describe('leading()', function() {
+ it('should call add with method and argument', function() {
+ var text = draw.text('Hello World')
+ , fx = text.animate(500)
+ spyOn(fx, 'add')
+ fx.leading(3)
+
+ expect(fx.add).toHaveBeenCalledWith('leading', jasmine.objectContaining({value:3}))
+ })
+
+ it('does nothiing when not called on text', function() {
+ spyOn(fx, 'add')
+ fx.leading(3)
+ expect(fx.add).not.toHaveBeenCalled()
+ })
+ })
+
+ describe('viewbox()', function() {
+ it('should call add with method and argument', function() {
+ var nested = draw.nested()
+ , fx = nested.animate(500)
+ spyOn(fx, 'add')
+ fx.viewbox(1,2,3,4)
+
+ expect(fx.add).toHaveBeenCalledWith('viewbox', jasmine.objectContaining({x:1, y:2, width:3, height:4}))
+ })
+
+ it('does nothing when not called on SVG.Container', function() {
+ spyOn(fx, 'add')
+ fx.viewbox(1,2,3,4)
+ expect(fx.add).not.toHaveBeenCalled()
+ })
+ })
+
+ describe('update()', function() {
+ it('should convert call with 3 arguments to call with obj', function() {
+ var stop = new SVG.Stop()
+ , fx = stop.animate()
+ spyOn(fx, 'update').and.callThrough()
+ fx.update(1,'#ccc',0.5)
+
+ expect(fx.update).toHaveBeenCalledWith({offset: 1, color: '#ccc', opacity: 0.5})
+ })
+
+ it('calls add with method argument and attrs as type', function() {
+ var stop = new SVG.Stop()
+ , fx = stop.animate()
+ spyOn(fx, 'add')
+ fx.update({offset: 1, color: '#ccc', opacity: 0.5})
+
+ expect(fx.add).toHaveBeenCalledWith('stop-opacity', 0.5, 'attrs')
+ expect(fx.add).toHaveBeenCalledWith('stop-color', '#ccc', 'attrs')
+ expect(fx.add).toHaveBeenCalledWith('offset', 1, 'attrs')
+ })
+
+ it('does nothing when not called on SVG.Stop', function() {
+ spyOn(fx, 'add')
+ fx.update({offset: 1, color: '#ccc', opacity: 0.5})
+ expect(fx.add).not.toHaveBeenCalled()
+ })
+ })
+
+ describe('transform()', function() {
+ it('returns itself when no valid transformation was found', function() {
+ expect(fx.transform({})).toBe(fx)
+ })
+ it('gets the current transforms', function() {
+ expect(fx.transform()).toEqual(new SVG.Matrix(rect).extract())
+ })
+ it('gets a certain transformation if used with an argument', function() {
+ expect(fx.transform('x')).toEqual(0)
+ })
+ it('adds an entry to transforms when matrix given', function() {
+ var matrix = new SVG.Matrix(1,2,3,4,5,6)
+ fx.transform(matrix)
+ expect(fx.situation.transforms[0]).toEqual(jasmine.objectContaining(matrix))
+ })
+ it('sets relative flag when given', function() {
+ var matrix = new SVG.Matrix(1,2,3,4,5,6)
+ fx.transform(matrix, true)
+ expect(fx.situation.transforms[0]).toEqual(jasmine.objectContaining(matrix))
+ expect(fx.situation.transforms[0].relative).toBe(true)
+ })
+ it('adds an entry to transforms when rotation given', function() {
+ fx.transform({rotation: 30, cx:0, cy:0})
+ expect(fx.situation.transforms[0]).toEqual(jasmine.objectContaining(new SVG.Rotate(30, 0, 0)))
+ })
+ it('adds an entry to transforms when scale given', function() {
+ fx.transform({scale: 2, cx:0, cy:0})
+ expect(fx.situation.transforms[0]).toEqual(jasmine.objectContaining(new SVG.Scale(2, 2, 0, 0)))
+ })
+ it('adds an entry to transforms when scaleX given', function() {
+ fx.transform({scaleX: 2, cx:0, cy:0})
+ expect(fx.situation.transforms[0]).toEqual(jasmine.objectContaining(new SVG.Scale(2, 1, 0, 0)))
+ })
+ it('adds an entry to transforms when scaleY given', function() {
+ fx.transform({scaleY: 2, cx:0, cy:0})
+ expect(fx.situation.transforms[0]).toEqual(jasmine.objectContaining(new SVG.Scale(1, 2, 0, 0)))
+ })
+ it('adds an entry to transforms when skewX given', function() {
+ fx.transform({skewX: 2, cx:0, cy:0})
+ expect(fx.situation.transforms[0]).toEqual(jasmine.objectContaining(new SVG.Skew(2, 0, 0, 0)))
+ })
+ it('adds an entry to transforms when skewY given', function() {
+ fx.transform({skewY: 2, cx:0, cy:0})
+ expect(fx.situation.transforms[0]).toEqual(jasmine.objectContaining(new SVG.Skew(0, 2, 0, 0)))
+ })
+ it('adds an entry to transforms when flip x given', function() {
+ fx.transform({flip: 'x'})
+ expect(fx.situation.transforms[0]).toEqual(jasmine.objectContaining((new SVG.Matrix()).flip('x', 150)))
+ })
+ it('adds an entry to transforms when flip x with offset given', function() {
+ fx.transform({flip: 'x', offset: 100})
+ expect(fx.situation.transforms[0]).toEqual(jasmine.objectContaining((new SVG.Matrix()).flip('x', 100)))
+ })
+ it('adds an entry to transforms when flip y given', function() {
+ fx.transform({flip: 'y'})
+ expect(fx.situation.transforms[0]).toEqual(jasmine.objectContaining((new SVG.Matrix()).flip('y', 150)))
+ })
+ it('adds an entry to transforms when x given', function() {
+ fx.transform({x:20})
+ expect(fx.situation.transforms[0]).toEqual(jasmine.objectContaining(new SVG.Translate(20, undefined)))
+ })
+ it('adds an entry to transforms when y given', function() {
+ fx.transform({y:20})
+ expect(fx.situation.transforms[0]).toEqual(jasmine.objectContaining(new SVG.Translate(undefined, 20)))
+ })
+ })
+
+ /* shortcuts for animation */
+ describe('animate()', function() {
+ it('creates a new fx instance on the element', function() {
+ var rect = draw.rect(100,100)
+ rect.animate(100)
+ expect(rect.fx instanceof SVG.FX).toBeTruthy()
+ })
+
+ it('redirects the call to fx.animate()', function() {
+ spyOn(fx, 'animate')
+ rect.animate()
+ expect(fx.animate).toHaveBeenCalled()
+ })
+ })
+
+ describe('delay()', function() {
+ it('creates a new fx instance on the element', function() {
+ var rect = draw.rect(100,100)
+ rect.delay(100)
+ expect(rect.fx instanceof SVG.FX).toBeTruthy()
+ })
+
+ it('redirects the call to fx.delay()', function() {
+ spyOn(fx, 'delay')
+ rect.delay(5)
+ expect(fx.delay).toHaveBeenCalled()
+ })
+ })
+
+ describe('stop()', function() {
+ it('redirects the call to fx.stop()', function() {
+ spyOn(fx, 'stop')
+ rect.stop()
+ expect(fx.stop).toHaveBeenCalled()
+ })
+ })
+
+ describe('finish()', function() {
+ it('redirects the call to fx.finish()', function() {
+ spyOn(fx, 'finish')
+ rect.finish()
+ expect(fx.finish).toHaveBeenCalled()
+ })
+ })
+
+ describe('pause()', function() {
+ it('redirects the call to fx.pause()', function() {
+ spyOn(fx, 'pause')
+ rect.pause()
+ expect(fx.pause).toHaveBeenCalled()
+ })
+ })
+
+ describe('play()', function() {
+ it('redirects the call to fx.play()', function() {
+ spyOn(fx, 'play')
+ rect.play()
+ expect(fx.play).toHaveBeenCalled()
+ })
+ })
+
+ describe('speed()', function() {
+ it('redirects the call to fx.speed() as getter', function() {
+ spyOn(fx, 'speed')
+ rect.speed()
+ expect(fx.speed).toHaveBeenCalled()
+ })
+
+ it('redirects the call to fx.speed() as setter', function() {
+ spyOn(fx, 'speed').and.callThrough()
+ expect(rect.speed(5)).toBe(rect)
+ expect(fx.speed).toHaveBeenCalled()
+ })
+ })
+})
+
+describe('SVG.MorphObj', function() {
+ it('accepts color strings and converts them to SVG.Color', function() {
+ var obj = new SVG.MorphObj('#000', '#fff')
+ expect(obj instanceof SVG.Color).toBeTruthy()
+
+ obj = new SVG.MorphObj('rgb(0,0,0)', 'rgb(255,255,255)')
+ expect(obj instanceof SVG.Color).toBeTruthy()
+ })
+
+ it('accepts numbers and converts them to SVG.Number', function() {
+ var obj = new SVG.MorphObj('0', '10')
+ expect(obj instanceof SVG.Number).toBeTruthy()
+
+ var obj = new SVG.MorphObj(0, 10)
+ expect(obj instanceof SVG.Number).toBeTruthy()
+ })
+
+ it('accepts arrays and converts them to SVG.Array', function () {
+ var obj = new SVG.MorphObj([1,2,3], [4,5,6])
+ expect(obj instanceof SVG.Array).toBeTruthy()
+
+ var obj = new SVG.MorphObj("1 2 3", "4 5 6")
+ expect(obj instanceof SVG.Array).toBeTruthy()
+
+ var obj = new SVG.MorphObj("1,2,3", "4,5,6")
+ expect(obj instanceof SVG.Array).toBeTruthy()
+
+ var obj = new SVG.MorphObj("1, 2, 3", "4, 5, 6")
+ expect(obj instanceof SVG.Array).toBeTruthy()
+ })
+
+ it('accepts path strings and convert them to SVG.PathArray', function() {
+ // use values from issue #847
+ var startValue = 'M 832 512 L 192 896 L 192 128 L 832 512 Z M 832 512'
+ , endValue = 'M 832 800 L 192 896 L 192 128 L 832 800 Z M 832 800'
+ , obj = new SVG.MorphObj(startValue, endValue)
+
+ expect(obj instanceof SVG.PathArray).toBeTruthy()
+ })
+
+ it('accepts any other values', function() {
+ var obj = new SVG.MorphObj('Hello', 'World')
+
+ expect(obj.value).toBe('Hello')
+ expect(obj.destination).toBe('World')
+ })
+
+ it('morphes unmorphable objects with plain morphing', function() {
+ var obj = new SVG.MorphObj('Hello', 'World')
+
+ expect(obj.at(0,0)).toBe('Hello')
+ expect(obj.at(0.5,0.5)).toBe('Hello')
+ expect(obj.at(1,1)).toBe('World')
+ })
+
+ it('converts to its value when casted', function() {
+ var obj = new SVG.MorphObj('Hello', 'World')
+ expect(obj.valueOf()).toBe('Hello')
+ expect(obj + 'World').toBe('HelloWorld')
+ })
+})
diff --git a/node_modules/svg.js/spec/spec/gradient.js b/node_modules/svg.js/spec/spec/gradient.js
new file mode 100644
index 0000000..3fb74f9
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/gradient.js
@@ -0,0 +1,151 @@
+describe('Gradient', function() {
+ var rect, gradient
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ gradient = draw.gradient('linear', function(stop) {
+ stop.at({ offset: 0, color: '#333', opacity: 1 })
+ stop.at({ offset: 1, color: '#fff', opacity: 1 })
+ })
+ radial = draw.gradient('radial', function(stop) {
+ stop.at({ offset: 0, color: '#333', opacity: 1 })
+ stop.at({ offset: 1, color: '#fff', opacity: 1 })
+ })
+ })
+
+ afterEach(function() {
+ rect.remove()
+ gradient.remove()
+ })
+
+ it('is an instance of SVG.Gradient', function() {
+ expect(gradient instanceof SVG.Gradient).toBe(true)
+ })
+
+ it('allows creation of a new gradient without block', function() {
+ gradient = draw.gradient('linear')
+ expect(gradient.children().length).toBe(0)
+ })
+
+ describe('fill()', function() {
+ it('returns the id of the gradient wrapped in url()', function() {
+ expect(gradient.fill()).toBe('url(#' + gradient.attr('id') + ')')
+ })
+ })
+
+ describe('from()', function() {
+ it('sets fx and fy attribute for radial gradients', function() {
+ radial.from(7, 10)
+ expect(radial.attr('fx')).toBe(7)
+ expect(radial.attr('fy')).toBe(10)
+ })
+ it('sets x1 and y1 attribute for linear gradients', function() {
+ gradient.from(7, 10)
+ expect(gradient.attr('x1')).toBe(7)
+ expect(gradient.attr('y1')).toBe(10)
+ })
+ })
+
+ describe('to()', function() {
+ it('sets cx and cy attribute for radial gradients', function() {
+ radial.to(75, 105)
+ expect(radial.attr('cx')).toBe(75)
+ expect(radial.attr('cy')).toBe(105)
+ })
+ it('sets x2 and y2 attribute for linear gradients', function() {
+ gradient.to(75, 105)
+ expect(gradient.attr('x2')).toBe(75)
+ expect(gradient.attr('y2')).toBe(105)
+ })
+ })
+
+ describe('attr()', function() {
+ it('will catch transform attribues and convert them to gradientTransform', function() {
+ expect(gradient.translate(100,100).attr('gradientTransform')).toBe('matrix(1,0,0,1,100,100)')
+ })
+ })
+
+ describe('toString()', function() {
+ it('returns the id of the gradient wrapped in url()', function() {
+ expect(gradient + '').toBe('url(#' + gradient.attr('id') + ')')
+ })
+ it('is called when instance is passed as an attribute value', function() {
+ rect.attr('fill', gradient)
+ expect(rect.attr('fill')).toBe('url(#' + gradient.attr('id') + ')')
+ })
+ })
+
+ describe('input values', function() {
+ var s1, s2
+
+ it('accepts floats', function() {
+ gradient = draw.gradient('linear', function(stop) {
+ s1 = stop.at({ offset: 0.12, color: '#333', opacity: 1 })
+ s2 = stop.at({ offset: 0.93, color: '#fff', opacity: 1 })
+ })
+ expect(s1.attr('offset')).toBe(0.12)
+ expect(s2.attr('offset')).toBe(0.93)
+ })
+ it('accepts string floats', function() {
+ gradient = draw.gradient('linear', function(stop) {
+ s1 = stop.at({ offset: '0.13', color: '#333', opacity: 1 })
+ s2 = stop.at({ offset: '0.92', color: '#fff', opacity: 1 })
+ })
+ expect(s1.attr('offset')).toBe(0.13)
+ expect(s2.attr('offset')).toBe(0.92)
+ })
+ it('accept percentages', function() {
+ gradient = draw.gradient('linear', function(stop) {
+ s1 = stop.at({ offset: '14%', color: '#333', opacity: 1 })
+ s2 = stop.at({ offset: '91%', color: '#fff', opacity: 1 })
+ })
+ expect(s1.attr('offset')).toBe('14%')
+ expect(s2.attr('offset')).toBe('91%')
+ })
+ })
+
+ describe('update()', function() {
+
+ it('removes all existing children first', function() {
+ gradient = draw.gradient('linear', function(stop) {
+ s1 = stop.at({ offset: 0.12, color: '#333', opacity: 1 })
+ s2 = stop.at({ offset: 0.93, color: '#fff', opacity: 1 })
+ })
+ expect(gradient.children().length).toBe(2)
+ gradient.update(function(stop) {
+ s1 = stop.at({ offset: 0.33, color: '#666', opacity: 1 })
+ s2 = stop.at({ offset: 1, color: '#000', opacity: 1 })
+ })
+ expect(gradient.children().length).toBe(2)
+ })
+
+ it('accepts multiple aruments on fixed positions', function() {
+ gradient = draw.gradient('linear', function(stop) {
+ s1 = stop.at(0.11, '#333')
+ s2 = stop.at(0.94, '#fff', 0.5)
+ })
+ expect(gradient.children().length).toBe(2)
+ expect(s1.attr('offset')).toBe(0.11)
+ expect(s1.attr('stop-color')).toBe('#333333')
+ expect(s2.attr('offset')).toBe(0.94)
+ expect(s2.attr('stop-color')).toBe('#ffffff')
+ expect(s2.attr('stop-opacity')).toBe(0.5)
+ })
+
+ })
+
+ describe('get()', function() {
+
+ it('returns the stop at a given index', function() {
+ gradient = draw.gradient('linear', function(stop) {
+ s1 = stop.at({ offset: 0.12, color: '#333', opacity: 1 })
+ s2 = stop.at({ offset: 0.93, color: '#fff', opacity: 1 })
+ })
+ expect(gradient.get(0)).toBe(s1)
+ expect(gradient.get(1)).toBe(s2)
+ expect(gradient.get(2)).toBeNull()
+ })
+
+ })
+
+})
diff --git a/node_modules/svg.js/spec/spec/group.js b/node_modules/svg.js/spec/spec/group.js
new file mode 100644
index 0000000..4d5ff36
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/group.js
@@ -0,0 +1,116 @@
+describe('Group', function() {
+ var group
+
+ beforeEach(function() {
+ group = draw.group().move(50, 50)
+ group.rect(100,100)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ describe('x()', function() {
+ it('returns the value of x without an argument', function() {
+ expect(group.x()).toBe(50)
+ })
+ it('sets the value of x with the first argument', function() {
+ group.x(123)
+ var box = group.gbox()
+ expect(box.x).toBe(123)
+ })
+ it('sets the value of x correctly when called multiple times', function() {
+ group.x(10).x(100).x(13)
+ var box = group.gbox()
+ expect(box.x).toBe(13)
+ })
+ it('sets the value of x correctly when the first argument is a string number', function(){
+ group.x('123')
+ var box = group.gbox()
+ expect(box.x).toBe(123)
+ })
+ })
+
+ describe('y()', function() {
+ it('returns the value of y without an argument', function() {
+ expect(group.y()).toBe(50)
+ })
+ it('sets the value of y with the first argument', function() {
+ group.y(345)
+ var box = group.gbox()
+ expect(box.y).toBe(345)
+ })
+ it('sets the value of y correctly when called multiple times', function() {
+ group.y(1).y(10).y(15)
+ var box = group.gbox()
+ expect(box.y).toBe(15)
+ })
+ it('sets the value of y correctly when the first argument is a string number', function(){
+ group.y('124')
+ var box = group.gbox()
+ expect(box.y).toBe(124)
+ })
+ })
+
+ describe('cx()', function() {
+ it('returns the value of cx without an argument', function() {
+ expect(group.cx()).toBe(100)
+ })
+ it('sets the value of cx with the first argument', function() {
+ group.cx(123)
+ var box = group.gbox()
+ expect(box.cx).toBe(123)
+ })
+ })
+
+ describe('cy()', function() {
+ it('returns the value of cy without an argument', function() {
+ expect(group.cy()).toBe(100)
+ })
+ it('sets the value of cy with the first argument', function() {
+ group.cy(345)
+ var box = group.gbox()
+ expect(box.cy).toBe(345)
+ })
+ })
+
+ describe('move()', function() {
+ it('sets the x and y position', function() {
+ group.move(123,456)
+ expect(group.node.getAttribute('transform')).toBe('matrix(1,0,0,1,123,456)')
+ })
+ })
+
+ describe('center()', function() {
+ it('sets the cx and cy position', function() {
+ group.center(321,567)
+ var box = group.gbox()
+ expect(box.cx).toBe(321)
+ expect(box.cy).toBe(567)
+ })
+ })
+
+ describe('dx()', function() {
+ it('moves the x positon of the element relative to the current position', function() {
+ group.move(50,60)
+ group.dx(100)
+ expect(group.node.getAttribute('transform')).toBe('matrix(1,0,0,1,150,60)')
+ })
+ })
+
+ describe('dy()', function() {
+ it('moves the y positon of the element relative to the current position', function() {
+ group.move(50,60)
+ group.dy(120)
+ expect(group.node.getAttribute('transform')).toBe('matrix(1,0,0,1,50,180)')
+ })
+ })
+
+ describe('dmove()', function() {
+ it('moves the x and y positon of the element relative to the current position', function() {
+ group.move(50, 60)
+ group.dmove(80, 25)
+ expect(group.node.getAttribute('transform')).toBe('matrix(1,0,0,1,130,85)')
+ })
+ })
+})
diff --git a/node_modules/svg.js/spec/spec/helper.js b/node_modules/svg.js/spec/spec/helper.js
new file mode 100644
index 0000000..025c9dc
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/helper.js
@@ -0,0 +1,177 @@
+// create canavs
+//var drawing, window = window, document = document, SVG = SVG
+
+parserInDoc = false
+
+if(typeof exports === 'object'){
+ window = require('svgdom')
+ SVG = require('../../dist/svg.js')
+ document = window.document
+ drawing = document.documentElement
+ imageUrl = 'spec/fixtures/pixel.png'
+ parserInDoc = true
+
+ function tag(name, attrs, children) {
+ var el = document.createElement(name)
+ for(var i in attrs){
+ el.setAttribute(i, attrs[i])
+ }
+
+ for(var i in children){
+ if(typeof children[i] == 'string')
+ children[i] = document.createTextNode(children[i])
+
+ el.appendChild(children[i])
+ }
+
+ return el
+ }
+
+ // create fixtures in svgdom
+ var el = tag('svg', {
+ height:0,
+ width:0,
+ id:'inlineSVG'
+ },[
+ tag('defs', {}, [
+ tag('linearGradient', {}, [
+ tag('stop', {offset: '5%', 'stop-color': 'green'}),
+ tag('stop', {offset: '95%', 'stop-color': 'gold'}),
+ ]),
+ tag('radialGradient', {}, [
+ tag('stop', {offset: '5%', 'stop-color': 'green'}),
+ tag('stop', {offset: '95%', 'stop-color': 'gold'}),
+ ])
+ ]),
+ tag('desc', {}, ['Some description']),
+ tag('path', {
+ id: 'lineAB',
+ d: 'M 100 350 l 150 -300',
+ stroke: 'red',
+ 'stroke-width': '3',
+ fill: 'none'
+ }),
+ tag('path', {
+ id: 'lineBC',
+ d: 'M 250 50 l 150 300',
+ stroke: 'red',
+ 'stroke-width': '3',
+ fill: 'none'
+ }),
+ tag('path', {
+ d: 'M 175 200 l 150 0',
+ stroke: 'green',
+ 'stroke-width': '3',
+ fill: 'none'
+ }),
+ tag('path', {
+ d: 'M 100 350 q 150 -300 300 0',
+ stroke: 'blue',
+ 'stroke-width': '5',
+ fill: 'none'
+ }),
+ tag('g', {
+ stroke: 'black',
+ 'stroke-width': '2',
+ fill: 'black',
+ id: 'pointGroup'
+ },[
+ tag('circle', {
+ id: 'pointA',
+ cx: '100',
+ cy: '350',
+ r: '3',
+ }),
+ tag('circle', {
+ id: 'pointB',
+ cx: '250',
+ cy: '50',
+ r: '50',
+ }),
+ tag('circle', {
+ id: 'pointC',
+ cx: '400',
+ cy: '350',
+ r: '50',
+ })
+ ]),
+ tag('g', {
+ 'font-size': '30',
+ font: 'sans-serif',
+ fill: 'black',
+ stroke: 'none',
+ 'text-anchor': 'middle',
+ id: 'labelGroup'
+ },[
+ tag('text', {
+ x: '100',
+ y: '350',
+ dy: '-30',
+ }, ['A']),
+ tag('text', {
+ x: '250',
+ y: '50',
+ dy: '-10',
+ }, ['B']),
+ tag('text', {
+ x: '400',
+ y: '350',
+ dx: '30',
+ }, ['C'])
+ ]),
+ tag('polygon', {points: '200,10 250,190 160,210'}),
+ tag('polyline', {points: '20,20 40,25 60,40 80,120 120,140 200,180'})
+ ])
+
+ document.appendChild(el)
+
+}else{
+ drawing = document.createElement('div')
+ document.getElementsByTagName('body')[0].appendChild(drawing)
+ imageUrl = 'fixtures/pixel.png'
+}
+
+parserInDoc |= 0
+drawing.id = 'drawing'
+draw = SVG(drawing).size(100,100)
+
+parser = parserInDoc ? [SVG.parser.draw.instance] : []
+
+// raw path data
+svgPath = 'M88.006,61.994c3.203,0,6.216-1.248,8.481-3.514C98.752,56.215,100,53.203,100,50c0-3.204-1.248-6.216-3.513-8.481 c-2.266-2.265-5.278-3.513-8.481-3.513c-2.687,0-5.237,0.877-7.327,2.496h-7.746l5.479-5.479 c5.891-0.757,10.457-5.803,10.457-11.896c0-6.614-5.381-11.995-11.994-11.995c-6.093,0-11.14,4.567-11.896,10.457l-5.479,5.479 v-7.747c1.618-2.089,2.495-4.641,2.495-7.327c0-3.204-1.247-6.216-3.513-8.481C56.216,1.248,53.204,0,50,0 c-3.204,0-6.216,1.248-8.481,3.513c-2.265,2.265-3.513,5.277-3.513,8.481c0,2.686,0.877,5.237,2.495,7.327v7.747l-5.479-5.479 c-0.757-5.89-5.803-10.457-11.896-10.457c-6.614,0-11.995,5.381-11.995,11.995c0,6.093,4.567,11.139,10.458,11.896l5.479,5.479 h-7.747c-2.089-1.619-4.641-2.496-7.327-2.496c-3.204,0-6.216,1.248-8.481,3.513C1.248,43.784,0,46.796,0,50 c0,3.203,1.248,6.216,3.513,8.48c2.265,2.266,5.277,3.514,8.481,3.514c2.686,0,5.237-0.877,7.327-2.496h7.747l-5.479,5.479 c-5.891,0.757-10.458,5.804-10.458,11.896c0,6.614,5.381,11.994,11.995,11.994c6.093,0,11.139-4.566,11.896-10.457l5.479-5.479 v7.749c-3.63,4.7-3.291,11.497,1.018,15.806C43.784,98.752,46.796,100,50,100c3.204,0,6.216-1.248,8.481-3.514 c4.309-4.309,4.647-11.105,1.018-15.806v-7.749l5.479,5.479c0.757,5.891,5.804,10.457,11.896,10.457 c6.613,0,11.994-5.38,11.994-11.994c0-6.093-4.566-11.14-10.457-11.896l-5.479-5.479h7.746 C82.769,61.117,85.319,61.994,88.006,61.994z M76.874,68.354c4.705,0,8.52,3.814,8.52,8.521c0,4.705-3.814,8.52-8.52,8.52 s-8.52-3.814-8.52-8.52l-12.33-12.33V81.98c3.327,3.328,3.327,8.723,0,12.049c-3.327,3.328-8.722,3.328-12.049,0 c-3.327-3.326-3.327-8.721,0-12.049V64.544l-12.33,12.33c0,4.705-3.814,8.52-8.52,8.52s-8.52-3.814-8.52-8.52 c0-4.706,3.814-8.521,8.52-8.521l12.33-12.33H18.019c-3.327,3.328-8.722,3.328-12.049,0c-3.327-3.326-3.327-8.721,0-12.048 s8.722-3.327,12.049,0h17.438l-12.33-12.33c-4.706,0-8.52-3.814-8.52-8.52c0-4.706,3.814-8.52,8.52-8.52s8.52,3.814,8.52,8.52 l12.33,12.33V18.019c-3.327-3.327-3.327-8.722,0-12.049s8.722-3.327,12.049,0s3.327,8.722,0,12.049v17.438l12.33-12.33 c0-4.706,3.814-8.52,8.52-8.52s8.52,3.814,8.52,8.52c0,4.705-3.814,8.52-8.52,8.52l-12.33,12.33h17.438 c3.327-3.327,8.722-3.327,12.049,0s3.327,8.722,0,12.048c-3.327,3.328-8.722,3.328-12.049,0H64.544L76.874,68.354z'
+
+// image url
+
+
+// lorem ipsum text
+loremIpsum = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sodales\n imperdiet auctor. Nunc ultrices lectus at erat dictum pharetra\n elementum ante posuere. Duis turpis risus, blandit nec elementum et,\n posuere eget lacus. Aliquam et risus magna, eu aliquet nibh. Fusce\n consequat mi quis purus varius sagittis euismod urna interdum.\n Curabitur aliquet orci quis felis semper vulputate. Vestibulum ac nisi\n magna, id dictum diam. Proin sed metus vel magna blandit\n sodales. Pellentesque at neque ultricies nunc euismod rutrum ut in\n lorem. Mauris euismod tellus in tellus tempus interdum. Phasellus\n mattis sapien et leo feugiat dictum. Vestibulum at volutpat velit.'
+
+beforeEach(function(){
+ // test for touch device
+ this.isTouchDevice = 'ontouchstart' in document.documentElement
+})
+
+// strip spaces from result
+window.stripped = function(string) {
+ string = string.replace(/\s+/g, '')
+ if(string.slice(-1) == ';') string = string.slice(0, -1)
+ return string
+}
+
+// This is needed because of IE11 which uses space as a delimiter in matrix
+window.matrixStringToArray = function(source){
+ return source
+ .replace(/matrix\(|\)/, '')
+ .split(SVG.regex.delimiter)
+ .map(parseFloat)
+}
+
+// This is needed because of IE11 creating values like 2.99999 when calculating a transformed box
+window.roundBox = function(box) {
+ return new SVG.Box(
+ Math.round(box.x),
+ Math.round(box.y),
+ Math.round(box.width),
+ Math.round(box.height)
+ )
+}
diff --git a/node_modules/svg.js/spec/spec/hyperlink.js b/node_modules/svg.js/spec/spec/hyperlink.js
new file mode 100644
index 0000000..affef6d
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/hyperlink.js
@@ -0,0 +1,61 @@
+describe('Hyperlink', function() {
+ var link
+ , url = 'http://svgjs.com'
+
+ beforeEach(function() {
+ link = draw.link(url)
+ link.rect(100,100)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ it('creates a link', function() {
+ expect(link.attr('href')).toBe(url)
+ })
+
+ describe('to()', function() {
+ it('creates xlink:href attribute', function() {
+ link.to('http://apple.com')
+ expect(link.attr('href')).toBe('http://apple.com')
+ })
+ })
+
+ describe('show()', function() {
+ it('creates xlink:show attribute', function() {
+ link.show('replace')
+ expect(link.attr('show')).toBe('replace')
+ })
+ })
+
+ describe('target()', function() {
+ it('creates target attribute', function() {
+ link.target('_blank')
+ expect(link.attr('target')).toBe('_blank')
+ })
+ })
+
+ describe('SVG.Element', function() {
+ var element
+
+ beforeEach(function() {
+ element = draw.rect(100,100)
+ })
+
+ describe('linkTo()', function() {
+ it('wraps the called element in a link with given url', function() {
+ element.linkTo(url)
+ expect(element.parent().attr('href')).toBe(url)
+ })
+ it('wraps the called element in a link with given block', function() {
+ element.linkTo(function(link) {
+ link.to(url).target('_blank')
+ })
+ expect(element.parent().attr('href')).toBe(url)
+ expect(element.parent().attr('target')).toBe('_blank')
+ })
+ })
+ })
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/spec/image.js b/node_modules/svg.js/spec/spec/image.js
new file mode 100644
index 0000000..4b51a9b
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/image.js
@@ -0,0 +1,226 @@
+describe('Image', function() {
+ var image
+
+ beforeEach(function() {
+ image = draw.image(imageUrl, 100, 100)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+
+ describe('()', function() {
+ it('should set width and height automatically if no size is given', function(done) {
+ image = draw.image(imageUrl).loaded(function() {
+ expect(image.node.getAttribute('height')).toBe('1')
+ expect(image.node.getAttribute('width')).toBe('1')
+ done()
+ })
+ })
+ it('should set width and height if size is given', function(done) {
+ image = draw.image(imageUrl, 100, 100).loaded(function() {
+ expect(image.node.getAttribute('height')).toBe('100')
+ expect(image.node.getAttribute('width')).toBe('100')
+ done()
+ })
+ })
+ it('returns itself when no url given', function() {
+ var img = new SVG.Image()
+ expect(img.load()).toBe(img)
+ })
+ })
+
+ describe('loaded()', function() {
+ beforeEach(function(done) {
+ loadCb = {cb: function(){ done() }}
+ errorCb = jasmine.createSpy('errorCb')
+ spyOn(loadCb, 'cb').and.callThrough()
+ image = draw.image(imageUrl, 100, 100).loaded(loadCb.cb).error(errorCb)
+ })
+
+ it('should set the load callback', function() {
+ expect(image._loaded).toBe(loadCb.cb)
+ })
+ it('executes the load callback', function() {
+ expect(loadCb.cb).toHaveBeenCalledWith({
+ width: 1,
+ height: 1,
+ ratio: 1,
+ url: jasmine.any(String)
+ })
+ })
+ it('does not execute the error callback', function() {
+ expect(errorCb).not.toHaveBeenCalled()
+ })
+ })
+
+ describe('error()', function() {
+ beforeEach(function(done) {
+ loadCb = jasmine.createSpy('loadCb')
+ errorCb = {cb: function(){ done() }}
+ spyOn(errorCb, 'cb').and.callThrough()
+ image = draw.image('not_existant.jpg', 100, 100).loaded(loadCb).error(errorCb.cb)
+ })
+
+ it('should set the error callback', function() {
+ expect(image._error).toBe(errorCb.cb)
+ })
+ it('executes the error callback', function() {
+ expect(errorCb.cb).toHaveBeenCalledWith(jasmine.any(window.Event))
+ })
+ it('does not execute the load callback', function() {
+ expect(loadCb).not.toHaveBeenCalled()
+ })
+ })
+
+
+ describe('x()', function() {
+ it('should return the value of x without an argument', function() {
+ expect(image.x()).toBe(0)
+ })
+ it('should set the value of x with the first argument', function() {
+ image.x(123)
+ var box = image.bbox()
+ expect(box.x).toBe(123)
+ })
+ })
+
+ describe('y()', function() {
+ it('should return the value of y without an argument', function() {
+ expect(image.y()).toBe(0)
+ })
+ it('should set the value of y with the first argument', function() {
+ image.y(345)
+ var box = image.bbox()
+ expect(box.y).toBe(345)
+ })
+ })
+
+ describe('cx()', function() {
+ it('should return the value of cx without an argument', function() {
+ expect(image.cx()).toBe(50)
+ })
+ it('should set the value of cx with the first argument', function() {
+ image.cx(123)
+ var box = image.bbox()
+ expect(box.cx).toBe(123)
+ })
+ })
+
+ describe('cy()', function() {
+ it('should return the value of cy without an argument', function() {
+ expect(image.cy()).toBe(50)
+ })
+ it('should set the value of cy with the first argument', function() {
+ image.cy(345)
+ var box = image.bbox()
+ expect(box.cy).toBe(345)
+ })
+ })
+
+ describe('move()', function() {
+ it('should set the x and y position', function() {
+ image.move(123,456)
+ expect(image.node.getAttribute('x')).toBe('123')
+ expect(image.node.getAttribute('y')).toBe('456')
+ })
+ })
+
+ describe('dx()', function() {
+ it('moves the x positon of the element relative to the current position', function() {
+ image.move(50,60)
+ image.dx(100)
+ expect(image.node.getAttribute('x')).toBe('150')
+ })
+ })
+
+ describe('dy()', function() {
+ it('moves the y positon of the element relative to the current position', function() {
+ image.move(50,60)
+ image.dy(120)
+ expect(image.node.getAttribute('y')).toBe('180')
+ })
+ })
+
+ describe('dmove()', function() {
+ it('moves the x and y positon of the element relative to the current position', function() {
+ image.move(50,60)
+ image.dmove(80, 25)
+ expect(image.node.getAttribute('x')).toBe('130')
+ expect(image.node.getAttribute('y')).toBe('85')
+ })
+ })
+
+ describe('center()', function() {
+ it('should set the cx and cy position', function() {
+ image.center(321,567)
+ var box = image.bbox()
+ expect(box.cx).toBe(321)
+ expect(box.cy).toBe(567)
+ })
+ })
+
+ describe('width()', function() {
+ it('sets the width of the element', function() {
+ image.width(789)
+ expect(image.node.getAttribute('width')).toBe('789')
+ })
+ it('gets the width of the element if the argument is null', function() {
+ expect(image.width().toString()).toBe(image.node.getAttribute('width'))
+ })
+ })
+
+ describe('height()', function() {
+ it('sets the height of the element', function() {
+ image.height(1236)
+ expect(image.node.getAttribute('height')).toBe('1236')
+ })
+ it('gets the height of the element if the argument is null', function() {
+ expect(image.height().toString()).toBe(image.node.getAttribute('height'))
+ })
+ })
+
+ describe('size()', function() {
+ it('should define the width and height of the element', function() {
+ image.size(987,654)
+ expect(image.node.getAttribute('width')).toBe('987')
+ expect(image.node.getAttribute('height')).toBe('654')
+ })
+ it('defines the width and height proportionally with only the width value given', function() {
+ var box = image.bbox()
+ image.size(500)
+ expect(image.width()).toBe(500)
+ expect(image.width() / image.height()).toBe(box.width / box.height)
+ })
+ it('defines the width and height proportionally with only the height value given', function() {
+ var box = image.bbox()
+ image.size(null, 525)
+ expect(image.height()).toBe(525)
+ expect(image.width() / image.height()).toBe(box.width / box.height)
+ })
+ })
+
+ describe('scale()', function() {
+ it('should scale the element universally with one argument', function() {
+ var box = image.scale(2).rbox()
+
+ expect(box.width).toBe(image.attr('width') * 2)
+ expect(box.height).toBe(image.attr('height') * 2)
+ })
+ it('should scale the element over individual x and y axes with two arguments', function() {
+ var box = image.scale(2, 3.5).rbox()
+
+ expect(box.width).toBe(image.attr('width') * 2)
+ expect(box.height).toBe(image.attr('height') * 3.5)
+ })
+ })
+
+ describe('translate()', function() {
+ it('should set the translation of an element', function() {
+ image.transform({ x: 12, y: 12 })
+ expect(image.node.getAttribute('transform')).toBe('matrix(1,0,0,1,12,12)')
+ })
+ })
+
+})
diff --git a/node_modules/svg.js/spec/spec/line.js b/node_modules/svg.js/spec/spec/line.js
new file mode 100644
index 0000000..219043d
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/line.js
@@ -0,0 +1,244 @@
+describe('Line', function() {
+ var line
+
+ beforeEach(function() {
+ line = draw.line(0,100,100,0)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ // #487
+ describe('()', function(){
+ it('will take an array as input', function(){
+ line = draw.line([[0,100],[100,0]])
+ var attrs = line.attr()
+ expect(attrs.x1).toBe(0)
+ expect(attrs.y1).toBe(100)
+ expect(attrs.x2).toBe(100)
+ expect(attrs.y2).toBe(0)
+ })
+
+ it('falls back to a line with its two points at [0,0] without an argument', function() {
+ line = draw.line()
+ var attrs = line.attr()
+ expect(attrs.x1).toBe(0)
+ expect(attrs.y1).toBe(0)
+ expect(attrs.x2).toBe(0)
+ expect(attrs.y2).toBe(0)
+ })
+ })
+
+ describe('x()', function() {
+ it('should return the value of x without an argument', function() {
+ expect(line.x()).toBe(0)
+ })
+ it('should set the value of x with the first argument', function() {
+ line.x(123)
+ var box = line.bbox()
+ expect(box.x).toBe(123)
+ })
+ })
+
+ describe('y()', function() {
+ it('should return the value of y without an argument', function() {
+ expect(line.y()).toBe(0)
+ })
+ it('should set the value of y with the first argument', function() {
+ line.y(345)
+ var box = line.bbox()
+ expect(box.y).toBe(345)
+ })
+ })
+
+ describe('cx()', function() {
+ it('should return the value of cx without an argument', function() {
+ expect(line.cx()).toBe(50)
+ })
+ it('should set the value of cx with the first argument', function() {
+ line.cx(123)
+ var box = line.bbox()
+ expect(box.cx).toBe(123)
+ })
+ })
+
+ describe('cy()', function() {
+ it('should return the value of cy without an argument', function() {
+ expect(line.cy()).toBe(50)
+ })
+ it('should set the value of cy with the first argument', function() {
+ line.cy(345)
+ var box = line.bbox()
+ expect(box.cy).toBe(345)
+ })
+ })
+
+ describe('move()', function() {
+ it('should set the x and y position', function() {
+ line.move(123,456)
+ var box = line.bbox()
+ expect(box.x).toBe(123)
+ expect(box.y + box.height).toBe(556)
+ expect(box.x + box.width).toBe(223)
+ expect(box.y).toBe(456)
+ })
+ })
+
+ describe('dx()', function() {
+ it('moves the x positon of the element relative to the current position', function() {
+ line.move(50,60)
+ line.dx(100)
+ var box = line.bbox()
+ expect(box.x).toBe(150)
+ expect(box.y + box.height).toBe(160)
+ expect(box.x + box.width).toBe(250)
+ expect(box.y).toBe(60)
+ })
+ })
+
+ describe('dy()', function() {
+ it('moves the y positon of the element relative to the current position', function() {
+ line.move(50, 60)
+ line.dy(120)
+ var box = line.bbox()
+ expect(box.x).toBe(50)
+ expect(box.y + box.height).toBe(280)
+ expect(box.x + box.width).toBe(150)
+ expect(box.y).toBe(180)
+ })
+ })
+
+ describe('dmove()', function() {
+ it('moves the x and y positon of the element relative to the current position', function() {
+ line.move(50,60)
+ line.dmove(80, 25)
+ var box = line.bbox()
+ expect(box.x).toBe(130)
+ expect(box.y + box.height).toBe(185)
+ expect(box.x + box.width).toBe(230)
+ expect(box.y).toBe(85)
+ })
+ })
+
+ describe('center()', function() {
+ it('should set the cx and cy position', function() {
+ line.center(321,567)
+ var box = line.bbox()
+ expect(box.x).toBe(271)
+ expect(box.y + box.height).toBe(617)
+ expect(box.x + box.width).toBe(371)
+ expect(box.y).toBe(517)
+ })
+ })
+
+ describe('width()', function() {
+ it('sets the width of the element', function() {
+ line.width(400)
+ var box = line.bbox()
+ expect(box.x).toBe(0)
+ expect(box.x + box.width).toBe(400)
+ })
+ it('get the width of the element without argument', function() {
+ line.width(123)
+ var box = line.bbox()
+ expect(line.width()).toBe(box.width)
+ })
+ })
+
+ describe('height()', function() {
+ it('sets the height of the element', function() {
+ line.height(300)
+ var box = line.bbox()
+ expect(box.y).toBe(0)
+ expect(box.y + box.height).toBe(300)
+ })
+ it('gets the height of the element without argument', function() {
+ line.height(456)
+ var box = line.bbox()
+ expect(line.height()).toBe(box.height)
+ })
+ })
+
+ describe('size()', function() {
+ it('should define the width and height of the element', function() {
+ line.size(987,654)
+ var box = line.bbox()
+ expect(box.x).toBe(0)
+ expect(box.y + box.height).toBe(654)
+ expect(box.x + box.width).toBe(987)
+ expect(box.y).toBe(0)
+ })
+ it('defines the width and height proportionally with only the width value given', function() {
+ var box = line.bbox()
+ line.size(500)
+ expect(line.width()).toBe(500)
+ expect(line.width() / line.height()).toBe(box.width / box.height)
+ })
+ it('defines the width and height proportionally with only the height value given', function() {
+ var box = line.bbox()
+ line.size(null, 525)
+ expect(line.height()).toBe(525)
+ expect(line.width() / line.height()).toBe(box.width / box.height)
+ })
+ })
+
+ describe('scale()', function() {
+ it('should scale the element universally with one argument', function() {
+ var box1 = line.rbox()
+ , box2 = line.scale(2).rbox()
+
+ expect(box2.width).toBe(box1.width * 2)
+ expect(box2.height).toBe(box1.height * 2)
+ })
+ it('should scale the element over individual x and y axes with two arguments', function() {
+ var box1 = line.rbox()
+ , box2 = line.scale(2,3.5).rbox()
+
+ expect(box2.width).toBe(box1.width * 2)
+ expect(box2.height).toBe(box1.height * 3.5)
+ })
+ })
+
+ describe('translate()', function() {
+ it('should set the translation of an element', function() {
+ line.transform({ x: 12, y: 12 })
+ expect(line.node.getAttribute('transform')).toBe('matrix(1,0,0,1,12,12)')
+ })
+ })
+
+ describe('plot()', function() {
+ it('should update the start and end points', function() {
+ line.plot(100,200,300,400)
+ var box = line.bbox()
+ expect(box.x).toBe(100)
+ expect(box.y).toBe(200)
+ expect(box.x + box.width).toBe(300)
+ expect(box.y + box.height).toBe(400)
+ })
+ it('change the x1,y1,x2,y2 attributes of the underlying line node when a string is passed', function() {
+ expect(line.plot('100,50 200,10')).toBe(line)
+
+ var attrs = line.attr()
+ expect(attrs.x1).toBe(100)
+ expect(attrs.y1).toBe(50)
+ expect(attrs.x2).toBe(200)
+ expect(attrs.y2).toBe(10)
+ })
+ it('change the x1,y1,x2,y2 attributes of the underlying line node when 4 numbers are passed', function() {
+ expect(line.plot(45, 24, 220, 300)).toBe(line)
+
+ var attrs = line.attr()
+ expect(attrs.x1).toBe(45)
+ expect(attrs.y1).toBe(24)
+ expect(attrs.x2).toBe(220)
+ expect(attrs.y2).toBe(300)
+ })
+ it('return the coordinates in a point array when no arguments are passed', function () {
+ var attrs = line.attr()
+ , pointArray = new SVG.PointArray([[attrs.x1, attrs.y1], [attrs.x2, attrs.y2]])
+
+ expect(line.plot()).toEqual(pointArray)
+ })
+ })
+})
diff --git a/node_modules/svg.js/spec/spec/marker.js b/node_modules/svg.js/spec/spec/marker.js
new file mode 100644
index 0000000..7b902d4
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/marker.js
@@ -0,0 +1,89 @@
+describe('Marker', function() {
+
+ describe('on a container element', function() {
+ var marker
+
+ beforeEach(function() {
+ marker = draw.marker(10, 12, function(add) {
+ add.rect(10, 12)
+
+ this.ref(5, 6)
+ })
+ })
+
+ it('creates an instance of SVG.Marker', function() {
+ expect(marker instanceof SVG.Marker).toBeTruthy()
+ })
+
+ it('creates marker in defs', function() {
+ expect(marker.parent() instanceof SVG.Defs).toBeTruthy()
+ })
+
+ describe('marker()', function() {
+ it('returns the marker element', function() {
+ expect(marker = draw.marker(10, 12)).toBe(marker)
+ })
+ it('sets the refX to half of the given width', function() {
+ marker = draw.marker(10, 12)
+ expect(marker.node.getAttribute('refX')).toBe('5')
+ })
+ it('sets the refY to half of the given height', function() {
+ marker = draw.marker(13, 15)
+ expect(marker.node.getAttribute('refY')).toBe('7.5')
+ })
+ })
+
+ })
+
+ describe('on a target path', function() {
+ var path, marker
+
+ beforeEach(function() {
+ path = draw.path('M 100 200 C 200 100 300 0 400 100 C 500 200 600 300 700 200 C 800 100 900 100 900 100')
+
+ path.marker('mid', 10, 12, function(add) {
+ add.rect(10, 12)
+
+ this.ref(5, 6)
+ })
+
+ marker = path.marker('mid', 10, 10)
+ })
+
+ it('creates an instance of SVG.Marker', function() {
+ expect(path.reference('marker-mid') instanceof SVG.Marker).toBeTruthy()
+ })
+
+ describe('marker()', function() {
+ it('returns the target element', function() {
+ expect(path.marker('start', 10, 12)).toBe(path)
+ })
+ it('creates a marker and applies it to the marker-start attribute', function() {
+ path.marker('start', 10, 12)
+ marker = path.reference('marker-start')
+
+ expect(path.node.getAttribute('marker-start')).toBe(marker.toString())
+ })
+ it('creates a marker and applies it to the marker-mid attribute', function() {
+ path.marker('mid', 10, 12)
+ marker = path.reference('marker-mid')
+
+ expect(path.node.getAttribute('marker-mid')).toBe(marker.toString())
+ })
+ it('creates a marker and applies it to the marker-end attribute', function() {
+ path.marker('end', 10, 12)
+ marker = path.reference('marker-end')
+
+ expect(path.node.getAttribute('marker-end')).toBe(marker.toString())
+ })
+ it('accepts an instance of an existing marker element as the second argument', function() {
+ marker = draw.marker(11, 11)
+ path.marker('mid', marker)
+
+ expect(path.node.getAttribute('marker-mid')).toBe(marker.toString())
+ })
+ })
+ })
+
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/spec/mask.js b/node_modules/svg.js/spec/spec/mask.js
new file mode 100644
index 0000000..a7cd6d1
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/mask.js
@@ -0,0 +1,62 @@
+describe('Mask', function() {
+ var rect, circle
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ circle = draw.circle(100).move(50, 50).fill('#fff')
+ rect.maskWith(circle)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ it('moves the masking element to a new mask node', function() {
+ expect(circle.parent() instanceof SVG.Mask).toBe(true)
+ })
+
+ it('creates the mask node in the defs node', function() {
+ expect(circle.parent().parent()).toBe(draw.defs())
+ })
+
+ it('sets the "mask" attribute on the masked element with the mask id', function() {
+ expect(rect.attr('mask')).toBe('url("#' + circle.parent().attr('id') + '")')
+ })
+
+ it('references the mask element in the masked element', function() {
+ expect(rect.masker).toBe(circle.parent())
+ })
+
+ it('references the masked element in the mask target list', function() {
+ expect(rect.masker.targets.indexOf(rect) > -1).toBe(true)
+ })
+
+ it('reuses mask element when mask was given', function() {
+ var mask = rect.masker
+ expect(draw.rect(100,100).maskWith(mask).masker).toBe(mask)
+ })
+
+ it('unmasks all masked elements when being removed', function() {
+ rect.masker.remove()
+ expect(rect.attr('mask')).toBe(undefined)
+ })
+
+ describe('unmask()', function() {
+
+ it('clears the "mask" attribute on the masked element', function() {
+ rect.unmask()
+ expect(rect.attr('mask')).toBe(undefined)
+ })
+
+ it('removes the reference to the masking element', function() {
+ rect.unmask()
+ expect(rect.masker).toBe(undefined)
+ })
+
+ it('returns the element itslef', function() {
+ expect(rect.unmask()).toBe(rect)
+ })
+
+ })
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/spec/matrix.js b/node_modules/svg.js/spec/spec/matrix.js
new file mode 100644
index 0000000..0816f66
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/matrix.js
@@ -0,0 +1,471 @@
+describe('Matrix', function() {
+ var matrix
+
+ describe('initialization', function() {
+
+ describe('without a source', function() {
+
+ beforeEach(function() {
+ matrix = new SVG.Matrix
+ })
+
+ it('creates a new matrix with default values', function() {
+ expect(matrix.a).toBe(1)
+ expect(matrix.b).toBe(0)
+ expect(matrix.c).toBe(0)
+ expect(matrix.d).toBe(1)
+ expect(matrix.e).toBe(0)
+ expect(matrix.f).toBe(0)
+ })
+
+ describe('extract()', function() {
+ var extract
+
+ beforeEach(function() {
+ extract = matrix.extract()
+ })
+
+ it('parses translation values', function() {
+ expect(extract.x).toBe(0)
+ expect(extract.y).toBe(0)
+ })
+ it('parses skew values', function() {
+ expect(extract.skewX).toBe(0)
+ expect(extract.skewY).toBe(0)
+ })
+ it('parses scale values', function() {
+ expect(extract.scaleX).toBe(1)
+ expect(extract.scaleY).toBe(1)
+ })
+ it('parses rotatoin value', function() {
+ expect(extract.rotation).toBe(0)
+ })
+ })
+
+ describe('toString()' , function() {
+ it('exports correctly to a string', function() {
+ expect(matrix.toString()).toBe('matrix(1,0,0,1,0,0)')
+ })
+ })
+ })
+
+ describe('with an element given', function() {
+ var rect
+
+ beforeEach(function() {
+ rect = draw.rect(100, 100)
+ .transform({ rotation: -10 }, true)
+ .transform({ x: 40, y: 50 }, true)
+ .transform({ scale: 2 }, true)
+
+ matrix = new SVG.Matrix(rect)
+ })
+
+ it('parses the current transform matrix from an element', function() {
+ expect(matrix.a).toBeCloseTo(1.9696155786514282)
+ expect(matrix.b).toBeCloseTo(-0.3472963869571686)
+ expect(matrix.c).toBeCloseTo(0.3472963869571686)
+ expect(matrix.d).toBeCloseTo(1.9696155786514282)
+ expect(matrix.e).toBeCloseTo(-17.770875930786133)
+ expect(matrix.f).toBeCloseTo(11.178505897521973)
+ })
+
+ describe('extract()', function() {
+
+ it('parses translation values', function() {
+ var extract = new SVG.Matrix(draw.rect(100, 100).translate(40, 50)).extract()
+ expect(extract.x).toBeCloseTo(40)
+ expect(extract.y).toBeCloseTo(50)
+ })
+ it('parses skewX value', function() {
+ var extract = new SVG.Matrix(draw.rect(100, 100).skew(25, 0)).extract()
+ expect(extract.skewX).toBeCloseTo(25)
+ })
+ it('parses skewY value', function() {
+ var extract = new SVG.Matrix(draw.rect(100, 100).skew(0, 20)).extract()
+ expect(extract.skewY).toBeCloseTo(20)
+ })
+ it('parses scale values', function() {
+ var extract = new SVG.Matrix(draw.rect(100, 100).scale(2, 3)).extract()
+ expect(extract.scaleX).toBeCloseTo(2)
+ expect(extract.scaleY).toBeCloseTo(3)
+ })
+ it('parses rotatoin value', function() {
+ var extract = new SVG.Matrix(draw.rect(100, 100).rotate(-100)).extract()
+ expect(extract.rotation).toBeCloseTo(-100)
+ })
+
+ })
+
+ })
+
+ describe('with a string given', function() {
+ it('parses the string value correctly', function() {
+ var matrix = new SVG.Matrix('2, 0, 0, 2, 100, 50')
+
+ expect(matrix.a).toBe(2)
+ expect(matrix.b).toBe(0)
+ expect(matrix.c).toBe(0)
+ expect(matrix.d).toBe(2)
+ expect(matrix.e).toBe(100)
+ expect(matrix.f).toBe(50)
+ })
+ })
+
+ describe('with an array given', function() {
+ it('parses the array correctly', function() {
+ var matrix = new SVG.Matrix([2, 0, 0, 2, 100, 50])
+
+ expect(matrix.a).toBe(2)
+ expect(matrix.b).toBe(0)
+ expect(matrix.c).toBe(0)
+ expect(matrix.d).toBe(2)
+ expect(matrix.e).toBe(100)
+ expect(matrix.f).toBe(50)
+ })
+ })
+
+ describe('with an object given', function() {
+ it('parses the object correctly', function() {
+ var matrix = new SVG.Matrix({a:2, b:0, c:0, d:2, e:100, f:50})
+
+ expect(matrix.a).toBe(2)
+ expect(matrix.b).toBe(0)
+ expect(matrix.c).toBe(0)
+ expect(matrix.d).toBe(2)
+ expect(matrix.e).toBe(100)
+ expect(matrix.f).toBe(50)
+ })
+ })
+
+ describe('with 6 arguments given', function() {
+ it('parses the arguments correctly', function() {
+ var matrix = new SVG.Matrix(2, 0, 0, 2, 100, 50)
+
+ expect(matrix.a).toBe(2)
+ expect(matrix.b).toBe(0)
+ expect(matrix.c).toBe(0)
+ expect(matrix.d).toBe(2)
+ expect(matrix.e).toBe(100)
+ expect(matrix.f).toBe(50)
+ })
+ })
+
+ })
+
+ describe('clone()', function() {
+ it('returns a clone of the matrix', function() {
+ var matrix = new SVG.Matrix(2, 0, 0, 5, 0, 0)
+ , clone = matrix.clone()
+ expect(matrix).not.toBe(clone)
+ for(var i in 'abcdef') {
+ expect(matrix[i]).toEqual(clone[i])
+ }
+ })
+ })
+
+ describe('morph()', function() {
+ it('stores a given matrix for morphing', function() {
+ var matrix1 = new SVG.Matrix(2, 0, 0, 5, 0, 0)
+ , matrix2 = new SVG.Matrix(1, 0, 0, 1, 4, 3)
+
+ matrix1.morph(matrix2)
+
+ expect(matrix1.destination).toEqual(matrix2)
+ })
+ it('stores a clone, not the given matrix itself', function() {
+ var matrix1 = new SVG.Matrix(2, 0, 0, 5, 0, 0)
+ , matrix2 = new SVG.Matrix(1, 0, 0, 1, 4, 3)
+
+ matrix1.morph(matrix2)
+
+ expect(matrix1.destination).not.toBe(matrix2)
+ })
+ })
+
+ describe('at()', function() {
+ it('returns a morphed array at a given position', function() {
+ var matrix1 = new SVG.Matrix(2, 0, 0, 5, 0, 0)
+ , matrix2 = new SVG.Matrix(1, 0, 0, 1, 4, 3)
+ , matrix3 = matrix1.morph(matrix2).at(0.5)
+
+ expect(matrix1.toString()).toBe('matrix(2,0,0,5,0,0)')
+ expect(matrix2.toString()).toBe('matrix(1,0,0,1,4,3)')
+ expect(matrix3.toString()).toBe('matrix(1.5,0,0,3,2,1.5)')
+ })
+ it('returns itself when no destination specified', function() {
+ var matrix = new SVG.Matrix(2, 0, 0, 5, 0, 0)
+ expect(matrix.at(0.5)).toBe(matrix)
+ })
+ })
+
+ describe('multiply()', function() {
+ it('multiplies two matices', function() {
+ var matrix1 = new SVG.Matrix(2, 0, 0, 5, 0, 0)
+ , matrix2 = new SVG.Matrix(1, 0, 0, 1, 4, 3)
+ , matrix3 = matrix1.multiply(matrix2)
+
+ expect(matrix1.toString()).toBe('matrix(2,0,0,5,0,0)')
+ expect(matrix2.toString()).toBe('matrix(1,0,0,1,4,3)')
+ expect(matrix3.toString()).toBe('matrix(2,0,0,5,8,15)')
+ })
+ it('accepts matrices in any form', function() {
+ var matrix1 = new SVG.Matrix(2, 0, 0, 5, 0, 0)
+ , matrix2 = matrix1.multiply('1,0,0,1,4,3')
+
+ expect(matrix1.toString()).toBe('matrix(2,0,0,5,0,0)')
+ expect(matrix2.toString()).toBe('matrix(2,0,0,5,8,15)')
+ })
+ })
+
+ describe('inverse()', function() {
+ it('inverses matrix', function() {
+
+ var matrix1 = new SVG.Matrix(2, 0, 0, 5, 4, 3)
+ , matrix2 = matrix1.inverse()
+ , abcdef = [0.5,0,0,0.2,-2,-0.6]
+
+ expect(matrix1.toString()).toBe('matrix(2,0,0,5,4,3)')
+
+ for(var i in 'abcdef') {
+ expect(matrix2['abcdef'[i]]).toBeCloseTo(abcdef[i])
+ }
+ })
+ })
+
+ describe('translate()', function() {
+ it('translates matrix by given x and y values', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).translate(10, 12.5)
+
+ expect(matrix.e).toBe(14)
+ expect(matrix.f).toBe(15.5)
+ })
+ })
+
+ describe('scale()', function() {
+ it('performs a uniformal scale with one value', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).scale(3)
+
+ expect(matrix.a).toBe(3)
+ expect(matrix.d).toBe(3)
+ expect(matrix.e).toBe(4)
+ expect(matrix.f).toBe(3)
+ })
+ it('performs a non-uniformal scale with two values', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).scale(2.5, 3.5)
+
+ expect(matrix.a).toBe(2.5)
+ expect(matrix.d).toBe(3.5)
+ expect(matrix.e).toBe(4)
+ expect(matrix.f).toBe(3)
+ })
+ it('performs a uniformal scale at a given center point with three values', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).scale(3, 150, 100)
+
+ expect(matrix.a).toBe(3)
+ expect(matrix.d).toBe(3)
+ expect(matrix.e).toBe(-296)
+ expect(matrix.f).toBe(-197)
+ })
+ it('performs a non-uniformal scale at a given center point with four values', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).scale(3, 2, 150, 100)
+
+ expect(matrix.a).toBe(3)
+ expect(matrix.d).toBe(2)
+ expect(matrix.e).toBe(-296)
+ expect(matrix.f).toBe(-97)
+ })
+ })
+
+ describe('rotate()', function() {
+ it('performs a rotation with one argument', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).rotate(30)
+
+ expect(matrix.a).toBeCloseTo(0.8660254037844387)
+ expect(matrix.d).toBeCloseTo(0.8660254037844387)
+ expect(matrix.e).toBe(4)
+ expect(matrix.f).toBe(3)
+ })
+ it('performs a rotation on a given point with three arguments', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).rotate(30, 150, 100)
+
+ expect(matrix.a).toBeCloseTo(0.8660254037844387)
+ expect(matrix.d).toBeCloseTo(0.8660254037844387)
+ expect(matrix.e).toBeCloseTo(74.0961894323342)
+ expect(matrix.f).toBeCloseTo(-58.60254037844388)
+ })
+ })
+
+ describe('flip()', function() {
+ describe('with x given', function() {
+ it('performs a flip over the horizontal axis with one argument', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).flip('x')
+
+ expect(matrix.a).toBe(-1)
+ expect(matrix.d).toBe(1)
+ expect(matrix.e).toBe(4)
+ expect(matrix.f).toBe(3)
+ })
+ it('performs a flip over the horizontal axis over a given point with two arguments', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).flip('x', 150)
+
+ expect(matrix.a).toBe(-1)
+ expect(matrix.d).toBe(1)
+ expect(matrix.e).toBe(304)
+ expect(matrix.f).toBe(3)
+ })
+ })
+ describe('with y given', function() {
+ it('performs a flip over the vertical axis with one argument', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).flip('y')
+
+ expect(matrix.a).toBe(1)
+ expect(matrix.d).toBe(-1)
+ expect(matrix.e).toBe(4)
+ expect(matrix.f).toBe(3)
+ })
+ it('performs a flip over the vertical axis over a given point with two arguments', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).flip('y', 100)
+
+ expect(matrix.a).toBe(1)
+ expect(matrix.d).toBe(-1)
+ expect(matrix.e).toBe(4)
+ expect(matrix.f).toBe(203)
+ })
+ })
+ describe('with no axis given', function() {
+ it('performs a flip over the horizontal and vertical axis with no argument', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).flip()
+
+ expect(matrix.a).toBe(-1)
+ expect(matrix.d).toBe(-1)
+ expect(matrix.e).toBe(4)
+ expect(matrix.f).toBe(3)
+ })
+ it('performs a flip over the horizontal and vertical axis over a given point with one argument that represent both coordinates', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).flip(100)
+
+ expect(matrix.a).toBe(-1)
+ expect(matrix.d).toBe(-1)
+ expect(matrix.e).toBe(204)
+ expect(matrix.f).toBe(203)
+ })
+ it('performs a flip over the horizontal and vertical axis over a given point with two arguments', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).flip(50, 100)
+
+ expect(matrix.a).toBe(-1)
+ expect(matrix.d).toBe(-1)
+ expect(matrix.e).toBe(104)
+ expect(matrix.f).toBe(203)
+ })
+ })
+ })
+
+ describe('skew()', function() {
+ it('performs a uniformal skew with one value', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).skew(14)
+
+ expect(matrix.a).toBe(1)
+ expect(matrix.b).toBeCloseTo(0.24932800284318)
+ expect(matrix.c).toBeCloseTo(0.24932800284318)
+ expect(matrix.d).toBe(1)
+ expect(matrix.e).toBe(4)
+ expect(matrix.f).toBe(3)
+ })
+ it('performs a non-uniformal skew with two values', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).skew(8, 5)
+
+ expect(matrix.a).toBe(1)
+ expect(matrix.b).toBeCloseTo(0.087488663525924)
+ expect(matrix.c).toBeCloseTo(0.14054083470239)
+ expect(matrix.d).toBe(1)
+ expect(matrix.e).toBe(4)
+ expect(matrix.f).toBe(3)
+ })
+ it('performs a uniformal skew at a given center point with three values', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).skew(3, 150, 100)
+
+ expect(matrix.a).toBe(1)
+ expect(matrix.b).toBeCloseTo(0.052407779283041)
+ expect(matrix.c).toBeCloseTo(0.052407779283041)
+ expect(matrix.d).toBe(1)
+ expect(matrix.e).toBeCloseTo(-1.2407779283)
+ expect(matrix.f).toBeCloseTo(-4.8611668924562)
+ })
+ it('performs a non-uniformal skew at a given center point with four values', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).skew(9, 7, 150, 100)
+
+ expect(matrix.a).toBe(1)
+ expect(matrix.b).toBeCloseTo(0.1227845609029)
+ expect(matrix.c).toBeCloseTo(0.15838444032454)
+ expect(matrix.d).toBe(1)
+ expect(matrix.e).toBeCloseTo(-11.83844403245)
+ expect(matrix.f).toBeCloseTo(-15.417684135435)
+ })
+ it('can be chained', function(){
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).skew(9, 7).skew(20, 40)
+
+ expect(matrix.a).toBeCloseTo(1.1329003254605)
+ expect(matrix.b).toBeCloseTo(0.96188419208018)
+ expect(matrix.c).toBeCloseTo(0.52235467459074)
+ expect(matrix.d).toBeCloseTo(1.0446899253961)
+ expect(matrix.e).toBe(4)
+ expect(matrix.f).toBe(3)
+ })
+ })
+
+ describe('skewX', function(){
+ it('performs a skew along the x axis with one value', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).skewX(12)
+
+ expect(matrix.a).toBe(1)
+ expect(matrix.b).toBe(0)
+ expect(matrix.c).toBeCloseTo(0.21255656167002)
+ expect(matrix.d).toBe(1)
+ expect(matrix.e).toBe(4)
+ expect(matrix.f).toBe(3)
+ })
+
+ it('performs a skew along the x axis at a given center point with three values', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).skewX(5, 150, 100)
+
+ expect(matrix.a).toBe(1)
+ expect(matrix.b).toBe(0)
+ expect(matrix.c).toBeCloseTo(0.087488663525924)
+ expect(matrix.d).toBe(1)
+ expect(matrix.e).toBeCloseTo(-4.74886635259)
+ expect(matrix.f).toBe(3)
+ })
+ })
+
+ describe('skewY', function(){
+ it('performs a skew along the y axis with one value', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).skewY(12)
+
+ expect(matrix.a).toBe(1)
+ expect(matrix.b).toBeCloseTo(0.21255656167002)
+ expect(matrix.c).toBe(0)
+ expect(matrix.d).toBe(1)
+ expect(matrix.e).toBe(4)
+ expect(matrix.f).toBe(3)
+ })
+
+ it('performs a skew along the y axis at a given center point with three values', function() {
+ var matrix = new SVG.Matrix(1, 0, 0, 1, 4, 3).skewY(5, 150, 100)
+
+ expect(matrix.a).toBe(1)
+ expect(matrix.b).toBeCloseTo(0.087488663525924)
+ expect(matrix.c).toBe(0)
+ expect(matrix.d).toBe(1)
+ expect(matrix.e).toBe(4)
+ expect(matrix.f).toBeCloseTo(-10.123299528889)
+ })
+ })
+
+ describe('native()', function() {
+ it('returns the node reference', function() {
+ expect(new SVG.Matrix().native() instanceof window.SVGMatrix).toBeTruthy()
+ })
+ })
+
+})
diff --git a/node_modules/svg.js/spec/spec/memory.js b/node_modules/svg.js/spec/spec/memory.js
new file mode 100644
index 0000000..32773a0
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/memory.js
@@ -0,0 +1,58 @@
+describe('Memory', function () {
+ var rect, circle
+
+ beforeEach(function() {
+ rect = draw.rect(100,120)
+ circle = draw.circle(100)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ describe('remember()', function() {
+ it('accepts an object with values', function() {
+ rect.remember({ some: {cool:'and',nested:'stuff',foo:5} })
+ expect(rect.remember('some').foo).toBe(5)
+ })
+ it('accepts key / value arguments', function() {
+ rect.remember('fill', rect.attr('fill'))
+ rect.fill('#f09')
+ expect(rect.remember('fill')).toBe('#000000')
+ })
+ it('acts as a getter with one string argument', function() {
+ rect.remember('opacity', 0.85)
+ expect(rect.remember('opacity')).toBe(0.85)
+ })
+ it('saves values to individual objects', function() {
+ rect.remember('opacity', 0.85)
+ circle.remember('opacity', 0.5)
+ expect(rect.remember('opacity')).toBe(0.85)
+ expect(circle.remember('opacity')).toBe(0.5)
+ })
+ })
+
+ describe('forget()', function() {
+ it('deletes a given memory', function() {
+ rect.remember({ grass: 'is green', one: 1 })
+ rect.forget('grass')
+ expect(rect.remember('grass')).toBe(undefined)
+ expect(rect.remember('one')).toBe(1)
+ })
+ it('accepts multiple arguments as different memories', function() {
+ rect.remember({ grass: 'might be purple', two: 2, sea: true })
+ rect.forget('grass', 'sea')
+ expect(rect.remember('grass')).toBe(undefined)
+ expect(rect.remember('sea')).toBe(undefined)
+ expect(rect.remember('two')).toBe(2)
+ })
+ it('clears the whole memory without arguments', function() {
+ rect.remember({ grass: 'is never pink', three: 3, tree: true })
+ rect.forget()
+ expect(rect.remember('grass')).toBe(undefined)
+ expect(rect.remember('tree')).toBe(undefined)
+ expect(rect.remember('three')).toBe(undefined)
+ })
+ })
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/spec/nested.js b/node_modules/svg.js/spec/spec/nested.js
new file mode 100644
index 0000000..3113880
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/nested.js
@@ -0,0 +1,13 @@
+describe('Nested', function() {
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ describe('()', function() {
+ it('creates a nested svg of type SVG.Nested', function() {
+ expect(draw.nested() instanceof SVG.Nested).toBeTruthy()
+ })
+ })
+
+})
diff --git a/node_modules/svg.js/spec/spec/number.js b/node_modules/svg.js/spec/spec/number.js
new file mode 100644
index 0000000..58c14bd
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/number.js
@@ -0,0 +1,245 @@
+describe('Number', function() {
+ var number
+
+ beforeEach(function() {
+ number = new SVG.Number
+ })
+
+ describe('new', function() {
+ it('is zero', function() {
+ expect(number.value).toBe(0)
+ })
+ it('has a blank unit', function() {
+ expect(number.unit).toBe('')
+ })
+ it('accepts the unit as a second argument', function() {
+ number = new SVG.Number(30, '%')
+ expect(number.value).toBe(30)
+ expect(number.unit).toBe('%')
+ })
+ it('parses a pixel value', function() {
+ number = new SVG.Number('20px')
+ expect(number.value).toBe(20)
+ expect(number.unit).toBe('px')
+ })
+ it('parses a percent value', function() {
+ number = new SVG.Number('99%')
+ expect(number.value).toBe(0.99)
+ expect(number.unit).toBe('%')
+ })
+ it('parses a seconds value', function() {
+ number = new SVG.Number('2s')
+ expect(number.value).toBe(2000)
+ expect(number.unit).toBe('s')
+ })
+ it('parses a negative percent value', function() {
+ number = new SVG.Number('-89%')
+ expect(number.value).toBe(-0.89)
+ expect(number.unit).toBe('%')
+ })
+ it('falls back to 0 if given value is NaN', function() {
+ number = new SVG.Number(NaN)
+ expect(number.value).toBe(0)
+ })
+ it('falls back to maximum value if given number is positive infinite', function() {
+ number = new SVG.Number(1.7976931348623157E+10308)
+ expect(number.value).toBe(3.4e+38)
+ })
+ it('falls back to minimum value if given number is negative infinite', function() {
+ number = new SVG.Number(-1.7976931348623157E+10308)
+ expect(number.value).toBe(-3.4e+38)
+ })
+ })
+
+ describe('toString()', function() {
+ it('converts the number to a string', function() {
+ expect(number.toString()).toBe('0')
+ })
+ it('appends the unit', function() {
+ number.value = 1.21
+ number.unit = 'px'
+ expect(number.toString()).toBe('1.21px')
+ })
+ it('converts percent values properly', function() {
+ number.value = 1.36
+ number.unit = '%'
+ expect(number.toString()).toBe('136%')
+ })
+ it('converts second values properly', function() {
+ number.value = 2500
+ number.unit = 's'
+ expect(number.toString()).toBe('2.5s')
+ })
+ })
+
+ describe('valueOf()', function() {
+ it('returns a numeric value for default units', function() {
+ expect(typeof number.valueOf()).toBe('number')
+ number = new SVG.Number('12')
+ expect(typeof number.valueOf()).toBe('number')
+ number = new SVG.Number(13)
+ expect(typeof number.valueOf()).toBe('number')
+ })
+ it('returns a numeric value for pixel units', function() {
+ number = new SVG.Number('10px')
+ expect(typeof number.valueOf()).toBe('number')
+ })
+ it('returns a numeric value for percent units', function() {
+ number = new SVG.Number('20%')
+ expect(typeof number.valueOf()).toBe('number')
+ })
+ it('converts to a primitive when multiplying', function() {
+ number.value = 80
+ expect(number * 4).toBe(320)
+ })
+ })
+
+ describe('to()', function() {
+ beforeEach(function() {
+ number = number.plus(4)
+ })
+ it('returns a new instance', function() {
+ expect(number.to('em')).not.toBe(number)
+ expect(number.to('em') instanceof SVG.Number).toBeTruthy()
+ })
+ it('changes the unit value', function() {
+ number = number.to('%')
+ expect(number.unit).toBe('%')
+ })
+ it('changes the output value', function() {
+ var oldNumber = number.valueOf()
+ number = number.to('%')
+ expect(number.toString()).toBe('400%')
+ })
+ })
+
+ describe('plus()', function() {
+ it('returns a new instance', function() {
+ expect(number.plus(4.5)).not.toBe(number)
+ expect(number.plus(4.5) instanceof SVG.Number).toBeTruthy()
+ })
+ it('adds a given number', function() {
+ expect(number.plus(3.5).valueOf()).toBe(3.5)
+ })
+ it('adds a given percentage value', function() {
+ expect(number.plus('225%').valueOf()).toBe(2.25)
+ })
+ it('adds a given pixel value', function() {
+ expect(number.plus('83px').valueOf()).toBe(83)
+ })
+ it('use the unit of this number as the unit of the returned number by default', function (){
+ expect(new SVG.Number('12s').plus('3%').unit).toBe('s')
+ })
+ it('use the unit of the passed number as the unit of the returned number when this number as no unit', function() {
+ expect(number.plus('15%').unit).toBe('%')
+ })
+ })
+
+ describe('minus()', function() {
+ it('subtracts a given number', function() {
+ expect(number.minus(3.7).valueOf()).toBe(-3.7)
+ })
+ it('subtracts a given percentage value', function() {
+ expect(number.minus('223%').valueOf()).toBe(-2.23)
+ })
+ it('subtracts a given pixel value', function() {
+ expect(number.minus('85px').valueOf()).toBe(-85)
+ })
+ it('use the unit of this number as the unit of the returned number by default', function (){
+ expect(new SVG.Number('12s').minus('3%').unit).toBe('s')
+ })
+ it('use the unit of the passed number as the unit of the returned number when this number as no unit', function() {
+ expect(number.minus('15%').unit).toBe('%')
+ })
+ })
+
+ describe('times()', function() {
+ beforeEach(function() {
+ number = number.plus(4)
+ })
+ it('multiplies with a given number', function() {
+ expect(number.times(3).valueOf()).toBe(12)
+ })
+ it('multiplies with a given percentage value', function() {
+ expect(number.times('110%').valueOf()).toBe(4.4)
+ })
+ it('multiplies with a given pixel value', function() {
+ expect(number.times('85px').valueOf()).toBe(340)
+ })
+ it('use the unit of this number as the unit of the returned number by default', function (){
+ expect(new SVG.Number('12s').times('3%').unit).toBe('s')
+ })
+ it('use the unit of the passed number as the unit of the returned number when this number as no unit', function() {
+ expect(number.times('15%').unit).toBe('%')
+ })
+ })
+
+ describe('divide()', function() {
+ beforeEach(function() {
+ number = number.plus(90)
+ })
+ it('divides by a given number', function() {
+ expect(number.divide(3).valueOf()).toBe(30)
+ })
+ it('divides by a given percentage value', function() {
+ expect(number.divide('3000%').valueOf()).toBe(3)
+ })
+ it('divides by a given pixel value', function() {
+ expect(number.divide('45px').valueOf()).toBe(2)
+ })
+ it('use the unit of this number as the unit of the returned number by default', function (){
+ expect(new SVG.Number('12s').divide('3%').unit).toBe('s')
+ })
+ it('use the unit of the passed number as the unit of the returned number when this number as no unit', function() {
+ expect(number.divide('15%').unit).toBe('%')
+ })
+ })
+
+ describe('morph()', function() {
+ it('returns itself', function() {
+ expect(number.morph(new SVG.Number)).toBe(number)
+ })
+ it('prepares the color for morphing', function() {
+ var destination = new SVG.Number
+ number.morph(destination)
+ expect(number.destination).toEqual(destination)
+ })
+ it('if the passed object as a relative attribute set to true, destination is relative to the current value', function() {
+ var n1 = new SVG.Number(3)
+ , n2 = new SVG.Number(7)
+
+ n2.relative = true
+ n1.morph(n2)
+ expect(n1.destination.value).toBe(n1.value + n2.value)
+ })
+ })
+
+ describe('at()', function() {
+ it('returns a new instance', function() {
+ var destination = new SVG.Number(200)
+ var morphed = number.morph(destination).at(0.4)
+ expect(morphed).not.toBe(number)
+ expect(morphed).not.toBe(destination)
+ })
+ it('morphes number to a given position', function() {
+ var destination = new SVG.Number(200)
+ var morphed = number.morph(destination).at(0.4)
+ expect(morphed.valueOf()).toBe(80)
+ })
+ it('morphes number to a given percentage position', function() {
+ var destination = new SVG.Number('100%')
+ var morphed = number.morph(destination).at(0.72)
+ expect(morphed.toString()).toBe('72%')
+ })
+ it('use the unit of the destination number as the unit of the returned number by default', function() {
+ expect(new SVG.Number('100s').morph('50%').at(0.5).unit).toBe('%')
+ })
+ it('use the unit of this number as the unit of the returned number when the destination number as no unit', function() {
+ expect(expect(new SVG.Number('100s').morph(50).at(0.5).unit).toBe('s'))
+ })
+ it('returns itself when no destination specified', function() {
+ expect(number.at(0.5)).toBe(number)
+ })
+ })
+
+})
diff --git a/node_modules/svg.js/spec/spec/path.js b/node_modules/svg.js/spec/spec/path.js
new file mode 100644
index 0000000..f5b99ca
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/path.js
@@ -0,0 +1,252 @@
+describe('Path', function() {
+ var path
+
+ beforeEach(function() {
+ path = draw.path(svgPath)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ describe('()', function() {
+ it('falls back to a single point without an argument', function() {
+ path = draw.path()
+ expect(path.attr('d')).toBe('M0 0 ')
+ })
+ })
+
+ describe('array()', function() {
+ it('returns an instance of SVG.PathArray', function() {
+ expect(path.array() instanceof SVG.PathArray).toBeTruthy()
+ })
+ it('returns the value stored in the private variable _array', function() {
+ expect(path.array()).toBe(path._array)
+ })
+ })
+
+ describe('x()', function() {
+ it('returns the value of x without an argument', function() {
+ expect(path.x()).toBe(0)
+ })
+ it('sets the value of x with the first argument', function() {
+ path.x(123)
+ var box = path.bbox()
+ expect(box.x).toBe(123)
+ })
+ })
+
+ describe('y()', function() {
+ it('returns the value of y without an argument', function() {
+ expect(path.y()).toBe(0)
+ })
+ it('sets the value of y with the first argument', function() {
+ path.y(345)
+ var box = path.bbox()
+ expect(box.y).toBe(345)
+ })
+ })
+
+ describe('cx()', function() {
+ it('returns the value of cx without an argument', function() {
+ expect(path.cx()).toBe(50)
+ })
+ it('sets the value of cx with the first argument', function() {
+ path.cx(123)
+ var box = path.bbox()
+ expect(box.cx).toBe(123)
+ })
+ })
+
+ describe('cy()', function() {
+ it('returns the value of cy without an argument', function() {
+ expect(path.cy()).toBe(50)
+ })
+ it('sets the value of cy with the first argument', function() {
+ path.cy(345)
+ var box = path.bbox()
+ expect(box.cy).toBe(345)
+ })
+ })
+
+ describe('move()', function() {
+ it('sets the x and y position', function() {
+ path.move(123,456)
+ var box = path.bbox()
+ expect(box.x).toBe(123)
+ expect(box.y).toBe(456)
+ })
+ it('sets the x and y position when scaled to half its size', function() {
+ path.scale(0.5, 0, 0).move(123,456)
+ var box = path.bbox()
+ expect(box.x).toBe(123)
+ expect(box.y).toBe(456)
+ })
+ })
+
+ describe('dx()', function() {
+ it('moves the x positon of the element relative to the current position', function() {
+ path.move(50,60)
+ path.dx(100)
+ var box = path.bbox()
+ expect(box.x).toBe(150)
+ })
+ })
+
+ describe('dy()', function() {
+ it('moves the y positon of the element relative to the current position', function() {
+ path.move(50, 60)
+ path.dy(120)
+ var box = path.bbox()
+ expect(box.y).toBe(180)
+ })
+ })
+
+ describe('dmove()', function() {
+ it('moves the x and y positon of the element relative to the current position', function() {
+ path.move(50,60)
+ path.dmove(80, 25)
+ var box = path.bbox()
+ expect(box.x).toBe(130)
+ expect(box.y).toBe(85)
+ })
+ })
+
+ describe('center()', function() {
+ it('sets the cx and cy position', function() {
+ path.center(321,567)
+ var box = path.bbox()
+ expect(box.x).toBe(271)
+ expect(box.y).toBe(517)
+ })
+ })
+
+ describe('width()', function() {
+ it('sets the width of the element', function() {
+ path.width(234)
+ var box = path.bbox()
+ expect(box.width).toBeCloseTo(234)
+ })
+ it('gets the width of the element without an argument', function() {
+ path.width(456)
+ expect(path.width()).toBeCloseTo(456)
+ })
+ })
+
+ describe('height()', function() {
+ it('sets the height of the element', function() {
+ path.height(654)
+ var box = path.bbox()
+ expect(box.height).toBeCloseTo(654)
+ })
+ it('gets the height of the element without an argument', function() {
+ path.height(321)
+ expect(path.height()).toBeCloseTo(321)
+ })
+ })
+
+ describe('size()', function() {
+ it('defines the width and height of the element', function() {
+ path.size(987,654)
+ var box = path.bbox()
+ expect(box.width).toBeCloseTo(987)
+ expect(box.height).toBeCloseTo(654)
+ })
+ it('defines the width and height proportionally with only the width value given', function() {
+ var box = path.bbox()
+ path.size(500)
+ expect(path.width()).toBeCloseTo(500)
+ expect(path.width() / path.height()).toBe(box.width / box.height)
+ })
+ it('defines the width and height proportionally with only the height value given', function() {
+ var box = path.bbox()
+ path.size(null, 525)
+ expect(path.height()).toBe(525)
+ expect(path.width() / path.height()).toBeCloseTo(box.width / box.height)
+ })
+ })
+
+ describe('scale()', function() {
+ it('should scale the element universally with one argument', function() {
+ var box1 = path.rbox()
+ , box2 = path.scale(2).rbox()
+
+ expect(box1.width * 2).toBe(box2.width)
+ expect(box1.height * 2).toBe(box2.height)
+ })
+ it('should scale the element over individual x and y axes with two arguments', function() {
+ var box1 = path.rbox()
+ , box2 = path.scale(2, 3.5).rbox()
+
+ expect(box1.width * 2).toBe(box2.width)
+ expect(box1.height * 3.5).toBe(box2.height)
+ })
+ })
+
+ describe('translate()', function() {
+ it('sets the translation of an element', function() {
+ path.transform({ x: 12, y: 12 })
+ expect(path.node.getAttribute('transform')).toBe('matrix(1,0,0,1,12,12)')
+ })
+ })
+
+ describe('plot()', function() {
+ it('changes the d attribute of the underlying path node when a string is passed', function() {
+ var pathString = 'm 3,2 c 0,0 -0,13 8,14 L 5,4'
+ , pathArray = new SVG.PathArray(pathString)
+
+ expect(path.plot(pathString)).toBe(path)
+ expect(path.attr('d')).toBe(pathString)
+ })
+ it('clears the array cache when a value is passed', function() {
+ path = draw.path([ ['M', 50, 60], ['A', 60, 60, 0, 0, 0, 50, -60], ['z'] ])
+ expect(path._array instanceof SVG.PathArray).toBeTruthy()
+ path.plot('m 3,2 c 0,0 -0,13 8,14 L 5,4')
+ expect(path._array).toBeUndefined()
+ })
+ it('applies a given path string value as is', function() {
+ var pathString = 'm 3,2 c 0,0 -0,13 8,14 L 5,4'
+
+ path = draw.path(pathString)
+ expect(path.attr('d')).toBe(pathString)
+ })
+ it('does not parse and cache a given string value to SVG.PathArray', function() {
+ path = draw.path('m 3,2 c 0,0 -0,13 8,14 L 5,4')
+ expect(path._array).toBeUndefined()
+ })
+ it('caches a given array value', function() {
+ path = draw.path([ ['M', 50, 60], ['A', 60, 60, 0, 0, 0, 50, -60], ['H', 100], ['L', 20, 30], ['Z'] ])
+ expect(path._array instanceof SVG.PathArray).toBeTruthy()
+ })
+ it('returns the path array when no arguments are passed', function () {
+ expect(path.plot()).toBe(path.array())
+ })
+ })
+
+ describe('clear()', function() {
+ it('clears the cached SVG.PathArray instance', function() {
+ path = draw.path(svgPath)
+ path.clear()
+ expect(path._array).toBeUndefined()
+ })
+ })
+
+ describe('toString()', function() {
+ it('renders path array correctly to string', function() {
+ path = path.plot(['M', 50, 60, 'A', 60, 60, 0, 0, 0, 50, -60, 'H', 100, 'V', 100, 'L', 20, 30, 'C', 10, 20, 30, 40, 50, 60 ])
+ expect(path.node.getAttribute('d')).toBe('M50 60A60 60 0 0 0 50 -60H100V100L20 30C10 20 30 40 50 60 ')
+ })
+ })
+
+ describe('length()', function() {
+ it('gets the total length of the path', function() {
+ expect(path.length()).toBe(path.node.getTotalLength())
+ })
+ })
+
+ describe('pointAt()', function() {
+ it('gets a point at given length', function() {
+ expect(path.pointAt(100)).toEqual(path.node.getPointAtLength(100))
+ })
+ })
+})
diff --git a/node_modules/svg.js/spec/spec/pattern.js b/node_modules/svg.js/spec/spec/pattern.js
new file mode 100644
index 0000000..e12d11e
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/pattern.js
@@ -0,0 +1,69 @@
+describe('Pattern', function() {
+ var rect, pattern
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ pattern = draw.pattern(20, 30, function(add) {
+ add.rect(10,10).move(10,10)
+ add.circle(30)
+ })
+ })
+
+ afterEach(function() {
+ rect.remove()
+ pattern.remove()
+ })
+
+ it('is an instance of SVG.Pattern', function() {
+ expect(pattern instanceof SVG.Pattern).toBe(true)
+ })
+
+ it('allows creation of a new gradient without block', function() {
+ pattern = draw.pattern(10,30)
+ expect(pattern.children().length).toBe(0)
+ })
+
+ describe('fill()', function() {
+ it('returns the id of the pattern wrapped in url()', function() {
+ expect(pattern.fill()).toBe('url(#' + pattern.attr('id') + ')')
+ })
+ })
+
+ describe('attr()', function() {
+ it('will catch transform attribues and convert them to patternTransform', function() {
+ expect(pattern.translate(100,100).attr('patternTransform')).toBe('matrix(1,0,0,1,100,100)')
+ })
+ })
+
+ describe('toString()', function() {
+ it('returns the id of the pattern wrapped in url()', function() {
+ expect(pattern + '').toBe('url(#' + pattern.attr('id') + ')')
+ })
+ it('is called when instance is passed as an attribute value', function() {
+ rect.attr('fill', pattern)
+ expect(rect.attr('fill')).toBe('url(#' + pattern.attr('id') + ')')
+ })
+ it('is called when instance is passed in a fill() method', function() {
+ rect.fill(pattern)
+ expect(rect.attr('fill')).toBe('url(#' + pattern.attr('id') + ')')
+ })
+ })
+
+ describe('update()', function() {
+
+ it('removes all existing children first', function() {
+ pattern = draw.pattern(30, 30, function(add) {
+ add.rect(10,10).move(10,10)
+ add.circle(30)
+ })
+ expect(pattern.children().length).toBe(2)
+ pattern.update(function(add) {
+ add.rect(10,10).move(10,10)
+ add.circle(30)
+ })
+ expect(pattern.children().length).toBe(2)
+ })
+
+ })
+
+})
diff --git a/node_modules/svg.js/spec/spec/point.js b/node_modules/svg.js/spec/spec/point.js
new file mode 100644
index 0000000..8eacb84
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/point.js
@@ -0,0 +1,140 @@
+describe('Point', function() {
+ var point
+
+ describe('initialization', function() {
+
+ describe('without a source', function() {
+
+ beforeEach(function() {
+ point = new SVG.Point
+ })
+
+ it('creates a new point with default values', function() {
+ expect(point.x).toBe(0)
+ expect(point.y).toBe(0)
+ })
+
+ })
+
+ describe('with x and y given', function() {
+ it('creates a point with given values', function() {
+ var point = new SVG.Point(2,4)
+
+ expect(point.x).toBe(2)
+ expect(point.y).toBe(4)
+ })
+ })
+
+ describe('with only x given', function() {
+ it('creates a point using the given value for both x and y', function() {
+ var point = new SVG.Point(7)
+
+ expect(point.x).toBe(7)
+ expect(point.y).toBe(7)
+ })
+ })
+
+ describe('with array given', function() {
+ it('creates a point from array', function() {
+ var point = new SVG.Point([2,4])
+
+ expect(point.x).toBe(2)
+ expect(point.y).toBe(4)
+ })
+ })
+
+ describe('with object given', function() {
+ it('creates a point from object', function() {
+ var point = new SVG.Point({x:2,y:4})
+
+ expect(point.x).toBe(2)
+ expect(point.y).toBe(4)
+ })
+ })
+
+ describe('with SVG.Point given', function() {
+ it('creates a point from SVG.Point', function() {
+ var point = new SVG.Point(new SVG.Point(2,4))
+
+ expect(point.x).toBe(2)
+ expect(point.y).toBe(4)
+ })
+ })
+
+ describe('with native SVGPoint given', function() {
+ it('creates a point from native SVGPoint', function() {
+ var point = new SVG.Point(new SVG.Point(2,4).native())
+
+ expect(point.x).toBe(2)
+ expect(point.y).toBe(4)
+ })
+ })
+
+ })
+
+ describe('clone()', function() {
+ it('returns cloned point', function() {
+ var point1 = new SVG.Point(1,1)
+ , point2 = point1.clone()
+
+ expect(point1).toEqual(point2)
+ expect(point1).not.toBe(point2)
+ })
+ })
+
+ describe('morph()', function() {
+ it('stores a given point for morphing', function() {
+ var point1 = new SVG.Point(1,1)
+ , point2 = new SVG.Point(2,2)
+
+ point1.morph(point2)
+
+ expect(point1.destination).toEqual(point2)
+ })
+ it('stores a clone, not the given matrix itself', function() {
+ var point1 = new SVG.Point(1,1)
+ , point2 = new SVG.Point(2,2)
+
+ point1.morph(point2)
+
+ expect(point1.destination).not.toBe(point2)
+ })
+ it('allow passing the point by directly passing its coordinates', function() {
+ var point1 = new SVG.Point(1,1)
+ , point2 = new SVG.Point(2,2)
+
+ point1.morph(point2.x, point2.y)
+
+ expect(point1.destination).toEqual(point2)
+ })
+ })
+
+ describe('at()', function() {
+ it('returns a morphed point at a given position', function() {
+ var point1 = new SVG.Point(1,1)
+ , point2 = new SVG.Point(2,2)
+ , point3 = point1.morph(point2).at(0.5)
+
+ expect(point3).toEqual(new SVG.Point(1.5, 1.5))
+ })
+ it('returns itself when no destination specified', function() {
+ var point = new SVG.Point(1,1)
+ expect(point.at(0.4)).toBe(point)
+ })
+ })
+
+ describe('transform()', function() {
+ it('returns a point transformed with given matrix', function() {
+ var point = new SVG.Point(1,5)
+ , matrix = new SVG.Matrix(0,0,1,0,0,1)
+
+ expect(point.transform(matrix)).toEqual(new SVG.Point(5,1))
+ })
+ })
+
+ describe('native()', function() {
+ it('returns native SVGPoint', function() {
+ expect(new SVG.Point().native() instanceof window.SVGPoint).toBeTruthy()
+ })
+ })
+})
diff --git a/node_modules/svg.js/spec/spec/polygon.js b/node_modules/svg.js/spec/spec/polygon.js
new file mode 100644
index 0000000..789c3b9
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/polygon.js
@@ -0,0 +1,228 @@
+describe('Polygon', function() {
+ var polygon
+
+ beforeEach(function() {
+ polygon = draw.polygon('0,0 100,0 100,100 0,100')
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ describe('()', function(){
+ it('falls back to a single point without an argument', function() {
+ polygon = draw.polygon()
+ expect(polygon.attr('points')).toBe('0,0')
+ })
+ })
+
+
+ describe('array()', function() {
+ it('returns an instance of SVG.PointArray', function() {
+ expect(polygon.array() instanceof SVG.PointArray).toBeTruthy()
+ })
+ it('returns the value stored in the private variable _array', function() {
+ expect(polygon.array()).toBe(polygon._array)
+ })
+ })
+
+ describe('x()', function() {
+ it('returns the value of x without an argument', function() {
+ expect(polygon.x()).toBe(0)
+ })
+ it('sets the value of x with the first argument', function() {
+ polygon.x(123)
+ var box = polygon.bbox()
+ expect(box.x).toBe(123)
+ })
+ })
+
+ describe('y()', function() {
+ it('returns the value of y without an argument', function() {
+ expect(polygon.y()).toBe(0)
+ })
+ it('sets the value of y with the first argument', function() {
+ polygon.y(345)
+ var box = polygon.bbox()
+ expect(box.y).toBe(345)
+ })
+ })
+
+ describe('cx()', function() {
+ it('returns the value of cx without an argument', function() {
+ expect(polygon.cx()).toBe(50)
+ })
+ it('sets the value of cx with the first argument', function() {
+ polygon.cx(123)
+ var box = polygon.bbox()
+ expect(box.cx).toBe(123)
+ })
+ })
+
+ describe('cy()', function() {
+ it('returns the value of cy without an argument', function() {
+ expect(polygon.cy()).toBe(50)
+ })
+ it('sets the value of cy with the first argument', function() {
+ polygon.cy(345)
+ var box = polygon.bbox()
+ expect(box.cy).toBe(345)
+ })
+ })
+
+ describe('move()', function() {
+ it('sets the x and y position', function() {
+ polygon.move(123,456)
+ var box = polygon.bbox()
+ expect(box.x).toBe(123)
+ expect(box.y).toBe(456)
+ })
+ })
+
+ describe('dx()', function() {
+ it('moves the x positon of the element relative to the current position', function() {
+ polygon.move(50,60)
+ polygon.dx(100)
+ var box = polygon.bbox()
+ expect(box.x).toBe(150)
+ })
+ })
+
+ describe('dy()', function() {
+ it('moves the y positon of the element relative to the current position', function() {
+ polygon.move(50, 60)
+ polygon.dy(120)
+ var box = polygon.bbox()
+ expect(box.y).toBe(180)
+ })
+ })
+
+ describe('dmove()', function() {
+ it('moves the x and y positon of the element relative to the current position', function() {
+ polygon.move(50,60)
+ polygon.dmove(80, 25)
+ var box = polygon.bbox()
+ expect(box.x).toBe(130)
+ expect(box.y).toBe(85)
+ })
+ })
+
+ describe('center()', function() {
+ it('sets the cx and cy position', function() {
+ polygon.center(321,567)
+ var box = polygon.bbox()
+ expect(box.x).toBe(271)
+ expect(box.y).toBe(517)
+ })
+ })
+
+ describe('width()', function() {
+ it('sets the width and height of the element', function() {
+ polygon.width(987)
+ var box = polygon.bbox()
+ expect(box.width).toBeCloseTo(987)
+ })
+ it('gets the width and height of the element without an argument', function() {
+ polygon.width(789)
+ expect(polygon.width()).toBeCloseTo(789)
+ })
+ })
+
+ describe('height()', function() {
+ it('sets the height and height of the element', function() {
+ polygon.height(987)
+ var box = polygon.bbox()
+ expect(box.height).toBeCloseTo(987)
+ })
+ it('gets the height and height of the element without an argument', function() {
+ polygon.height(789)
+ expect(polygon.height()).toBeCloseTo(789)
+ })
+ })
+
+ describe('size()', function() {
+ it('should define the width and height of the element', function() {
+ polygon.size(987,654)
+ var box = polygon.bbox()
+ expect(box.width).toBeCloseTo(987)
+ expect(box.height).toBeCloseTo(654)
+ })
+ it('defines the width and height proportionally with only the width value given', function() {
+ var box = polygon.bbox()
+ polygon.size(500)
+ expect(polygon.width()).toBe(500)
+ expect(polygon.width() / polygon.height()).toBe(box.width / box.height)
+ })
+ it('defines the width and height proportionally with only the height value given', function() {
+ var box = polygon.bbox()
+ polygon.size(null, 525)
+ expect(polygon.height()).toBe(525)
+ expect(polygon.width() / polygon.height()).toBe(box.width / box.height)
+ })
+ })
+
+ describe('scale()', function() {
+ it('should scale the element universally with one argument', function() {
+ var box1 = polygon.rbox()
+ , box2 = polygon.scale(2).rbox()
+
+ expect(box2.width).toBe(box1.width * 2)
+ expect(box2.height).toBe(box1.height * 2)
+ })
+ it('should scale the element over individual x and y axes with two arguments', function() {
+ var box1 = polygon.rbox()
+ , box2 = polygon.scale(2, 3.5).rbox()
+
+ expect(box2.width).toBe(box1.width * 2)
+ expect(box2.height).toBe(box1.height * 3.5)
+ })
+ })
+
+ describe('translate()', function() {
+ it('sets the translation of an element', function() {
+ polygon.transform({ x: 12, y: 12 })
+ expect(polygon.node.getAttribute('transform')).toBe('matrix(1,0,0,1,12,12)')
+ })
+ })
+
+ describe('plot()', function() {
+ it('changes the points attribute of the underlying polygon node when a string is passed', function() {
+ var pointString = '100,50 75,20 200,100'
+ , pointArray = new SVG.PointArray(pointString)
+
+ expect(polygon.plot(pointString)).toBe(polygon)
+ expect(polygon.attr('points')).toBe(pointArray.toString())
+ })
+ it('returns the point array when no arguments are passed', function () {
+ expect(polygon.plot()).toBe(polygon.array())
+ })
+ it('clears the array cache when a value is passed', function() {
+ polygon = draw.polygon([100,50,75,20,200,100])
+ expect(polygon._array instanceof SVG.PointArray).toBeTruthy()
+ polygon.plot('100,50 75,20 200,100')
+ expect(polygon._array).toBeUndefined()
+ })
+ it('applies a given polygon string value as is', function() {
+ var polyString = '100,50,75,20,200,100'
+
+ polygon = draw.polygon(polyString)
+ expect(polygon.attr('points')).toBe(polyString)
+ })
+ it('does not parse and cache a given string value to SVG.PointArray', function() {
+ polygon = draw.polygon('100,50 75,20 200,100')
+ expect(polygon._array).toBeUndefined()
+ })
+ it('caches a given array value', function() {
+ polygon = draw.polygon([100,50,75,20,200,100])
+ expect(polygon._array instanceof SVG.PointArray).toBeTruthy()
+ })
+ })
+
+ describe('clear()', function() {
+ it('clears the cached SVG.PointArray instance', function() {
+ polygon = draw.polygon([100,50,75,20,200,100])
+ polygon.clear()
+ expect(polygon._array).toBeUndefined()
+ })
+ })
+})
diff --git a/node_modules/svg.js/spec/spec/polyline.js b/node_modules/svg.js/spec/spec/polyline.js
new file mode 100644
index 0000000..ca516bd
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/polyline.js
@@ -0,0 +1,228 @@
+describe('Polyline', function() {
+ var polyline
+
+ beforeEach(function() {
+ polyline = draw.polyline('0,0 100,0 100,100 0,100')
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ describe('()', function(){
+ it('falls back to a single point without an argument', function() {
+ polyline = draw.polyline()
+ expect(polyline.attr('points')).toBe('0,0')
+ })
+ })
+
+
+ describe('array()', function() {
+ it('returns an instance of SVG.PointArray', function() {
+ expect(polyline.array() instanceof SVG.PointArray).toBeTruthy()
+ })
+ it('returns the value stored in the private variable _array', function() {
+ expect(polyline.array()).toBe(polyline._array)
+ })
+ })
+
+ describe('x()', function() {
+ it('returns the value of x without an argument', function() {
+ expect(polyline.x()).toBe(0)
+ })
+ it('sets the value of x with the first argument', function() {
+ polyline.x(123)
+ var box = polyline.bbox()
+ expect(box.x).toBe(123)
+ })
+ })
+
+ describe('y()', function() {
+ it('returns the value of y without an argument', function() {
+ expect(polyline.y()).toBe(0)
+ })
+ it('sets the value of y with the first argument', function() {
+ polyline.y(345)
+ var box = polyline.bbox()
+ expect(box.y).toBe(345)
+ })
+ })
+
+ describe('cx()', function() {
+ it('returns the value of cx without an argument', function() {
+ expect(polyline.cx()).toBe(50)
+ })
+ it('sets the value of cx with the first argument', function() {
+ polyline.cx(123)
+ var box = polyline.bbox()
+ expect(box.cx).toBe(123)
+ })
+ })
+
+ describe('cy()', function() {
+ it('returns the value of cy without an argument', function() {
+ expect(polyline.cy()).toBe(50)
+ })
+ it('sets the value of cy with the first argument', function() {
+ polyline.cy(345)
+ var box = polyline.bbox()
+ expect(box.cy).toBe(345)
+ })
+ })
+
+ describe('move()', function() {
+ it('sets the x and y position', function() {
+ polyline.move(123,456)
+ var box = polyline.bbox()
+ expect(box.x).toBe(123)
+ expect(box.y).toBe(456)
+ })
+ })
+
+ describe('dx()', function() {
+ it('moves the x positon of the element relative to the current position', function() {
+ polyline.move(50,60)
+ polyline.dx(100)
+ var box = polyline.bbox()
+ expect(box.x).toBe(150)
+ })
+ })
+
+ describe('dy()', function() {
+ it('moves the y positon of the element relative to the current position', function() {
+ polyline.move(50, 60)
+ polyline.dy(120)
+ var box = polyline.bbox()
+ expect(box.y).toBe(180)
+ })
+ })
+
+ describe('dmove()', function() {
+ it('moves the x and y positon of the element relative to the current position', function() {
+ polyline.move(50,60)
+ polyline.dmove(80, 25)
+ var box = polyline.bbox()
+ expect(box.x).toBe(130)
+ expect(box.y).toBe(85)
+ })
+ })
+
+ describe('center()', function() {
+ it('sets the cx and cy position', function() {
+ polyline.center(321,567)
+ var box = polyline.bbox()
+ expect(box.x).toBe(271)
+ expect(box.y).toBe(517)
+ })
+ })
+
+ describe('width()', function() {
+ it('sets the width and height of the element', function() {
+ polyline.width(987)
+ var box = polyline.bbox()
+ expect(box.width).toBeCloseTo(987, 1)
+ })
+ it('gets the width and height of the element without an argument', function() {
+ polyline.width(789)
+ expect(polyline.width()).toBeCloseTo(789)
+ })
+ })
+
+ describe('height()', function() {
+ it('sets the height and height of the element', function() {
+ polyline.height(987)
+ var box = polyline.bbox()
+ expect(box.height).toBeCloseTo(987)
+ })
+ it('gets the height and height of the element without an argument', function() {
+ polyline.height(789)
+ expect(polyline.height()).toBeCloseTo(789)
+ })
+ })
+
+ describe('size()', function() {
+ it('should define the width and height of the element', function() {
+ polyline.size(987,654)
+ var box = polyline.bbox()
+ expect(box.width).toBeCloseTo(987)
+ expect(box.height).toBeCloseTo(654)
+ })
+ it('defines the width and height proportionally with only the width value given', function() {
+ var box = polyline.bbox()
+ polyline.size(500)
+ expect(polyline.width()).toBe(500)
+ expect(polyline.width() / polyline.height()).toBe(box.width / box.height)
+ })
+ it('defines the width and height proportionally with only the height value given', function() {
+ var box = polyline.bbox()
+ polyline.size(null, 525)
+ expect(polyline.height()).toBe(525)
+ expect(polyline.width() / polyline.height()).toBe(box.width / box.height)
+ })
+ })
+
+ describe('scale()', function() {
+ it('should scale the element universally with one argument', function() {
+ var box1 = polyline.rbox()
+ , box2 = polyline.scale(2).rbox()
+
+ expect(box2.width).toBe(box1.width * 2)
+ expect(box2.height).toBe(box1.height * 2)
+ })
+ it('should scale the element over individual x and y axes with two arguments', function() {
+ var box1 = polyline.rbox()
+ , box2 = polyline.scale(2, 3.5).rbox()
+
+ expect(box2.width).toBe(box1.width * 2)
+ expect(box2.height).toBe(box1.height * 3.5)
+ })
+ })
+
+ describe('translate()', function() {
+ it('sets the translation of an element', function() {
+ polyline.transform({ x: 12, y: 12 })
+ expect(polyline.node.getAttribute('transform')).toBe('matrix(1,0,0,1,12,12)')
+ })
+ })
+
+ describe('plot()', function() {
+ it('change the points attribute of the underlying polyline node when a string is passed', function() {
+ var pointString = '100,50 75,20 200,100'
+ , pointArray = new SVG.PointArray(pointString)
+
+ expect(polyline.plot(pointString)).toBe(polyline)
+ expect(polyline.attr('points')).toBe(pointArray.toString())
+ })
+ it('return the point array when no arguments are passed', function () {
+ expect(polyline.plot()).toBe(polyline.array())
+ })
+ it('clears the array cache when a value is passed', function() {
+ polyline = draw.polyline([100,50,75,20,200,100])
+ expect(polyline._array instanceof SVG.PointArray).toBeTruthy()
+ polyline.plot('100,50 75,20 200,100')
+ expect(polyline._array).toBeUndefined()
+ })
+ it('applies a given polyline string value as is', function() {
+ var polyString = '100,50,75,20,200,100'
+
+ polyline = draw.polyline(polyString)
+ expect(polyline.attr('points')).toBe(polyString)
+ })
+ it('does not parse and cache a given string value to SVG.PointArray', function() {
+ polyline = draw.polyline('100,50 75,20 200,100')
+ expect(polyline._array).toBeUndefined()
+ })
+ it('caches a given array value', function() {
+ polyline = draw.polyline([100,50,75,20,200,100])
+ expect(polyline._array instanceof SVG.PointArray).toBeTruthy()
+ })
+ })
+
+ describe('clear()', function() {
+ it('clears the cached SVG.PointArray instance', function() {
+ polyline = draw.polyline([100,50,75,20,200,100])
+ polyline.clear()
+ expect(polyline._array).toBeUndefined()
+ })
+ })
+})
diff --git a/node_modules/svg.js/spec/spec/rect.js b/node_modules/svg.js/spec/spec/rect.js
new file mode 100644
index 0000000..5ce65c0
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/rect.js
@@ -0,0 +1,179 @@
+describe('Rect', function() {
+ var rect
+
+ beforeEach(function() {
+ rect = draw.rect(220,100)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ describe('x()', function() {
+ it('should return the value of x without an argument', function() {
+ expect(rect.x()).toBe(0)
+ })
+ it('should set the value of x with the first argument', function() {
+ rect.x(123)
+ expect(rect.node.getAttribute('x')).toBe('123')
+ })
+ })
+
+ describe('y()', function() {
+ it('should return the value of y without an argument', function() {
+ expect(rect.y()).toBe(0)
+ })
+ it('should set the value of y with the first argument', function() {
+ rect.y(345)
+ expect(rect.node.getAttribute('y')).toBe('345')
+ })
+ })
+
+ describe('cx()', function() {
+ it('should return the value of cx without an argument', function() {
+ expect(rect.cx()).toBe(110)
+ })
+ it('should set the value of cx with the first argument', function() {
+ rect.cx(123)
+ var box = rect.bbox()
+ expect(box.cx).toBe(123)
+ })
+ })
+
+ describe('cy()', function() {
+ it('should return the value of cy without an argument', function() {
+ expect(rect.cy()).toBe(50)
+ })
+ it('should set the value of cy with the first argument', function() {
+ rect.cy(345)
+ var box = rect.bbox()
+ expect(box.cy).toBe(345)
+ })
+ })
+
+ describe('radius()', function() {
+ it('should set the rx and ry', function() {
+ rect.radius(10,20)
+ expect(rect.node.getAttribute('rx')).toBe('10')
+ expect(rect.node.getAttribute('ry')).toBe('20')
+ })
+ it('should set the rx and ry if only rx given', function() {
+ rect.radius(30)
+ expect(rect.node.getAttribute('rx')).toBe('30')
+ expect(rect.node.getAttribute('ry')).toBe('30')
+ })
+ })
+
+ describe('move()', function() {
+ it('should set the x and y position', function() {
+ rect.move(123,456)
+ expect(rect.node.getAttribute('x')).toBe('123')
+ expect(rect.node.getAttribute('y')).toBe('456')
+ })
+ })
+
+ describe('dx()', function() {
+ it('moves the x positon of the element relative to the current position', function() {
+ rect.move(50,60)
+ rect.dx(100)
+ expect(rect.node.getAttribute('x')).toBe('150')
+ })
+ })
+
+ describe('dy()', function() {
+ it('moves the y positon of the element relative to the current position', function() {
+ rect.move(50,60)
+ rect.dy(120)
+ expect(rect.node.getAttribute('y')).toBe('180')
+ })
+ })
+
+ describe('dmove()', function() {
+ it('moves the x and y positon of the element relative to the current position', function() {
+ rect.move(50,60)
+ rect.dmove(80, 25)
+ expect(rect.node.getAttribute('x')).toBe('130')
+ expect(rect.node.getAttribute('y')).toBe('85')
+ })
+ })
+
+ describe('center()', function() {
+ it('should set the cx and cy position', function() {
+ rect.center(321,567)
+ var box = rect.bbox()
+ expect(box.cx).toBe(321)
+ expect(box.cy).toBe(567)
+ })
+ })
+
+ describe('width()', function() {
+ it('sets the width of the element', function() {
+ rect.width(789)
+ expect(rect.node.getAttribute('width')).toBe('789')
+ })
+ it('gets the width of the element if the argument is null', function() {
+ expect(rect.width().toString()).toBe(rect.node.getAttribute('width'))
+ })
+ })
+
+ describe('height()', function() {
+ it('sets the height of the element', function() {
+ rect.height(1236)
+ expect(rect.node.getAttribute('height')).toBe('1236')
+ })
+ it('gets the height of the element if the argument is null', function() {
+ expect(rect.height().toString()).toBe(rect.node.getAttribute('height'))
+ })
+ })
+
+ describe('size()', function() {
+ it('should define the width and height of the element', function() {
+ rect.size(987,654)
+ expect(rect.node.getAttribute('width')).toBe('987')
+ expect(rect.node.getAttribute('height')).toBe('654')
+ })
+ it('defines the width and height proportionally with only the width value given', function() {
+ var box = rect.bbox()
+ rect.size(500)
+ expect(rect.width()).toBe(500)
+ expect(rect.width() / rect.height()).toBe(box.width / box.height)
+ })
+ it('defines the width and height proportionally with only the height value given', function() {
+ var box = rect.bbox()
+ rect.size(null, 525)
+ expect(rect.height()).toBe(525)
+ expect(rect.width() / rect.height()).toBe(box.width / box.height)
+ })
+ })
+
+ describe('scale()', function() {
+ it('should scale the element universally with one argument', function() {
+ var box = rect.scale(2).rbox()
+
+ expect(box.width).toBe(rect.attr('width') * 2)
+ expect(box.height).toBe(rect.attr('height') * 2)
+ })
+ it('should scale the element over individual x and y axes with two arguments', function() {
+ var box = rect.scale(2, 3.5).rbox()
+
+ expect(box.width).toBe(rect.attr('width') * 2)
+ expect(box.height).toBe(rect.attr('height') * 3.5)
+ })
+ })
+
+ describe('translate()', function() {
+ it('should set the translation of an element', function() {
+ rect.transform({ x: 12, y: 12 })
+ expect(rect.node.getAttribute('transform')).toBe('matrix(1,0,0,1,12,12)')
+ })
+ })
+
+})
+
+
+
+
+
+
+
+
diff --git a/node_modules/svg.js/spec/spec/regex.js b/node_modules/svg.js/spec/spec/regex.js
new file mode 100644
index 0000000..9a14bec
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/regex.js
@@ -0,0 +1,92 @@
+describe('Regex', function() {
+
+ describe('matchers', function() {
+
+ describe('numberAndUnit', function() {
+ var match
+
+ it('is true with a positive unit value', function() {
+ match = ('10%').match(SVG.regex.numberAndUnit)
+ expect(match[1]).toBe('10')
+ expect(match[5]).toBe('%')
+ })
+ it('is true with a negative unit value', function() {
+ match = ('-11%').match(SVG.regex.numberAndUnit)
+ expect(match[1]).toBe('-11')
+ expect(match[5]).toBe('%')
+ })
+ it('is false with a positive unit value', function() {
+ match = ('NotAUnit').match(SVG.regex.numberAndUnit)
+ expect(match).toBeNull()
+ })
+
+ it('is true with a number', function() {
+ ["1", "-1", "+15", "1.55", ".5", "5.", "1.3e2", "1E-4", "1e+12"].forEach(function(s) {
+ expect(SVG.regex.numberAndUnit.test(s)).toBeTruthy()
+ })
+ })
+ it('is false with a faulty number', function() {
+ ["+-1", "1.2.3", "1+1", "1e4.5", ".5.", "1f5", "."].forEach(function(s) {
+ expect(SVG.regex.numberAndUnit.test(s)).toBeFalsy()
+ })
+ })
+ it('is true with a number with unit', function() {
+ ["1px", "-1em", "+15%", "1.55s", ".5pt", "5.deg", "1.3e2rad", "1E-4grad", "1e+12cm"].forEach(function(s) {
+ expect(SVG.regex.numberAndUnit.test(s)).toBeTruthy()
+ })
+ })
+ it('is false with a faulty number or wrong unit', function() {
+ ["1em1", "-1eq,5"].forEach(function(s) {
+ expect(SVG.regex.numberAndUnit.test(s)).toBeFalsy()
+ })
+ })
+
+ })
+ })
+
+ describe('testers', function() {
+
+ describe('isHex', function() {
+ it('is true with a three based hex', function() {
+ expect(SVG.regex.isHex.test('#f09')).toBeTruthy()
+ })
+ it('is true with a six based hex', function() {
+ expect(SVG.regex.isHex.test('#fe0198')).toBeTruthy()
+ })
+ it('is false with a faulty hex', function() {
+ expect(SVG.regex.isHex.test('###')).toBeFalsy()
+ expect(SVG.regex.isHex.test('#0')).toBeFalsy()
+ expect(SVG.regex.isHex.test('f06')).toBeFalsy()
+ })
+ })
+
+ describe('isRgb', function() {
+ it('is true with an rgb value', function() {
+ expect(SVG.regex.isRgb.test('rgb(255,66,100)')).toBeTruthy()
+ })
+ it('is false with a non-rgb value', function() {
+ expect(SVG.regex.isRgb.test('hsb(255, 100, 100)')).toBeFalsy()
+ })
+ })
+
+ describe('isNumber', function() {
+
+ it('is true with a number', function() {
+ ["1", "-1", "+15", "1.55", ".5", "5.", "1.3e2", "1E-4", "1e+12"].forEach(function(s) {
+ expect(SVG.regex.isNumber.test(s)).toBeTruthy()
+ })
+ })
+
+ it('is false with a faulty number', function() {
+ ["1a", "+-1", "1.2.3", "1+1", "1e4.5", ".5.", "1f5", "."].forEach(function(s) {
+ expect(SVG.regex.isNumber.test(s)).toBeFalsy()
+ })
+ })
+
+ })
+
+
+
+ })
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/spec/selector.js b/node_modules/svg.js/spec/spec/selector.js
new file mode 100644
index 0000000..b28213d
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/selector.js
@@ -0,0 +1,57 @@
+describe('Selector', function() {
+
+ describe('get()', function() {
+ it('gets an element\'s instance by id', function() {
+ var rect = draw.rect(111, 333)
+
+ expect(SVG.get(rect.attr('id'))).toBe(rect)
+ })
+ it('makes all the element\'s methods available', function() {
+ var element = draw.group()
+ , got = SVG.get(element.attr('id'))
+
+ expect(got.attr()).toEqual(element.attr())
+ })
+ it('gets a referenced element by attribute value', function() {
+ var rect = draw.defs().rect(100, 100)
+ , use = draw.use(rect)
+ , mark = draw.marker(10, 10)
+ , path = draw.path(svgPath).marker('end', mark)
+
+ expect(SVG.get(use.attr('href'))).toBe(rect)
+ expect(SVG.get(path.attr('marker-end'))).toBe(mark)
+ })
+ })
+
+ describe('select()', function() {
+ var e1, e2, e3, e4 ,e5
+
+ beforeEach(function() {
+ e1 = draw.rect(100, 100).addClass('selectable-element')
+ e2 = draw.rect(100, 100).addClass('unselectable-element')
+ e3 = draw.rect(100, 100).addClass('selectable-element')
+ e4 = draw.rect(100, 100).addClass('unselectable-element')
+ e5 = draw.rect(100, 100).addClass('selectable-element')
+ })
+ it('gets all elements with a given class name', function() {
+ expect(SVG.select('rect.selectable-element').valueOf()).toEqual([e1, e3, e5])
+ })
+ it('returns an instance of SVG.Set', function() {
+ expect(SVG.select('rect.selectable-element') instanceof SVG.Set).toBe(true)
+ })
+ })
+
+ describe('Parent#select()', function() {
+ it('gets all elements with a given class name inside a given element', function() {
+ var group = draw.group()
+ , e1 = draw.rect(100, 100).addClass('selectable-element')
+ , e2 = draw.rect(100, 100).addClass('unselectable-element')
+ , e3 = group.rect(100, 100).addClass('selectable-element')
+ , e4 = draw.rect(100, 100).addClass('unselectable-element')
+ , e5 = group.rect(100, 100).addClass('selectable-element')
+
+ expect(group.select('rect.selectable-element').valueOf()).toEqual([e3, e5])
+ })
+ })
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/spec/set.js b/node_modules/svg.js/spec/spec/set.js
new file mode 100644
index 0000000..5532abf
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/set.js
@@ -0,0 +1,214 @@
+describe('Set', function() {
+ var set, e1, e2, e3, e4, e5
+
+ beforeEach(function() {
+ draw.attr('viewBox', null)
+ set = draw.set()
+ e1 = draw.rect(100,100).attr('id', 'e1').move(200,250)
+ e2 = draw.ellipse(100,100).attr('id', 'e2')
+ e3 = draw.line(0,0,100,100).attr('id', 'e3')
+ e4 = draw.circle(50).attr('id', 'e4')
+ e5 = draw.polyline('0,0 10,20 30,50 80,100').attr('id', 'e5')
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ it('creates the set method on SVG.Container instances', function() {
+ expect(draw.set() instanceof SVG.Set).toBeTruthy()
+ })
+
+ it('creates a set with initial value', function() {
+ var members = [1, 2, 4]
+
+ expect(draw.set(members).valueOf()).toBe(members)
+ })
+
+ it('creates a set when passing another set', function() {
+ var set = new SVG.Set([1, 2, 4])
+ var set2 = new SVG.Set(set)
+
+ expect(set.valueOf()).not.toBe(set2.valueOf())
+ expect(set.valueOf()).toEqual(set2.valueOf())
+ })
+
+ describe('add()', function() {
+ it('returns the set instance', function() {
+ expect(set.add(e1)).toBe(set)
+ })
+ it('stores given element', function() {
+ set.add(e1).add(e2).add(e3)
+ expect(set.valueOf()).toEqual([e1,e2,e3])
+ expect(set.members.length).toBe(3)
+ })
+ it('accepts multiple elements at once', function() {
+ set.add(e1, e2, e3, e4, e5)
+ expect(set.valueOf()).toEqual([e1, e2, e3, e4, e5])
+ expect(set.members.length).toBe(5)
+ })
+ })
+
+ describe('remove()', function() {
+ it('returns the set instance', function() {
+ set.add(e1)
+ expect(set.remove(e1)).toBe(set)
+ })
+ it('removes given element', function() {
+ set.add(e1).add(e2).add(e3).remove(e2)
+ expect(set.valueOf()).toEqual([e1,e3])
+ expect(set.members.length).toBe(2)
+ })
+ })
+
+ describe('each()', function() {
+ it('returns the set instance', function() {
+ expect(set.each(function(){})).toBe(set)
+ })
+ it('iterates over all members of the set', function() {
+ var ids = []
+ set.add(e1).add(e2).add(e3)
+
+ set.each(function() {
+ ids.push(this.attr('id'))
+ })
+
+ expect(ids.length).toBe(3)
+ expect(ids).toEqual(['e1','e2','e3'])
+ })
+ })
+
+ describe('clear()', function() {
+ it('returns the set instance', function() {
+ expect(set.clear()).toBe(set)
+ })
+ it('removes all members from set', function() {
+ set.add(e1).add(e2).add(e3).add(e4).add(e5).clear()
+ expect(set.members.length).toBe(0)
+ })
+ })
+
+ describe('get()', function() {
+ it('returns member at given index', function() {
+ set.add(e1).add(e2).add(e3).add(e4).add(e5)
+ expect(set.get(2)).toBe(e3)
+ })
+ })
+
+ describe('first()', function() {
+ it('returns first member', function() {
+ set.add(e1).add(e2).add(e3).add(e4).add(e5)
+ expect(set.first()).toBe(e1)
+ })
+ })
+
+ describe('last()', function() {
+ it('returns last member', function() {
+ set.add(e1).add(e2).add(e3).add(e4).add(e5)
+ expect(set.last()).toBe(e5)
+ })
+ })
+
+ describe('has()', function() {
+ it('checks if a given element is present in set', function() {
+ set.add(e1).add(e2).add(e3).add(e4).add(e5)
+ expect(set.has(e4)).toBeTruthy()
+ })
+ })
+
+ describe('length()', function() {
+ it('gets the length of the set', function() {
+ set.add(e1).add(e2).add(e3).add(e4).add(e5)
+ expect(set.length()).toBe(5)
+ })
+ })
+
+ describe('index()', function() {
+ it('returns the index of a given element within the set', function() {
+ set.add(e1).add(e2).add(e3).add(e5)
+ expect(set.index(e1)).toBe(0)
+ expect(set.index(e2)).toBe(1)
+ expect(set.index(e3)).toBe(2)
+ expect(set.index(e4)).toBe(-1)
+ expect(set.index(e5)).toBe(3)
+ })
+ })
+
+ describe('valueOf()', function() {
+ it('returns the members array', function() {
+ set.add(e1)
+ expect(set.valueOf()).toBe(set.members)
+ })
+ })
+
+ describe('bbox()', function() {
+ it('returns the bounding box of all elements', function() {
+ set.add(e1).add(e2).add(e3).add(e4).add(e5)
+
+ var box = set.bbox()
+
+ expect(box.x).toBeCloseTo(0)
+ expect(box.y).toBeCloseTo(0, 0)
+ expect(box.width).toBeCloseTo(300)
+ expect(box.height).toBeCloseTo(350)
+ })
+ it('returns an instance of SVG.RBox', function() {
+ set.add(e1).add(e2).add(e3).add(e4).add(e5)
+
+ expect(set.bbox() instanceof SVG.RBox).toBeTruthy()
+ })
+ it('returns an empty bounding box wiht no members', function() {
+ var box = set.bbox()
+
+ expect(box.x).toBe(0)
+ expect(box.y).toBe(0)
+ expect(box.width).toBe(0)
+ expect(box.height).toBe(0)
+ })
+ })
+
+ describe('method alias', function() {
+
+ describe('attr()', function() {
+ it('is applied to every member of the set', function() {
+ var fills = []
+
+ set.add(e1).add(e2).add(e3).add(e4).add(e5).attr('fill', '#ff0099')
+ set.each(function() {
+ fills.push(this.attr('fill'))
+ })
+
+ expect(fills).toEqual(['#ff0099','#ff0099','#ff0099','#ff0099','#ff0099'])
+ })
+ })
+
+ })
+
+ describe('method inheritance', function() {
+
+ beforeEach(function() {
+ SVG.extend(SVG.Element, {
+ orange: function() {
+ this.fill('#ff6600')
+ }
+ })
+ })
+
+ it('inherits newly added element methods after initialisation', function() {
+ expect(typeof set.orange).toBe('function')
+ })
+
+ it('applies newly inherited methods properly to members', function() {
+ var fills = []
+
+ set.add(e1).add(e2).add(e3).add(e4).add(e5).orange()
+ set.each(function() {
+ fills.push(this.attr('fill'))
+ })
+
+ expect(fills).toEqual(['#ff6600','#ff6600','#ff6600','#ff6600','#ff6600'])
+ })
+
+ })
+
+})
diff --git a/node_modules/svg.js/spec/spec/sugar.js b/node_modules/svg.js/spec/spec/sugar.js
new file mode 100644
index 0000000..d550886
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/sugar.js
@@ -0,0 +1,358 @@
+describe('Sugar', function() {
+
+ var rect
+
+ beforeEach(function() {
+ draw.attr('viewBox', null)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ describe('fill()', function() {
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ })
+
+ afterEach(function() {
+ rect.remove()
+ })
+
+ it('returns the node reference', function() {
+ expect(rect.fill('red')).toBe(rect)
+ })
+
+ it('sets the given value', function() {
+ expect(rect.fill('red').attr('fill')).toBe('red')
+ })
+
+ it('sets the given value with object given', function() {
+ rect.fill({color: 'red', opacity: 0.5, rule: 'odd'})
+ expect(rect.attr('fill')).toBe('red')
+ expect(rect.attr('fill-opacity')).toBe(0.5)
+ expect(rect.attr('fill-rule')).toBe('odd')
+ })
+
+ it('is a nop with no argument given and returns node reference', function() {
+ rect.fill('red')
+ expect(rect.fill()).toBe(rect)
+ expect(rect.attr('fill')).toBe('red')
+ })
+ })
+
+ describe('rotate()', function() {
+ var rect, spy, undefined
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ spyOn(rect, 'transform')
+ })
+
+ afterEach(function() {
+ rect.remove()
+ rect.transform.calls.reset()
+ })
+
+ it('redirects to transform()', function() {
+ rect.rotate(1,2,3)
+ expect(rect.transform).toHaveBeenCalledWith({ rotation: 1, cx: 2, cy: 3 })
+ })
+ })
+
+ describe('skew()', function() {
+ var rect, spy, undefined
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ spyOn(rect, 'transform')
+ })
+
+ afterEach(function() {
+ rect.remove()
+ rect.transform.calls.reset()
+ })
+
+ it('redirects to transform() with no argument', function() {
+ rect.skew()
+ expect(rect.transform).toHaveBeenCalledWith({ skewX: undefined, skewY: undefined, cx: undefined, cy: undefined })
+ })
+
+ it('redirects to transform() with one argument', function() {
+ rect.skew(5)
+ expect(rect.transform).toHaveBeenCalledWith({ skew: 5, cx: undefined, cy: undefined })
+ })
+
+ it('redirects to transform() with two argument', function() {
+ rect.skew(5, 6)
+ expect(rect.transform).toHaveBeenCalledWith({ skewX: 5, skewY: 6, cx: undefined, cy: undefined })
+ })
+
+ it('redirects to transform() with three arguments', function() {
+ rect.skew(5, 6, 7)
+ expect(rect.transform).toHaveBeenCalledWith({ skew: 5, cx: 6, cy: 7 })
+ })
+
+ it('redirects to transform() with four arguments', function() {
+ rect.skew(5, 6, 7, 8)
+ expect(rect.transform).toHaveBeenCalledWith({ skewX: 5, skewY: 6, cx: 7, cy: 8 })
+ })
+ })
+
+ describe('scale()', function() {
+ var rect, spy, undefined
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ spyOn(rect, 'transform')
+ })
+
+ afterEach(function() {
+ rect.remove()
+ rect.transform.calls.reset()
+ })
+
+ it('redirects to transform() with no argument', function() {
+ rect.scale()
+ expect(rect.transform).toHaveBeenCalledWith({ scaleX: undefined, scaleY: undefined, cx: undefined, cy: undefined })
+ })
+
+ it('redirects to transform() with one argument', function() {
+ rect.scale(5)
+ expect(rect.transform).toHaveBeenCalledWith({ scale: 5, cx: undefined, cy: undefined })
+ })
+
+ it('redirects to transform() with two argument', function() {
+ rect.scale(5, 6)
+ expect(rect.transform).toHaveBeenCalledWith({ scaleX: 5, scaleY: 6, cx: undefined, cy: undefined })
+ })
+
+ it('redirects to transform() with three arguments', function() {
+ rect.scale(5, 6, 7)
+ expect(rect.transform).toHaveBeenCalledWith({ scale: 5, cx: 6, cy: 7 })
+ })
+
+ it('redirects to transform() with four arguments', function() {
+ rect.scale(5, 6, 7, 8)
+ expect(rect.transform).toHaveBeenCalledWith({ scaleX: 5, scaleY: 6, cx: 7, cy: 8 })
+ })
+ })
+
+ describe('translate()', function() {
+ var rect, spy, undefined
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ spyOn(rect, 'transform')
+ })
+
+ afterEach(function() {
+ rect.remove()
+ rect.transform.calls.reset()
+ })
+
+ it('redirects to transform()', function() {
+ rect.translate(1,2)
+ expect(rect.transform).toHaveBeenCalledWith({ x: 1, y: 2 })
+ })
+ })
+
+ describe('flip()', function() {
+ var rect, spy, undefined
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ spyOn(rect, 'transform')
+ })
+
+ afterEach(function() {
+ rect.remove()
+ rect.transform.calls.reset()
+ })
+
+ it('redirects to transform()', function() {
+ rect.flip('x',2)
+ expect(rect.transform).toHaveBeenCalledWith({ flip: 'x', offset: 2 })
+ })
+
+ it('sets flip to "both" when calling without anything', function() {
+ rect.flip()
+ expect(rect.transform).toHaveBeenCalledWith({ flip: 'both', offset: undefined })
+ })
+
+ // this works because only x and y are valid flip values. Evereything else flips on both axis
+ it('sets flip to number and offset to number when called with offset only', function() {
+ rect.flip(5)
+ expect(rect.transform).toHaveBeenCalledWith({ flip: 5, offset: 5 })
+ })
+ })
+
+ describe('matrix()', function() {
+ var rect, spy, undefined
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ spyOn(rect, 'attr')
+ })
+
+ afterEach(function() {
+ rect.remove()
+ rect.attr.calls.reset()
+ })
+
+ it('redirects to attr() directly with one argument', function() {
+ rect.matrix([1,2,3,4,5,6])
+ expect(rect.attr).toHaveBeenCalledWith('transform', new SVG.Matrix([1,2,3,4,5,6]))
+ })
+
+ it('redirects to attr() directly with 6 arguments', function() {
+ rect.matrix(1,2,3,4,5,6)
+ expect(rect.attr).toHaveBeenCalledWith('transform', new SVG.Matrix([1,2,3,4,5,6]))
+ })
+ })
+
+ describe('opacity()', function() {
+ var rect, spy, undefined
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ spyOn(rect, 'attr')
+ })
+
+ afterEach(function() {
+ rect.remove()
+ rect.attr.calls.reset()
+ })
+
+ it('redirects to attr() directly', function() {
+ rect.opacity(0.5)
+ expect(rect.attr).toHaveBeenCalledWith('opacity', 0.5)
+ })
+ })
+
+ describe('dx() / dy()', function() {
+ var rect, spy, undefined
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ spyOn(rect, 'x').and.callThrough()
+ spyOn(rect, 'y').and.callThrough()
+ })
+
+ afterEach(function() {
+ rect.remove()
+ rect.x.calls.reset()
+ rect.y.calls.reset()
+ })
+
+ it('redirects to x() / y() with adding the current value', function() {
+ rect.dx(5)
+ rect.dy(5)
+ expect(rect.x).toHaveBeenCalledWith(jasmine.objectContaining(new SVG.Number('5')), true)
+ expect(rect.y).toHaveBeenCalledWith(jasmine.objectContaining(new SVG.Number('5')), true)
+ })
+
+ it('allows to add a percentage value', function() {
+ rect.move('5%', '5%')
+
+ rect.dx('5%')
+ rect.dy('5%')
+
+ expect(rect.x).toHaveBeenCalledWith(jasmine.objectContaining(new SVG.Number('10%')), true)
+ expect(rect.y).toHaveBeenCalledWith(jasmine.objectContaining(new SVG.Number('10%')), true)
+ })
+
+ it('allows to add a percentage value when no x/y is set', function() {
+ rect.dx('5%')
+ rect.dy('5%')
+
+ expect(rect.x).toHaveBeenCalledWith(jasmine.objectContaining(new SVG.Number('5%')), true)
+ expect(rect.y).toHaveBeenCalledWith(jasmine.objectContaining(new SVG.Number('5%')), true)
+ })
+ })
+
+ describe('dmove()', function() {
+ var rect, spy, undefined
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ spyOn(rect, 'dx').and.callThrough()
+ spyOn(rect, 'dy').and.callThrough()
+ })
+
+ afterEach(function() {
+ rect.remove()
+ rect.dx.calls.reset()
+ rect.dy.calls.reset()
+ })
+
+ it('redirects to dx() / dy() directly', function() {
+ rect.dmove(5,5)
+ expect(rect.dx).toHaveBeenCalledWith(5)
+ expect(rect.dy).toHaveBeenCalledWith(5)
+ })
+ })
+
+ describe('font()', function() {
+ var text, spy, undefined
+
+ beforeEach(function() {
+ text = draw.text(loremIpsum)
+ spyOn(text, 'leading')
+ spyOn(text, 'attr')
+ })
+
+ afterEach(function() {
+ text.remove()
+ text.leading.calls.reset()
+ text.attr.calls.reset()
+ })
+
+ it('sets leading when given', function() {
+ text.font({leading: 3})
+ expect(text.leading).toHaveBeenCalledWith(3)
+ })
+
+ it('sets text-anchor when anchor given', function() {
+ text.font({anchor: 'start'})
+ expect(text.attr).toHaveBeenCalledWith('text-anchor', 'start')
+ })
+
+ it('sets all font properties via attr()', function() {
+ text.font({
+ size: 20,
+ family: 'Verdana',
+ weight: 'bold',
+ stretch: 'wider',
+ variant: 'small-caps',
+ style: 'italic'
+ })
+ expect(text.attr).toHaveBeenCalledWith('font-size', 20)
+ expect(text.attr).toHaveBeenCalledWith('font-family', 'Verdana')
+ expect(text.attr).toHaveBeenCalledWith('font-weight', 'bold')
+ expect(text.attr).toHaveBeenCalledWith('font-stretch', 'wider')
+ expect(text.attr).toHaveBeenCalledWith('font-variant', 'small-caps')
+ expect(text.attr).toHaveBeenCalledWith('font-style', 'italic')
+ })
+
+ it('redirects all other stuff directly to attr()', function() {
+ text.font({
+ foo:'bar',
+ bar:'baz'
+ })
+ expect(text.attr).toHaveBeenCalledWith('foo', 'bar')
+ expect(text.attr).toHaveBeenCalledWith('bar', 'baz')
+ })
+
+ it('sets key value pair when called with 2 parameters', function() {
+ text.font('size', 20)
+ expect(text.attr).toHaveBeenCalledWith('font-size', 20)
+ })
+
+ it('gets value if called with one parameter', function() {
+ text.font('size')
+ expect(text.attr).toHaveBeenCalledWith('font-size', undefined)
+ })
+ })
+
+})
diff --git a/node_modules/svg.js/spec/spec/svg.js b/node_modules/svg.js/spec/spec/svg.js
new file mode 100644
index 0000000..7738b0e
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/svg.js
@@ -0,0 +1,115 @@
+describe('SVG', function() {
+
+ describe('()', function() {
+ var drawing, wrapper
+
+ beforeEach(function() {
+ wrapper = document.createElement('svg')
+ document.documentElement.appendChild(wrapper)
+ drawing = SVG(wrapper)
+ })
+
+ afterEach(function() {
+ wrapper.parentNode.removeChild(wrapper)
+ })
+
+ it('creates a new svg drawing', function() {
+ expect(drawing.type).toBe('svg')
+ })
+ it('creates an instance of SVG.Doc', function() {
+ expect(drawing instanceof SVG.Doc).toBe(true)
+ })
+
+ if(parserInDoc){
+ it('sets no default size in svg documents', function() {
+ expect(drawing.width()).toBe(0)
+ expect(drawing.height()).toBe(0)
+ })
+ }else{
+ it('sets size to 100% in html documents', function() {
+ expect(drawing.width()).toBe('100%')
+ expect(drawing.height()).toBe('100%')
+ })
+ }
+ })
+
+ describe('create()', function() {
+ it('creates an element with given node name and return it', function() {
+ var element = SVG.create('rect')
+
+ expect(element.nodeName).toBe('rect')
+ })
+ it('increases the global id sequence', function() {
+ var did = SVG.did
+ , element = SVG.create('rect')
+
+ expect(did + 1).toBe(SVG.did)
+ })
+ it('adds a unique id containing the node name', function() {
+ var did = SVG.did
+ , element = SVG.create('rect')
+
+ expect(element.getAttribute('id')).toBe('SvgjsRect' + did)
+ })
+ })
+
+ describe('extend()', function() {
+ it('adds all functions in the given object to the target object', function() {
+ SVG.extend(SVG.Rect, {
+ soft: function() {
+ return this.opacity(0.2)
+ }
+ })
+
+ expect(typeof SVG.Rect.prototype.soft).toBe('function')
+ expect(draw.rect(100,100).soft().attr('opacity')).toBe(0.2)
+ })
+ it('accepts and extend multiple modules at once', function() {
+ SVG.extend(SVG.Rect, SVG.Ellipse, SVG.Path, {
+ soft: function() {
+ return this.opacity(0.5)
+ }
+ })
+
+ expect(typeof SVG.Rect.prototype.soft).toBe('function')
+ expect(draw.rect(100,100).soft().attr('opacity')).toBe(0.5)
+ expect(typeof SVG.Ellipse.prototype.soft).toBe('function')
+ expect(draw.ellipse(100,100).soft().attr('opacity')).toBe(0.5)
+ expect(typeof SVG.Path.prototype.soft).toBe('function')
+ expect(draw.path().soft().attr('opacity')).toBe(0.5)
+ })
+ it('ignores non existant objects', function() {
+ SVG.extend(SVG.Rect, SVG.Bogus, {
+ soft: function() {
+ return this.opacity(0.3)
+ }
+ })
+
+ expect(typeof SVG.Rect.prototype.soft).toBe('function')
+ expect(draw.rect(100,100).soft().attr('opacity')).toBe(0.3)
+ expect(typeof SVG.Bogus).toBe('undefined')
+ })
+ })
+
+ describe('prepare()', function() {
+ var drawing, wrapper, parser
+
+ beforeEach(function() {
+ wrapper = document.createElement('svg')
+ document.documentElement.appendChild(wrapper)
+ drawing = SVG(wrapper)
+ })
+
+ it('creates a parser element when calling SVG()', function() {
+ expect(SVG.parser.draw.nodeName).toBe('svg')
+ })
+ it('hides the parser', function() {
+ expect(window.stripped(SVG.parser.draw.getAttribute('style'))).toBe('overflow:hidden;top:-100%;left:-100%;position:absolute;opacity:0')
+ })
+ it('holds polyline and path', function() {
+ expect(SVG.select('polyline', SVG.parser.draw).first().type).toBe('polyline')
+ expect(SVG.select('path', SVG.parser.draw).first().type).toBe('path')
+ })
+ })
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/spec/symbol.js b/node_modules/svg.js/spec/spec/symbol.js
new file mode 100644
index 0000000..a4febbd
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/symbol.js
@@ -0,0 +1,16 @@
+describe('Symbol', function() {
+ describe('()', function() {
+ var element
+
+ beforeEach(function() {
+ element = draw.symbol()
+ })
+
+ it('creates an instance of SVG.Symbol', function() {
+ expect(element instanceof SVG.Symbol).toBeTruthy()
+ })
+ it('is an instance of SVG.Container', function() {
+ expect(element instanceof SVG.Container).toBeTruthy()
+ })
+ })
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/spec/text.js b/node_modules/svg.js/spec/spec/text.js
new file mode 100644
index 0000000..84a12c2
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/text.js
@@ -0,0 +1,304 @@
+// IMPORTANT!!!
+// The native getBBox() on text elements isn't always accurate in the decimals.
+// Therefore sometimes some rounding is used to make test work as expected.
+
+describe('Text', function() {
+ var text
+
+ beforeEach(function() {
+ text = draw.text(loremIpsum).size(5)
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ describe('leading()', function() {
+ it('returns the leading value of the text without an argument', function() {
+ expect(text.leading() instanceof SVG.Number)
+ expect(text.leading().valueOf()).toBe(1.3)
+ })
+ it('sets the leading value of the text with the first argument', function() {
+ expect(text.leading(1.5).dom.leading.valueOf()).toBe(1.5)
+ })
+ })
+
+ describe('rebuild()', function() {
+ it('disables the rebuild if called with false', function() {
+ expect(text.rebuild(false)._rebuild).toBeFalsy()
+ })
+ it('enables the rebuild if called with true', function() {
+ expect(text.rebuild(true)._rebuild).toBeTruthy()
+ })
+ it('rebuilds the text without an argument given', function() {
+ var dy = text.lines().get(2).attr('dy')
+ text.leading(1.7)
+ expect(dy == text.lines().get(2).attr('dy')).toBeFalsy()
+ })
+ })
+
+ describe('x()', function() {
+ it('returns the value of x without an argument', function() {
+ expect(text.x()).toBe(0)
+ })
+ it('sets the value of x with the first argument', function() {
+ text.x(123)
+ expect(text.node.getAttribute('x')).toBeCloseTo(123)
+ })
+ it('sets the value of all lines', function() {
+ text.x(200)
+ text.lines().each(function() {
+ expect(this.x()).toBe(200)
+ })
+ })
+ it('sets the value of y with a percent value', function() {
+ text.x('40%')
+ expect(text.node.getAttribute('x')).toBe('40%')
+ })
+ it('returns the value of x when x is a percentual value', function() {
+ expect(text.x('45%').x()).toBe('45%')
+ })
+ // Woow this test is old. The functionality not even implemented anymore
+ // Was a good feature. Maybe we add it back?
+ /*it('sets the value of x based on the anchor with the first argument', function() {
+ text.x(123, true)
+ var box = text.bbox()
+ expect(box.x).toBeCloseTo(123)
+ })*/
+ })
+
+ describe('y()', function() {
+ it('returns the value of y without an argument', function() {
+ expect(text.y(0).y()).toBeCloseTo(0)
+ })
+ it('returns the value of y when y is a percentual value', function() {
+ expect(text.y('45%').y()).toBe('45%')
+ })
+ it('sets the value of y with the first argument', function() {
+ text.y(345)
+ var box = text.bbox()
+ expect(box.y).toBe(345)
+ })
+ it('sets the value of y with SVG.Number as first argument', function() {
+ text.y(new SVG.Number(345))
+ var box = text.bbox()
+ expect(box.y).toBe(345)
+ })
+ it('sets the value of y with a percent value', function() {
+ text.y('40%')
+ expect(text.node.getAttribute('y')).toBe('40%')
+ })
+ })
+
+ describe('cx()', function() {
+ it('returns the value of cx without an argument', function() {
+ var box = text.bbox()
+ expect(text.cx()).toBeCloseTo(box.x + box.width / 2)
+ })
+ it('sets the value of cx with the first argument', function() {
+ text.cx(123)
+ var box = text.bbox()
+ // this is a hack. it should be exactly 123 since you set it. But bbox with text is a thing...
+ expect(box.cx).toBeCloseTo(box.x + box.width/2)
+ })
+ // not implemented anymore
+ /*it('sets the value of cx based on the anchor with the first argument', function() {
+ text.cx(123, true)
+ var box = text.bbox()
+ expect(box.cx).toBeCloseTo(123)
+ })*/
+ })
+
+ describe('cy()', function() {
+ it('returns the value of cy without an argument', function() {
+ var box = text.bbox()
+ expect(text.cy()).toBe(box.cy)
+ })
+ it('sets the value of cy with the first argument', function() {
+ text.cy(345)
+ var box = text.bbox()
+ expect(Math.round(box.cy * 10) / 10).toBe(345)
+ })
+ })
+
+ describe('move()', function() {
+ it('sets the x and y position', function() {
+ text.move(123,456)
+ expect(text.node.getAttribute('x')).toBe('123')
+ expect(text.y()).toBeCloseTo(456)
+ })
+ })
+
+ describe('center()', function() {
+ it('sets the cx and cy position', function() {
+ text.center(321, 567)
+ var box = text.bbox()
+ expect(+text.node.getAttribute('x') + box.width / 2).toBeCloseTo(321, 1)
+ expect(text.y() + box.height / 2).toBeCloseTo(567)
+ })
+ })
+
+ describe('size()', function() {
+ it('should define the width and height of the element', function() {
+ text.size(50)
+ expect(text.attr('font-size').valueOf()).toBe(50)
+ })
+ })
+
+ describe('translate()', function() {
+ it('sets the translation of an element', function() {
+ text.transform({ x: 12, y: 12 })
+ expect(text.node.getAttribute('transform')).toBe('matrix(1,0,0,1,12,12)')
+ })
+ })
+
+ describe('text()', function() {
+ it('adds content in a nested tspan', function() {
+ text.text('It is a bear!')
+ expect(text.node.childNodes[0].nodeType).toBe(1)
+ expect(text.node.childNodes[0].childNodes[0].data).toBe('It is a bear!')
+ })
+ it('adds content in a nested tspan even with an empty string', function() {
+ text.text('')
+ expect(text.node.childNodes[0].nodeType).toBe(1)
+ expect(text.node.childNodes[0].childNodes[0].data).toBe('')
+ })
+ it('creates multiple lines with a newline separated string', function() {
+ text.text('It is\nJUST\na bear!')
+ expect(text.node.childNodes.length).toBe(3)
+ })
+ it('restores the content from the dom', function() {
+ text.text('It is\nJUST\na bear!')
+ expect(text.text()).toBe('It is\nJUST\na bear!')
+ })
+ it('gets the given content of a text element without an argument', function() {
+ text.text('It is another bear!')
+ expect(text.node.childNodes[0].nodeType).toBe(1)
+ expect(text.text()).toMatch('It is another bear!')
+ })
+ it('accepts a block as first arguments', function() {
+ text.text(function(add) {
+ add.tspan('mastaba')
+ add.plain('hut')
+ })
+ expect(text.node.childNodes[0].nodeType).toBe(1)
+ expect(text.node.childNodes[0].childNodes[0].data).toBe('mastaba')
+ expect(text.node.childNodes[1].nodeType).toBe(3)
+ expect(text.node.childNodes[1].data).toBe('hut')
+ })
+ })
+
+ describe('plain()', function() {
+ it('adds content without a tspan', function() {
+ text.plain('It is a bear!')
+ expect(text.node.childNodes[0].nodeType).toBe(3)
+ expect(text.node.childNodes[0].data).toBe('It is a bear!')
+ })
+ it('clears content before adding new content', function() {
+ text.plain('It is not a bear!')
+ expect(text.node.childNodes.length).toBe(1)
+ expect(text.node.childNodes[0].data).toBe('It is not a bear!')
+ })
+ it('restores the content from the dom', function() {
+ text.plain('Just plain text!')
+ expect(text.text()).toBe('Just plain text!')
+ })
+ })
+
+ describe('tspan()', function() {
+ it('adds content in a tspan', function() {
+ text.tspan('It is a bear!')
+ expect(text.node.childNodes[0].nodeType).toBe(1)
+ expect(text.node.childNodes[0].childNodes[0].data).toBe('It is a bear!')
+ })
+ it('clears content before adding new content', function() {
+ text.tspan('It is not a bear!')
+ expect(text.node.childNodes.length).toBe(1)
+ expect(text.node.childNodes[0].childNodes[0].data).toBe('It is not a bear!')
+ })
+ })
+
+ describe('clear()', function() {
+ it('removes all content', function() {
+ text.text(function(add) {
+ add.tspan('The first.')
+ add.tspan('The second.')
+ add.tspan('The third.')
+ })
+ expect(text.node.childNodes.length).toBe(3)
+ text.clear()
+ expect(text.node.childNodes.length).toBe(0)
+ })
+ })
+
+ describe('lines()', function() {
+ it('gets an array of individual lines as an instance of SVG.Set', function() {
+ var l1, l2, l3
+ text.text(function(add) {
+ l1 = add.tspan('The first.')
+ l2 = add.tspan('The second.')
+ l3 = add.tspan('The third.')
+ })
+ expect(text.lines().length()).toBe(3)
+ expect(text.lines().get(0)).toBe(l1)
+ expect(text.lines().get(1)).toBe(l2)
+ expect(text.lines().get(2)).toBe(l3)
+ })
+ })
+
+ describe('length()', function() {
+ it('gets total length of text', function() {
+ text.text(function(add) {
+ add.tspan('The first.')
+ add.tspan('The second.')
+ add.tspan('The third.')
+ })
+ expect(text.length()).toBeCloseTo(text.lines().get(0).length() + text.lines().get(1).length() + text.lines().get(2).length(), 3)
+ })
+ })
+
+ describe('build()', function() {
+ it('enables adding multiple plain text nodes when given true', function() {
+ text.clear().build(true)
+ text.plain('A great piece!')
+ text.plain('Another great piece!')
+ expect(text.node.childNodes[0].data).toBe('A great piece!')
+ expect(text.node.childNodes[1].data).toBe('Another great piece!')
+ })
+ it('enables adding multiple tspan nodes when given true', function() {
+ text.clear().build(true)
+ text.tspan('A great piece!')
+ text.tspan('Another great piece!')
+ expect(text.node.childNodes[0].childNodes[0].data).toBe('A great piece!')
+ expect(text.node.childNodes[1].childNodes[0].data).toBe('Another great piece!')
+ })
+ it('disables adding multiple plain text nodes when given false', function() {
+ text.clear().build(true)
+ text.plain('A great piece!')
+ text.build(false).plain('Another great piece!')
+ expect(text.node.childNodes[0].data).toBe('Another great piece!')
+ expect(text.node.childNodes[1]).toBe(undefined)
+ })
+ it('disables adding multiple tspan nodes when given false', function() {
+ text.clear().build(true)
+ text.tspan('A great piece!')
+ text.build(false).tspan('Another great piece!')
+ expect(text.node.childNodes[0].childNodes[0].data).toBe('Another great piece!')
+ expect(text.node.childNodes[1]).toBe(undefined)
+ })
+ })
+
+ describe('setData()', function() {
+ it('read all data from the svgjs:data attribute and assign it to el.dom', function(){
+
+ text.attr('svgjs:data', '{"foo":"bar","leading":"3px"}')
+ text.setData(JSON.parse(text.attr('svgjs:data')))
+
+ expect(text.dom.foo).toBe('bar')
+ expect(text.dom.leading instanceof SVG.Number).toBeTruthy()
+ expect(text.dom.leading.value).toBe(3)
+ expect(text.dom.leading.unit).toBe('px')
+ })
+ })
+
+})
diff --git a/node_modules/svg.js/spec/spec/textpath.js b/node_modules/svg.js/spec/spec/textpath.js
new file mode 100644
index 0000000..ee046ea
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/textpath.js
@@ -0,0 +1,62 @@
+describe('TextPath', function() {
+ var text
+ , data = 'M 100 200 C 200 100 300 0 400 100 C 500 200 600 300 700 200 C 800 100 900 100 900 100'
+
+ beforeEach(function() {
+ text = draw.text('We go up, then we go down, then up again')
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ describe('path()', function() {
+ it('returns the text element', function() {
+ expect(text.path(data)).toBe(text)
+ })
+ it('creates a textPath node in the text element', function() {
+ text.path(data)
+ expect(text.node.firstChild.nodeName).toBe('textPath')
+ })
+ })
+
+ describe('textPath()', function() {
+ it('creates a reference to the textPath', function() {
+ expect(text.path(data).textPath() instanceof SVG.TextPath).toBe(true)
+ })
+ })
+
+ describe('track()', function() {
+ it('creates a reference to the path', function() {
+ expect(text.path(data).track() instanceof SVG.Path).toBe(true)
+ })
+ })
+
+ describe('array()', function() {
+ it('return the path array of the underlying path', function() {
+ expect(text.path(data).array()).toEqual(new SVG.PathArray(data))
+ })
+ it('return null if there is no underlying path', function () {
+ expect(text.array()).toBe(null)
+ })
+ })
+
+ describe('plot()', function() {
+ it('change the array of the underlying path when a string is passed', function() {
+ expect(text.path().plot(data)).toBe(text)
+ expect(text.array()).toEqual(new SVG.PathArray(data))
+ })
+ it('do nothing when a string is passed and there is no underlying path', function() {
+ expect(text.plot(data)).toBe(text)
+ expect(text.array()).toEqual(null)
+ })
+ it('return the path array of the underlying path when no arguments is passed', function () {
+ text.path(data)
+ expect(text.plot()).toBe(text.array())
+ expect(text.plot()).not.toBe(null)
+ })
+ it('return null when no arguments is passed and there is no underlying path', function () {
+ expect(text.plot()).toBe(null)
+ })
+ })
+})
diff --git a/node_modules/svg.js/spec/spec/transformations.js b/node_modules/svg.js/spec/spec/transformations.js
new file mode 100644
index 0000000..3399981
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/transformations.js
@@ -0,0 +1,282 @@
+describe('Transformations:', function() {
+ var translated, scaled, rotated, skewed
+
+ beforeEach(function() {
+ translated = draw.rect(100,100).translate(100,100)
+ scaled = draw.rect(100,100).scale(2)
+ rotated = draw.rect(100,100).rotate(45, 50, 50)
+ skewed = draw.rect(100,100).skew(30)
+ })
+
+ /* SVG.Transformation is not tested because it is an abstract prototype */
+
+ describe('SVG.Transformation', function() {
+ it('marks the transformation as inversed when inverse flag given', function() {
+ var trans = new SVG.Transformation([], true)
+ expect(trans.inversed).toBeTruthy()
+ })
+ })
+
+ describe('SVG.Translate', function() {
+ var trans
+
+ beforeEach(function(){
+ trans = new SVG.Translate(translated.transform())
+ })
+
+
+ it('creates an object of type SVG.Transformation', function() {
+ expect(trans instanceof SVG.Transformation).toBeTruthy()
+ })
+
+ it('uses transformedX and transformedY as arguments', function() {
+ expect(trans.arguments).toEqual(['transformedX', 'transformedY'])
+ })
+
+ it('s method is translate()', function() {
+ expect(trans.method).toEqual('translate')
+ })
+
+ it('sets the necessary parameters at creation', function() {
+ expect(trans.transformedX).toBe(100)
+ expect(trans.transformedY).toBe(100)
+ })
+
+ describe('undo', function() {
+ it('sets the undo matrix which can undo the translation', function() {
+ var extracted = (new SVG.Matrix(1,0,0,1,20,20)).extract()
+ trans.undo(extracted)
+ expect(trans._undo.toString()).toEqual('matrix(1,0,0,1,-20,-20)')
+
+ var extracted = (new SVG.Matrix(10,0,0,10,20,20)).extract()
+ trans.undo(extracted)
+ expect(trans._undo.toString()).toEqual('matrix(1,0,0,1,-2,-2)')
+
+ var extracted = (new SVG.Matrix(10,50,50,30,20,20)).extract()
+ trans.undo(extracted)
+ expect(trans._undo.e).toBeCloseTo(-extracted.transformedX)
+ expect(trans._undo.f).toBeCloseTo(-extracted.transformedY)
+ })
+ })
+
+ describe('at', function() {
+ it('creates a matrix at a certain position', function() {
+ expect(trans.at(0.3).toString()).toEqual('matrix(1,0,0,1,30,30)')
+ })
+ it('returns the inversed matrix from a specific position when created with inverse flag', function() {
+ expect((new SVG.Translate(translated.transform(), true)).at(0.3).toString()).toEqual('matrix(1,0,0,1,-30,-30)')
+ })
+ it('returns the resulting transformation which has to be made to set an absolute translation', function() {
+ trans.undo(new SVG.Matrix(10,50,50,30,20,20).extract())
+
+ expect(trans.at(0.4).a).toEqual(1)
+ expect(trans.at(0.4).b).toEqual(0)
+ expect(trans.at(0.4).c).toEqual(0)
+ expect(trans.at(0.4).d).toEqual(1)
+ expect(trans.at(0.4).e).toBeCloseTo(100 * 0.4 + trans._undo.e * 0.4)
+ expect(trans.at(0.4).f).toBeCloseTo(100 * 0.4 + trans._undo.f * 0.4)
+ })
+ })
+ })
+
+ describe('SVG.Rotate', function() {
+ var trans
+
+ beforeEach(function(){
+ trans = new SVG.Rotate(45, 50, 50)
+ })
+
+
+ it('creates an object of type SVG.Transformation', function() {
+ expect(trans instanceof SVG.Transformation).toBeTruthy()
+ })
+
+ it('uses rotation, cx and cy as arguments', function() {
+ expect(trans.arguments).toEqual(['rotation', 'cx', 'cy'])
+ })
+
+ it('s method is rotate()', function() {
+ expect(trans.method).toEqual('rotate')
+ })
+
+ it('sets the necessary parameters at creation', function() {
+ expect(trans.rotation).toBe(45)
+ expect(trans.cx).toBe(50)
+ expect(trans.cy).toBe(50)
+ })
+
+ describe('undo', function() {
+ it('sets an undo object which holds rotation', function() {
+ var extracted = (new SVG.Matrix(1,0,0,1,0,0)).rotate(20, 50, 50).extract()
+ trans.undo(extracted)
+ expect(trans._undo.rotation).toBeCloseTo(20)
+ })
+ })
+
+ describe('at', function() {
+ it('creates a matrix at a certain position', function() {
+ expect(trans.at(0.3).toString()).toEqual((new SVG.Matrix()).rotate(0.3 * 45, 50, 50).toString())
+ })
+ it('returns the resulting transformation which has to be made to set an absolute translation', function() {
+ trans.undo((new SVG.Matrix()).rotate(20, 50, 50).extract())
+
+ expect(trans.at(0.4).a).toBeCloseTo(1,1)
+ expect(trans.at(0.4).b).toEqual(jasmine.any(Number))
+ expect(trans.at(0.4).c).toEqual(jasmine.any(Number))
+ expect(trans.at(0.4).d).toBeCloseTo(1,1)
+ expect(trans.at(0.4).e).toEqual(jasmine.any(Number))
+ expect(trans.at(0.4).f).toEqual(jasmine.any(Number))
+ })
+ })
+ })
+
+
+ describe('SVG.Scale', function() {
+ var trans
+
+ beforeEach(function(){
+ trans = new SVG.Scale(2,2,50,50)
+ })
+
+
+ it('creates an object of type SVG.Transformation', function() {
+ expect(trans instanceof SVG.Transformation).toBeTruthy()
+ })
+
+ it('uses scaleX, scaleY, cx and cy as arguments', function() {
+ expect(trans.arguments).toEqual(['scaleX', 'scaleY', 'cx', 'cy'])
+ })
+
+ it('s method is scale()', function() {
+ expect(trans.method).toEqual('scale')
+ })
+
+ it('sets the necessary parameters at creation', function() {
+ expect(trans.scaleX).toBe(2)
+ expect(trans.scaleY).toBe(2)
+ expect(trans.cx).toBe(50)
+ expect(trans.cy).toBe(50)
+ })
+
+ describe('undo', function() {
+ it('sets the undo matrix which can undo the translation', function() {
+ var extracted = (new SVG.Matrix(4,0,0,4,0,0)).extract()
+ trans.undo(extracted)
+ expect(trans._undo.toString()).toEqual('matrix(0.25,0,0,0.25,37.5,37.5)')
+
+ var extracted = (new SVG.Matrix(10,0,0,10,20,20)).extract()
+ trans.undo(extracted)
+ expect(trans._undo.a).toBeCloseTo(1/extracted.scaleX)
+ expect(trans._undo.d).toBeCloseTo(1/extracted.scaleY)
+ expect(trans._undo.e).toBeCloseTo(45)
+ expect(trans._undo.f).toBeCloseTo(45)
+
+ var extracted = (new SVG.Matrix(10,50,50,30,20,20)).extract()
+ trans.undo(extracted)
+ expect(trans._undo.a).toBeCloseTo(1/extracted.scaleX)
+ expect(trans._undo.d).toBeCloseTo(1/extracted.scaleY)
+ })
+ })
+
+ describe('at', function() {
+ it('creates a matrix at a certain position', function() {
+ expect(trans.at(0.75).toString()).toEqual('matrix(1.75,0,0,1.75,-37.5,-37.5)')
+ })
+ it('returns the inversed matrix from a specific position when created with inverse flag', function() {
+ var morphed = (new SVG.Scale(scaled.transform(2,2,50,50), true)).at(0.25)
+
+ expect(morphed.a).toBeCloseTo(0.8)
+ expect(morphed.d).toBeCloseTo(0.8)
+ })
+ it('returns the resulting transformation which has to be made to set an absolute translation', function() {
+
+ var morphed = trans.undo((new SVG.Matrix(10,0,0,10,0,0)).extract()).at(0.5)
+
+ expect(morphed.a).toBeCloseTo(0.6)
+ expect(morphed.b).toEqual(0)
+ expect(morphed.c).toEqual(0)
+ expect(morphed.d).toBeCloseTo(0.6)
+ expect(morphed.e).toBeCloseTo(20)
+ expect(morphed.f).toBeCloseTo(20)
+ })
+ })
+ })
+
+ describe('SVG.Skew', function() {
+ var trans
+
+ beforeEach(function(){
+ trans = new SVG.Skew(30,-30,50,50)
+ })
+
+
+ it('creates an object of type SVG.Transformation', function() {
+ expect(trans instanceof SVG.Transformation).toBeTruthy()
+ })
+
+ it('uses scaleX, scaleY, cx and cy as arguments', function() {
+ expect(trans.arguments).toEqual(['skewX', 'skewY', 'cx', 'cy'])
+ })
+
+ it('s method is skew()', function() {
+ expect(trans.method).toEqual('skew')
+ })
+
+ it('sets the necessary parameters at creation', function() {
+ expect(trans.skewX).toBe(30)
+ expect(trans.skewY).toBe(-30)
+ expect(trans.cx).toBe(50)
+ expect(trans.cy).toBe(50)
+ })
+
+ describe('undo', function() {
+ it('sets the undo matrix which can undo the translation', function() {
+ var extracted = (new SVG.Matrix()).skew(90, 90, 0, 0).extract()
+ trans.undo(extracted)
+ expect(trans._undo.a).toBeCloseTo(0)
+ expect(trans._undo.b).toBeCloseTo(0)
+ expect(trans._undo.c).toBeCloseTo(0)
+ expect(trans._undo.d).toBeCloseTo(0)
+ expect(trans._undo.e).toBeCloseTo(50)
+ expect(trans._undo.f).toBeCloseTo(50)
+
+ var extracted = (new SVG.Matrix(10,0,0,10,20,20)).extract()
+ trans.undo(extracted)
+ expect(trans._undo.a).toBeCloseTo(1)
+ expect(trans._undo.b).toBeCloseTo(0)
+ expect(trans._undo.c).toBeCloseTo(0)
+ expect(trans._undo.d).toBeCloseTo(1)
+ expect(trans._undo.e).toBeCloseTo(0)
+ expect(trans._undo.f).toBeCloseTo(0)
+ })
+ })
+
+ describe('at', function() {
+ it('creates a matrix at a certain position', function() {
+ expect(trans.at(0.75)).toEqual((new SVG.Matrix()).morph((new SVG.Matrix()).skew(30, -30, 50, 50)).at(0.75))
+ })
+ it('returns the inversed matrix from a specific position when created with inverse flag', function() {
+ var morphed = (new SVG.Scale(skewed.transform(20,-20,50,50), true)).at(0.25)
+
+ expect(morphed.a).toBeCloseTo(0.963)
+ expect(morphed.b).toBeCloseTo(0)
+ expect(morphed.c).toBeCloseTo(0)
+ expect(morphed.d).toBeCloseTo(0.963)
+ expect(morphed.e).toBeCloseTo(0)
+ expect(morphed.f).toBeCloseTo(0)
+ })
+ it('returns the resulting transformation which has to be made to set an absolute translation', function() {
+
+ var morphed = trans.undo((new SVG.Matrix(10,0,0,10,0,0)).skew(20, 30, 20, 10).extract()).at(0.5)
+
+ expect(morphed.a).toBeCloseTo(1.266)
+ expect(morphed.b).toBeCloseTo(-0.7310)
+ expect(morphed.c).toBeCloseTo(0.1351)
+ expect(morphed.d).toBeCloseTo(0.9220)
+ expect(morphed.e).toBeCloseTo(-20.05593)
+ expect(morphed.f).toBeCloseTo(40.4468)
+ })
+ })
+ })
+
+})
diff --git a/node_modules/svg.js/spec/spec/tspan.js b/node_modules/svg.js/spec/spec/tspan.js
new file mode 100644
index 0000000..d8ac4b1
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/tspan.js
@@ -0,0 +1,46 @@
+describe('Tspan', function() {
+ var text, tspan
+
+ beforeEach(function() {
+ text = draw.text(loremIpsum)
+ tspan = text.tspan('Hello World')
+ })
+
+ afterEach(function() {
+ draw.clear()
+ })
+
+ describe('newLine()', function() {
+ it('converts the tspan to a line', function() {
+ tspan = text.tspan('Hello World')
+ expect(tspan.newLine().dom.newLined).toBeTruthy()
+ })
+ })
+
+ describe('text()', function() {
+ it('returns the text of the tspan without newline when not newlined', function() {
+ tspan = text.tspan('Hello World')
+ expect(tspan.text()).toBe('Hello World')
+ })
+ it('returns the text of the tspan with newline when newlined', function() {
+ tspan = text.tspan('Hello World').newLine()
+ expect(tspan.text()).toBe('Hello World\n')
+ })
+ it('calls the function when function given', function() {
+ var spy = jasmine.createSpy('dummy')
+ tspan = text.tspan('Hello World')
+ tspan.text(spy)
+ expect(spy).toHaveBeenCalledWith(tspan)
+ })
+ })
+
+ describe('dx()', function() {
+ it('gets the dx value with no argument', function() {
+ tspan.attr('dx', 25)
+ expect(tspan.dx()).toBe(25)
+ })
+ it('sets the dx value whith the first argument', function() {
+ expect(tspan.dx(25).attr('dx')).toBe(25)
+ })
+ })
+})
diff --git a/node_modules/svg.js/spec/spec/use.js b/node_modules/svg.js/spec/spec/use.js
new file mode 100644
index 0000000..0de5b39
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/use.js
@@ -0,0 +1,43 @@
+describe('Use', function() {
+ var use
+
+ describe('on a container element', function() {
+ var rect
+
+ beforeEach(function() {
+ rect = draw.rect(100,100)
+ use = draw.use(rect)
+ })
+
+ it('creates an instance of SVG.Use', function() {
+ expect(use instanceof SVG.Use).toBe(true)
+ })
+
+ it('sets the target element id to its href attribute', function() {
+ expect(use.node.getAttributeNS(SVG.xlink, 'href')).toBe('#' + rect)
+ })
+
+ it('adopts the geometry of the target element', function() {
+ expect(use.bbox()).toEqual(rect.bbox())
+ })
+ })
+
+ describe('on an external path', function() {
+ var file = 'http://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg'
+ , id = 'flowRoot1882'
+
+ beforeEach(function() {
+ use = draw.use(id, file)
+ })
+
+ it('creates an instance of SVG.Use', function() {
+ expect(use instanceof SVG.Use).toBe(true)
+ })
+
+ it('sets the target element id and file path to its href attribute', function() {
+ expect(use.node.getAttributeNS(SVG.xlink, 'href')).toBe(file + '#' + id)
+ })
+
+ })
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/spec/utils.js b/node_modules/svg.js/spec/spec/utils.js
new file mode 100644
index 0000000..be8262e
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/utils.js
@@ -0,0 +1,10 @@
+describe('SVG.utils', function() {
+ describe('degrees()', function() {
+ it('converts radiant to degrees', function() {
+ expect(SVG.utils.degrees(Math.PI)).toBe(180)
+ })
+ it('maps to 0 - 360 degree only', function() {
+ expect(SVG.utils.degrees(2.5 * Math.PI)).toBe(90)
+ })
+ })
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/spec/viewbox.js b/node_modules/svg.js/spec/spec/viewbox.js
new file mode 100644
index 0000000..cf6ec5c
--- /dev/null
+++ b/node_modules/svg.js/spec/spec/viewbox.js
@@ -0,0 +1,162 @@
+describe('Viewbox', function() {
+ var viewbox
+
+ beforeEach(function() {
+ draw.clear()
+ })
+
+ describe('initialization', function() {
+
+
+ it('creates a new viewbox with default values', function() {
+ viewbox = new SVG.ViewBox()
+
+ expect(viewbox.x).toBe(0)
+ expect(viewbox.y).toBe(0)
+ expect(viewbox.width).toBe(0)
+ expect(viewbox.height).toBe(0)
+ })
+
+
+
+ it('creates a new viewbox from parsed string', function() {
+ viewbox = new SVG.ViewBox('10. 100 200 300')
+
+ expect(viewbox.x).toBe(10)
+ expect(viewbox.y).toBe(100)
+ expect(viewbox.width).toBe(200)
+ expect(viewbox.height).toBe(300)
+ })
+
+
+
+ it('creates a new viewbox from array', function() {
+ viewbox = new SVG.ViewBox([10, 100, 200, 300])
+
+ expect(viewbox.x).toBe(10)
+ expect(viewbox.y).toBe(100)
+ expect(viewbox.width).toBe(200)
+ expect(viewbox.height).toBe(300)
+ })
+
+
+
+ it('creates a new viewbox from object', function() {
+ viewbox = new SVG.ViewBox({x:10, y:100, width:200, height:300})
+
+ expect(viewbox.x).toBe(10)
+ expect(viewbox.y).toBe(100)
+ expect(viewbox.width).toBe(200)
+ expect(viewbox.height).toBe(300)
+ })
+
+
+
+ it('creates a new viewbox from 4 arguments given', function() {
+ viewbox = new SVG.ViewBox(10, 100, 200, 300)
+
+ expect(viewbox.x).toBe(10)
+ expect(viewbox.y).toBe(100)
+ expect(viewbox.width).toBe(200)
+ expect(viewbox.height).toBe(300)
+ })
+
+
+ it('creates a new viewbox from parsed string with exponential values', function() {
+ viewbox = new SVG.ViewBox('-1.12e1 1e-2 +2e2 +.3e+4')
+
+ expect(viewbox.x).toBe(-11.2)
+ expect(viewbox.y).toBe(0.01)
+ expect(viewbox.width).toBe(200)
+ expect(viewbox.height).toBe(3000)
+ })
+
+ it('creates a new viewbox with element given', function() {
+ draw.attr('viewBox', '-1.12e1 1e-2 +2e2 +.3e+4')
+ viewbox = new SVG.ViewBox(draw)
+
+ expect(viewbox.x).toBe(-11.2)
+ expect(viewbox.y).toBe(0.01)
+ expect(viewbox.width).toBe(200)
+ expect(viewbox.height).toBe(3000)
+ })
+
+ })
+
+
+ describe('viewbox()', function() {
+
+ beforeEach(function() {
+ draw.attr('viewBox', null)
+ })
+ afterEach(function() {
+ draw.attr('viewBox', null)
+ })
+
+ it('should set the viewbox when four arguments are provided', function() {
+ draw.viewbox(0,0,100,100)
+ expect(draw.node.getAttribute('viewBox')).toBe('0 0 100 100')
+ })
+ it('should set the viewbox when an object is provided as first argument', function() {
+ draw.viewbox({ x: 0, y: 0, width: 50, height: 50 })
+ expect(draw.node.getAttribute('viewBox')).toBe('0 0 50 50')
+ })
+ it('should set the viewbox when a string is provided as first argument', function() {
+ draw.viewbox('0 0 50 50')
+ expect(draw.node.getAttribute('viewBox')).toBe('0 0 50 50')
+ })
+ it('should set the viewbox when an array is provided as first argument', function() {
+ draw.viewbox([0, 0, 50, 50])
+ expect(draw.node.getAttribute('viewBox')).toBe('0 0 50 50')
+ })
+ it('should accept negative values', function() {
+ draw.size(100,100).viewbox(-100, -100, 50, 50)
+ expect(draw.node.getAttribute('viewBox')).toEqual('-100 -100 50 50')
+ })
+ it('should get the viewbox if no arguments are given', function() {
+ draw.viewbox(0, 0, 100, 100)
+ expect(draw.viewbox()).toEqual(new SVG.ViewBox(draw))
+ })
+ it('should define the zoom of the viewbox in relation to the canvas size', function() {
+ draw.size(100,100).viewbox(0,0,50,50)
+ expect(draw.viewbox().zoom).toEqual(100 / 50)
+ })
+
+ })
+
+ describe('morph()', function() {
+ it('stores a given viewbox for morphing', function() {
+ var viewbox1 = new SVG.ViewBox(10, 100, 200, 300)
+ , viewbox2 = new SVG.ViewBox(50, -100, 300, 300)
+
+ viewbox1.morph(viewbox2)
+
+ expect(viewbox1.destination).toEqual(viewbox2)
+ })
+ it('stores a clone, not the given viewbox itself', function() {
+ var viewbox1 = new SVG.ViewBox(10, 100, 200, 300)
+ , viewbox2 = new SVG.ViewBox(50, -100, 300, 300)
+
+ viewbox1.morph(viewbox2)
+
+ expect(viewbox1.destination).not.toBe(viewbox2)
+ })
+ })
+
+ describe('at()', function() {
+ it('returns a morphed viewbox at a given position', function() {
+ var viewbox1 = new SVG.ViewBox(10, 100, 200, 300)
+ , viewbox2 = new SVG.ViewBox(50, -100, 300, 300)
+ , viewbox3 = viewbox1.morph(viewbox2).at(0.5)
+
+ expect(viewbox1.toString()).toBe('10 100 200 300')
+ expect(viewbox2.toString()).toBe('50 -100 300 300')
+ expect(viewbox3.toString()).toBe('30 0 250 300')
+ })
+ it('returns itself when no destination given', function() {
+ var viewbox = new SVG.ViewBox(10, 100, 200, 300)
+ expect(viewbox.at(0.5)).toBe(viewbox)
+ })
+ })
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/spec/support/jasmine.json b/node_modules/svg.js/spec/support/jasmine.json
new file mode 100644
index 0000000..c7845fc
--- /dev/null
+++ b/node_modules/svg.js/spec/support/jasmine.json
@@ -0,0 +1,9 @@
+{
+ "spec_dir": "spec/spec",
+ "spec_files": [
+ "!(helpers).js"
+ ],
+ "helpers": [
+ "helpers.js"
+ ]
+}
diff --git a/node_modules/svg.js/src/arrange.js b/node_modules/svg.js/src/arrange.js
new file mode 100644
index 0000000..904381f
--- /dev/null
+++ b/node_modules/svg.js/src/arrange.js
@@ -0,0 +1,85 @@
+// ### This module adds backward / forward functionality to elements.
+
+//
+SVG.extend(SVG.Element, {
+ // Get all siblings, including myself
+ siblings: function() {
+ return this.parent().children()
+ }
+ // Get the curent position siblings
+, position: function() {
+ return this.parent().index(this)
+ }
+ // Get the next element (will return null if there is none)
+, next: function() {
+ return this.siblings()[this.position() + 1]
+ }
+ // Get the next element (will return null if there is none)
+, previous: function() {
+ return this.siblings()[this.position() - 1]
+ }
+ // Send given element one step forward
+, forward: function() {
+ var i = this.position() + 1
+ , p = this.parent()
+
+ // move node one step forward
+ p.removeElement(this).add(this, i)
+
+ // make sure defs node is always at the top
+ if (p instanceof SVG.Doc)
+ p.node.appendChild(p.defs().node)
+
+ return this
+ }
+ // Send given element one step backward
+, backward: function() {
+ var i = this.position()
+
+ if (i > 0)
+ this.parent().removeElement(this).add(this, i - 1)
+
+ return this
+ }
+ // Send given element all the way to the front
+, front: function() {
+ var p = this.parent()
+
+ // Move node forward
+ p.node.appendChild(this.node)
+
+ // Make sure defs node is always at the top
+ if (p instanceof SVG.Doc)
+ p.node.appendChild(p.defs().node)
+
+ return this
+ }
+ // Send given element all the way to the back
+, back: function() {
+ if (this.position() > 0)
+ this.parent().removeElement(this).add(this, 0)
+
+ return this
+ }
+ // Inserts a given element before the targeted element
+, before: function(element) {
+ element.remove()
+
+ var i = this.position()
+
+ this.parent().add(element, i)
+
+ return this
+ }
+ // Insters a given element after the targeted element
+, after: function(element) {
+ element.remove()
+
+ var i = this.position()
+
+ this.parent().add(element, i + 1)
+
+ return this
+ }
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/array.js b/node_modules/svg.js/src/array.js
new file mode 100644
index 0000000..9947242
--- /dev/null
+++ b/node_modules/svg.js/src/array.js
@@ -0,0 +1,84 @@
+// Module for array conversion
+SVG.Array = function(array, fallback) {
+ array = (array || []).valueOf()
+
+ // if array is empty and fallback is provided, use fallback
+ if (array.length == 0 && fallback)
+ array = fallback.valueOf()
+
+ // parse array
+ this.value = this.parse(array)
+}
+
+SVG.extend(SVG.Array, {
+ // Make array morphable
+ morph: function(array) {
+ this.destination = this.parse(array)
+
+ // normalize length of arrays
+ if (this.value.length != this.destination.length) {
+ var lastValue = this.value[this.value.length - 1]
+ , lastDestination = this.destination[this.destination.length - 1]
+
+ while(this.value.length > this.destination.length)
+ this.destination.push(lastDestination)
+ while(this.value.length < this.destination.length)
+ this.value.push(lastValue)
+ }
+
+ return this
+ }
+ // Clean up any duplicate points
+, settle: function() {
+ // find all unique values
+ for (var i = 0, il = this.value.length, seen = []; i < il; i++)
+ if (seen.indexOf(this.value[i]) == -1)
+ seen.push(this.value[i])
+
+ // set new value
+ return this.value = seen
+ }
+ // Get morphed array at given position
+, at: function(pos) {
+ // make sure a destination is defined
+ if (!this.destination) return this
+
+ // generate morphed array
+ for (var i = 0, il = this.value.length, array = []; i < il; i++)
+ array.push(this.value[i] + (this.destination[i] - this.value[i]) * pos)
+
+ return new SVG.Array(array)
+ }
+ // Convert array to string
+, toString: function() {
+ return this.value.join(' ')
+ }
+ // Real value
+, valueOf: function() {
+ return this.value
+ }
+ // Parse whitespace separated string
+, parse: function(array) {
+ array = array.valueOf()
+
+ // if already is an array, no need to parse it
+ if (Array.isArray(array)) return array
+
+ return this.split(array)
+ }
+ // Strip unnecessary whitespace
+, split: function(string) {
+ return string.trim().split(SVG.regex.delimiter).map(parseFloat)
+ }
+ // Reverse array
+, reverse: function() {
+ this.value.reverse()
+
+ return this
+ }
+, clone: function() {
+ var clone = new this.constructor()
+ clone.value = array_clone(this.value)
+ return clone
+ }
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/attr.js b/node_modules/svg.js/src/attr.js
new file mode 100644
index 0000000..134ac19
--- /dev/null
+++ b/node_modules/svg.js/src/attr.js
@@ -0,0 +1,79 @@
+SVG.extend(SVG.Element, {
+ // Set svg element attribute
+ attr: function(a, v, n) {
+ // act as full getter
+ if (a == null) {
+ // get an object of attributes
+ a = {}
+ v = this.node.attributes
+ for (n = v.length - 1; n >= 0; n--)
+ a[v[n].nodeName] = SVG.regex.isNumber.test(v[n].nodeValue) ? parseFloat(v[n].nodeValue) : v[n].nodeValue
+
+ return a
+
+ } else if (typeof a == 'object') {
+ // apply every attribute individually if an object is passed
+ for (v in a) this.attr(v, a[v])
+
+ } else if (v === null) {
+ // remove value
+ this.node.removeAttribute(a)
+
+ } else if (v == null) {
+ // act as a getter if the first and only argument is not an object
+ v = this.node.getAttribute(a)
+ return v == null ?
+ SVG.defaults.attrs[a] :
+ SVG.regex.isNumber.test(v) ?
+ parseFloat(v) : v
+
+ } else {
+ // BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0
+ if (a == 'stroke-width')
+ this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null)
+ else if (a == 'stroke')
+ this._stroke = v
+
+ // convert image fill and stroke to patterns
+ if (a == 'fill' || a == 'stroke') {
+ if (SVG.regex.isImage.test(v))
+ v = this.doc().defs().image(v, 0, 0)
+
+ if (v instanceof SVG.Image)
+ v = this.doc().defs().pattern(0, 0, function() {
+ this.add(v)
+ })
+ }
+
+ // ensure correct numeric values (also accepts NaN and Infinity)
+ if (typeof v === 'number')
+ v = new SVG.Number(v)
+
+ // ensure full hex color
+ else if (SVG.Color.isColor(v))
+ v = new SVG.Color(v)
+
+ // parse array values
+ else if (Array.isArray(v))
+ v = new SVG.Array(v)
+
+ // if the passed attribute is leading...
+ if (a == 'leading') {
+ // ... call the leading method instead
+ if (this.leading)
+ this.leading(v)
+ } else {
+ // set given attribute on node
+ typeof n === 'string' ?
+ this.node.setAttributeNS(n, a, v.toString()) :
+ this.node.setAttribute(a, v.toString())
+ }
+
+ // rebuild if required
+ if (this.rebuild && (a == 'font-size' || a == 'x'))
+ this.rebuild(a, v)
+ }
+
+ return this
+ }
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/bare.js b/node_modules/svg.js/src/bare.js
new file mode 100644
index 0000000..5ac4173
--- /dev/null
+++ b/node_modules/svg.js/src/bare.js
@@ -0,0 +1,40 @@
+
+SVG.Bare = SVG.invent({
+ // Initialize
+ create: function(element, inherit) {
+ // construct element
+ this.constructor.call(this, SVG.create(element))
+
+ // inherit custom methods
+ if (inherit)
+ for (var method in inherit.prototype)
+ if (typeof inherit.prototype[method] === 'function')
+ this[method] = inherit.prototype[method]
+ }
+
+ // Inherit from
+, inherit: SVG.Element
+
+ // Add methods
+, extend: {
+ // Insert some plain text
+ words: function(text) {
+ // remove contents
+ while (this.node.hasChildNodes())
+ this.node.removeChild(this.node.lastChild)
+
+ // create text node
+ this.node.appendChild(document.createTextNode(text))
+
+ return this
+ }
+ }
+})
+
+
+SVG.extend(SVG.Parent, {
+ // Create an element that is not described by SVG.js
+ element: function(element, inherit) {
+ return this.put(new SVG.Bare(element, inherit))
+ }
+})
diff --git a/node_modules/svg.js/src/boxes.js b/node_modules/svg.js/src/boxes.js
new file mode 100644
index 0000000..2467625
--- /dev/null
+++ b/node_modules/svg.js/src/boxes.js
@@ -0,0 +1,168 @@
+SVG.Box = SVG.invent({
+ create: function(x, y, width, height) {
+ if (typeof x == 'object' && !(x instanceof SVG.Element)) {
+ // chromes getBoundingClientRect has no x and y property
+ return SVG.Box.call(this, x.left != null ? x.left : x.x , x.top != null ? x.top : x.y, x.width, x.height)
+ } else if (arguments.length == 4) {
+ this.x = x
+ this.y = y
+ this.width = width
+ this.height = height
+ }
+
+ // add center, right, bottom...
+ fullBox(this)
+ }
+, extend: {
+ // Merge rect box with another, return a new instance
+ merge: function(box) {
+ var b = new this.constructor()
+
+ // merge boxes
+ b.x = Math.min(this.x, box.x)
+ b.y = Math.min(this.y, box.y)
+ b.width = Math.max(this.x + this.width, box.x + box.width) - b.x
+ b.height = Math.max(this.y + this.height, box.y + box.height) - b.y
+
+ return fullBox(b)
+ }
+
+ , transform: function(m) {
+ var xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, p, bbox
+
+ var pts = [
+ new SVG.Point(this.x, this.y),
+ new SVG.Point(this.x2, this.y),
+ new SVG.Point(this.x, this.y2),
+ new SVG.Point(this.x2, this.y2)
+ ]
+
+ pts.forEach(function(p) {
+ p = p.transform(m)
+ xMin = Math.min(xMin,p.x)
+ xMax = Math.max(xMax,p.x)
+ yMin = Math.min(yMin,p.y)
+ yMax = Math.max(yMax,p.y)
+ })
+
+ bbox = new this.constructor()
+ bbox.x = xMin
+ bbox.width = xMax-xMin
+ bbox.y = yMin
+ bbox.height = yMax-yMin
+
+ fullBox(bbox)
+
+ return bbox
+ }
+ }
+})
+
+SVG.BBox = SVG.invent({
+ // Initialize
+ create: function(element) {
+ SVG.Box.apply(this, [].slice.call(arguments))
+
+ // get values if element is given
+ if (element instanceof SVG.Element) {
+ var box
+
+ // yes this is ugly, but Firefox can be a pain when it comes to elements that are not yet rendered
+ try {
+
+ if (!document.documentElement.contains){
+ // This is IE - it does not support contains() for top-level SVGs
+ var topParent = element.node
+ while (topParent.parentNode){
+ topParent = topParent.parentNode
+ }
+ if (topParent != document) throw new Exception('Element not in the dom')
+ } else {
+ // the element is NOT in the dom, throw error
+ if(!document.documentElement.contains(element.node)) throw new Exception('Element not in the dom')
+ }
+
+ // find native bbox
+ box = element.node.getBBox()
+ } catch(e) {
+ if(element instanceof SVG.Shape){
+ var clone = element.clone(SVG.parser.draw.instance).show()
+ box = clone.node.getBBox()
+ clone.remove()
+ }else{
+ box = {
+ x: element.node.clientLeft
+ , y: element.node.clientTop
+ , width: element.node.clientWidth
+ , height: element.node.clientHeight
+ }
+ }
+ }
+
+ SVG.Box.call(this, box)
+ }
+
+ }
+
+ // Define ancestor
+, inherit: SVG.Box
+
+ // Define Parent
+, parent: SVG.Element
+
+ // Constructor
+, construct: {
+ // Get bounding box
+ bbox: function() {
+ return new SVG.BBox(this)
+ }
+ }
+
+})
+
+SVG.BBox.prototype.constructor = SVG.BBox
+
+
+SVG.extend(SVG.Element, {
+ tbox: function(){
+ console.warn('Use of TBox is deprecated and mapped to RBox. Use .rbox() instead.')
+ return this.rbox(this.doc())
+ }
+})
+
+SVG.RBox = SVG.invent({
+ // Initialize
+ create: function(element) {
+ SVG.Box.apply(this, [].slice.call(arguments))
+
+ if (element instanceof SVG.Element) {
+ SVG.Box.call(this, element.node.getBoundingClientRect())
+ }
+ }
+
+, inherit: SVG.Box
+
+ // define Parent
+, parent: SVG.Element
+
+, extend: {
+ addOffset: function() {
+ // offset by window scroll position, because getBoundingClientRect changes when window is scrolled
+ this.x += window.pageXOffset
+ this.y += window.pageYOffset
+ return this
+ }
+ }
+
+ // Constructor
+, construct: {
+ // Get rect box
+ rbox: function(el) {
+ if (el) return new SVG.RBox(this).transform(el.screenCTM().inverse())
+ return new SVG.RBox(this).addOffset()
+ }
+ }
+
+})
+
+SVG.RBox.prototype.constructor = SVG.RBox
diff --git a/node_modules/svg.js/src/clip.js b/node_modules/svg.js/src/clip.js
new file mode 100644
index 0000000..2a92e44
--- /dev/null
+++ b/node_modules/svg.js/src/clip.js
@@ -0,0 +1,58 @@
+SVG.ClipPath = SVG.invent({
+ // Initialize node
+ create: function() {
+ this.constructor.call(this, SVG.create('clipPath'))
+
+ // keep references to clipped elements
+ this.targets = []
+ }
+
+ // Inherit from
+, inherit: SVG.Container
+
+ // Add class methods
+, extend: {
+ // Unclip all clipped elements and remove itself
+ remove: function() {
+ // unclip all targets
+ for (var i = this.targets.length - 1; i >= 0; i--)
+ if (this.targets[i])
+ this.targets[i].unclip()
+ this.targets = []
+
+ // remove clipPath from parent
+ this.parent().removeElement(this)
+
+ return this
+ }
+ }
+
+ // Add parent method
+, construct: {
+ // Create clipping element
+ clip: function() {
+ return this.defs().put(new SVG.ClipPath)
+ }
+ }
+})
+
+//
+SVG.extend(SVG.Element, {
+ // Distribute clipPath to svg element
+ clipWith: function(element) {
+ // use given clip or create a new one
+ this.clipper = element instanceof SVG.ClipPath ? element : this.parent().clip().add(element)
+
+ // store reverence on self in mask
+ this.clipper.targets.push(this)
+
+ // apply mask
+ return this.attr('clip-path', 'url("#' + this.clipper.attr('id') + '")')
+ }
+ // Unclip element
+, unclip: function() {
+ delete this.clipper
+ return this.attr('clip-path', null)
+ }
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/color.js b/node_modules/svg.js/src/color.js
new file mode 100644
index 0000000..9d77d4e
--- /dev/null
+++ b/node_modules/svg.js/src/color.js
@@ -0,0 +1,108 @@
+// Module for color convertions
+SVG.Color = function(color) {
+ var match
+
+ // initialize defaults
+ this.r = 0
+ this.g = 0
+ this.b = 0
+
+ if(!color) return
+
+ // parse color
+ if (typeof color === 'string') {
+ if (SVG.regex.isRgb.test(color)) {
+ // get rgb values
+ match = SVG.regex.rgb.exec(color.replace(SVG.regex.whitespace,''))
+
+ // parse numeric values
+ this.r = parseInt(match[1])
+ this.g = parseInt(match[2])
+ this.b = parseInt(match[3])
+
+ } else if (SVG.regex.isHex.test(color)) {
+ // get hex values
+ match = SVG.regex.hex.exec(fullHex(color))
+
+ // parse numeric values
+ this.r = parseInt(match[1], 16)
+ this.g = parseInt(match[2], 16)
+ this.b = parseInt(match[3], 16)
+
+ }
+
+ } else if (typeof color === 'object') {
+ this.r = color.r
+ this.g = color.g
+ this.b = color.b
+
+ }
+
+}
+
+SVG.extend(SVG.Color, {
+ // Default to hex conversion
+ toString: function() {
+ return this.toHex()
+ }
+ // Build hex value
+, toHex: function() {
+ return '#'
+ + compToHex(this.r)
+ + compToHex(this.g)
+ + compToHex(this.b)
+ }
+ // Build rgb value
+, toRgb: function() {
+ return 'rgb(' + [this.r, this.g, this.b].join() + ')'
+ }
+ // Calculate true brightness
+, brightness: function() {
+ return (this.r / 255 * 0.30)
+ + (this.g / 255 * 0.59)
+ + (this.b / 255 * 0.11)
+ }
+ // Make color morphable
+, morph: function(color) {
+ this.destination = new SVG.Color(color)
+
+ return this
+ }
+ // Get morphed color at given position
+, at: function(pos) {
+ // make sure a destination is defined
+ if (!this.destination) return this
+
+ // normalise pos
+ pos = pos < 0 ? 0 : pos > 1 ? 1 : pos
+
+ // generate morphed color
+ return new SVG.Color({
+ r: ~~(this.r + (this.destination.r - this.r) * pos)
+ , g: ~~(this.g + (this.destination.g - this.g) * pos)
+ , b: ~~(this.b + (this.destination.b - this.b) * pos)
+ })
+ }
+
+})
+
+// Testers
+
+// Test if given value is a color string
+SVG.Color.test = function(color) {
+ color += ''
+ return SVG.regex.isHex.test(color)
+ || SVG.regex.isRgb.test(color)
+}
+
+// Test if given value is a rgb object
+SVG.Color.isRgb = function(color) {
+ return color && typeof color.r == 'number'
+ && typeof color.g == 'number'
+ && typeof color.b == 'number'
+}
+
+// Test if given value is a color
+SVG.Color.isColor = function(color) {
+ return SVG.Color.isRgb(color) || SVG.Color.test(color)
+}
\ No newline at end of file
diff --git a/node_modules/svg.js/src/container.js b/node_modules/svg.js/src/container.js
new file mode 100644
index 0000000..68adf93
--- /dev/null
+++ b/node_modules/svg.js/src/container.js
@@ -0,0 +1,10 @@
+SVG.Container = SVG.invent({
+ // Initialize node
+ create: function(element) {
+ this.constructor.call(this, element)
+ }
+
+ // Inherit from
+, inherit: SVG.Parent
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/data.js b/node_modules/svg.js/src/data.js
new file mode 100644
index 0000000..cefa21c
--- /dev/null
+++ b/node_modules/svg.js/src/data.js
@@ -0,0 +1,29 @@
+
+SVG.extend(SVG.Element, {
+ // Store data values on svg nodes
+ data: function(a, v, r) {
+ if (typeof a == 'object') {
+ for (v in a)
+ this.data(v, a[v])
+
+ } else if (arguments.length < 2) {
+ try {
+ return JSON.parse(this.attr('data-' + a))
+ } catch(e) {
+ return this.attr('data-' + a)
+ }
+
+ } else {
+ this.attr(
+ 'data-' + a
+ , v === null ?
+ null :
+ r === true || typeof v === 'string' || typeof v === 'number' ?
+ v :
+ JSON.stringify(v)
+ )
+ }
+
+ return this
+ }
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/default.js b/node_modules/svg.js/src/default.js
new file mode 100644
index 0000000..d371ca0
--- /dev/null
+++ b/node_modules/svg.js/src/default.js
@@ -0,0 +1,36 @@
+
+SVG.defaults = {
+ // Default attribute values
+ attrs: {
+ // fill and stroke
+ 'fill-opacity': 1
+ , 'stroke-opacity': 1
+ , 'stroke-width': 0
+ , 'stroke-linejoin': 'miter'
+ , 'stroke-linecap': 'butt'
+ , fill: '#000000'
+ , stroke: '#000000'
+ , opacity: 1
+ // position
+ , x: 0
+ , y: 0
+ , cx: 0
+ , cy: 0
+ // size
+ , width: 0
+ , height: 0
+ // radius
+ , r: 0
+ , rx: 0
+ , ry: 0
+ // gradient
+ , offset: 0
+ , 'stop-opacity': 1
+ , 'stop-color': '#000000'
+ // text
+ , 'font-size': 16
+ , 'font-family': 'Helvetica, Arial, sans-serif'
+ , 'text-anchor': 'start'
+ }
+
+}
\ No newline at end of file
diff --git a/node_modules/svg.js/src/defs.js b/node_modules/svg.js/src/defs.js
new file mode 100644
index 0000000..ad66cc5
--- /dev/null
+++ b/node_modules/svg.js/src/defs.js
@@ -0,0 +1,9 @@
+
+SVG.Defs = SVG.invent({
+ // Initialize node
+ create: 'defs'
+
+ // Inherit from
+, inherit: SVG.Container
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/doc.js b/node_modules/svg.js/src/doc.js
new file mode 100644
index 0000000..1be4607
--- /dev/null
+++ b/node_modules/svg.js/src/doc.js
@@ -0,0 +1,115 @@
+SVG.Doc = SVG.invent({
+ // Initialize node
+ create: function(element) {
+ if (element) {
+ // ensure the presence of a dom element
+ element = typeof element == 'string' ?
+ document.getElementById(element) :
+ element
+
+ // If the target is an svg element, use that element as the main wrapper.
+ // This allows svg.js to work with svg documents as well.
+ if (element.nodeName == 'svg') {
+ this.constructor.call(this, element)
+ } else {
+ this.constructor.call(this, SVG.create('svg'))
+ element.appendChild(this.node)
+ this.size('100%', '100%')
+ }
+
+ // set svg element attributes and ensure defs node
+ this.namespace().defs()
+ }
+ }
+
+ // Inherit from
+, inherit: SVG.Container
+
+ // Add class methods
+, extend: {
+ // Add namespaces
+ namespace: function() {
+ return this
+ .attr({ xmlns: SVG.ns, version: '1.1' })
+ .attr('xmlns:xlink', SVG.xlink, SVG.xmlns)
+ .attr('xmlns:svgjs', SVG.svgjs, SVG.xmlns)
+ }
+ // Creates and returns defs element
+ , defs: function() {
+ if (!this._defs) {
+ var defs
+
+ // Find or create a defs element in this instance
+ if (defs = this.node.getElementsByTagName('defs')[0])
+ this._defs = SVG.adopt(defs)
+ else
+ this._defs = new SVG.Defs
+
+ // Make sure the defs node is at the end of the stack
+ this.node.appendChild(this._defs.node)
+ }
+
+ return this._defs
+ }
+ // custom parent method
+ , parent: function() {
+ if(!this.node.parentNode || this.node.parentNode.nodeName == '#document' || this.node.parentNode.nodeName == '#document-fragment') return null
+ return this.node.parentNode
+ }
+ // Fix for possible sub-pixel offset. See:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=608812
+ , spof: function() {
+ var pos = this.node.getScreenCTM()
+
+ if (pos)
+ this
+ .style('left', (-pos.e % 1) + 'px')
+ .style('top', (-pos.f % 1) + 'px')
+
+ return this
+ }
+
+ // Removes the doc from the DOM
+ , remove: function() {
+ if(this.parent()) {
+ this.parent().removeChild(this.node)
+ }
+
+ return this
+ }
+ , clear: function() {
+ // remove children
+ while(this.node.hasChildNodes())
+ this.node.removeChild(this.node.lastChild)
+
+ // remove defs reference
+ delete this._defs
+
+ // add back parser
+ if(!SVG.parser.draw.parentNode)
+ this.node.appendChild(SVG.parser.draw)
+
+ return this
+ }
+ , clone: function (parent) {
+ // write dom data to the dom so the clone can pickup the data
+ this.writeDataToDom()
+
+ // get reference to node
+ var node = this.node
+
+ // clone element and assign new id
+ var clone = assignNewId(node.cloneNode(true))
+
+ // insert the clone in the given parent or after myself
+ if(parent) {
+ (parent.node || parent).appendChild(clone.node)
+ } else {
+ node.parentNode.insertBefore(clone.node, node.nextSibling)
+ }
+
+ return clone
+ }
+ }
+
+})
diff --git a/node_modules/svg.js/src/element.js b/node_modules/svg.js/src/element.js
new file mode 100644
index 0000000..d7dcbc1
--- /dev/null
+++ b/node_modules/svg.js/src/element.js
@@ -0,0 +1,271 @@
+
+SVG.Element = SVG.invent({
+ // Initialize node
+ create: function(node) {
+ // make stroke value accessible dynamically
+ this._stroke = SVG.defaults.attrs.stroke
+ this._event = null
+ this._events = {}
+
+ // initialize data object
+ this.dom = {}
+
+ // create circular reference
+ if (this.node = node) {
+ this.type = node.nodeName
+ this.node.instance = this
+ this._events = node._events || {}
+
+ // store current attribute value
+ this._stroke = node.getAttribute('stroke') || this._stroke
+ }
+ }
+
+ // Add class methods
+, extend: {
+ // Move over x-axis
+ x: function(x) {
+ return this.attr('x', x)
+ }
+ // Move over y-axis
+ , y: function(y) {
+ return this.attr('y', y)
+ }
+ // Move by center over x-axis
+ , cx: function(x) {
+ return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2)
+ }
+ // Move by center over y-axis
+ , cy: function(y) {
+ return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2)
+ }
+ // Move element to given x and y values
+ , move: function(x, y) {
+ return this.x(x).y(y)
+ }
+ // Move element by its center
+ , center: function(x, y) {
+ return this.cx(x).cy(y)
+ }
+ // Set width of element
+ , width: function(width) {
+ return this.attr('width', width)
+ }
+ // Set height of element
+ , height: function(height) {
+ return this.attr('height', height)
+ }
+ // Set element size to given width and height
+ , size: function(width, height) {
+ var p = proportionalSize(this, width, height)
+
+ return this
+ .width(new SVG.Number(p.width))
+ .height(new SVG.Number(p.height))
+ }
+ // Clone element
+ , clone: function(parent) {
+ // write dom data to the dom so the clone can pickup the data
+ this.writeDataToDom()
+
+ // clone element and assign new id
+ var clone = assignNewId(this.node.cloneNode(true))
+
+ // insert the clone in the given parent or after myself
+ if(parent) parent.add(clone)
+ else this.after(clone)
+
+ return clone
+ }
+ // Remove element
+ , remove: function() {
+ if (this.parent())
+ this.parent().removeElement(this)
+
+ return this
+ }
+ // Replace element
+ , replace: function(element) {
+ this.after(element).remove()
+
+ return element
+ }
+ // Add element to given container and return self
+ , addTo: function(parent) {
+ return parent.put(this)
+ }
+ // Add element to given container and return container
+ , putIn: function(parent) {
+ return parent.add(this)
+ }
+ // Get / set id
+ , id: function(id) {
+ return this.attr('id', id)
+ }
+ // Checks whether the given point inside the bounding box of the element
+ , inside: function(x, y) {
+ var box = this.bbox()
+
+ return x > box.x
+ && y > box.y
+ && x < box.x + box.width
+ && y < box.y + box.height
+ }
+ // Show element
+ , show: function() {
+ return this.style('display', '')
+ }
+ // Hide element
+ , hide: function() {
+ return this.style('display', 'none')
+ }
+ // Is element visible?
+ , visible: function() {
+ return this.style('display') != 'none'
+ }
+ // Return id on string conversion
+ , toString: function() {
+ return this.attr('id')
+ }
+ // Return array of classes on the node
+ , classes: function() {
+ var attr = this.attr('class')
+
+ return attr == null ? [] : attr.trim().split(SVG.regex.delimiter)
+ }
+ // Return true if class exists on the node, false otherwise
+ , hasClass: function(name) {
+ return this.classes().indexOf(name) != -1
+ }
+ // Add class to the node
+ , addClass: function(name) {
+ if (!this.hasClass(name)) {
+ var array = this.classes()
+ array.push(name)
+ this.attr('class', array.join(' '))
+ }
+
+ return this
+ }
+ // Remove class from the node
+ , removeClass: function(name) {
+ if (this.hasClass(name)) {
+ this.attr('class', this.classes().filter(function(c) {
+ return c != name
+ }).join(' '))
+ }
+
+ return this
+ }
+ // Toggle the presence of a class on the node
+ , toggleClass: function(name) {
+ return this.hasClass(name) ? this.removeClass(name) : this.addClass(name)
+ }
+ // Get referenced element form attribute value
+ , reference: function(attr) {
+ return SVG.get(this.attr(attr))
+ }
+ // Returns the parent element instance
+ , parent: function(type) {
+ var parent = this
+
+ // check for parent
+ if(!parent.node.parentNode) return null
+
+ // get parent element
+ parent = SVG.adopt(parent.node.parentNode)
+
+ if(!type) return parent
+
+ // loop trough ancestors if type is given
+ while(parent && parent.node instanceof window.SVGElement){
+ if(typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent
+ if(!parent.node.parentNode || parent.node.parentNode.nodeName == '#document' || parent.node.parentNode.nodeName == '#document-fragment') return null // #759, #720
+ parent = SVG.adopt(parent.node.parentNode)
+ }
+ }
+ // Get parent document
+ , doc: function() {
+ return this instanceof SVG.Doc ? this : this.parent(SVG.Doc)
+ }
+ // return array of all ancestors of given type up to the root svg
+ , parents: function(type) {
+ var parents = [], parent = this
+
+ do{
+ parent = parent.parent(type)
+ if(!parent || !parent.node) break
+
+ parents.push(parent)
+ } while(parent.parent)
+
+ return parents
+ }
+ // matches the element vs a css selector
+ , matches: function(selector){
+ return matches(this.node, selector)
+ }
+ // Returns the svg node to call native svg methods on it
+ , native: function() {
+ return this.node
+ }
+ // Import raw svg
+ , svg: function(svg) {
+ // create temporary holder
+ var well = document.createElement('svg')
+
+ // act as a setter if svg is given
+ if (svg && this instanceof SVG.Parent) {
+ // dump raw svg
+ well.innerHTML = '' + svg.replace(/\n/, '').replace(/<([\w:-]+)([^<]+?)\/>/g, '<$1$2>$1>') + ' '
+
+ // transplant nodes
+ for (var i = 0, il = well.firstChild.childNodes.length; i < il; i++)
+ this.node.appendChild(well.firstChild.firstChild)
+
+ // otherwise act as a getter
+ } else {
+ // create a wrapping svg element in case of partial content
+ well.appendChild(svg = document.createElement('svg'))
+
+ // write svgjs data to the dom
+ this.writeDataToDom()
+
+ // insert a copy of this node
+ svg.appendChild(this.node.cloneNode(true))
+
+ // return target element
+ return well.innerHTML.replace(/^/, '').replace(/<\/svg>$/, '')
+ }
+
+ return this
+ }
+ // write svgjs data to the dom
+ , writeDataToDom: function() {
+
+ // dump variables recursively
+ if(this.each || this.lines){
+ var fn = this.each ? this : this.lines();
+ fn.each(function(){
+ this.writeDataToDom()
+ })
+ }
+
+ // remove previously set data
+ this.node.removeAttribute('svgjs:data')
+
+ if(Object.keys(this.dom).length)
+ this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)) // see #428
+
+ return this
+ }
+ // set given data to the elements data property
+ , setData: function(o){
+ this.dom = o
+ return this
+ }
+ , is: function(obj){
+ return is(this, obj)
+ }
+ }
+})
diff --git a/node_modules/svg.js/src/ellipse.js b/node_modules/svg.js/src/ellipse.js
new file mode 100644
index 0000000..043084c
--- /dev/null
+++ b/node_modules/svg.js/src/ellipse.js
@@ -0,0 +1,89 @@
+SVG.Circle = SVG.invent({
+ // Initialize node
+ create: 'circle'
+
+ // Inherit from
+, inherit: SVG.Shape
+
+ // Add parent method
+, construct: {
+ // Create circle element, based on ellipse
+ circle: function(size) {
+ return this.put(new SVG.Circle).rx(new SVG.Number(size).divide(2)).move(0, 0)
+ }
+ }
+})
+
+SVG.extend(SVG.Circle, SVG.FX, {
+ // Radius x value
+ rx: function(rx) {
+ return this.attr('r', rx)
+ }
+ // Alias radius x value
+, ry: function(ry) {
+ return this.rx(ry)
+ }
+})
+
+SVG.Ellipse = SVG.invent({
+ // Initialize node
+ create: 'ellipse'
+
+ // Inherit from
+, inherit: SVG.Shape
+
+ // Add parent method
+, construct: {
+ // Create an ellipse
+ ellipse: function(width, height) {
+ return this.put(new SVG.Ellipse).size(width, height).move(0, 0)
+ }
+ }
+})
+
+SVG.extend(SVG.Ellipse, SVG.Rect, SVG.FX, {
+ // Radius x value
+ rx: function(rx) {
+ return this.attr('rx', rx)
+ }
+ // Radius y value
+, ry: function(ry) {
+ return this.attr('ry', ry)
+ }
+})
+
+// Add common method
+SVG.extend(SVG.Circle, SVG.Ellipse, {
+ // Move over x-axis
+ x: function(x) {
+ return x == null ? this.cx() - this.rx() : this.cx(x + this.rx())
+ }
+ // Move over y-axis
+ , y: function(y) {
+ return y == null ? this.cy() - this.ry() : this.cy(y + this.ry())
+ }
+ // Move by center over x-axis
+ , cx: function(x) {
+ return x == null ? this.attr('cx') : this.attr('cx', x)
+ }
+ // Move by center over y-axis
+ , cy: function(y) {
+ return y == null ? this.attr('cy') : this.attr('cy', y)
+ }
+ // Set width of element
+ , width: function(width) {
+ return width == null ? this.rx() * 2 : this.rx(new SVG.Number(width).divide(2))
+ }
+ // Set height of element
+ , height: function(height) {
+ return height == null ? this.ry() * 2 : this.ry(new SVG.Number(height).divide(2))
+ }
+ // Custom size function
+ , size: function(width, height) {
+ var p = proportionalSize(this, width, height)
+
+ return this
+ .rx(new SVG.Number(p.width).divide(2))
+ .ry(new SVG.Number(p.height).divide(2))
+ }
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/event.js b/node_modules/svg.js/src/event.js
new file mode 100644
index 0000000..1097e25
--- /dev/null
+++ b/node_modules/svg.js/src/event.js
@@ -0,0 +1,140 @@
+// Add events to elements
+
+;[ 'click',
+ 'dblclick',
+ 'mousedown',
+ 'mouseup',
+ 'mouseover',
+ 'mouseout',
+ 'mousemove',
+ 'mouseenter',
+ 'mouseleave',
+ 'touchstart',
+ 'touchmove',
+ 'touchleave',
+ 'touchend',
+ 'touchcancel' ].forEach(function (event) {
+ // add event to SVG.Element
+ SVG.Element.prototype[event] = function (f) {
+ // bind event to element rather than element node
+ if (f == null) {
+ SVG.off(this, event)
+ } else {
+ SVG.on(this, event, f)
+ }
+ return this
+ }
+ })
+
+SVG.listenerId = 0
+
+// Add event binder in the SVG namespace
+SVG.on = function (node, events, listener, binding, options) {
+ var l = listener.bind(binding || node)
+ var n = node instanceof SVG.Element ? node.node : node
+
+ // ensure instance object for nodes which are not adopted
+ n.instance = n.instance || {_events: {}}
+
+ var bag = n.instance._events
+
+ // add id to listener
+ if (!listener._svgjsListenerId) { listener._svgjsListenerId = ++SVG.listenerId }
+
+ events.split(SVG.regex.delimiter).forEach(function (event) {
+ var ev = event.split('.')[0]
+ var ns = event.split('.')[1] || '*'
+
+ // ensure valid object
+ bag[ev] = bag[ev] || {}
+ bag[ev][ns] = bag[ev][ns] || {}
+
+ // reference listener
+ bag[ev][ns][listener._svgjsListenerId] = l
+
+ // add listener
+ n.addEventListener(ev, l, options || false)
+ })
+}
+
+// Add event unbinder in the SVG namespace
+SVG.off = function (node, events, listener, options) {
+ var n = node instanceof SVG.Element ? node.node : node
+ if (!n.instance) return
+
+ // listener can be a function or a number
+ if (typeof listener === 'function') {
+ listener = listener._svgjsListenerId
+ if (!listener) return
+ }
+
+ var bag = n.instance._events
+
+ ;(events || '').split(SVG.regex.delimiter).forEach(function (event) {
+ var ev = event && event.split('.')[0]
+ var ns = event && event.split('.')[1]
+ var namespace, l
+
+ if (listener) {
+ // remove listener reference
+ if (bag[ev] && bag[ev][ns || '*']) {
+ // removeListener
+ n.removeEventListener(ev, bag[ev][ns || '*'][listener], options || false)
+
+ delete bag[ev][ns || '*'][listener]
+ }
+ } else if (ev && ns) {
+ // remove all listeners for a namespaced event
+ if (bag[ev] && bag[ev][ns]) {
+ for (l in bag[ev][ns]) { SVG.off(n, [ev, ns].join('.'), l) }
+
+ delete bag[ev][ns]
+ }
+ } else if (ns) {
+ // remove all listeners for a specific namespace
+ for (event in bag) {
+ for (namespace in bag[event]) {
+ if (ns === namespace) { SVG.off(n, [event, ns].join('.')) }
+ }
+ }
+ } else if (ev) {
+ // remove all listeners for the event
+ if (bag[ev]) {
+ for (namespace in bag[ev]) { SVG.off(n, [ev, namespace].join('.')) }
+
+ delete bag[ev]
+ }
+ } else {
+ // remove all listeners on a given node
+ for (event in bag) { SVG.off(n, event) }
+
+ n.instance._events = {}
+ }
+ })
+}
+
+SVG.extend(SVG.Element, {
+ // Bind given event to listener
+ on: function (event, listener, binding, options) {
+ SVG.on(this, event, listener, binding, options)
+ return this
+ },
+ // Unbind event from listener
+ off: function (event, listener) {
+ SVG.off(this.node, event, listener)
+ return this
+ },
+ fire: function (event, data) {
+ // Dispatch event
+ if (event instanceof window.Event) {
+ this.node.dispatchEvent(event)
+ } else {
+ this.node.dispatchEvent(event = new SVG.CustomEvent(event, {detail: data, cancelable: true}))
+ }
+ this._event = event
+ return this
+ },
+ event: function() {
+ return this._event
+ }
+})
diff --git a/node_modules/svg.js/src/fx.js b/node_modules/svg.js/src/fx.js
new file mode 100644
index 0000000..ea1c293
--- /dev/null
+++ b/node_modules/svg.js/src/fx.js
@@ -0,0 +1,920 @@
+SVG.easing = {
+ '-': function(pos){return pos}
+, '<>':function(pos){return -Math.cos(pos * Math.PI) / 2 + 0.5}
+, '>': function(pos){return Math.sin(pos * Math.PI / 2)}
+, '<': function(pos){return -Math.cos(pos * Math.PI / 2) + 1}
+}
+
+SVG.morph = function(pos){
+ return function(from, to) {
+ return new SVG.MorphObj(from, to).at(pos)
+ }
+}
+
+SVG.Situation = SVG.invent({
+
+ create: function(o){
+ this.init = false
+ this.reversed = false
+ this.reversing = false
+
+ this.duration = new SVG.Number(o.duration).valueOf()
+ this.delay = new SVG.Number(o.delay).valueOf()
+
+ this.start = +new Date() + this.delay
+ this.finish = this.start + this.duration
+ this.ease = o.ease
+
+ // this.loop is incremented from 0 to this.loops
+ // it is also incremented when in an infinite loop (when this.loops is true)
+ this.loop = 0
+ this.loops = false
+
+ this.animations = {
+ // functionToCall: [list of morphable objects]
+ // e.g. move: [SVG.Number, SVG.Number]
+ }
+
+ this.attrs = {
+ // holds all attributes which are not represented from a function svg.js provides
+ // e.g. someAttr: SVG.Number
+ }
+
+ this.styles = {
+ // holds all styles which should be animated
+ // e.g. fill-color: SVG.Color
+ }
+
+ this.transforms = [
+ // holds all transformations as transformation objects
+ // e.g. [SVG.Rotate, SVG.Translate, SVG.Matrix]
+ ]
+
+ this.once = {
+ // functions to fire at a specific position
+ // e.g. "0.5": function foo(){}
+ }
+
+ }
+
+})
+
+
+SVG.FX = SVG.invent({
+
+ create: function(element) {
+ this._target = element
+ this.situations = []
+ this.active = false
+ this.situation = null
+ this.paused = false
+ this.lastPos = 0
+ this.pos = 0
+ // The absolute position of an animation is its position in the context of its complete duration (including delay and loops)
+ // When performing a delay, absPos is below 0 and when performing a loop, its value is above 1
+ this.absPos = 0
+ this._speed = 1
+ }
+
+, extend: {
+
+ /**
+ * sets or returns the target of this animation
+ * @param o object || number In case of Object it holds all parameters. In case of number its the duration of the animation
+ * @param ease function || string Function which should be used for easing or easing keyword
+ * @param delay Number indicating the delay before the animation starts
+ * @return target || this
+ */
+ animate: function(o, ease, delay){
+
+ if(typeof o == 'object'){
+ ease = o.ease
+ delay = o.delay
+ o = o.duration
+ }
+
+ var situation = new SVG.Situation({
+ duration: o || 1000,
+ delay: delay || 0,
+ ease: SVG.easing[ease || '-'] || ease
+ })
+
+ this.queue(situation)
+
+ return this
+ }
+
+ /**
+ * sets a delay before the next element of the queue is called
+ * @param delay Duration of delay in milliseconds
+ * @return this.target()
+ */
+ , delay: function(delay){
+ // The delay is performed by an empty situation with its duration
+ // attribute set to the duration of the delay
+ var situation = new SVG.Situation({
+ duration: delay,
+ delay: 0,
+ ease: SVG.easing['-']
+ })
+
+ return this.queue(situation)
+ }
+
+ /**
+ * sets or returns the target of this animation
+ * @param null || target SVG.Element which should be set as new target
+ * @return target || this
+ */
+ , target: function(target){
+ if(target && target instanceof SVG.Element){
+ this._target = target
+ return this
+ }
+
+ return this._target
+ }
+
+ // returns the absolute position at a given time
+ , timeToAbsPos: function(timestamp){
+ return (timestamp - this.situation.start) / (this.situation.duration/this._speed)
+ }
+
+ // returns the timestamp from a given absolute positon
+ , absPosToTime: function(absPos){
+ return this.situation.duration/this._speed * absPos + this.situation.start
+ }
+
+ // starts the animationloop
+ , startAnimFrame: function(){
+ this.stopAnimFrame()
+ this.animationFrame = window.requestAnimationFrame(function(){ this.step() }.bind(this))
+ }
+
+ // cancels the animationframe
+ , stopAnimFrame: function(){
+ window.cancelAnimationFrame(this.animationFrame)
+ }
+
+ // kicks off the animation - only does something when the queue is currently not active and at least one situation is set
+ , start: function(){
+ // dont start if already started
+ if(!this.active && this.situation){
+ this.active = true
+ this.startCurrent()
+ }
+
+ return this
+ }
+
+ // start the current situation
+ , startCurrent: function(){
+ this.situation.start = +new Date + this.situation.delay/this._speed
+ this.situation.finish = this.situation.start + this.situation.duration/this._speed
+ return this.initAnimations().step()
+ }
+
+ /**
+ * adds a function / Situation to the animation queue
+ * @param fn function / situation to add
+ * @return this
+ */
+ , queue: function(fn){
+ if(typeof fn == 'function' || fn instanceof SVG.Situation)
+ this.situations.push(fn)
+
+ if(!this.situation) this.situation = this.situations.shift()
+
+ return this
+ }
+
+ /**
+ * pulls next element from the queue and execute it
+ * @return this
+ */
+ , dequeue: function(){
+ // stop current animation
+ this.stop()
+
+ // get next animation from queue
+ this.situation = this.situations.shift()
+
+ if(this.situation){
+ if(this.situation instanceof SVG.Situation) {
+ this.start()
+ } else {
+ // If it is not a SVG.Situation, then it is a function, we execute it
+ this.situation.call(this)
+ }
+ }
+
+ return this
+ }
+
+ // updates all animations to the current state of the element
+ // this is important when one property could be changed from another property
+ , initAnimations: function() {
+ var i, j, source
+ var s = this.situation
+
+ if(s.init) return this
+
+ for(i in s.animations){
+ source = this.target()[i]()
+
+ if(!Array.isArray(source)) {
+ source = [source]
+ }
+
+ if(!Array.isArray(s.animations[i])) {
+ s.animations[i] = [s.animations[i]]
+ }
+
+ //if(s.animations[i].length > source.length) {
+ // source.concat = source.concat(s.animations[i].slice(source.length, s.animations[i].length))
+ //}
+
+ for(j = source.length; j--;) {
+ // The condition is because some methods return a normal number instead
+ // of a SVG.Number
+ if(s.animations[i][j] instanceof SVG.Number)
+ source[j] = new SVG.Number(source[j])
+
+ s.animations[i][j] = source[j].morph(s.animations[i][j])
+ }
+ }
+
+ for(i in s.attrs){
+ s.attrs[i] = new SVG.MorphObj(this.target().attr(i), s.attrs[i])
+ }
+
+ for(i in s.styles){
+ s.styles[i] = new SVG.MorphObj(this.target().style(i), s.styles[i])
+ }
+
+ s.initialTransformation = this.target().matrixify()
+
+ s.init = true
+ return this
+ }
+ , clearQueue: function(){
+ this.situations = []
+ return this
+ }
+ , clearCurrent: function(){
+ this.situation = null
+ return this
+ }
+ /** stops the animation immediately
+ * @param jumpToEnd A Boolean indicating whether to complete the current animation immediately.
+ * @param clearQueue A Boolean indicating whether to remove queued animation as well.
+ * @return this
+ */
+ , stop: function(jumpToEnd, clearQueue){
+ var active = this.active
+ this.active = false
+
+ if(clearQueue){
+ this.clearQueue()
+ }
+
+ if(jumpToEnd && this.situation){
+ // initialize the situation if it was not
+ !active && this.startCurrent()
+ this.atEnd()
+ }
+
+ this.stopAnimFrame()
+
+ return this.clearCurrent()
+ }
+
+ /** resets the element to the state where the current element has started
+ * @return this
+ */
+ , reset: function(){
+ if(this.situation){
+ var temp = this.situation
+ this.stop()
+ this.situation = temp
+ this.atStart()
+ }
+ return this
+ }
+
+ // Stop the currently-running animation, remove all queued animations, and complete all animations for the element.
+ , finish: function(){
+
+ this.stop(true, false)
+
+ while(this.dequeue().situation && this.stop(true, false));
+
+ this.clearQueue().clearCurrent()
+
+ return this
+ }
+
+ // set the internal animation pointer at the start position, before any loops, and updates the visualisation
+ , atStart: function() {
+ return this.at(0, true)
+ }
+
+ // set the internal animation pointer at the end position, after all the loops, and updates the visualisation
+ , atEnd: function() {
+ if (this.situation.loops === true) {
+ // If in a infinite loop, we end the current iteration
+ this.situation.loops = this.situation.loop + 1
+ }
+
+ if(typeof this.situation.loops == 'number') {
+ // If performing a finite number of loops, we go after all the loops
+ return this.at(this.situation.loops, true)
+ } else {
+ // If no loops, we just go at the end
+ return this.at(1, true)
+ }
+ }
+
+ // set the internal animation pointer to the specified position and updates the visualisation
+ // if isAbsPos is true, pos is treated as an absolute position
+ , at: function(pos, isAbsPos){
+ var durDivSpd = this.situation.duration/this._speed
+
+ this.absPos = pos
+ // If pos is not an absolute position, we convert it into one
+ if (!isAbsPos) {
+ if (this.situation.reversed) this.absPos = 1 - this.absPos
+ this.absPos += this.situation.loop
+ }
+
+ this.situation.start = +new Date - this.absPos * durDivSpd
+ this.situation.finish = this.situation.start + durDivSpd
+
+ return this.step(true)
+ }
+
+ /**
+ * sets or returns the speed of the animations
+ * @param speed null || Number The new speed of the animations
+ * @return Number || this
+ */
+ , speed: function(speed){
+ if (speed === 0) return this.pause()
+
+ if (speed) {
+ this._speed = speed
+ // We use an absolute position here so that speed can affect the delay before the animation
+ return this.at(this.absPos, true)
+ } else return this._speed
+ }
+
+ // Make loopable
+ , loop: function(times, reverse) {
+ var c = this.last()
+
+ // store total loops
+ c.loops = (times != null) ? times : true
+ c.loop = 0
+
+ if(reverse) c.reversing = true
+ return this
+ }
+
+ // pauses the animation
+ , pause: function(){
+ this.paused = true
+ this.stopAnimFrame()
+
+ return this
+ }
+
+ // unpause the animation
+ , play: function(){
+ if(!this.paused) return this
+ this.paused = false
+ // We use an absolute position here so that the delay before the animation can be paused
+ return this.at(this.absPos, true)
+ }
+
+ /**
+ * toggle or set the direction of the animation
+ * true sets direction to backwards while false sets it to forwards
+ * @param reversed Boolean indicating whether to reverse the animation or not (default: toggle the reverse status)
+ * @return this
+ */
+ , reverse: function(reversed){
+ var c = this.last()
+
+ if(typeof reversed == 'undefined') c.reversed = !c.reversed
+ else c.reversed = reversed
+
+ return this
+ }
+
+
+ /**
+ * returns a float from 0-1 indicating the progress of the current animation
+ * @param eased Boolean indicating whether the returned position should be eased or not
+ * @return number
+ */
+ , progress: function(easeIt){
+ return easeIt ? this.situation.ease(this.pos) : this.pos
+ }
+
+ /**
+ * adds a callback function which is called when the current animation is finished
+ * @param fn Function which should be executed as callback
+ * @return number
+ */
+ , after: function(fn){
+ var c = this.last()
+ , wrapper = function wrapper(e){
+ if(e.detail.situation == c){
+ fn.call(this, c)
+ this.off('finished.fx', wrapper) // prevent memory leak
+ }
+ }
+
+ this.target().on('finished.fx', wrapper)
+
+ return this._callStart()
+ }
+
+ // adds a callback which is called whenever one animation step is performed
+ , during: function(fn){
+ var c = this.last()
+ , wrapper = function(e){
+ if(e.detail.situation == c){
+ fn.call(this, e.detail.pos, SVG.morph(e.detail.pos), e.detail.eased, c)
+ }
+ }
+
+ // see above
+ this.target().off('during.fx', wrapper).on('during.fx', wrapper)
+
+ this.after(function(){
+ this.off('during.fx', wrapper)
+ })
+
+ return this._callStart()
+ }
+
+ // calls after ALL animations in the queue are finished
+ , afterAll: function(fn){
+ var wrapper = function wrapper(e){
+ fn.call(this)
+ this.off('allfinished.fx', wrapper)
+ }
+
+ // see above
+ this.target().off('allfinished.fx', wrapper).on('allfinished.fx', wrapper)
+
+ return this._callStart()
+ }
+
+ // calls on every animation step for all animations
+ , duringAll: function(fn){
+ var wrapper = function(e){
+ fn.call(this, e.detail.pos, SVG.morph(e.detail.pos), e.detail.eased, e.detail.situation)
+ }
+
+ this.target().off('during.fx', wrapper).on('during.fx', wrapper)
+
+ this.afterAll(function(){
+ this.off('during.fx', wrapper)
+ })
+
+ return this._callStart()
+ }
+
+ , last: function(){
+ return this.situations.length ? this.situations[this.situations.length-1] : this.situation
+ }
+
+ // adds one property to the animations
+ , add: function(method, args, type){
+ this.last()[type || 'animations'][method] = args
+ return this._callStart()
+ }
+
+ /** perform one step of the animation
+ * @param ignoreTime Boolean indicating whether to ignore time and use position directly or recalculate position based on time
+ * @return this
+ */
+ , step: function(ignoreTime){
+
+ // convert current time to an absolute position
+ if(!ignoreTime) this.absPos = this.timeToAbsPos(+new Date)
+
+ // This part convert an absolute position to a position
+ if(this.situation.loops !== false) {
+ var absPos, absPosInt, lastLoop
+
+ // If the absolute position is below 0, we just treat it as if it was 0
+ absPos = Math.max(this.absPos, 0)
+ absPosInt = Math.floor(absPos)
+
+ if(this.situation.loops === true || absPosInt < this.situation.loops) {
+ this.pos = absPos - absPosInt
+ lastLoop = this.situation.loop
+ this.situation.loop = absPosInt
+ } else {
+ this.absPos = this.situation.loops
+ this.pos = 1
+ // The -1 here is because we don't want to toggle reversed when all the loops have been completed
+ lastLoop = this.situation.loop - 1
+ this.situation.loop = this.situation.loops
+ }
+
+ if(this.situation.reversing) {
+ // Toggle reversed if an odd number of loops as occured since the last call of step
+ this.situation.reversed = this.situation.reversed != Boolean((this.situation.loop - lastLoop) % 2)
+ }
+
+ } else {
+ // If there are no loop, the absolute position must not be above 1
+ this.absPos = Math.min(this.absPos, 1)
+ this.pos = this.absPos
+ }
+
+ // while the absolute position can be below 0, the position must not be below 0
+ if(this.pos < 0) this.pos = 0
+
+ if(this.situation.reversed) this.pos = 1 - this.pos
+
+
+ // apply easing
+ var eased = this.situation.ease(this.pos)
+
+ // call once-callbacks
+ for(var i in this.situation.once){
+ if(i > this.lastPos && i <= eased){
+ this.situation.once[i].call(this.target(), this.pos, eased)
+ delete this.situation.once[i]
+ }
+ }
+
+ // fire during callback with position, eased position and current situation as parameter
+ if(this.active) this.target().fire('during', {pos: this.pos, eased: eased, fx: this, situation: this.situation})
+
+ // the user may call stop or finish in the during callback
+ // so make sure that we still have a valid situation
+ if(!this.situation){
+ return this
+ }
+
+ // apply the actual animation to every property
+ this.eachAt()
+
+ // do final code when situation is finished
+ if((this.pos == 1 && !this.situation.reversed) || (this.situation.reversed && this.pos == 0)){
+
+ // stop animation callback
+ this.stopAnimFrame()
+
+ // fire finished callback with current situation as parameter
+ this.target().fire('finished', {fx:this, situation: this.situation})
+
+ if(!this.situations.length){
+ this.target().fire('allfinished')
+
+ // Recheck the length since the user may call animate in the afterAll callback
+ if(!this.situations.length){
+ this.target().off('.fx') // there shouldnt be any binding left, but to make sure...
+ this.active = false
+ }
+ }
+
+ // start next animation
+ if(this.active) this.dequeue()
+ else this.clearCurrent()
+
+ }else if(!this.paused && this.active){
+ // we continue animating when we are not at the end
+ this.startAnimFrame()
+ }
+
+ // save last eased position for once callback triggering
+ this.lastPos = eased
+ return this
+
+ }
+
+ // calculates the step for every property and calls block with it
+ , eachAt: function(){
+ var i, len, at, self = this, target = this.target(), s = this.situation
+
+ // apply animations which can be called trough a method
+ for(i in s.animations){
+
+ at = [].concat(s.animations[i]).map(function(el){
+ return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el
+ })
+
+ target[i].apply(target, at)
+
+ }
+
+ // apply animation which has to be applied with attr()
+ for(i in s.attrs){
+
+ at = [i].concat(s.attrs[i]).map(function(el){
+ return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el
+ })
+
+ target.attr.apply(target, at)
+
+ }
+
+ // apply animation which has to be applied with style()
+ for(i in s.styles){
+
+ at = [i].concat(s.styles[i]).map(function(el){
+ return typeof el !== 'string' && el.at ? el.at(s.ease(self.pos), self.pos) : el
+ })
+
+ target.style.apply(target, at)
+
+ }
+
+ // animate initialTransformation which has to be chained
+ if(s.transforms.length){
+
+ // get initial initialTransformation
+ at = s.initialTransformation
+ for(i = 0, len = s.transforms.length; i < len; i++){
+
+ // get next transformation in chain
+ var a = s.transforms[i]
+
+ // multiply matrix directly
+ if(a instanceof SVG.Matrix){
+
+ if(a.relative){
+ at = at.multiply(new SVG.Matrix().morph(a).at(s.ease(this.pos)))
+ }else{
+ at = at.morph(a).at(s.ease(this.pos))
+ }
+ continue
+ }
+
+ // when transformation is absolute we have to reset the needed transformation first
+ if(!a.relative)
+ a.undo(at.extract())
+
+ // and reapply it after
+ at = at.multiply(a.at(s.ease(this.pos)))
+
+ }
+
+ // set new matrix on element
+ target.matrix(at)
+ }
+
+ return this
+
+ }
+
+
+ // adds an once-callback which is called at a specific position and never again
+ , once: function(pos, fn, isEased){
+ var c = this.last()
+ if(!isEased) pos = c.ease(pos)
+
+ c.once[pos] = fn
+
+ return this
+ }
+
+ , _callStart: function() {
+ setTimeout(function(){this.start()}.bind(this), 0)
+ return this
+ }
+
+ }
+
+, parent: SVG.Element
+
+ // Add method to parent elements
+, construct: {
+ // Get fx module or create a new one, then animate with given duration and ease
+ animate: function(o, ease, delay) {
+ return (this.fx || (this.fx = new SVG.FX(this))).animate(o, ease, delay)
+ }
+ , delay: function(delay){
+ return (this.fx || (this.fx = new SVG.FX(this))).delay(delay)
+ }
+ , stop: function(jumpToEnd, clearQueue) {
+ if (this.fx)
+ this.fx.stop(jumpToEnd, clearQueue)
+
+ return this
+ }
+ , finish: function() {
+ if (this.fx)
+ this.fx.finish()
+
+ return this
+ }
+ // Pause current animation
+ , pause: function() {
+ if (this.fx)
+ this.fx.pause()
+
+ return this
+ }
+ // Play paused current animation
+ , play: function() {
+ if (this.fx)
+ this.fx.play()
+
+ return this
+ }
+ // Set/Get the speed of the animations
+ , speed: function(speed) {
+ if (this.fx)
+ if (speed == null)
+ return this.fx.speed()
+ else
+ this.fx.speed(speed)
+
+ return this
+ }
+ }
+
+})
+
+// MorphObj is used whenever no morphable object is given
+SVG.MorphObj = SVG.invent({
+
+ create: function(from, to){
+ // prepare color for morphing
+ if(SVG.Color.isColor(to)) return new SVG.Color(from).morph(to)
+ // check if we have a list of values
+ if(SVG.regex.delimiter.test(from)) {
+ // prepare path for morphing
+ if(SVG.regex.pathLetters.test(from)) return new SVG.PathArray(from).morph(to)
+ // prepare value list for morphing
+ else return new SVG.Array(from).morph(to)
+ }
+ // prepare number for morphing
+ if(SVG.regex.numberAndUnit.test(to)) return new SVG.Number(from).morph(to)
+
+ // prepare for plain morphing
+ this.value = from
+ this.destination = to
+ }
+
+, extend: {
+ at: function(pos, real){
+ return real < 1 ? this.value : this.destination
+ },
+
+ valueOf: function(){
+ return this.value
+ }
+ }
+
+})
+
+SVG.extend(SVG.FX, {
+ // Add animatable attributes
+ attr: function(a, v, relative) {
+ // apply attributes individually
+ if (typeof a == 'object') {
+ for (var key in a)
+ this.attr(key, a[key])
+
+ } else {
+ this.add(a, v, 'attrs')
+ }
+
+ return this
+ }
+ // Add animatable styles
+, style: function(s, v) {
+ if (typeof s == 'object')
+ for (var key in s)
+ this.style(key, s[key])
+
+ else
+ this.add(s, v, 'styles')
+
+ return this
+ }
+ // Animatable x-axis
+, x: function(x, relative) {
+ if(this.target() instanceof SVG.G){
+ this.transform({x:x}, relative)
+ return this
+ }
+
+ var num = new SVG.Number(x)
+ num.relative = relative
+ return this.add('x', num)
+ }
+ // Animatable y-axis
+, y: function(y, relative) {
+ if(this.target() instanceof SVG.G){
+ this.transform({y:y}, relative)
+ return this
+ }
+
+ var num = new SVG.Number(y)
+ num.relative = relative
+ return this.add('y', num)
+ }
+ // Animatable center x-axis
+, cx: function(x) {
+ return this.add('cx', new SVG.Number(x))
+ }
+ // Animatable center y-axis
+, cy: function(y) {
+ return this.add('cy', new SVG.Number(y))
+ }
+ // Add animatable move
+, move: function(x, y) {
+ return this.x(x).y(y)
+ }
+ // Add animatable center
+, center: function(x, y) {
+ return this.cx(x).cy(y)
+ }
+ // Add animatable size
+, size: function(width, height) {
+ if (this.target() instanceof SVG.Text) {
+ // animate font size for Text elements
+ this.attr('font-size', width)
+
+ } else {
+ // animate bbox based size for all other elements
+ var box
+
+ if(!width || !height){
+ box = this.target().bbox()
+ }
+
+ if(!width){
+ width = box.width / box.height * height
+ }
+
+ if(!height){
+ height = box.height / box.width * width
+ }
+
+ this.add('width' , new SVG.Number(width))
+ .add('height', new SVG.Number(height))
+
+ }
+
+ return this
+ }
+ // Add animatable width
+, width: function(width) {
+ return this.add('width', new SVG.Number(width))
+ }
+ // Add animatable height
+, height: function(height) {
+ return this.add('height', new SVG.Number(height))
+ }
+ // Add animatable plot
+, plot: function(a, b, c, d) {
+ // Lines can be plotted with 4 arguments
+ if(arguments.length == 4) {
+ return this.plot([a, b, c, d])
+ }
+
+ return this.add('plot', new (this.target().morphArray)(a))
+ }
+ // Add leading method
+, leading: function(value) {
+ return this.target().leading ?
+ this.add('leading', new SVG.Number(value)) :
+ this
+ }
+ // Add animatable viewbox
+, viewbox: function(x, y, width, height) {
+ if (this.target() instanceof SVG.Container) {
+ this.add('viewbox', new SVG.ViewBox(x, y, width, height))
+ }
+
+ return this
+ }
+, update: function(o) {
+ if (this.target() instanceof SVG.Stop) {
+ if (typeof o == 'number' || o instanceof SVG.Number) {
+ return this.update({
+ offset: arguments[0]
+ , color: arguments[1]
+ , opacity: arguments[2]
+ })
+ }
+
+ if (o.opacity != null) this.attr('stop-opacity', o.opacity)
+ if (o.color != null) this.attr('stop-color', o.color)
+ if (o.offset != null) this.attr('offset', o.offset)
+ }
+
+ return this
+ }
+})
diff --git a/node_modules/svg.js/src/gradient.js b/node_modules/svg.js/src/gradient.js
new file mode 100644
index 0000000..17145e6
--- /dev/null
+++ b/node_modules/svg.js/src/gradient.js
@@ -0,0 +1,107 @@
+SVG.Gradient = SVG.invent({
+ // Initialize node
+ create: function(type) {
+ this.constructor.call(this, SVG.create(type + 'Gradient'))
+
+ // store type
+ this.type = type
+ }
+
+ // Inherit from
+, inherit: SVG.Container
+
+ // Add class methods
+, extend: {
+ // Add a color stop
+ at: function(offset, color, opacity) {
+ return this.put(new SVG.Stop).update(offset, color, opacity)
+ }
+ // Update gradient
+ , update: function(block) {
+ // remove all stops
+ this.clear()
+
+ // invoke passed block
+ if (typeof block == 'function')
+ block.call(this, this)
+
+ return this
+ }
+ // Return the fill id
+ , fill: function() {
+ return 'url(#' + this.id() + ')'
+ }
+ // Alias string convertion to fill
+ , toString: function() {
+ return this.fill()
+ }
+ // custom attr to handle transform
+ , attr: function(a, b, c) {
+ if(a == 'transform') a = 'gradientTransform'
+ return SVG.Container.prototype.attr.call(this, a, b, c)
+ }
+ }
+
+ // Add parent method
+, construct: {
+ // Create gradient element in defs
+ gradient: function(type, block) {
+ return this.defs().gradient(type, block)
+ }
+ }
+})
+
+// Add animatable methods to both gradient and fx module
+SVG.extend(SVG.Gradient, SVG.FX, {
+ // From position
+ from: function(x, y) {
+ return (this._target || this).type == 'radial' ?
+ this.attr({ fx: new SVG.Number(x), fy: new SVG.Number(y) }) :
+ this.attr({ x1: new SVG.Number(x), y1: new SVG.Number(y) })
+ }
+ // To position
+, to: function(x, y) {
+ return (this._target || this).type == 'radial' ?
+ this.attr({ cx: new SVG.Number(x), cy: new SVG.Number(y) }) :
+ this.attr({ x2: new SVG.Number(x), y2: new SVG.Number(y) })
+ }
+})
+
+// Base gradient generation
+SVG.extend(SVG.Defs, {
+ // define gradient
+ gradient: function(type, block) {
+ return this.put(new SVG.Gradient(type)).update(block)
+ }
+
+})
+
+SVG.Stop = SVG.invent({
+ // Initialize node
+ create: 'stop'
+
+ // Inherit from
+, inherit: SVG.Element
+
+ // Add class methods
+, extend: {
+ // add color stops
+ update: function(o) {
+ if (typeof o == 'number' || o instanceof SVG.Number) {
+ o = {
+ offset: arguments[0]
+ , color: arguments[1]
+ , opacity: arguments[2]
+ }
+ }
+
+ // set attributes
+ if (o.opacity != null) this.attr('stop-opacity', o.opacity)
+ if (o.color != null) this.attr('stop-color', o.color)
+ if (o.offset != null) this.attr('offset', new SVG.Number(o.offset))
+
+ return this
+ }
+ }
+
+})
diff --git a/node_modules/svg.js/src/group.js b/node_modules/svg.js/src/group.js
new file mode 100644
index 0000000..9ec89f2
--- /dev/null
+++ b/node_modules/svg.js/src/group.js
@@ -0,0 +1,50 @@
+SVG.G = SVG.invent({
+ // Initialize node
+ create: 'g'
+
+ // Inherit from
+, inherit: SVG.Container
+
+ // Add class methods
+, extend: {
+ // Move over x-axis
+ x: function(x) {
+ return x == null ? this.transform('x') : this.transform({ x: x - this.x() }, true)
+ }
+ // Move over y-axis
+ , y: function(y) {
+ return y == null ? this.transform('y') : this.transform({ y: y - this.y() }, true)
+ }
+ // Move by center over x-axis
+ , cx: function(x) {
+ return x == null ? this.gbox().cx : this.x(x - this.gbox().width / 2)
+ }
+ // Move by center over y-axis
+ , cy: function(y) {
+ return y == null ? this.gbox().cy : this.y(y - this.gbox().height / 2)
+ }
+ , gbox: function() {
+
+ var bbox = this.bbox()
+ , trans = this.transform()
+
+ bbox.x += trans.x
+ bbox.x2 += trans.x
+ bbox.cx += trans.x
+
+ bbox.y += trans.y
+ bbox.y2 += trans.y
+ bbox.cy += trans.y
+
+ return bbox
+ }
+ }
+
+ // Add parent method
+, construct: {
+ // Create a group element
+ group: function() {
+ return this.put(new SVG.G)
+ }
+ }
+})
diff --git a/node_modules/svg.js/src/helpers.js b/node_modules/svg.js/src/helpers.js
new file mode 100644
index 0000000..d87ea9a
--- /dev/null
+++ b/node_modules/svg.js/src/helpers.js
@@ -0,0 +1,178 @@
+function pathRegReplace(a, b, c, d) {
+ return c + d.replace(SVG.regex.dots, ' .')
+}
+
+// creates deep clone of array
+function array_clone(arr){
+ var clone = arr.slice(0)
+ for(var i = clone.length; i--;){
+ if(Array.isArray(clone[i])){
+ clone[i] = array_clone(clone[i])
+ }
+ }
+ return clone
+}
+
+// tests if a given element is instance of an object
+function is(el, obj){
+ return el instanceof obj
+}
+
+// tests if a given selector matches an element
+function matches(el, selector) {
+ return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector).call(el, selector);
+}
+
+// Convert dash-separated-string to camelCase
+function camelCase(s) {
+ return s.toLowerCase().replace(/-(.)/g, function(m, g) {
+ return g.toUpperCase()
+ })
+}
+
+// Capitalize first letter of a string
+function capitalize(s) {
+ return s.charAt(0).toUpperCase() + s.slice(1)
+}
+
+// Ensure to six-based hex
+function fullHex(hex) {
+ return hex.length == 4 ?
+ [ '#',
+ hex.substring(1, 2), hex.substring(1, 2)
+ , hex.substring(2, 3), hex.substring(2, 3)
+ , hex.substring(3, 4), hex.substring(3, 4)
+ ].join('') : hex
+}
+
+// Component to hex value
+function compToHex(comp) {
+ var hex = comp.toString(16)
+ return hex.length == 1 ? '0' + hex : hex
+}
+
+// Calculate proportional width and height values when necessary
+function proportionalSize(element, width, height) {
+ if (width == null || height == null) {
+ var box = element.bbox()
+
+ if (width == null)
+ width = box.width / box.height * height
+ else if (height == null)
+ height = box.height / box.width * width
+ }
+
+ return {
+ width: width
+ , height: height
+ }
+}
+
+// Delta transform point
+function deltaTransformPoint(matrix, x, y) {
+ return {
+ x: x * matrix.a + y * matrix.c + 0
+ , y: x * matrix.b + y * matrix.d + 0
+ }
+}
+
+// Map matrix array to object
+function arrayToMatrix(a) {
+ return { a: a[0], b: a[1], c: a[2], d: a[3], e: a[4], f: a[5] }
+}
+
+// Parse matrix if required
+function parseMatrix(matrix) {
+ if (!(matrix instanceof SVG.Matrix))
+ matrix = new SVG.Matrix(matrix)
+
+ return matrix
+}
+
+// Add centre point to transform object
+function ensureCentre(o, target) {
+ o.cx = o.cx == null ? target.bbox().cx : o.cx
+ o.cy = o.cy == null ? target.bbox().cy : o.cy
+}
+
+// PathArray Helpers
+function arrayToString(a) {
+ for (var i = 0, il = a.length, s = ''; i < il; i++) {
+ s += a[i][0]
+
+ if (a[i][1] != null) {
+ s += a[i][1]
+
+ if (a[i][2] != null) {
+ s += ' '
+ s += a[i][2]
+
+ if (a[i][3] != null) {
+ s += ' '
+ s += a[i][3]
+ s += ' '
+ s += a[i][4]
+
+ if (a[i][5] != null) {
+ s += ' '
+ s += a[i][5]
+ s += ' '
+ s += a[i][6]
+
+ if (a[i][7] != null) {
+ s += ' '
+ s += a[i][7]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return s + ' '
+}
+
+// Deep new id assignment
+function assignNewId(node) {
+ // do the same for SVG child nodes as well
+ for (var i = node.childNodes.length - 1; i >= 0; i--)
+ if (node.childNodes[i] instanceof window.SVGElement)
+ assignNewId(node.childNodes[i])
+
+ return SVG.adopt(node).id(SVG.eid(node.nodeName))
+}
+
+// Add more bounding box properties
+function fullBox(b) {
+ if (b.x == null) {
+ b.x = 0
+ b.y = 0
+ b.width = 0
+ b.height = 0
+ }
+
+ b.w = b.width
+ b.h = b.height
+ b.x2 = b.x + b.width
+ b.y2 = b.y + b.height
+ b.cx = b.x + b.width / 2
+ b.cy = b.y + b.height / 2
+
+ return b
+}
+
+// Get id from reference string
+function idFromReference(url) {
+ var m = (url || '').toString().match(SVG.regex.reference)
+
+ if (m) return m[1]
+}
+
+// If values like 1e-88 are passed, this is not a valid 32 bit float,
+// but in those cases, we are so close to 0 that 0 works well!
+function float32String(v) {
+ return Math.abs(v) > 1e-37 ? v : 0
+}
+
+// Create matrix array for looping
+var abcdef = 'abcdef'.split('')
diff --git a/node_modules/svg.js/src/hyperlink.js b/node_modules/svg.js/src/hyperlink.js
new file mode 100644
index 0000000..a967707
--- /dev/null
+++ b/node_modules/svg.js/src/hyperlink.js
@@ -0,0 +1,46 @@
+SVG.A = SVG.invent({
+ // Initialize node
+ create: 'a'
+
+ // Inherit from
+, inherit: SVG.Container
+
+ // Add class methods
+, extend: {
+ // Link url
+ to: function(url) {
+ return this.attr('href', url, SVG.xlink)
+ }
+ // Link show attribute
+ , show: function(target) {
+ return this.attr('show', target, SVG.xlink)
+ }
+ // Link target attribute
+ , target: function(target) {
+ return this.attr('target', target)
+ }
+ }
+
+ // Add parent method
+, construct: {
+ // Create a hyperlink element
+ link: function(url) {
+ return this.put(new SVG.A).to(url)
+ }
+ }
+})
+
+SVG.extend(SVG.Element, {
+ // Create a hyperlink element
+ linkTo: function(url) {
+ var link = new SVG.A
+
+ if (typeof url == 'function')
+ url.call(link, link)
+ else
+ link.to(url)
+
+ return this.parent().put(link).put(this)
+ }
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/image.js b/node_modules/svg.js/src/image.js
new file mode 100644
index 0000000..02bfb11
--- /dev/null
+++ b/node_modules/svg.js/src/image.js
@@ -0,0 +1,73 @@
+SVG.Image = SVG.invent({
+ // Initialize node
+ create: 'image'
+
+ // Inherit from
+, inherit: SVG.Shape
+
+ // Add class methods
+, extend: {
+ // (re)load image
+ load: function(url) {
+ if (!url) return this
+
+ var self = this
+ , img = new window.Image()
+
+ // preload image
+ SVG.on(img, 'load', function() {
+ SVG.off(img)
+
+ var p = self.parent(SVG.Pattern)
+
+ if(p === null) return
+
+ // ensure image size
+ if (self.width() == 0 && self.height() == 0)
+ self.size(img.width, img.height)
+
+ // ensure pattern size if not set
+ if (p && p.width() == 0 && p.height() == 0)
+ p.size(self.width(), self.height())
+
+ // callback
+ if (typeof self._loaded === 'function')
+ self._loaded.call(self, {
+ width: img.width
+ , height: img.height
+ , ratio: img.width / img.height
+ , url: url
+ })
+ })
+
+ SVG.on(img, 'error', function(e){
+ SVG.off(img)
+
+ if (typeof self._error === 'function'){
+ self._error.call(self, e)
+ }
+ })
+
+ return this.attr('href', (img.src = this.src = url), SVG.xlink)
+ }
+ // Add loaded callback
+ , loaded: function(loaded) {
+ this._loaded = loaded
+ return this
+ }
+
+ , error: function(error) {
+ this._error = error
+ return this
+ }
+ }
+
+ // Add parent method
+, construct: {
+ // create image element, load image and set its size
+ image: function(source, width, height) {
+ return this.put(new SVG.Image).load(source).size(width || 0, height || width || 0)
+ }
+ }
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/line.js b/node_modules/svg.js/src/line.js
new file mode 100644
index 0000000..9e8692f
--- /dev/null
+++ b/node_modules/svg.js/src/line.js
@@ -0,0 +1,52 @@
+SVG.Line = SVG.invent({
+ // Initialize node
+ create: 'line'
+
+ // Inherit from
+, inherit: SVG.Shape
+
+ // Add class methods
+, extend: {
+ // Get array
+ array: function() {
+ return new SVG.PointArray([
+ [ this.attr('x1'), this.attr('y1') ]
+ , [ this.attr('x2'), this.attr('y2') ]
+ ])
+ }
+ // Overwrite native plot() method
+ , plot: function(x1, y1, x2, y2) {
+ if (x1 == null)
+ return this.array()
+ else if (typeof y1 !== 'undefined')
+ x1 = { x1: x1, y1: y1, x2: x2, y2: y2 }
+ else
+ x1 = new SVG.PointArray(x1).toLine()
+
+ return this.attr(x1)
+ }
+ // Move by left top corner
+ , move: function(x, y) {
+ return this.attr(this.array().move(x, y).toLine())
+ }
+ // Set element size to given width and height
+ , size: function(width, height) {
+ var p = proportionalSize(this, width, height)
+
+ return this.attr(this.array().size(p.width, p.height).toLine())
+ }
+ }
+
+ // Add parent method
+, construct: {
+ // Create a line element
+ line: function(x1, y1, x2, y2) {
+ // make sure plot is called as a setter
+ // x1 is not necessarily a number, it can also be an array, a string and a SVG.PointArray
+ return SVG.Line.prototype.plot.apply(
+ this.put(new SVG.Line)
+ , x1 != null ? [x1, y1, x2, y2] : [0, 0, 0, 0]
+ )
+ }
+ }
+})
diff --git a/node_modules/svg.js/src/marker.js b/node_modules/svg.js/src/marker.js
new file mode 100644
index 0000000..cdde0ff
--- /dev/null
+++ b/node_modules/svg.js/src/marker.js
@@ -0,0 +1,80 @@
+SVG.Marker = SVG.invent({
+ // Initialize node
+ create: 'marker'
+
+ // Inherit from
+, inherit: SVG.Container
+
+ // Add class methods
+, extend: {
+ // Set width of element
+ width: function(width) {
+ return this.attr('markerWidth', width)
+ }
+ // Set height of element
+ , height: function(height) {
+ return this.attr('markerHeight', height)
+ }
+ // Set marker refX and refY
+ , ref: function(x, y) {
+ return this.attr('refX', x).attr('refY', y)
+ }
+ // Update marker
+ , update: function(block) {
+ // remove all content
+ this.clear()
+
+ // invoke passed block
+ if (typeof block == 'function')
+ block.call(this, this)
+
+ return this
+ }
+ // Return the fill id
+ , toString: function() {
+ return 'url(#' + this.id() + ')'
+ }
+ }
+
+ // Add parent method
+, construct: {
+ marker: function(width, height, block) {
+ // Create marker element in defs
+ return this.defs().marker(width, height, block)
+ }
+ }
+
+})
+
+SVG.extend(SVG.Defs, {
+ // Create marker
+ marker: function(width, height, block) {
+ // Set default viewbox to match the width and height, set ref to cx and cy and set orient to auto
+ return this.put(new SVG.Marker)
+ .size(width, height)
+ .ref(width / 2, height / 2)
+ .viewbox(0, 0, width, height)
+ .attr('orient', 'auto')
+ .update(block)
+ }
+
+})
+
+SVG.extend(SVG.Line, SVG.Polyline, SVG.Polygon, SVG.Path, {
+ // Create and attach markers
+ marker: function(marker, width, height, block) {
+ var attr = ['marker']
+
+ // Build attribute name
+ if (marker != 'all') attr.push(marker)
+ attr = attr.join('-')
+
+ // Set marker attribute
+ marker = arguments[1] instanceof SVG.Marker ?
+ arguments[1] :
+ this.doc().marker(width, height, block)
+
+ return this.attr(attr, marker)
+ }
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/mask.js b/node_modules/svg.js/src/mask.js
new file mode 100644
index 0000000..7d90861
--- /dev/null
+++ b/node_modules/svg.js/src/mask.js
@@ -0,0 +1,58 @@
+SVG.Mask = SVG.invent({
+ // Initialize node
+ create: function() {
+ this.constructor.call(this, SVG.create('mask'))
+
+ // keep references to masked elements
+ this.targets = []
+ }
+
+ // Inherit from
+, inherit: SVG.Container
+
+ // Add class methods
+, extend: {
+ // Unmask all masked elements and remove itself
+ remove: function() {
+ // unmask all targets
+ for (var i = this.targets.length - 1; i >= 0; i--)
+ if (this.targets[i])
+ this.targets[i].unmask()
+ this.targets = []
+
+ // remove mask from parent
+ SVG.Element.prototype.remove.call(this)
+
+ return this
+ }
+ }
+
+ // Add parent method
+, construct: {
+ // Create masking element
+ mask: function() {
+ return this.defs().put(new SVG.Mask)
+ }
+ }
+})
+
+
+SVG.extend(SVG.Element, {
+ // Distribute mask to svg element
+ maskWith: function(element) {
+ // use given mask or create a new one
+ this.masker = element instanceof SVG.Mask ? element : this.parent().mask().add(element)
+
+ // store reverence on self in mask
+ this.masker.targets.push(this)
+
+ // apply mask
+ return this.attr('mask', 'url("#' + this.masker.attr('id') + '")')
+ }
+ // Unmask element
+, unmask: function() {
+ delete this.masker
+ return this.attr('mask', null)
+ }
+
+})
diff --git a/node_modules/svg.js/src/matrix.js b/node_modules/svg.js/src/matrix.js
new file mode 100644
index 0000000..bce00fa
--- /dev/null
+++ b/node_modules/svg.js/src/matrix.js
@@ -0,0 +1,203 @@
+SVG.Matrix = SVG.invent({
+ // Initialize
+ create: function(source) {
+ var i, base = arrayToMatrix([1, 0, 0, 1, 0, 0])
+
+ // ensure source as object
+ source = source instanceof SVG.Element ?
+ source.matrixify() :
+ typeof source === 'string' ?
+ arrayToMatrix(source.split(SVG.regex.delimiter).map(parseFloat)) :
+ arguments.length == 6 ?
+ arrayToMatrix([].slice.call(arguments)) :
+ Array.isArray(source) ?
+ arrayToMatrix(source) :
+ typeof source === 'object' ?
+ source : base
+
+ // merge source
+ for (i = abcdef.length - 1; i >= 0; --i)
+ this[abcdef[i]] = source[abcdef[i]] != null ?
+ source[abcdef[i]] : base[abcdef[i]]
+ }
+
+ // Add methods
+, extend: {
+ // Extract individual transformations
+ extract: function() {
+ // find delta transform points
+ var px = deltaTransformPoint(this, 0, 1)
+ , py = deltaTransformPoint(this, 1, 0)
+ , skewX = 180 / Math.PI * Math.atan2(px.y, px.x) - 90
+
+ return {
+ // translation
+ x: this.e
+ , y: this.f
+ , transformedX:(this.e * Math.cos(skewX * Math.PI / 180) + this.f * Math.sin(skewX * Math.PI / 180)) / Math.sqrt(this.a * this.a + this.b * this.b)
+ , transformedY:(this.f * Math.cos(skewX * Math.PI / 180) + this.e * Math.sin(-skewX * Math.PI / 180)) / Math.sqrt(this.c * this.c + this.d * this.d)
+ // skew
+ , skewX: -skewX
+ , skewY: 180 / Math.PI * Math.atan2(py.y, py.x)
+ // scale
+ , scaleX: Math.sqrt(this.a * this.a + this.b * this.b)
+ , scaleY: Math.sqrt(this.c * this.c + this.d * this.d)
+ // rotation
+ , rotation: skewX
+ , a: this.a
+ , b: this.b
+ , c: this.c
+ , d: this.d
+ , e: this.e
+ , f: this.f
+ , matrix: new SVG.Matrix(this)
+ }
+ }
+ // Clone matrix
+ , clone: function() {
+ return new SVG.Matrix(this)
+ }
+ // Morph one matrix into another
+ , morph: function(matrix) {
+ // store new destination
+ this.destination = new SVG.Matrix(matrix)
+
+ return this
+ }
+ // Get morphed matrix at a given position
+ , at: function(pos) {
+ // make sure a destination is defined
+ if (!this.destination) return this
+
+ // calculate morphed matrix at a given position
+ var matrix = new SVG.Matrix({
+ a: this.a + (this.destination.a - this.a) * pos
+ , b: this.b + (this.destination.b - this.b) * pos
+ , c: this.c + (this.destination.c - this.c) * pos
+ , d: this.d + (this.destination.d - this.d) * pos
+ , e: this.e + (this.destination.e - this.e) * pos
+ , f: this.f + (this.destination.f - this.f) * pos
+ })
+
+ return matrix
+ }
+ // Multiplies by given matrix
+ , multiply: function(matrix) {
+ return new SVG.Matrix(this.native().multiply(parseMatrix(matrix).native()))
+ }
+ // Inverses matrix
+ , inverse: function() {
+ return new SVG.Matrix(this.native().inverse())
+ }
+ // Translate matrix
+ , translate: function(x, y) {
+ return new SVG.Matrix(this.native().translate(x || 0, y || 0))
+ }
+ // Scale matrix
+ , scale: function(x, y, cx, cy) {
+ // support uniformal scale
+ if (arguments.length == 1) {
+ y = x
+ } else if (arguments.length == 3) {
+ cy = cx
+ cx = y
+ y = x
+ }
+
+ return this.around(cx, cy, new SVG.Matrix(x, 0, 0, y, 0, 0))
+ }
+ // Rotate matrix
+ , rotate: function(r, cx, cy) {
+ // convert degrees to radians
+ r = SVG.utils.radians(r)
+
+ return this.around(cx, cy, new SVG.Matrix(Math.cos(r), Math.sin(r), -Math.sin(r), Math.cos(r), 0, 0))
+ }
+ // Flip matrix on x or y, at a given offset
+ , flip: function(a, o) {
+ return a == 'x' ?
+ this.scale(-1, 1, o, 0) :
+ a == 'y' ?
+ this.scale(1, -1, 0, o) :
+ this.scale(-1, -1, a, o != null ? o : a)
+ }
+ // Skew
+ , skew: function(x, y, cx, cy) {
+ // support uniformal skew
+ if (arguments.length == 1) {
+ y = x
+ } else if (arguments.length == 3) {
+ cy = cx
+ cx = y
+ y = x
+ }
+
+ // convert degrees to radians
+ x = SVG.utils.radians(x)
+ y = SVG.utils.radians(y)
+
+ return this.around(cx, cy, new SVG.Matrix(1, Math.tan(y), Math.tan(x), 1, 0, 0))
+ }
+ // SkewX
+ , skewX: function(x, cx, cy) {
+ return this.skew(x, 0, cx, cy)
+ }
+ // SkewY
+ , skewY: function(y, cx, cy) {
+ return this.skew(0, y, cx, cy)
+ }
+ // Transform around a center point
+ , around: function(cx, cy, matrix) {
+ return this
+ .multiply(new SVG.Matrix(1, 0, 0, 1, cx || 0, cy || 0))
+ .multiply(matrix)
+ .multiply(new SVG.Matrix(1, 0, 0, 1, -cx || 0, -cy || 0))
+ }
+ // Convert to native SVGMatrix
+ , native: function() {
+ // create new matrix
+ var matrix = SVG.parser.native.createSVGMatrix()
+
+ // update with current values
+ for (var i = abcdef.length - 1; i >= 0; i--)
+ matrix[abcdef[i]] = this[abcdef[i]]
+
+ return matrix
+ }
+ // Convert matrix to string
+ , toString: function() {
+ // Construct the matrix directly, avoid values that are too small
+ return 'matrix(' + float32String(this.a) + ',' + float32String(this.b)
+ + ',' + float32String(this.c) + ',' + float32String(this.d)
+ + ',' + float32String(this.e) + ',' + float32String(this.f)
+ + ')'
+ }
+ }
+
+ // Define parent
+, parent: SVG.Element
+
+ // Add parent method
+, construct: {
+ // Get current matrix
+ ctm: function() {
+ return new SVG.Matrix(this.node.getCTM())
+ },
+ // Get current screen matrix
+ screenCTM: function() {
+ /* https://bugzilla.mozilla.org/show_bug.cgi?id=1344537
+ This is needed because FF does not return the transformation matrix
+ for the inner coordinate system when getScreenCTM() is called on nested svgs.
+ However all other Browsers do that */
+ if(this instanceof SVG.Nested) {
+ var rect = this.rect(1,1)
+ var m = rect.node.getScreenCTM()
+ rect.remove()
+ return new SVG.Matrix(m)
+ }
+ return new SVG.Matrix(this.node.getScreenCTM())
+ }
+
+ }
+
+})
diff --git a/node_modules/svg.js/src/memory.js b/node_modules/svg.js/src/memory.js
new file mode 100644
index 0000000..1a30faa
--- /dev/null
+++ b/node_modules/svg.js/src/memory.js
@@ -0,0 +1,36 @@
+SVG.extend(SVG.Element, {
+ // Remember arbitrary data
+ remember: function(k, v) {
+ // remember every item in an object individually
+ if (typeof arguments[0] == 'object')
+ for (var v in k)
+ this.remember(v, k[v])
+
+ // retrieve memory
+ else if (arguments.length == 1)
+ return this.memory()[k]
+
+ // store memory
+ else
+ this.memory()[k] = v
+
+ return this
+ }
+
+ // Erase a given memory
+, forget: function() {
+ if (arguments.length == 0)
+ this._memory = {}
+ else
+ for (var i = arguments.length - 1; i >= 0; i--)
+ delete this.memory()[arguments[i]]
+
+ return this
+ }
+
+ // Initialize or return local memory object
+, memory: function() {
+ return this._memory || (this._memory = {})
+ }
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/nested.js b/node_modules/svg.js/src/nested.js
new file mode 100644
index 0000000..f856e52
--- /dev/null
+++ b/node_modules/svg.js/src/nested.js
@@ -0,0 +1,19 @@
+SVG.Nested = SVG.invent({
+ // Initialize node
+ create: function() {
+ this.constructor.call(this, SVG.create('svg'))
+
+ this.style('overflow', 'visible')
+ }
+
+ // Inherit from
+, inherit: SVG.Container
+
+ // Add parent method
+, construct: {
+ // Create nested svg document
+ nested: function() {
+ return this.put(new SVG.Nested)
+ }
+ }
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/number.js b/node_modules/svg.js/src/number.js
new file mode 100644
index 0000000..8198e7c
--- /dev/null
+++ b/node_modules/svg.js/src/number.js
@@ -0,0 +1,110 @@
+// Module for unit convertions
+SVG.Number = SVG.invent({
+ // Initialize
+ create: function(value, unit) {
+ // initialize defaults
+ this.value = 0
+ this.unit = unit || ''
+
+ // parse value
+ if (typeof value === 'number') {
+ // ensure a valid numeric value
+ this.value = isNaN(value) ? 0 : !isFinite(value) ? (value < 0 ? -3.4e+38 : +3.4e+38) : value
+
+ } else if (typeof value === 'string') {
+ unit = value.match(SVG.regex.numberAndUnit)
+
+ if (unit) {
+ // make value numeric
+ this.value = parseFloat(unit[1])
+
+ // normalize
+ if (unit[5] == '%')
+ this.value /= 100
+ else if (unit[5] == 's')
+ this.value *= 1000
+
+ // store unit
+ this.unit = unit[5]
+ }
+
+ } else {
+ if (value instanceof SVG.Number) {
+ this.value = value.valueOf()
+ this.unit = value.unit
+ }
+ }
+
+ }
+ // Add methods
+, extend: {
+ // Stringalize
+ toString: function() {
+ return (
+ this.unit == '%' ?
+ ~~(this.value * 1e8) / 1e6:
+ this.unit == 's' ?
+ this.value / 1e3 :
+ this.value
+ ) + this.unit
+ }
+ , toJSON: function() {
+ return this.toString()
+ }
+ , // Convert to primitive
+ valueOf: function() {
+ return this.value
+ }
+ // Add number
+ , plus: function(number) {
+ number = new SVG.Number(number)
+ return new SVG.Number(this + number, this.unit || number.unit)
+ }
+ // Subtract number
+ , minus: function(number) {
+ number = new SVG.Number(number)
+ return new SVG.Number(this - number, this.unit || number.unit)
+ }
+ // Multiply number
+ , times: function(number) {
+ number = new SVG.Number(number)
+ return new SVG.Number(this * number, this.unit || number.unit)
+ }
+ // Divide number
+ , divide: function(number) {
+ number = new SVG.Number(number)
+ return new SVG.Number(this / number, this.unit || number.unit)
+ }
+ // Convert to different unit
+ , to: function(unit) {
+ var number = new SVG.Number(this)
+
+ if (typeof unit === 'string')
+ number.unit = unit
+
+ return number
+ }
+ // Make number morphable
+ , morph: function(number) {
+ this.destination = new SVG.Number(number)
+
+ if(number.relative) {
+ this.destination.value += this.value
+ }
+
+ return this
+ }
+ // Get morphed number at given position
+ , at: function(pos) {
+ // Make sure a destination is defined
+ if (!this.destination) return this
+
+ // Generate new morphed number
+ return new SVG.Number(this.destination)
+ .minus(this)
+ .times(pos)
+ .plus(this)
+ }
+
+ }
+})
diff --git a/node_modules/svg.js/src/parent.js b/node_modules/svg.js/src/parent.js
new file mode 100644
index 0000000..ee99e4d
--- /dev/null
+++ b/node_modules/svg.js/src/parent.js
@@ -0,0 +1,90 @@
+SVG.Parent = SVG.invent({
+ // Initialize node
+ create: function(element) {
+ this.constructor.call(this, element)
+ }
+
+ // Inherit from
+, inherit: SVG.Element
+
+ // Add class methods
+, extend: {
+ // Returns all child elements
+ children: function() {
+ return SVG.utils.map(SVG.utils.filterSVGElements(this.node.childNodes), function(node) {
+ return SVG.adopt(node)
+ })
+ }
+ // Add given element at a position
+ , add: function(element, i) {
+ if (i == null)
+ this.node.appendChild(element.node)
+ else if (element.node != this.node.childNodes[i])
+ this.node.insertBefore(element.node, this.node.childNodes[i])
+
+ return this
+ }
+ // Basically does the same as `add()` but returns the added element instead
+ , put: function(element, i) {
+ this.add(element, i)
+ return element
+ }
+ // Checks if the given element is a child
+ , has: function(element) {
+ return this.index(element) >= 0
+ }
+ // Gets index of given element
+ , index: function(element) {
+ return [].slice.call(this.node.childNodes).indexOf(element.node)
+ }
+ // Get a element at the given index
+ , get: function(i) {
+ return SVG.adopt(this.node.childNodes[i])
+ }
+ // Get first child
+ , first: function() {
+ return this.get(0)
+ }
+ // Get the last child
+ , last: function() {
+ return this.get(this.node.childNodes.length - 1)
+ }
+ // Iterates over all children and invokes a given block
+ , each: function(block, deep) {
+ var i, il
+ , children = this.children()
+
+ for (i = 0, il = children.length; i < il; i++) {
+ if (children[i] instanceof SVG.Element)
+ block.apply(children[i], [i, children])
+
+ if (deep && (children[i] instanceof SVG.Container))
+ children[i].each(block, deep)
+ }
+
+ return this
+ }
+ // Remove a given child
+ , removeElement: function(element) {
+ this.node.removeChild(element.node)
+
+ return this
+ }
+ // Remove all elements in this container
+ , clear: function() {
+ // remove children
+ while(this.node.hasChildNodes())
+ this.node.removeChild(this.node.lastChild)
+
+ // remove defs reference
+ delete this._defs
+
+ return this
+ }
+ , // Get defs
+ defs: function() {
+ return this.doc().defs()
+ }
+ }
+
+})
diff --git a/node_modules/svg.js/src/path.js b/node_modules/svg.js/src/path.js
new file mode 100644
index 0000000..e5504bb
--- /dev/null
+++ b/node_modules/svg.js/src/path.js
@@ -0,0 +1,64 @@
+SVG.Path = SVG.invent({
+ // Initialize node
+ create: 'path'
+
+ // Inherit from
+, inherit: SVG.Shape
+
+ // Add class methods
+, extend: {
+ // Define morphable array
+ morphArray: SVG.PathArray
+ // Get array
+ , array: function() {
+ return this._array || (this._array = new SVG.PathArray(this.attr('d')))
+ }
+ // Plot new path
+ , plot: function(d) {
+ return (d == null) ?
+ this.array() :
+ this.clear().attr('d', typeof d == 'string' ? d : (this._array = new SVG.PathArray(d)))
+ }
+ // Clear array cache
+ , clear: function() {
+ delete this._array
+ return this
+ }
+ // Move by left top corner
+ , move: function(x, y) {
+ return this.attr('d', this.array().move(x, y))
+ }
+ // Move by left top corner over x-axis
+ , x: function(x) {
+ return x == null ? this.bbox().x : this.move(x, this.bbox().y)
+ }
+ // Move by left top corner over y-axis
+ , y: function(y) {
+ return y == null ? this.bbox().y : this.move(this.bbox().x, y)
+ }
+ // Set element size to given width and height
+ , size: function(width, height) {
+ var p = proportionalSize(this, width, height)
+
+ return this.attr('d', this.array().size(p.width, p.height))
+ }
+ // Set width of element
+ , width: function(width) {
+ return width == null ? this.bbox().width : this.size(width, this.bbox().height)
+ }
+ // Set height of element
+ , height: function(height) {
+ return height == null ? this.bbox().height : this.size(this.bbox().width, height)
+ }
+
+ }
+
+ // Add parent method
+, construct: {
+ // Create a wrapped path element
+ path: function(d) {
+ // make sure plot is called as a setter
+ return this.put(new SVG.Path).plot(d || new SVG.PathArray)
+ }
+ }
+})
diff --git a/node_modules/svg.js/src/patharray.js b/node_modules/svg.js/src/patharray.js
new file mode 100644
index 0000000..4fb9318
--- /dev/null
+++ b/node_modules/svg.js/src/patharray.js
@@ -0,0 +1,297 @@
+var pathHandlers = {
+ M: function(c, p, p0) {
+ p.x = p0.x = c[0]
+ p.y = p0.y = c[1]
+
+ return ['M', p.x, p.y]
+ },
+ L: function(c, p) {
+ p.x = c[0]
+ p.y = c[1]
+ return ['L', c[0], c[1]]
+ },
+ H: function(c, p) {
+ p.x = c[0]
+ return ['H', c[0]]
+ },
+ V: function(c, p) {
+ p.y = c[0]
+ return ['V', c[0]]
+ },
+ C: function(c, p) {
+ p.x = c[4]
+ p.y = c[5]
+ return ['C', c[0], c[1], c[2], c[3], c[4], c[5]]
+ },
+ S: function(c, p) {
+ p.x = c[2]
+ p.y = c[3]
+ return ['S', c[0], c[1], c[2], c[3]]
+ },
+ Q: function(c, p) {
+ p.x = c[2]
+ p.y = c[3]
+ return ['Q', c[0], c[1], c[2], c[3]]
+ },
+ T: function(c, p) {
+ p.x = c[0]
+ p.y = c[1]
+ return ['T', c[0], c[1]]
+ },
+ Z: function(c, p, p0) {
+ p.x = p0.x
+ p.y = p0.y
+ return ['Z']
+ },
+ A: function(c, p) {
+ p.x = c[5]
+ p.y = c[6]
+ return ['A', c[0], c[1], c[2], c[3], c[4], c[5], c[6]]
+ }
+}
+
+var mlhvqtcsa = 'mlhvqtcsaz'.split('')
+
+for(var i = 0, il = mlhvqtcsa.length; i < il; ++i){
+ pathHandlers[mlhvqtcsa[i]] = (function(i){
+ return function(c, p, p0) {
+ if(i == 'H') c[0] = c[0] + p.x
+ else if(i == 'V') c[0] = c[0] + p.y
+ else if(i == 'A'){
+ c[5] = c[5] + p.x,
+ c[6] = c[6] + p.y
+ }
+ else
+ for(var j = 0, jl = c.length; j < jl; ++j) {
+ c[j] = c[j] + (j%2 ? p.y : p.x)
+ }
+
+ return pathHandlers[i](c, p, p0)
+ }
+ })(mlhvqtcsa[i].toUpperCase())
+}
+
+// Path points array
+SVG.PathArray = function(array, fallback) {
+ SVG.Array.call(this, array, fallback || [['M', 0, 0]])
+}
+
+// Inherit from SVG.Array
+SVG.PathArray.prototype = new SVG.Array
+SVG.PathArray.prototype.constructor = SVG.PathArray
+
+SVG.extend(SVG.PathArray, {
+ // Convert array to string
+ toString: function() {
+ return arrayToString(this.value)
+ }
+ // Move path string
+, move: function(x, y) {
+ // get bounding box of current situation
+ var box = this.bbox()
+
+ // get relative offset
+ x -= box.x
+ y -= box.y
+
+ if (!isNaN(x) && !isNaN(y)) {
+ // move every point
+ for (var l, i = this.value.length - 1; i >= 0; i--) {
+ l = this.value[i][0]
+
+ if (l == 'M' || l == 'L' || l == 'T') {
+ this.value[i][1] += x
+ this.value[i][2] += y
+
+ } else if (l == 'H') {
+ this.value[i][1] += x
+
+ } else if (l == 'V') {
+ this.value[i][1] += y
+
+ } else if (l == 'C' || l == 'S' || l == 'Q') {
+ this.value[i][1] += x
+ this.value[i][2] += y
+ this.value[i][3] += x
+ this.value[i][4] += y
+
+ if (l == 'C') {
+ this.value[i][5] += x
+ this.value[i][6] += y
+ }
+
+ } else if (l == 'A') {
+ this.value[i][6] += x
+ this.value[i][7] += y
+ }
+
+ }
+ }
+
+ return this
+ }
+ // Resize path string
+, size: function(width, height) {
+ // get bounding box of current situation
+ var i, l, box = this.bbox()
+
+ // recalculate position of all points according to new size
+ for (i = this.value.length - 1; i >= 0; i--) {
+ l = this.value[i][0]
+
+ if (l == 'M' || l == 'L' || l == 'T') {
+ this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x
+ this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y
+
+ } else if (l == 'H') {
+ this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x
+
+ } else if (l == 'V') {
+ this.value[i][1] = ((this.value[i][1] - box.y) * height) / box.height + box.y
+
+ } else if (l == 'C' || l == 'S' || l == 'Q') {
+ this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x
+ this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y
+ this.value[i][3] = ((this.value[i][3] - box.x) * width) / box.width + box.x
+ this.value[i][4] = ((this.value[i][4] - box.y) * height) / box.height + box.y
+
+ if (l == 'C') {
+ this.value[i][5] = ((this.value[i][5] - box.x) * width) / box.width + box.x
+ this.value[i][6] = ((this.value[i][6] - box.y) * height) / box.height + box.y
+ }
+
+ } else if (l == 'A') {
+ // resize radii
+ this.value[i][1] = (this.value[i][1] * width) / box.width
+ this.value[i][2] = (this.value[i][2] * height) / box.height
+
+ // move position values
+ this.value[i][6] = ((this.value[i][6] - box.x) * width) / box.width + box.x
+ this.value[i][7] = ((this.value[i][7] - box.y) * height) / box.height + box.y
+ }
+
+ }
+
+ return this
+ }
+ // Test if the passed path array use the same path data commands as this path array
+, equalCommands: function(pathArray) {
+ var i, il, equalCommands
+
+ pathArray = new SVG.PathArray(pathArray)
+
+ equalCommands = this.value.length === pathArray.value.length
+ for(i = 0, il = this.value.length; equalCommands && i < il; i++) {
+ equalCommands = this.value[i][0] === pathArray.value[i][0]
+ }
+
+ return equalCommands
+ }
+ // Make path array morphable
+, morph: function(pathArray) {
+ pathArray = new SVG.PathArray(pathArray)
+
+ if(this.equalCommands(pathArray)) {
+ this.destination = pathArray
+ } else {
+ this.destination = null
+ }
+
+ return this
+ }
+ // Get morphed path array at given position
+, at: function(pos) {
+ // make sure a destination is defined
+ if (!this.destination) return this
+
+ var sourceArray = this.value
+ , destinationArray = this.destination.value
+ , array = [], pathArray = new SVG.PathArray()
+ , i, il, j, jl
+
+ // Animate has specified in the SVG spec
+ // See: https://www.w3.org/TR/SVG11/paths.html#PathElement
+ for (i = 0, il = sourceArray.length; i < il; i++) {
+ array[i] = [sourceArray[i][0]]
+ for(j = 1, jl = sourceArray[i].length; j < jl; j++) {
+ array[i][j] = sourceArray[i][j] + (destinationArray[i][j] - sourceArray[i][j]) * pos
+ }
+ // For the two flags of the elliptical arc command, the SVG spec say:
+ // Flags and booleans are interpolated as fractions between zero and one, with any non-zero value considered to be a value of one/true
+ // Elliptical arc command as an array followed by corresponding indexes:
+ // ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
+ // 0 1 2 3 4 5 6 7
+ if(array[i][0] === 'A') {
+ array[i][4] = +(array[i][4] != 0)
+ array[i][5] = +(array[i][5] != 0)
+ }
+ }
+
+ // Directly modify the value of a path array, this is done this way for performance
+ pathArray.value = array
+ return pathArray
+ }
+ // Absolutize and parse path to array
+, parse: function(array) {
+ // if it's already a patharray, no need to parse it
+ if (array instanceof SVG.PathArray) return array.valueOf()
+
+ // prepare for parsing
+ var i, x0, y0, s, seg, arr
+ , x = 0
+ , y = 0
+ , paramCnt = { 'M':2, 'L':2, 'H':1, 'V':1, 'C':6, 'S':4, 'Q':4, 'T':2, 'A':7, 'Z':0 }
+
+ if(typeof array == 'string'){
+
+ array = array
+ .replace(SVG.regex.numbersWithDots, pathRegReplace) // convert 45.123.123 to 45.123 .123
+ .replace(SVG.regex.pathLetters, ' $& ') // put some room between letters and numbers
+ .replace(SVG.regex.hyphen, '$1 -') // add space before hyphen
+ .trim() // trim
+ .split(SVG.regex.delimiter) // split into array
+
+ }else{
+ array = array.reduce(function(prev, curr){
+ return [].concat.call(prev, curr)
+ }, [])
+ }
+
+ // array now is an array containing all parts of a path e.g. ['M', '0', '0', 'L', '30', '30' ...]
+ var arr = []
+ , p = new SVG.Point()
+ , p0 = new SVG.Point()
+ , index = 0
+ , len = array.length
+
+ do{
+ // Test if we have a path letter
+ if(SVG.regex.isPathLetter.test(array[index])){
+ s = array[index]
+ ++index
+ // If last letter was a move command and we got no new, it defaults to [L]ine
+ }else if(s == 'M'){
+ s = 'L'
+ }else if(s == 'm'){
+ s = 'l'
+ }
+
+ arr.push(pathHandlers[s].call(null,
+ array.slice(index, (index = index + paramCnt[s.toUpperCase()])).map(parseFloat),
+ p, p0
+ )
+ )
+
+ }while(len > index)
+
+ return arr
+
+ }
+ // Get bounding box of path
+, bbox: function() {
+ SVG.parser.path.setAttribute('d', this.toString())
+
+ return SVG.parser.path.getBBox()
+ }
+
+})
diff --git a/node_modules/svg.js/src/pattern.js b/node_modules/svg.js/src/pattern.js
new file mode 100644
index 0000000..fed33c8
--- /dev/null
+++ b/node_modules/svg.js/src/pattern.js
@@ -0,0 +1,58 @@
+SVG.Pattern = SVG.invent({
+ // Initialize node
+ create: 'pattern'
+
+ // Inherit from
+, inherit: SVG.Container
+
+ // Add class methods
+, extend: {
+ // Return the fill id
+ fill: function() {
+ return 'url(#' + this.id() + ')'
+ }
+ // Update pattern by rebuilding
+ , update: function(block) {
+ // remove content
+ this.clear()
+
+ // invoke passed block
+ if (typeof block == 'function')
+ block.call(this, this)
+
+ return this
+ }
+ // Alias string convertion to fill
+ , toString: function() {
+ return this.fill()
+ }
+ // custom attr to handle transform
+ , attr: function(a, b, c) {
+ if(a == 'transform') a = 'patternTransform'
+ return SVG.Container.prototype.attr.call(this, a, b, c)
+ }
+
+ }
+
+ // Add parent method
+, construct: {
+ // Create pattern element in defs
+ pattern: function(width, height, block) {
+ return this.defs().pattern(width, height, block)
+ }
+ }
+})
+
+SVG.extend(SVG.Defs, {
+ // Define gradient
+ pattern: function(width, height, block) {
+ return this.put(new SVG.Pattern).update(block).attr({
+ x: 0
+ , y: 0
+ , width: width
+ , height: height
+ , patternUnits: 'userSpaceOnUse'
+ })
+ }
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/point.js b/node_modules/svg.js/src/point.js
new file mode 100644
index 0000000..3a54d43
--- /dev/null
+++ b/node_modules/svg.js/src/point.js
@@ -0,0 +1,72 @@
+SVG.Point = SVG.invent({
+ // Initialize
+ create: function(x,y) {
+ var i, source, base = {x:0, y:0}
+
+ // ensure source as object
+ source = Array.isArray(x) ?
+ {x:x[0], y:x[1]} :
+ typeof x === 'object' ?
+ {x:x.x, y:x.y} :
+ x != null ?
+ {x:x, y:(y != null ? y : x)} : base // If y has no value, then x is used has its value
+
+ // merge source
+ this.x = source.x
+ this.y = source.y
+ }
+
+ // Add methods
+, extend: {
+ // Clone point
+ clone: function() {
+ return new SVG.Point(this)
+ }
+ // Morph one point into another
+ , morph: function(x, y) {
+ // store new destination
+ this.destination = new SVG.Point(x, y)
+
+ return this
+ }
+ // Get morphed point at a given position
+ , at: function(pos) {
+ // make sure a destination is defined
+ if (!this.destination) return this
+
+ // calculate morphed matrix at a given position
+ var point = new SVG.Point({
+ x: this.x + (this.destination.x - this.x) * pos
+ , y: this.y + (this.destination.y - this.y) * pos
+ })
+
+ return point
+ }
+ // Convert to native SVGPoint
+ , native: function() {
+ // create new point
+ var point = SVG.parser.native.createSVGPoint()
+
+ // update with current values
+ point.x = this.x
+ point.y = this.y
+
+ return point
+ }
+ // transform point with matrix
+ , transform: function(matrix) {
+ return new SVG.Point(this.native().matrixTransform(matrix.native()))
+ }
+
+ }
+
+})
+
+SVG.extend(SVG.Element, {
+
+ // Get point
+ point: function(x, y) {
+ return new SVG.Point(x,y).transform(this.screenCTM().inverse());
+ }
+
+})
diff --git a/node_modules/svg.js/src/pointarray.js b/node_modules/svg.js/src/pointarray.js
new file mode 100644
index 0000000..dae836e
--- /dev/null
+++ b/node_modules/svg.js/src/pointarray.js
@@ -0,0 +1,106 @@
+// Poly points array
+SVG.PointArray = function(array, fallback) {
+ SVG.Array.call(this, array, fallback || [[0,0]])
+}
+
+// Inherit from SVG.Array
+SVG.PointArray.prototype = new SVG.Array
+SVG.PointArray.prototype.constructor = SVG.PointArray
+
+SVG.extend(SVG.PointArray, {
+ // Convert array to string
+ toString: function() {
+ // convert to a poly point string
+ for (var i = 0, il = this.value.length, array = []; i < il; i++)
+ array.push(this.value[i].join(','))
+
+ return array.join(' ')
+ }
+ // Convert array to line object
+, toLine: function() {
+ return {
+ x1: this.value[0][0]
+ , y1: this.value[0][1]
+ , x2: this.value[1][0]
+ , y2: this.value[1][1]
+ }
+ }
+ // Get morphed array at given position
+, at: function(pos) {
+ // make sure a destination is defined
+ if (!this.destination) return this
+
+ // generate morphed point string
+ for (var i = 0, il = this.value.length, array = []; i < il; i++)
+ array.push([
+ this.value[i][0] + (this.destination[i][0] - this.value[i][0]) * pos
+ , this.value[i][1] + (this.destination[i][1] - this.value[i][1]) * pos
+ ])
+
+ return new SVG.PointArray(array)
+ }
+ // Parse point string and flat array
+, parse: function(array) {
+ var points = []
+
+ array = array.valueOf()
+
+ // if it is an array
+ if (Array.isArray(array)) {
+ // and it is not flat, there is no need to parse it
+ if(Array.isArray(array[0])) {
+ // make sure to use a clone
+ return array.map(function (el) { return el.slice() })
+ } else if (array[0].x != null){
+ // allow point objects to be passed
+ return array.map(function (el) { return [el.x, el.y] })
+ }
+ } else { // Else, it is considered as a string
+ // parse points
+ array = array.trim().split(SVG.regex.delimiter).map(parseFloat)
+ }
+
+ // validate points - https://svgwg.org/svg2-draft/shapes.html#DataTypePoints
+ // Odd number of coordinates is an error. In such cases, drop the last odd coordinate.
+ if (array.length % 2 !== 0) array.pop()
+
+ // wrap points in two-tuples and parse points as floats
+ for(var i = 0, len = array.length; i < len; i = i + 2)
+ points.push([ array[i], array[i+1] ])
+
+ return points
+ }
+ // Move point string
+, move: function(x, y) {
+ var box = this.bbox()
+
+ // get relative offset
+ x -= box.x
+ y -= box.y
+
+ // move every point
+ if (!isNaN(x) && !isNaN(y))
+ for (var i = this.value.length - 1; i >= 0; i--)
+ this.value[i] = [this.value[i][0] + x, this.value[i][1] + y]
+
+ return this
+ }
+ // Resize poly string
+, size: function(width, height) {
+ var i, box = this.bbox()
+
+ // recalculate position of all points according to new size
+ for (i = this.value.length - 1; i >= 0; i--) {
+ if(box.width) this.value[i][0] = ((this.value[i][0] - box.x) * width) / box.width + box.x
+ if(box.height) this.value[i][1] = ((this.value[i][1] - box.y) * height) / box.height + box.y
+ }
+
+ return this
+ }
+ // Get bounding box of points
+, bbox: function() {
+ SVG.parser.poly.setAttribute('points', this.toString())
+
+ return SVG.parser.poly.getBBox()
+ }
+})
diff --git a/node_modules/svg.js/src/pointed.js b/node_modules/svg.js/src/pointed.js
new file mode 100644
index 0000000..02ff44e
--- /dev/null
+++ b/node_modules/svg.js/src/pointed.js
@@ -0,0 +1,25 @@
+// unify all point to point elements
+SVG.extend(SVG.Line, SVG.Polyline, SVG.Polygon, {
+ // Define morphable array
+ morphArray: SVG.PointArray
+ // Move by left top corner over x-axis
+, x: function(x) {
+ return x == null ? this.bbox().x : this.move(x, this.bbox().y)
+ }
+ // Move by left top corner over y-axis
+, y: function(y) {
+ return y == null ? this.bbox().y : this.move(this.bbox().x, y)
+ }
+ // Set width of element
+, width: function(width) {
+ var b = this.bbox()
+
+ return width == null ? b.width : this.size(width, b.height)
+ }
+ // Set height of element
+, height: function(height) {
+ var b = this.bbox()
+
+ return height == null ? b.height : this.size(b.width, height)
+ }
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/poly.js b/node_modules/svg.js/src/poly.js
new file mode 100644
index 0000000..269b112
--- /dev/null
+++ b/node_modules/svg.js/src/poly.js
@@ -0,0 +1,63 @@
+SVG.Polyline = SVG.invent({
+ // Initialize node
+ create: 'polyline'
+
+ // Inherit from
+, inherit: SVG.Shape
+
+ // Add parent method
+, construct: {
+ // Create a wrapped polyline element
+ polyline: function(p) {
+ // make sure plot is called as a setter
+ return this.put(new SVG.Polyline).plot(p || new SVG.PointArray)
+ }
+ }
+})
+
+SVG.Polygon = SVG.invent({
+ // Initialize node
+ create: 'polygon'
+
+ // Inherit from
+, inherit: SVG.Shape
+
+ // Add parent method
+, construct: {
+ // Create a wrapped polygon element
+ polygon: function(p) {
+ // make sure plot is called as a setter
+ return this.put(new SVG.Polygon).plot(p || new SVG.PointArray)
+ }
+ }
+})
+
+// Add polygon-specific functions
+SVG.extend(SVG.Polyline, SVG.Polygon, {
+ // Get array
+ array: function() {
+ return this._array || (this._array = new SVG.PointArray(this.attr('points')))
+ }
+ // Plot new path
+, plot: function(p) {
+ return (p == null) ?
+ this.array() :
+ this.clear().attr('points', typeof p == 'string' ? p : (this._array = new SVG.PointArray(p)))
+ }
+ // Clear array cache
+, clear: function() {
+ delete this._array
+ return this
+ }
+ // Move by left top corner
+, move: function(x, y) {
+ return this.attr('points', this.array().move(x, y))
+ }
+ // Set element size to given width and height
+, size: function(width, height) {
+ var p = proportionalSize(this, width, height)
+
+ return this.attr('points', this.array().size(p.width, p.height))
+ }
+
+})
diff --git a/node_modules/svg.js/src/polyfill.js b/node_modules/svg.js/src/polyfill.js
new file mode 100644
index 0000000..5ab3153
--- /dev/null
+++ b/node_modules/svg.js/src/polyfill.js
@@ -0,0 +1,44 @@
+// Add CustomEvent to IE9 and IE10
+if (typeof window.CustomEvent !== 'function') {
+ // Code from: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent
+ var CustomEventPoly = function(event, options) {
+ options = options || { bubbles: false, cancelable: false, detail: undefined }
+ var e = document.createEvent('CustomEvent')
+ e.initCustomEvent(event, options.bubbles, options.cancelable, options.detail)
+ return e
+ }
+
+ CustomEventPoly.prototype = window.Event.prototype
+
+ SVG.CustomEvent = CustomEventPoly
+} else {
+ SVG.CustomEvent = window.CustomEvent
+}
+
+// requestAnimationFrame / cancelAnimationFrame Polyfill with fallback based on Paul Irish
+(function(w) {
+ var lastTime = 0
+ var vendors = ['moz', 'webkit']
+
+ for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
+ w.requestAnimationFrame = w[vendors[x] + 'RequestAnimationFrame']
+ w.cancelAnimationFrame = w[vendors[x] + 'CancelAnimationFrame'] ||
+ w[vendors[x] + 'CancelRequestAnimationFrame']
+ }
+
+ w.requestAnimationFrame = w.requestAnimationFrame ||
+ function(callback) {
+ var currTime = new Date().getTime()
+ var timeToCall = Math.max(0, 16 - (currTime - lastTime))
+
+ var id = w.setTimeout(function() {
+ callback(currTime + timeToCall)
+ }, timeToCall)
+
+ lastTime = currTime + timeToCall
+ return id
+ }
+
+ w.cancelAnimationFrame = w.cancelAnimationFrame || w.clearTimeout;
+
+}(window))
\ No newline at end of file
diff --git a/node_modules/svg.js/src/rect.js b/node_modules/svg.js/src/rect.js
new file mode 100644
index 0000000..6c639fe
--- /dev/null
+++ b/node_modules/svg.js/src/rect.js
@@ -0,0 +1,15 @@
+SVG.Rect = SVG.invent({
+ // Initialize node
+ create: 'rect'
+
+ // Inherit from
+, inherit: SVG.Shape
+
+ // Add parent method
+, construct: {
+ // Create a rect element
+ rect: function(width, height) {
+ return this.put(new SVG.Rect()).size(width, height)
+ }
+ }
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/regex.js b/node_modules/svg.js/src/regex.js
new file mode 100644
index 0000000..c0ca706
--- /dev/null
+++ b/node_modules/svg.js/src/regex.js
@@ -0,0 +1,61 @@
+// Storage for regular expressions
+SVG.regex = {
+ // Parse unit value
+ numberAndUnit: /^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i
+
+ // Parse hex value
+, hex: /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i
+
+ // Parse rgb value
+, rgb: /rgb\((\d+),(\d+),(\d+)\)/
+
+ // Parse reference id
+, reference: /#([a-z0-9\-_]+)/i
+
+ // splits a transformation chain
+, transforms: /\)\s*,?\s*/
+
+ // Whitespace
+, whitespace: /\s/g
+
+ // Test hex value
+, isHex: /^#[a-f0-9]{3,6}$/i
+
+ // Test rgb value
+, isRgb: /^rgb\(/
+
+ // Test css declaration
+, isCss: /[^:]+:[^;]+;?/
+
+ // Test for blank string
+, isBlank: /^(\s+)?$/
+
+ // Test for numeric string
+, isNumber: /^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i
+
+ // Test for percent value
+, isPercent: /^-?[\d\.]+%$/
+
+ // Test for image url
+, isImage: /\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i
+
+ // split at whitespace and comma
+, delimiter: /[\s,]+/
+
+ // The following regex are used to parse the d attribute of a path
+
+ // Matches all hyphens which are not after an exponent
+, hyphen: /([^e])\-/gi
+
+ // Replaces and tests for all path letters
+, pathLetters: /[MLHVCSQTAZ]/gi
+
+ // yes we need this one, too
+, isPathLetter: /[MLHVCSQTAZ]/i
+
+ // matches 0.154.23.45
+, numbersWithDots: /((\d?\.\d+(?:e[+-]?\d+)?)((?:\.\d+(?:e[+-]?\d+)?)+))+/gi
+
+ // matches .
+, dots: /\./g
+}
diff --git a/node_modules/svg.js/src/selector.js b/node_modules/svg.js/src/selector.js
new file mode 100644
index 0000000..fe87e4e
--- /dev/null
+++ b/node_modules/svg.js/src/selector.js
@@ -0,0 +1,22 @@
+// Method for getting an element by id
+SVG.get = function(id) {
+ var node = document.getElementById(idFromReference(id) || id)
+ return SVG.adopt(node)
+}
+
+// Select elements by query string
+SVG.select = function(query, parent) {
+ return new SVG.Set(
+ SVG.utils.map((parent || document).querySelectorAll(query), function(node) {
+ return SVG.adopt(node)
+ })
+ )
+}
+
+SVG.extend(SVG.Parent, {
+ // Scoped select method
+ select: function(query) {
+ return SVG.select(query, this.node)
+ }
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/set.js b/node_modules/svg.js/src/set.js
new file mode 100644
index 0000000..a602d1d
--- /dev/null
+++ b/node_modules/svg.js/src/set.js
@@ -0,0 +1,148 @@
+SVG.Set = SVG.invent({
+ // Initialize
+ create: function(members) {
+ if (members instanceof SVG.Set) {
+ this.members = members.members.slice()
+ } else {
+ Array.isArray(members) ? this.members = members : this.clear()
+ }
+ }
+
+ // Add class methods
+, extend: {
+ // Add element to set
+ add: function() {
+ var i, il, elements = [].slice.call(arguments)
+
+ for (i = 0, il = elements.length; i < il; i++)
+ this.members.push(elements[i])
+
+ return this
+ }
+ // Remove element from set
+ , remove: function(element) {
+ var i = this.index(element)
+
+ // remove given child
+ if (i > -1)
+ this.members.splice(i, 1)
+
+ return this
+ }
+ // Iterate over all members
+ , each: function(block) {
+ for (var i = 0, il = this.members.length; i < il; i++)
+ block.apply(this.members[i], [i, this.members])
+
+ return this
+ }
+ // Restore to defaults
+ , clear: function() {
+ // initialize store
+ this.members = []
+
+ return this
+ }
+ // Get the length of a set
+ , length: function() {
+ return this.members.length
+ }
+ // Checks if a given element is present in set
+ , has: function(element) {
+ return this.index(element) >= 0
+ }
+ // retuns index of given element in set
+ , index: function(element) {
+ return this.members.indexOf(element)
+ }
+ // Get member at given index
+ , get: function(i) {
+ return this.members[i]
+ }
+ // Get first member
+ , first: function() {
+ return this.get(0)
+ }
+ // Get last member
+ , last: function() {
+ return this.get(this.members.length - 1)
+ }
+ // Default value
+ , valueOf: function() {
+ return this.members
+ }
+ // Get the bounding box of all members included or empty box if set has no items
+ , bbox: function(){
+ // return an empty box of there are no members
+ if (this.members.length == 0)
+ return new SVG.RBox()
+
+ // get the first rbox and update the target bbox
+ var rbox = this.members[0].rbox(this.members[0].doc())
+
+ this.each(function() {
+ // user rbox for correct position and visual representation
+ rbox = rbox.merge(this.rbox(this.doc()))
+ })
+
+ return rbox
+ }
+ }
+
+ // Add parent method
+, construct: {
+ // Create a new set
+ set: function(members) {
+ return new SVG.Set(members)
+ }
+ }
+})
+
+SVG.FX.Set = SVG.invent({
+ // Initialize node
+ create: function(set) {
+ // store reference to set
+ this.set = set
+ }
+
+})
+
+// Alias methods
+SVG.Set.inherit = function() {
+ var m
+ , methods = []
+
+ // gather shape methods
+ for(var m in SVG.Shape.prototype)
+ if (typeof SVG.Shape.prototype[m] == 'function' && typeof SVG.Set.prototype[m] != 'function')
+ methods.push(m)
+
+ // apply shape aliasses
+ methods.forEach(function(method) {
+ SVG.Set.prototype[method] = function() {
+ for (var i = 0, il = this.members.length; i < il; i++)
+ if (this.members[i] && typeof this.members[i][method] == 'function')
+ this.members[i][method].apply(this.members[i], arguments)
+
+ return method == 'animate' ? (this.fx || (this.fx = new SVG.FX.Set(this))) : this
+ }
+ })
+
+ // clear methods for the next round
+ methods = []
+
+ // gather fx methods
+ for(var m in SVG.FX.prototype)
+ if (typeof SVG.FX.prototype[m] == 'function' && typeof SVG.FX.Set.prototype[m] != 'function')
+ methods.push(m)
+
+ // apply fx aliasses
+ methods.forEach(function(method) {
+ SVG.FX.Set.prototype[method] = function() {
+ for (var i = 0, il = this.set.members.length; i < il; i++)
+ this.set.members[i].fx[method].apply(this.set.members[i].fx, arguments)
+
+ return this
+ }
+ })
+}
diff --git a/node_modules/svg.js/src/shape.js b/node_modules/svg.js/src/shape.js
new file mode 100644
index 0000000..3bfc210
--- /dev/null
+++ b/node_modules/svg.js/src/shape.js
@@ -0,0 +1,10 @@
+SVG.Shape = SVG.invent({
+ // Initialize node
+ create: function(element) {
+ this.constructor.call(this, element)
+ }
+
+ // Inherit from
+, inherit: SVG.Element
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/style.js b/node_modules/svg.js/src/style.js
new file mode 100644
index 0000000..053d606
--- /dev/null
+++ b/node_modules/svg.js/src/style.js
@@ -0,0 +1,35 @@
+SVG.extend(SVG.Element, {
+ // Dynamic style generator
+ style: function(s, v) {
+ if (arguments.length == 0) {
+ // get full style
+ return this.node.style.cssText || ''
+
+ } else if (arguments.length < 2) {
+ // apply every style individually if an object is passed
+ if (typeof s == 'object') {
+ for (v in s) this.style(v, s[v])
+
+ } else if (SVG.regex.isCss.test(s)) {
+ // parse css string
+ s = s.split(/\s*;\s*/)
+ // filter out suffix ; and stuff like ;;
+ .filter(function(e) { return !!e })
+ .map(function(e){ return e.split(/\s*:\s*/) })
+
+ // apply every definition individually
+ while (v = s.pop()) {
+ this.style(v[0], v[1])
+ }
+ } else {
+ // act as a getter if the first and only argument is not an object
+ return this.node.style[camelCase(s)]
+ }
+
+ } else {
+ this.node.style[camelCase(s)] = v === null || SVG.regex.isBlank.test(v) ? '' : v
+ }
+
+ return this
+ }
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/sugar.js b/node_modules/svg.js/src/sugar.js
new file mode 100644
index 0000000..e577ea7
--- /dev/null
+++ b/node_modules/svg.js/src/sugar.js
@@ -0,0 +1,117 @@
+// Define list of available attributes for stroke and fill
+var sugar = {
+ stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset']
+, fill: ['color', 'opacity', 'rule']
+, prefix: function(t, a) {
+ return a == 'color' ? t : t + '-' + a
+ }
+}
+
+// Add sugar for fill and stroke
+;['fill', 'stroke'].forEach(function(m) {
+ var i, extension = {}
+
+ extension[m] = function(o) {
+ if (typeof o == 'undefined')
+ return this
+ if (typeof o == 'string' || SVG.Color.isRgb(o) || (o && typeof o.fill === 'function'))
+ this.attr(m, o)
+
+ else
+ // set all attributes from sugar.fill and sugar.stroke list
+ for (i = sugar[m].length - 1; i >= 0; i--)
+ if (o[sugar[m][i]] != null)
+ this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]])
+
+ return this
+ }
+
+ SVG.extend(SVG.Element, SVG.FX, extension)
+
+})
+
+SVG.extend(SVG.Element, SVG.FX, {
+ // Map rotation to transform
+ rotate: function(d, cx, cy) {
+ return this.transform({ rotation: d, cx: cx, cy: cy })
+ }
+ // Map skew to transform
+, skew: function(x, y, cx, cy) {
+ return arguments.length == 1 || arguments.length == 3 ?
+ this.transform({ skew: x, cx: y, cy: cx }) :
+ this.transform({ skewX: x, skewY: y, cx: cx, cy: cy })
+ }
+ // Map scale to transform
+, scale: function(x, y, cx, cy) {
+ return arguments.length == 1 || arguments.length == 3 ?
+ this.transform({ scale: x, cx: y, cy: cx }) :
+ this.transform({ scaleX: x, scaleY: y, cx: cx, cy: cy })
+ }
+ // Map translate to transform
+, translate: function(x, y) {
+ return this.transform({ x: x, y: y })
+ }
+ // Map flip to transform
+, flip: function(a, o) {
+ o = typeof a == 'number' ? a : o
+ return this.transform({ flip: a || 'both', offset: o })
+ }
+ // Map matrix to transform
+, matrix: function(m) {
+ return this.attr('transform', new SVG.Matrix(arguments.length == 6 ? [].slice.call(arguments) : m))
+ }
+ // Opacity
+, opacity: function(value) {
+ return this.attr('opacity', value)
+ }
+ // Relative move over x axis
+, dx: function(x) {
+ return this.x(new SVG.Number(x).plus(this instanceof SVG.FX ? 0 : this.x()), true)
+ }
+ // Relative move over y axis
+, dy: function(y) {
+ return this.y(new SVG.Number(y).plus(this instanceof SVG.FX ? 0 : this.y()), true)
+ }
+ // Relative move over x and y axes
+, dmove: function(x, y) {
+ return this.dx(x).dy(y)
+ }
+})
+
+SVG.extend(SVG.Rect, SVG.Ellipse, SVG.Circle, SVG.Gradient, SVG.FX, {
+ // Add x and y radius
+ radius: function(x, y) {
+ var type = (this._target || this).type;
+ return type == 'radial' || type == 'circle' ?
+ this.attr('r', new SVG.Number(x)) :
+ this.rx(x).ry(y == null ? x : y)
+ }
+})
+
+SVG.extend(SVG.Path, {
+ // Get path length
+ length: function() {
+ return this.node.getTotalLength()
+ }
+ // Get point at length
+, pointAt: function(length) {
+ return this.node.getPointAtLength(length)
+ }
+})
+
+SVG.extend(SVG.Parent, SVG.Text, SVG.Tspan, SVG.FX, {
+ // Set font
+ font: function(a, v) {
+ if (typeof a == 'object') {
+ for (v in a) this.font(v, a[v])
+ }
+
+ return a == 'leading' ?
+ this.leading(v) :
+ a == 'anchor' ?
+ this.attr('text-anchor', v) :
+ a == 'size' || a == 'family' || a == 'weight' || a == 'stretch' || a == 'variant' || a == 'style' ?
+ this.attr('font-'+ a, v) :
+ this.attr(a, v)
+ }
+})
diff --git a/node_modules/svg.js/src/svg.js b/node_modules/svg.js/src/svg.js
new file mode 100644
index 0000000..6312736
--- /dev/null
+++ b/node_modules/svg.js/src/svg.js
@@ -0,0 +1,156 @@
+// Find global reference - uses 'this' by default when available,
+// falls back to 'window' otherwise (for bundlers like Webpack)
+var globalRef = (typeof this !== "undefined") ? this : window;
+
+// The main wrapping element
+var SVG = globalRef.SVG = function(element) {
+ if (SVG.supported) {
+ element = new SVG.Doc(element)
+
+ if(!SVG.parser.draw)
+ SVG.prepare()
+
+ return element
+ }
+}
+
+// Default namespaces
+SVG.ns = 'http://www.w3.org/2000/svg'
+SVG.xmlns = 'http://www.w3.org/2000/xmlns/'
+SVG.xlink = 'http://www.w3.org/1999/xlink'
+SVG.svgjs = 'http://svgjs.com/svgjs'
+
+// Svg support test
+SVG.supported = (function() {
+ return !! document.createElementNS &&
+ !! document.createElementNS(SVG.ns,'svg').createSVGRect
+})()
+
+// Don't bother to continue if SVG is not supported
+if (!SVG.supported) return false
+
+// Element id sequence
+SVG.did = 1000
+
+// Get next named element id
+SVG.eid = function(name) {
+ return 'Svgjs' + capitalize(name) + (SVG.did++)
+}
+
+// Method for element creation
+SVG.create = function(name) {
+ // create element
+ var element = document.createElementNS(this.ns, name)
+
+ // apply unique id
+ element.setAttribute('id', this.eid(name))
+
+ return element
+}
+
+// Method for extending objects
+SVG.extend = function() {
+ var modules, methods, key, i
+
+ // Get list of modules
+ modules = [].slice.call(arguments)
+
+ // Get object with extensions
+ methods = modules.pop()
+
+ for (i = modules.length - 1; i >= 0; i--)
+ if (modules[i])
+ for (key in methods)
+ modules[i].prototype[key] = methods[key]
+
+ // Make sure SVG.Set inherits any newly added methods
+ if (SVG.Set && SVG.Set.inherit)
+ SVG.Set.inherit()
+}
+
+// Invent new element
+SVG.invent = function(config) {
+ // Create element initializer
+ var initializer = typeof config.create == 'function' ?
+ config.create :
+ function() {
+ this.constructor.call(this, SVG.create(config.create))
+ }
+
+ // Inherit prototype
+ if (config.inherit)
+ initializer.prototype = new config.inherit
+
+ // Extend with methods
+ if (config.extend)
+ SVG.extend(initializer, config.extend)
+
+ // Attach construct method to parent
+ if (config.construct)
+ SVG.extend(config.parent || SVG.Container, config.construct)
+
+ return initializer
+}
+
+// Adopt existing svg elements
+SVG.adopt = function(node) {
+ // check for presence of node
+ if (!node) return null
+
+ // make sure a node isn't already adopted
+ if (node.instance) return node.instance
+
+ // initialize variables
+ var element
+
+ // adopt with element-specific settings
+ if (node.nodeName == 'svg')
+ element = node.parentNode instanceof window.SVGElement ? new SVG.Nested : new SVG.Doc
+ else if (node.nodeName == 'linearGradient')
+ element = new SVG.Gradient('linear')
+ else if (node.nodeName == 'radialGradient')
+ element = new SVG.Gradient('radial')
+ else if (SVG[capitalize(node.nodeName)])
+ element = new SVG[capitalize(node.nodeName)]
+ else
+ element = new SVG.Element(node)
+
+ // ensure references
+ element.type = node.nodeName
+ element.node = node
+ node.instance = element
+
+ // SVG.Class specific preparations
+ if (element instanceof SVG.Doc)
+ element.namespace().defs()
+
+ // pull svgjs data from the dom (getAttributeNS doesn't work in html5)
+ element.setData(JSON.parse(node.getAttribute('svgjs:data')) || {})
+
+ return element
+}
+
+// Initialize parsing element
+SVG.prepare = function() {
+ // Select document body and create invisible svg element
+ var body = document.getElementsByTagName('body')[0]
+ , draw = (body ? new SVG.Doc(body) : SVG.adopt(document.documentElement).nested()).size(2, 0)
+
+ // Create parser object
+ SVG.parser = {
+ body: body || document.documentElement
+ , draw: draw.style('opacity:0;position:absolute;left:-100%;top:-100%;overflow:hidden').attr('focusable', 'false').node
+ , poly: draw.polyline().node
+ , path: draw.path().node
+ , native: SVG.create('svg')
+ }
+}
+
+SVG.parser = {
+ native: SVG.create('svg')
+}
+
+document.addEventListener('DOMContentLoaded', function() {
+ if(!SVG.parser.draw)
+ SVG.prepare()
+}, false)
diff --git a/node_modules/svg.js/src/symbol.js b/node_modules/svg.js/src/symbol.js
new file mode 100644
index 0000000..f9c83e9
--- /dev/null
+++ b/node_modules/svg.js/src/symbol.js
@@ -0,0 +1,14 @@
+SVG.Symbol = SVG.invent({
+ // Initialize node
+ create: 'symbol'
+
+ // Inherit from
+, inherit: SVG.Container
+
+, construct: {
+ // create symbol
+ symbol: function() {
+ return this.put(new SVG.Symbol)
+ }
+ }
+})
diff --git a/node_modules/svg.js/src/text.js b/node_modules/svg.js/src/text.js
new file mode 100644
index 0000000..9895a35
--- /dev/null
+++ b/node_modules/svg.js/src/text.js
@@ -0,0 +1,249 @@
+SVG.Text = SVG.invent({
+ // Initialize node
+ create: function() {
+ this.constructor.call(this, SVG.create('text'))
+
+ this.dom.leading = new SVG.Number(1.3) // store leading value for rebuilding
+ this._rebuild = true // enable automatic updating of dy values
+ this._build = false // disable build mode for adding multiple lines
+
+ // set default font
+ this.attr('font-family', SVG.defaults.attrs['font-family'])
+ }
+
+ // Inherit from
+, inherit: SVG.Shape
+
+ // Add class methods
+, extend: {
+ // Move over x-axis
+ x: function(x) {
+ // act as getter
+ if (x == null)
+ return this.attr('x')
+
+ return this.attr('x', x)
+ }
+ // Move over y-axis
+ , y: function(y) {
+ var oy = this.attr('y')
+ , o = typeof oy === 'number' ? oy - this.bbox().y : 0
+
+ // act as getter
+ if (y == null)
+ return typeof oy === 'number' ? oy - o : oy
+
+ return this.attr('y', typeof y.valueOf() === 'number' ? y + o : y)
+ }
+ // Move center over x-axis
+ , cx: function(x) {
+ return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2)
+ }
+ // Move center over y-axis
+ , cy: function(y) {
+ return y == null ? this.bbox().cy : this.y(y - this.bbox().height / 2)
+ }
+ // Set the text content
+ , text: function(text) {
+ // act as getter
+ if (typeof text === 'undefined'){
+ var text = ''
+ var children = this.node.childNodes
+ for(var i = 0, len = children.length; i < len; ++i){
+
+ // add newline if its not the first child and newLined is set to true
+ if(i != 0 && children[i].nodeType != 3 && SVG.adopt(children[i]).dom.newLined == true){
+ text += '\n'
+ }
+
+ // add content of this node
+ text += children[i].textContent
+ }
+
+ return text
+ }
+
+ // remove existing content
+ this.clear().build(true)
+
+ if (typeof text === 'function') {
+ // call block
+ text.call(this, this)
+
+ } else {
+ // store text and make sure text is not blank
+ text = text.split('\n')
+
+ // build new lines
+ for (var i = 0, il = text.length; i < il; i++)
+ this.tspan(text[i]).newLine()
+ }
+
+ // disable build mode and rebuild lines
+ return this.build(false).rebuild()
+ }
+ // Set font size
+ , size: function(size) {
+ return this.attr('font-size', size).rebuild()
+ }
+ // Set / get leading
+ , leading: function(value) {
+ // act as getter
+ if (value == null)
+ return this.dom.leading
+
+ // act as setter
+ this.dom.leading = new SVG.Number(value)
+
+ return this.rebuild()
+ }
+ // Get all the first level lines
+ , lines: function() {
+ var node = (this.textPath && this.textPath() || this).node
+
+ // filter tspans and map them to SVG.js instances
+ var lines = SVG.utils.map(SVG.utils.filterSVGElements(node.childNodes), function(el){
+ return SVG.adopt(el)
+ })
+
+ // return an instance of SVG.set
+ return new SVG.Set(lines)
+ }
+ // Rebuild appearance type
+ , rebuild: function(rebuild) {
+ // store new rebuild flag if given
+ if (typeof rebuild == 'boolean')
+ this._rebuild = rebuild
+
+ // define position of all lines
+ if (this._rebuild) {
+ var self = this
+ , blankLineOffset = 0
+ , dy = this.dom.leading * new SVG.Number(this.attr('font-size'))
+
+ this.lines().each(function() {
+ if (this.dom.newLined) {
+ if (!self.textPath())
+ this.attr('x', self.attr('x'))
+ if(this.text() == '\n') {
+ blankLineOffset += dy
+ }else{
+ this.attr('dy', dy + blankLineOffset)
+ blankLineOffset = 0
+ }
+ }
+ })
+
+ this.fire('rebuild')
+ }
+
+ return this
+ }
+ // Enable / disable build mode
+ , build: function(build) {
+ this._build = !!build
+ return this
+ }
+ // overwrite method from parent to set data properly
+ , setData: function(o){
+ this.dom = o
+ this.dom.leading = new SVG.Number(o.leading || 1.3)
+ return this
+ }
+ }
+
+ // Add parent method
+, construct: {
+ // Create text element
+ text: function(text) {
+ return this.put(new SVG.Text).text(text)
+ }
+ // Create plain text element
+ , plain: function(text) {
+ return this.put(new SVG.Text).plain(text)
+ }
+ }
+
+})
+
+SVG.Tspan = SVG.invent({
+ // Initialize node
+ create: 'tspan'
+
+ // Inherit from
+, inherit: SVG.Shape
+
+ // Add class methods
+, extend: {
+ // Set text content
+ text: function(text) {
+ if(text == null) return this.node.textContent + (this.dom.newLined ? '\n' : '')
+
+ typeof text === 'function' ? text.call(this, this) : this.plain(text)
+
+ return this
+ }
+ // Shortcut dx
+ , dx: function(dx) {
+ return this.attr('dx', dx)
+ }
+ // Shortcut dy
+ , dy: function(dy) {
+ return this.attr('dy', dy)
+ }
+ // Create new line
+ , newLine: function() {
+ // fetch text parent
+ var t = this.parent(SVG.Text)
+
+ // mark new line
+ this.dom.newLined = true
+
+ // apply new hy¡n
+ return this.dy(t.dom.leading * t.attr('font-size')).attr('x', t.x())
+ }
+ }
+
+})
+
+SVG.extend(SVG.Text, SVG.Tspan, {
+ // Create plain text node
+ plain: function(text) {
+ // clear if build mode is disabled
+ if (this._build === false)
+ this.clear()
+
+ // create text node
+ this.node.appendChild(document.createTextNode(text))
+
+ return this
+ }
+ // Create a tspan
+, tspan: function(text) {
+ var node = (this.textPath && this.textPath() || this).node
+ , tspan = new SVG.Tspan
+
+ // clear if build mode is disabled
+ if (this._build === false)
+ this.clear()
+
+ // add new tspan
+ node.appendChild(tspan.node)
+
+ return tspan.text(text)
+ }
+ // Clear all lines
+, clear: function() {
+ var node = (this.textPath && this.textPath() || this).node
+
+ // remove existing child nodes
+ while (node.hasChildNodes())
+ node.removeChild(node.lastChild)
+
+ return this
+ }
+ // Get length of text element
+, length: function() {
+ return this.node.getComputedTextLength()
+ }
+})
diff --git a/node_modules/svg.js/src/textpath.js b/node_modules/svg.js/src/textpath.js
new file mode 100644
index 0000000..18e2149
--- /dev/null
+++ b/node_modules/svg.js/src/textpath.js
@@ -0,0 +1,62 @@
+SVG.TextPath = SVG.invent({
+ // Initialize node
+ create: 'textPath'
+
+ // Inherit from
+, inherit: SVG.Parent
+
+ // Define parent class
+, parent: SVG.Text
+
+ // Add parent method
+, construct: {
+ morphArray: SVG.PathArray
+ // Create path for text to run on
+ , path: function(d) {
+ // create textPath element
+ var path = new SVG.TextPath
+ , track = this.doc().defs().path(d)
+
+ // move lines to textpath
+ while (this.node.hasChildNodes())
+ path.node.appendChild(this.node.firstChild)
+
+ // add textPath element as child node
+ this.node.appendChild(path.node)
+
+ // link textPath to path and add content
+ path.attr('href', '#' + track, SVG.xlink)
+
+ return this
+ }
+ // return the array of the path track element
+ , array: function() {
+ var track = this.track()
+
+ return track ? track.array() : null
+ }
+ // Plot path if any
+ , plot: function(d) {
+ var track = this.track()
+ , pathArray = null
+
+ if (track) {
+ pathArray = track.plot(d)
+ }
+
+ return (d == null) ? pathArray : this
+ }
+ // Get the path track element
+ , track: function() {
+ var path = this.textPath()
+
+ if (path)
+ return path.reference('href')
+ }
+ // Get the textPath child
+ , textPath: function() {
+ if (this.node.firstChild && this.node.firstChild.nodeName == 'textPath')
+ return SVG.adopt(this.node.firstChild)
+ }
+ }
+})
diff --git a/node_modules/svg.js/src/transform.js b/node_modules/svg.js/src/transform.js
new file mode 100644
index 0000000..b738603
--- /dev/null
+++ b/node_modules/svg.js/src/transform.js
@@ -0,0 +1,370 @@
+SVG.extend(SVG.Element, {
+ // Add transformations
+ transform: function(o, relative) {
+ // get target in case of the fx module, otherwise reference this
+ var target = this
+ , matrix, bbox
+
+ // act as a getter
+ if (typeof o !== 'object') {
+ // get current matrix
+ matrix = new SVG.Matrix(target).extract()
+
+ return typeof o === 'string' ? matrix[o] : matrix
+ }
+
+ // get current matrix
+ matrix = new SVG.Matrix(target)
+
+ // ensure relative flag
+ relative = !!relative || !!o.relative
+
+ // act on matrix
+ if (o.a != null) {
+ matrix = relative ?
+ // relative
+ matrix.multiply(new SVG.Matrix(o)) :
+ // absolute
+ new SVG.Matrix(o)
+
+ // act on rotation
+ } else if (o.rotation != null) {
+ // ensure centre point
+ ensureCentre(o, target)
+
+ // apply transformation
+ matrix = relative ?
+ // relative
+ matrix.rotate(o.rotation, o.cx, o.cy) :
+ // absolute
+ matrix.rotate(o.rotation - matrix.extract().rotation, o.cx, o.cy)
+
+ // act on scale
+ } else if (o.scale != null || o.scaleX != null || o.scaleY != null) {
+ // ensure centre point
+ ensureCentre(o, target)
+
+ // ensure scale values on both axes
+ o.scaleX = o.scale != null ? o.scale : o.scaleX != null ? o.scaleX : 1
+ o.scaleY = o.scale != null ? o.scale : o.scaleY != null ? o.scaleY : 1
+
+ if (!relative) {
+ // absolute; multiply inversed values
+ var e = matrix.extract()
+ o.scaleX = o.scaleX * 1 / e.scaleX
+ o.scaleY = o.scaleY * 1 / e.scaleY
+ }
+
+ matrix = matrix.scale(o.scaleX, o.scaleY, o.cx, o.cy)
+
+ // act on skew
+ } else if (o.skew != null || o.skewX != null || o.skewY != null) {
+ // ensure centre point
+ ensureCentre(o, target)
+
+ // ensure skew values on both axes
+ o.skewX = o.skew != null ? o.skew : o.skewX != null ? o.skewX : 0
+ o.skewY = o.skew != null ? o.skew : o.skewY != null ? o.skewY : 0
+
+ if (!relative) {
+ // absolute; reset skew values
+ var e = matrix.extract()
+ matrix = matrix.multiply(new SVG.Matrix().skew(e.skewX, e.skewY, o.cx, o.cy).inverse())
+ }
+
+ matrix = matrix.skew(o.skewX, o.skewY, o.cx, o.cy)
+
+ // act on flip
+ } else if (o.flip) {
+ if(o.flip == 'x' || o.flip == 'y') {
+ o.offset = o.offset == null ? target.bbox()['c' + o.flip] : o.offset
+ } else {
+ if(o.offset == null) {
+ bbox = target.bbox()
+ o.flip = bbox.cx
+ o.offset = bbox.cy
+ } else {
+ o.flip = o.offset
+ }
+ }
+
+ matrix = new SVG.Matrix().flip(o.flip, o.offset)
+
+ // act on translate
+ } else if (o.x != null || o.y != null) {
+ if (relative) {
+ // relative
+ matrix = matrix.translate(o.x, o.y)
+ } else {
+ // absolute
+ if (o.x != null) matrix.e = o.x
+ if (o.y != null) matrix.f = o.y
+ }
+ }
+
+ return this.attr('transform', matrix)
+ }
+})
+
+SVG.extend(SVG.FX, {
+ transform: function(o, relative) {
+ // get target in case of the fx module, otherwise reference this
+ var target = this.target()
+ , matrix, bbox
+
+ // act as a getter
+ if (typeof o !== 'object') {
+ // get current matrix
+ matrix = new SVG.Matrix(target).extract()
+
+ return typeof o === 'string' ? matrix[o] : matrix
+ }
+
+ // ensure relative flag
+ relative = !!relative || !!o.relative
+
+ // act on matrix
+ if (o.a != null) {
+ matrix = new SVG.Matrix(o)
+
+ // act on rotation
+ } else if (o.rotation != null) {
+ // ensure centre point
+ ensureCentre(o, target)
+
+ // apply transformation
+ matrix = new SVG.Rotate(o.rotation, o.cx, o.cy)
+
+ // act on scale
+ } else if (o.scale != null || o.scaleX != null || o.scaleY != null) {
+ // ensure centre point
+ ensureCentre(o, target)
+
+ // ensure scale values on both axes
+ o.scaleX = o.scale != null ? o.scale : o.scaleX != null ? o.scaleX : 1
+ o.scaleY = o.scale != null ? o.scale : o.scaleY != null ? o.scaleY : 1
+
+ matrix = new SVG.Scale(o.scaleX, o.scaleY, o.cx, o.cy)
+
+ // act on skew
+ } else if (o.skewX != null || o.skewY != null) {
+ // ensure centre point
+ ensureCentre(o, target)
+
+ // ensure skew values on both axes
+ o.skewX = o.skewX != null ? o.skewX : 0
+ o.skewY = o.skewY != null ? o.skewY : 0
+
+ matrix = new SVG.Skew(o.skewX, o.skewY, o.cx, o.cy)
+
+ // act on flip
+ } else if (o.flip) {
+ if(o.flip == 'x' || o.flip == 'y') {
+ o.offset = o.offset == null ? target.bbox()['c' + o.flip] : o.offset
+ } else {
+ if(o.offset == null) {
+ bbox = target.bbox()
+ o.flip = bbox.cx
+ o.offset = bbox.cy
+ } else {
+ o.flip = o.offset
+ }
+ }
+
+ matrix = new SVG.Matrix().flip(o.flip, o.offset)
+
+ // act on translate
+ } else if (o.x != null || o.y != null) {
+ matrix = new SVG.Translate(o.x, o.y)
+ }
+
+ if(!matrix) return this
+
+ matrix.relative = relative
+
+ this.last().transforms.push(matrix)
+
+ return this._callStart()
+ }
+})
+
+SVG.extend(SVG.Element, {
+ // Reset all transformations
+ untransform: function() {
+ return this.attr('transform', null)
+ },
+ // merge the whole transformation chain into one matrix and returns it
+ matrixify: function() {
+
+ var matrix = (this.attr('transform') || '')
+ // split transformations
+ .split(SVG.regex.transforms).slice(0,-1).map(function(str){
+ // generate key => value pairs
+ var kv = str.trim().split('(')
+ return [kv[0], kv[1].split(SVG.regex.delimiter).map(function(str){ return parseFloat(str) })]
+ })
+ // merge every transformation into one matrix
+ .reduce(function(matrix, transform){
+
+ if(transform[0] == 'matrix') return matrix.multiply(arrayToMatrix(transform[1]))
+ return matrix[transform[0]].apply(matrix, transform[1])
+
+ }, new SVG.Matrix())
+
+ return matrix
+ },
+ // add an element to another parent without changing the visual representation on the screen
+ toParent: function(parent) {
+ if(this == parent) return this
+ var ctm = this.screenCTM()
+ var pCtm = parent.screenCTM().inverse()
+
+ this.addTo(parent).untransform().transform(pCtm.multiply(ctm))
+
+ return this
+ },
+ // same as above with parent equals root-svg
+ toDoc: function() {
+ return this.toParent(this.doc())
+ }
+
+})
+
+SVG.Transformation = SVG.invent({
+
+ create: function(source, inversed){
+
+ if(arguments.length > 1 && typeof inversed != 'boolean'){
+ return this.constructor.call(this, [].slice.call(arguments))
+ }
+
+ if(Array.isArray(source)){
+ for(var i = 0, len = this.arguments.length; i < len; ++i){
+ this[this.arguments[i]] = source[i]
+ }
+ } else if(typeof source == 'object'){
+ for(var i = 0, len = this.arguments.length; i < len; ++i){
+ this[this.arguments[i]] = source[this.arguments[i]]
+ }
+ }
+
+ this.inversed = false
+
+ if(inversed === true){
+ this.inversed = true
+ }
+
+ }
+
+, extend: {
+
+ arguments: []
+ , method: ''
+
+ , at: function(pos){
+
+ var params = []
+
+ for(var i = 0, len = this.arguments.length; i < len; ++i){
+ params.push(this[this.arguments[i]])
+ }
+
+ var m = this._undo || new SVG.Matrix()
+
+ m = new SVG.Matrix().morph(SVG.Matrix.prototype[this.method].apply(m, params)).at(pos)
+
+ return this.inversed ? m.inverse() : m
+
+ }
+
+ , undo: function(o){
+ for(var i = 0, len = this.arguments.length; i < len; ++i){
+ o[this.arguments[i]] = typeof this[this.arguments[i]] == 'undefined' ? 0 : o[this.arguments[i]]
+ }
+
+ // The method SVG.Matrix.extract which was used before calling this
+ // method to obtain a value for the parameter o doesn't return a cx and
+ // a cy so we use the ones that were provided to this object at its creation
+ o.cx = this.cx
+ o.cy = this.cy
+
+ this._undo = new SVG[capitalize(this.method)](o, true).at(1)
+
+ return this
+ }
+
+ }
+
+})
+
+SVG.Translate = SVG.invent({
+
+ parent: SVG.Matrix
+, inherit: SVG.Transformation
+
+, create: function(source, inversed){
+ this.constructor.apply(this, [].slice.call(arguments))
+ }
+
+, extend: {
+ arguments: ['transformedX', 'transformedY']
+ , method: 'translate'
+ }
+
+})
+
+SVG.Rotate = SVG.invent({
+
+ parent: SVG.Matrix
+, inherit: SVG.Transformation
+
+, create: function(source, inversed){
+ this.constructor.apply(this, [].slice.call(arguments))
+ }
+
+, extend: {
+ arguments: ['rotation', 'cx', 'cy']
+ , method: 'rotate'
+ , at: function(pos){
+ var m = new SVG.Matrix().rotate(new SVG.Number().morph(this.rotation - (this._undo ? this._undo.rotation : 0)).at(pos), this.cx, this.cy)
+ return this.inversed ? m.inverse() : m
+ }
+ , undo: function(o){
+ this._undo = o
+ return this
+ }
+ }
+
+})
+
+SVG.Scale = SVG.invent({
+
+ parent: SVG.Matrix
+, inherit: SVG.Transformation
+
+, create: function(source, inversed){
+ this.constructor.apply(this, [].slice.call(arguments))
+ }
+
+, extend: {
+ arguments: ['scaleX', 'scaleY', 'cx', 'cy']
+ , method: 'scale'
+ }
+
+})
+
+SVG.Skew = SVG.invent({
+
+ parent: SVG.Matrix
+, inherit: SVG.Transformation
+
+, create: function(source, inversed){
+ this.constructor.apply(this, [].slice.call(arguments))
+ }
+
+, extend: {
+ arguments: ['skewX', 'skewY', 'cx', 'cy']
+ , method: 'skew'
+ }
+
+})
diff --git a/node_modules/svg.js/src/umd.js b/node_modules/svg.js/src/umd.js
new file mode 100644
index 0000000..55a9de0
--- /dev/null
+++ b/node_modules/svg.js/src/umd.js
@@ -0,0 +1,18 @@
+(function(root, factory) {
+ /* istanbul ignore next */
+ if (typeof define === 'function' && define.amd) {
+ define(function(){
+ return factory(root, root.document)
+ })
+ } else if (typeof exports === 'object') {
+ module.exports = root.document ? factory(root, root.document) : function(w){ return factory(w, w.document) }
+ } else {
+ root.SVG = factory(root, root.document)
+ }
+}(typeof window !== "undefined" ? window : this, function(window, document) {
+
+<%= contents %>
+
+return SVG
+
+}));
diff --git a/node_modules/svg.js/src/ungroup.js b/node_modules/svg.js/src/ungroup.js
new file mode 100644
index 0000000..a76a5a6
--- /dev/null
+++ b/node_modules/svg.js/src/ungroup.js
@@ -0,0 +1,24 @@
+SVG.extend(SVG.Parent, {
+
+ ungroup: function(parent, depth) {
+ if(depth === 0 || this instanceof SVG.Defs || this.node == SVG.parser.draw) return this
+
+ parent = parent || (this instanceof SVG.Doc ? this : this.parent(SVG.Parent))
+ depth = depth || Infinity
+
+ this.each(function(){
+ if(this instanceof SVG.Defs) return this
+ if(this instanceof SVG.Parent) return this.ungroup(parent, depth-1)
+ return this.toParent(parent)
+ })
+
+ this.node.firstChild || this.remove()
+
+ return this
+ },
+
+ flatten: function(parent, depth) {
+ return this.ungroup(parent, depth)
+ }
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/use.js b/node_modules/svg.js/src/use.js
new file mode 100644
index 0000000..d3150f7
--- /dev/null
+++ b/node_modules/svg.js/src/use.js
@@ -0,0 +1,24 @@
+SVG.Use = SVG.invent({
+ // Initialize node
+ create: 'use'
+
+ // Inherit from
+, inherit: SVG.Shape
+
+ // Add class methods
+, extend: {
+ // Use element as a reference
+ element: function(element, file) {
+ // Set lined element
+ return this.attr('href', (file || '') + '#' + element, SVG.xlink)
+ }
+ }
+
+ // Add parent method
+, construct: {
+ // Create a use element
+ use: function(element, file) {
+ return this.put(new SVG.Use).element(element, file)
+ }
+ }
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/src/utilities.js b/node_modules/svg.js/src/utilities.js
new file mode 100644
index 0000000..c41e8e4
--- /dev/null
+++ b/node_modules/svg.js/src/utilities.js
@@ -0,0 +1,41 @@
+SVG.utils = {
+ // Map function
+ map: function(array, block) {
+ var i
+ , il = array.length
+ , result = []
+
+ for (i = 0; i < il; i++)
+ result.push(block(array[i]))
+
+ return result
+ }
+
+ // Filter function
+, filter: function(array, block) {
+ var i
+ , il = array.length
+ , result = []
+
+ for (i = 0; i < il; i++)
+ if (block(array[i]))
+ result.push(array[i])
+
+ return result
+ }
+
+ // Degrees to radians
+, radians: function(d) {
+ return d % 360 * Math.PI / 180
+ }
+
+ // Radians to degrees
+, degrees: function(r) {
+ return r * 180 / Math.PI % 360
+ }
+
+, filterSVGElements: function(nodes) {
+ return this.filter( nodes, function(el) { return el instanceof window.SVGElement })
+ }
+
+}
\ No newline at end of file
diff --git a/node_modules/svg.js/src/viewbox.js b/node_modules/svg.js/src/viewbox.js
new file mode 100644
index 0000000..8ce7e04
--- /dev/null
+++ b/node_modules/svg.js/src/viewbox.js
@@ -0,0 +1,127 @@
+
+SVG.ViewBox = SVG.invent({
+
+ create: function(source) {
+ var i, base = [0, 0, 0, 0]
+
+ var x, y, width, height, box, view, we, he
+ , wm = 1 // width multiplier
+ , hm = 1 // height multiplier
+ , reg = /[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/gi
+
+ if(source instanceof SVG.Element){
+
+ we = source
+ he = source
+ view = (source.attr('viewBox') || '').match(reg)
+ box = source.bbox
+
+ // get dimensions of current node
+ width = new SVG.Number(source.width())
+ height = new SVG.Number(source.height())
+
+ // find nearest non-percentual dimensions
+ while (width.unit == '%') {
+ wm *= width.value
+ width = new SVG.Number(we instanceof SVG.Doc ? we.parent().offsetWidth : we.parent().width())
+ we = we.parent()
+ }
+ while (height.unit == '%') {
+ hm *= height.value
+ height = new SVG.Number(he instanceof SVG.Doc ? he.parent().offsetHeight : he.parent().height())
+ he = he.parent()
+ }
+
+ // ensure defaults
+ this.x = 0
+ this.y = 0
+ this.width = width * wm
+ this.height = height * hm
+ this.zoom = 1
+
+ if (view) {
+ // get width and height from viewbox
+ x = parseFloat(view[0])
+ y = parseFloat(view[1])
+ width = parseFloat(view[2])
+ height = parseFloat(view[3])
+
+ // calculate zoom accoring to viewbox
+ this.zoom = ((this.width / this.height) > (width / height)) ?
+ this.height / height :
+ this.width / width
+
+ // calculate real pixel dimensions on parent SVG.Doc element
+ this.x = x
+ this.y = y
+ this.width = width
+ this.height = height
+
+ }
+
+ }else{
+
+ // ensure source as object
+ source = typeof source === 'string' ?
+ source.match(reg).map(function(el){ return parseFloat(el) }) :
+ Array.isArray(source) ?
+ source :
+ typeof source == 'object' ?
+ [source.x, source.y, source.width, source.height] :
+ arguments.length == 4 ?
+ [].slice.call(arguments) :
+ base
+
+ this.x = source[0]
+ this.y = source[1]
+ this.width = source[2]
+ this.height = source[3]
+ }
+
+
+ }
+
+, extend: {
+
+ toString: function() {
+ return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height
+ }
+ , morph: function(x, y, width, height){
+ this.destination = new SVG.ViewBox(x, y, width, height)
+ return this
+ }
+
+ , at: function(pos) {
+
+ if(!this.destination) return this
+
+ return new SVG.ViewBox([
+ this.x + (this.destination.x - this.x) * pos
+ , this.y + (this.destination.y - this.y) * pos
+ , this.width + (this.destination.width - this.width) * pos
+ , this.height + (this.destination.height - this.height) * pos
+ ])
+
+ }
+
+ }
+
+ // Define parent
+, parent: SVG.Container
+
+ // Add parent method
+, construct: {
+
+ // get/set viewbox
+ viewbox: function(x, y, width, height) {
+ if (arguments.length == 0)
+ // act as a getter if there are no arguments
+ return new SVG.ViewBox(this)
+
+ // otherwise act as a setter
+ return this.attr('viewBox', new SVG.ViewBox(x, y, width, height))
+ }
+
+ }
+
+})
\ No newline at end of file
diff --git a/node_modules/svg.js/svg.js.d.ts b/node_modules/svg.js/svg.js.d.ts
new file mode 100644
index 0000000..39ae5cf
--- /dev/null
+++ b/node_modules/svg.js/svg.js.d.ts
@@ -0,0 +1,1034 @@
+export = svgjs;
+export as namespace svgjs;
+
+declare var svgjs: svgjs.Library;
+
+// todo add SVG.FX
+declare namespace svgjs {
+ export interface Library {
+ (id: string): Doc;
+ (domElement: HTMLElement): Doc;
+ ns: string;
+ xmlns: string;
+ xlink: string;
+ svgjs: string;
+ supported: boolean;
+
+ did: number;
+ eid(name: string): string;
+
+ create(name: string): any;
+ extend(parent: Object, obj: Object): void;
+ invent(config: Object): any;
+ adopt(node: HTMLElement): Element;
+ prepare(element: HTMLElement): void;
+ }
+ interface LinkedHTMLElement extends HTMLElement {
+ instance: Element;
+ }
+
+ // arrange.js
+ interface Element {
+ front(): this;
+ back(): this;
+ forward(): this;
+ backward(): this;
+
+ siblings(): Element[];
+ position(): number;
+ next(): Element;
+ previous(): Element;
+ before(element: Element): Element;
+ after(element: Element): Element;
+ }
+
+ // array.js
+ type ArrayAlias = _Array | number[] | string;
+
+ interface _Array {
+ new (array?: ArrayAlias, fallback?: number[]): _Array;
+ value: number[];
+ morph(array: number[]): this;
+ settle(): number[];
+ at(pos: NumberAlias): _Array;
+ toString(): string;
+ valueOf(): number[];
+ parse(array: ArrayAlias): number[];
+ split(string: string): number[];
+ reverse(): this;
+ clone(): _Array;
+ }
+ interface Library { Array: _Array }
+
+ // attr.js
+ interface Element {
+ attr(): object;
+ attr(name: string): any;
+ attr(obj: Object): this;
+ attr(name: string, value: any, namespace?: string): this;
+ }
+
+ // bare.js
+ export interface Bare extends Element {
+ new (element: string, inherit?: any): Bare;
+ words(text: string): this;
+ }
+ interface Parent {
+ element(element: string, inherit?: Object): Bare;
+ }
+ interface Library { Bare: Bare; }
+
+ // boxes.js
+ interface Box {
+ height: number;
+ width: number;
+ y: number;
+ x: number;
+ cx: number;
+ cy: number;
+ w: number;
+ h: number;
+ x2: number;
+ y2: number;
+ merge(box: Box): Box;
+ transform(m: Matrix): Box
+ }
+
+ export interface BBox extends Box {
+ new (element?: Element): BBox;
+ }
+ export interface RBox extends Box {
+ new (element?: Element): RBox;
+ }
+ export interface TBox extends Box {
+ new (element?: Element): TBox;
+ }
+ interface Element {
+ bbox(): BBox;
+ rbox(element?: Element): RBox;
+ tbox(): TBox;
+ }
+ interface Library {
+ BBox: BBox;
+ RBox: RBox;
+ TBox: TBox;
+ }
+
+ // clip.js
+ export interface ClipPath extends Container {
+ new (): ClipPath;
+ targets: Element[];
+ remove(): this;
+ }
+ interface Container {
+ clip(): ClipPath;
+ }
+ interface Element {
+ clipWith(element: Element): this;
+ clipper: ClipPath;
+ unclip(): this;
+ }
+ interface Library { ClipPath: ClipPath; }
+
+ // color.js
+ interface ColorLike {
+ r: number;
+ g: number;
+ b: number;
+ }
+
+ type ColorAlias = string | ColorLike;
+
+ export interface Color extends ColorLike{
+ new (): Color;
+ new (color: ColorAlias): Color;
+
+ toString(): string;
+ toHex(): string;
+ toRgb(): string;
+ brightness(): number;
+ morph(color: ColorAlias): Color;
+ at(pos: number): Color;
+ }
+ interface Library { Color: Color; }
+
+ // container.js
+ interface ViewBoxLike {
+ x: number;
+ y: number;
+ width: number;
+ height:number;
+ }
+
+ export interface Container extends Parent {
+ new (): Container;
+ }
+ interface Library { Container: Container }
+
+ // data.js
+ interface Element {
+ data(name: string): any;
+ data(name: string, value: any, sustain?: boolean): this;
+ }
+
+ // default.js
+ interface Library {
+ defaults: {
+ attrs: {
+ 'fill-opacity': number;
+ 'stroke-opacity': number;
+ 'stroke-width': number;
+ 'stroke-linejoin': string;
+ 'stroke-linecap': string;
+ 'fill': string;
+ 'stroke': string;
+ 'opacity': number;
+ 'x': number;
+ 'y': number;
+ 'cx': number;
+ 'cy': number;
+ 'width': number;
+ 'height': number;
+ 'r': number;
+ 'rx': number;
+ 'ry': number;
+ 'offset': number;
+ 'stop-opacity': number;
+ 'stop-color': string;
+ 'font-size': number;
+ 'font-family': string;
+ 'text-anchor': string;
+ }
+ }
+ }
+
+ // defs.js
+ export interface Defs extends Container {
+ new (): Defs;
+ }
+ interface Library { Defs: Defs }
+
+ // doc.js
+ export interface Doc extends Container {
+ new (): Doc;
+ new (id: string): Doc;
+ new (domElement: HTMLElement): Doc;
+ namespace(): this;
+ defs(): Defs;
+ parent(): HTMLElement;
+ spof(): this;
+ remove(): this;
+ }
+ interface Library { Doc: Doc; }
+
+ type ParentTypeAlias = string | Doc | Nested | G;
+ // element.js
+ export interface Element {
+ new (): Element;
+ node: LinkedHTMLElement;
+ type: string;
+
+ x(x: NumberAlias): this;
+ x(): number;
+ y(y: NumberAlias): this;
+ y(): number;
+ //cx(x: number, anchor?: boolean): this;
+ cx(x: number): this;
+ cx(): number;
+ //cy(y: number, anchor?: boolean): this;
+ cy(y: number): this;
+ cy(): number;
+ move(x: NumberAlias, y: NumberAlias): this;
+ center(x: number, y: number): this;
+
+ width(width: NumberAlias): this;
+ width(): number;
+ height(height: NumberAlias): this;
+ height(): number;
+ size(width?: NumberAlias, height?: NumberAlias): this;
+
+ clone(): Element;
+ remove(): this;
+ replace(element: Element): Element;
+
+ addTo(parent: Parent): this;
+ putIn(parent: Parent): Parent;
+
+ id(): string;
+ id(id: string): this;
+
+ inside(x: number, y: number): boolean;
+
+ show(): this;
+ hide(): this;
+ visible(): boolean;
+
+ toString(): string;
+
+ classes(): string[];
+ hasClass(name: string): boolean;
+ addClass(name: string): this;
+ removeClass(name: string): this;
+ toggleClass(name: string): this;
+
+ reference(type: string): Element;
+ // Add HTMLElement for Doc inheritance
+ parent(type?: ParentTypeAlias): Parent | HTMLElement;
+ doc(): Parent;
+ parents(): Parent[];
+
+ matches(selector: string): boolean;
+ native(): LinkedHTMLElement;
+
+ svg(svg: string): this;
+ svg(): string;
+
+ writeDataToDom(): this;
+ setData(data: object): this;
+
+ is(cls: any): boolean;
+ }
+ interface Library { Element: Element; }
+
+ // ellipse.js
+ interface CircleMethods extends Shape {
+ rx(rx: number): this;
+ rx(): this;
+ ry(ry: number): this;
+ ry(): this;
+
+ radius(x: number, y?: number): this;
+ }
+ export interface Circle extends CircleMethods {
+ new (): Circle;
+ }
+ export interface Ellipse extends CircleMethods {
+ new (): Ellipse;
+ }
+ interface Container {
+ circle(size?: number): Circle;
+ ellipse(width?: number, height?: number): Ellipse;
+ }
+ interface Library {
+ Circle: Circle;
+ Ellipse: Ellipse;
+ }
+
+ // event.js
+ interface Element {
+ on(event: string, cb: Function, context?: Object): this;
+ off(event: string, cb?: Function, context?: Object): this;
+ fire(event: string, data?: any): this;
+ fire(event: Event): this;
+ event(): Event | CustomEvent;
+
+ click(cb: Function): this;
+ dblclick(cb: Function): this;
+ mousedown(cb: Function): this;
+ mouseup(cb: Function): this;
+ mouseover(cb: Function): this;
+ mouseout(cb: Function): this;
+ mousemove(cb: Function): this;
+ touchstart(cb: Function): this;
+ touchmove(cb: Function): this;
+ touchleave(cb: Function): this;
+ touchend(cb: Function): this;
+ touchcancel(cb: Function): this;
+ }
+
+ //fx.js
+ interface Library {
+ easing: {
+ '-'(pos: number): number;
+ '<>'(pos: number): number;
+ '>'(pos: number): number;
+ '<'(pos: number): number;
+ }
+ }
+ interface Element {
+ animate(duration?: number, ease?: string, delay?: number): Animation;
+ animate(info: { ease?: string; duration?: number; delay?: number }): Animation;
+ stop(jumpToEnd:boolean,clearQueue:boolean): Animation;
+ }
+ // TODO finishs FX
+ interface StopProperties {
+ color?: ColorAlias;
+ offset?: number;
+ opacity?: number;
+ }
+
+ // gradient.js
+ export interface Stop extends Element {
+ new (): Stop;
+ update(offset?: number, color?: ColorAlias, opacity?: number): this;
+ update(opts: StopProperties): this;
+ }
+ export interface Gradient extends Container {
+ new (type: string): Gradient;
+ at(offset?: number, color?: ColorAlias, opacity?: number): Stop;
+ at(opts: StopProperties): Stop;
+ update(block?: Function): this;
+ fill(): string;
+ fill(...params: any[]): never;
+ toString(): string;
+ from(x: number, y: number): this;
+ to(x: number, y: number): this;
+ radius(x: number, y?: number): this;
+ }
+ interface Container {
+ gradient(type: string, block?: (stop: Gradient) => void): Gradient;
+ }
+ interface Library {
+ Gradient: Gradient;
+ Stop: Stop;
+ }
+
+ // group.js
+ export interface G extends Container {
+ new (): G;
+ gbox(): BBox;
+ }
+ interface Container { group(): G; }
+ interface Library { G: G; }
+
+ // hyperlink.js
+ export interface A extends Container {
+ new (): A;
+ to(url: string): this;
+ to(): string;
+ show(target: string): this;
+ show(): string;
+ show(...params: any[]): never;
+ target(target: string): this;
+ target(): string;
+ }
+ interface Container {
+ link(url: string): A;
+ }
+ interface Element {
+ linkTo(url: string): A;
+ linkTo(url: (link: A) => void): A;
+ }
+ interface Library { A: A; }
+
+ // image.js
+ export interface Image extends Shape {
+ new (): Image;
+ load(url?: string): this;
+ loaded(cb: (info: { width: number, height: number, ratio: number, url: string }) => void): this;
+ error(cb: (event: Event) => void): this;
+ }
+ interface Container {
+ image(): Image;
+ image(href: string, size?: number): Image;
+ image(href: string, width?: number, height?: number): Image;
+ }
+ interface Library { Image: Image; }
+
+ // line.js
+ interface ArrayPoint extends Array { }
+ type PointArrayAlias = ArrayPoint[] | number[] | PointArray | string;
+
+ export interface Line extends Shape {
+ new (): Line;
+ array(): PointArray;
+ plot(points: PointArrayAlias): this;
+ plot(x1: number, y1: number, x2: number, y2: number): this;
+ move(x: number, y: number): this;
+ size(width?: number, height?: number): this;
+ }
+ interface Container {
+ line(points: PointArrayAlias): Line;
+ line(x1: number, y1: number, x2: number, y2: number): Line;
+ }
+ interface Library { Line: Line; }
+
+ // marker.js
+ export interface Marker extends Container {
+ new (): Marker;
+ ref(x: string | number, y: string | number): this;
+ update(block: (marker: Marker) => void): this;
+ toString(): string;
+ }
+ interface Container {
+ marker(width?: number, height?: number, block?: (marker: Marker) => void): Marker
+ }
+ interface Defs {
+ marker(width?: number, height?: number, block?: (marker: Marker) => void): Marker
+ }
+ interface Line {
+ marker(position: string, width?: number, height?: number, block?: (marker: Marker) => void): Marker;
+ marker(position: string, marker: Marker): Marker;
+ }
+ interface Polyline {
+ marker(position: string, width?: number, height?: number, block?: (marker: Marker) => void): Marker;
+ marker(position: string, marker: Marker): Marker;
+ }
+ interface Polygon {
+ marker(position: string, width?: number, height?: number, block?: (marker: Marker) => void): Marker;
+ marker(position: string, marker: Marker): Marker;
+ }
+ interface Path {
+ marker(position: string, width?: number, height?: number, block?: (marker: Marker) => void): Marker;
+ marker(position: string, marker: Marker): Marker;
+ }
+ interface Library {
+ Marker: Marker;
+ }
+
+ // mask.js
+ export interface Mask extends Container {
+ new (): Mask;
+ targets: Element[];
+ }
+ interface Container { mask(): Mask; }
+ interface Element {
+ maskWith(mask: Mask): this;
+ maskWith(element: Element): this;
+ masker: Mask;
+ unmask(): this;
+ }
+ interface Library { Mask: Mask; }
+
+ // matrix.js
+ interface MatrixExtract {
+ x: number;
+ y: number;
+ transformedX: number;
+ transformedY: number;
+ skewX: number;
+ skewY: number;
+ scaleX: number;
+ scaleY: number;
+ rotation: number;
+ a: number;
+ b: number;
+ c: number;
+ d: number;
+ e: number;
+ f: number;
+ matrix: Matrix;
+ }
+
+ interface MatrixLike {
+ a: number;
+ b: number;
+ c: number;
+ d: number;
+ e: number;
+ f: number;
+ }
+
+ type MatrixAlias = MatrixLike | number[] | Element | string;
+
+ export interface Matrix {
+ new (): Matrix;
+ new (source: MatrixAlias): Matrix;
+ new (a: number, b: number, c: number, d: number, e: number, f: number): Matrix;
+ a: number;
+ b: number;
+ c: number;
+ d: number;
+ e: number;
+ f: number;
+ extract(): MatrixExtract;
+ clone(): Matrix;
+ morph(matrix: Matrix): this;
+ at(pos: number): Matrix;
+ multiply(matrix: Matrix): Matrix;
+ inverse(): Matrix;
+ translate(x: number, y: number): Matrix;
+ scale(x: number, y?: number, cx?: number, cy?: number): Matrix;
+ rotate(r: number, cx?: number, cy?: number): Matrix;
+ flip(a: string, offset?: number): Matrix;
+ flip(offset?: number): Matrix;
+ skew(x: number, y?: number, cx?: number, cy?: number): Matrix;
+ skewX(x: number, cx?: number, cy?: number): Matrix;
+ skewY(y: number, cx?: number, cy?: number): Matrix;
+ around(cx: number, cy: number, matrix: Matrix): Matrix;
+ native(): SVGMatrix;
+ toString(): string;
+ }
+ interface Element {
+ ctm(): Matrix;
+ screenCTM(): Matrix;
+ }
+ interface Library { Matrix: Matrix }
+
+ // memory.js
+ interface Element {
+ remember(name: string, value: any): this;
+ remember(name: string): any;
+ remember(obj: Object): this;
+ forget(...keys: string[]): this;
+ forget(): this;
+ memory(): Object;
+ }
+
+ // nested.js
+ export interface Nested extends Container {
+ new (): Nested;
+ }
+ interface Container { nested(): Nested; }
+ interface Library { Nested: Nested; }
+
+ // number.js
+ interface _Number {
+ new (): _Number;
+ new (value: _Number): _Number;
+ new (value: string): _Number;
+ new (value: number, unit?: any): _Number;
+ toString(): string;
+ toJSON(): Object;
+ valueOf(): number;
+ plus(number: number): _Number;
+ minus(number: number): _Number;
+ times(number: number): _Number;
+ divide(number: number): _Number;
+ to(unit: string): _Number;
+ morph(number: any): this;
+ at(pos: number): _Number;
+ }
+ interface Library { Number: _Number; }
+
+ type NumberAlias = _Number | number | string;
+
+ // parent.js
+ export interface Parent extends Element {
+ new (): Parent;
+ children(): Element[];
+ add(element: Element, i?: number): this;
+ put(element: Element, i?: number): Element;
+ has(element: Element): boolean;
+ index(element: Element): number;
+ get(i: number): Element;
+ first(): Element;
+ last(): Element;
+ each(block: (index: number, children: Element[]) => void, deep?: boolean): this;
+ removeElement(element: Element): this;
+ clear(): this;
+ defs(): Defs;
+ }
+ interface Library{ Parent: Parent }
+
+ // path.js
+ interface PathArrayPoint extends Array { }
+ type PathArrayAlias = PathArray | (string | number)[] | PathArrayPoint[] | string;
+
+ export interface Path extends Shape {
+ new (): Path;
+ morphArray: PathArray;
+ array(): PathArray;
+ plot(d: PathArrayAlias): this;
+ }
+ interface Container {
+ path(): Path;
+ path(d: PathArrayAlias): Path;
+ }
+ interface Library{ Path: Path }
+
+ // pathArray.js
+ export interface PathArray extends _Array {
+ new (): PathArray;
+ new (d: PathArrayAlias): PathArray;
+ move(x: number, y: number): this;
+ size(width?: number, height?: number): this;
+ parse(array: PathArrayAlias): PathArrayPoint[];
+ parse(array: ArrayAlias): never;
+ bbox(): BBox;
+ }
+ interface Library { PathArray: PathArray; }
+
+ // pattern.js
+ export interface Pattern extends Container {
+ new (): Pattern;
+ fill(): string;
+ fill(...rest: any[]): never;
+ update(block: (pattern: Pattern) => void): this;
+ toString(): string;
+ }
+ interface Container {
+ pattern(width?: number, height?: number, block?: (pattern: Pattern) => void): Pattern
+ }
+ interface Library { Pattern: Pattern }
+
+ // point.js
+ export interface Point {
+ new (): Point;
+ new (position: ArrayPoint): Point;
+ new (point: Point): Point;
+ new (position: { x: number, y: number }): Point;
+ new (x: number, y: number): Point;
+
+ x: number;
+ y: number;
+
+ clone(): Point;
+ morph(point: Point): this;
+ at(pos: number): Point;
+ native(): SVGPoint;
+ transform(matrix: Matrix): Point;
+ }
+ interface Library { Point: Point; }
+ interface Element {
+ point(): Point;
+ point(position: ArrayPoint): Point;
+ point(position: { x: number, y: number }): Point;
+ point(x: number, y: number): Point;
+ }
+
+ // pointArray.js
+ export interface PointArray extends _Array {
+ new (): PointArray;
+ new (points: PointArrayAlias): PointArray;
+ toString(): string;
+ toLine(): {
+ x1: number;
+ y1: number;
+ x2: number;
+ y2: number;
+ };
+ parse(points: PointArrayAlias): ArrayPoint[];
+ parse(array: ArrayAlias): never;
+ move(x: number, y: number): this;
+ size(width?: number, height?: number): this;
+ bbox(): BBox;
+ }
+ interface Library { PointArray: PointArray }
+
+ // poly.js
+ interface poly extends Shape {
+ array(): PointArray;
+ plot(p: PointArrayAlias): this;
+ move(x: number, y: number): this;
+ size(width: number, height: number): this;
+ }
+ export interface PolyLine extends poly {
+ new (): PolyLine;
+ }
+ interface Library { PolyLine: PolyLine; }
+ interface Container {
+ polyline(points: PointArrayAlias): PolyLine;
+ }
+ export interface Polygon extends poly {
+ new (): Polygon;
+ }
+ interface Library { Polygon: Polygon; }
+ interface Container {
+ polygon(points: PointArrayAlias): Polygon;
+ }
+
+ // rect.js
+ export interface Rect extends Shape {
+ new (): Rect;
+ radius(x: number, y?: number): this;
+ }
+ interface Library { Rect: Rect; }
+ interface Container {
+ rect(width?: number, height?: number): Rect;
+ }
+
+ // regex.js
+ interface Library {
+ regex: {
+ numberAndUnit: RegExp;
+ hex: RegExp;
+ rgb: RegExp;
+ reference: RegExp;
+ transforms: RegExp;
+ whitespace: RegExp;
+ isHex: RegExp;
+ isRgb: RegExp;
+ isCss: RegExp;
+ isBlank: RegExp;
+ isNumber: RegExp;
+ isPercent: RegExp;
+ isImage: RegExp;
+ delimiter: RegExp;
+ hyphen: RegExp;
+ pathLetters: RegExp;
+ isPathLetter: RegExp;
+ dots: RegExp;
+ }
+ }
+
+ // selector.js
+ interface Library {
+ get(id: string): Element;
+ select(query: string, parent?: HTMLElement): Set;
+ }
+ interface Parent {
+ select(query: string): Set;
+ }
+
+ // set.js
+ export interface Set {
+ new (members?: Element[]): Set;
+ add(...elments: Element[]): this;
+ remove(element: Element): this;
+ each(block: (index: number, members: Element[]) => void): this;
+ clear(): this;
+ length(): number;
+ has(element: Element): this;
+ index(element: Element): number;
+ get(i: number): Element;
+ first(): Element;
+ last(): Element;
+ valueOf(): Element[];
+ bbox(): BBox;
+ click(cb: Function): Set;
+ }
+ interface Container { set(members?: Element[]): Set; }
+ interface Library { Set: Set; }
+
+ // shape.js
+ export interface Shape extends Element {
+ new (): Shape;
+ }
+ interface Library { Shape: Shape; }
+
+ // style.js
+ interface Element {
+ style(styles: Object): this;
+ style(style: string): any;
+ style(style: string, value: any): this;
+ }
+
+ // sugar.js
+ interface StrokeData {
+ color?: string;
+ width?: number;
+ opacity?: number;
+ linecap?: string;
+ linejoin?: string;
+ miterlimit?: number;
+ dasharray?: string;
+ dashoffset?: number;
+ }
+ interface Element {
+ fill(fill: { color?: string; opacity?: number, rule?: string }): this;
+ fill(color: string): this;
+ fill(pattern: Element): this;
+ fill(image: Image): this;
+ stroke(stroke: StrokeData): this;
+ stroke(color: string): this;
+ rotate(d: number, cx?: number, cy?: number): this;
+ skew(x: number, y?: number, cx?: number, cy?: number): this;
+ scale(x: number, y?: number, cx?: number, cy?: number): this;
+ translate(x: number, y: number): this;
+ flip(a: string, offset?: number): this;
+ flip(offset?: number): this;
+ matrix(m: MatrixAlias): this;
+ matrix(a: number, b: number, c: number, d: number, e: number, f: number): this;
+ opacity(o: number): this;
+ opacity(): number;
+ dx(x: NumberAlias): this;
+ dy(y: NumberAlias): this;
+ dmove(x: NumberAlias, y: NumberAlias): this;
+ }
+ interface Path {
+ length(): number;
+ pointAt(length: number): { x: number, y: number };
+ }
+ interface FontData {
+ family?: string;
+ size?: NumberAlias;
+ anchor?: string;
+ leading?: NumberAlias;
+ weight?: string;
+ style?: string
+ }
+ interface Parent {
+ font(font: FontData): this;
+ }
+ interface Text {
+ font(font: FontData): this;
+ }
+
+ // text.js
+ export interface Text extends Shape {
+ new (): Text;
+ clone(): Text;
+ text(): string;
+ text(text: string): this;
+ text(block: (text: Text) => void): this;
+ size(fontSize: NumberAlias): this;
+ leading(): number;
+ leading(leading: NumberAlias): this;
+ lines(): Set;
+ rebuild(enabled: boolean): this;
+ build(enabled: boolean): this;
+ plain(text: string): this;
+ tspan(text: string): Tspan;
+ tspan(block: (tspan: Tspan) => void): this;
+ clear(): this;
+ length(): number;
+ }
+ interface Container {
+ text(text: string): Text;
+ text(block: (tspan: Tspan) => void): Text;
+ plain(text: string): Text;
+ }
+ interface Library { Text: Text; }
+ export interface Tspan extends Shape {
+ new (): Tspan;
+ text(): string;
+ text(text: string): Tspan;
+ text(block: (tspan: Tspan) => void): this;
+ dx(x: NumberAlias): this;
+ dy(y: NumberAlias): this;
+ newLine(): this;
+ plain(text: any): this;
+ tspan(text: string): Tspan;
+ tspan(block: (tspan: Tspan) => void): this;
+ clear(): this;
+ length(): number;
+ }
+ interface Library { Tspan: Tspan; }
+
+ // textpath.js
+ export interface TextPath extends Parent {
+ new (): TextPath;
+ }
+ interface Text {
+ path(d: PathArrayAlias): this;
+ track(): Element;
+ textPath(): Element;
+ }
+ interface Library { TextPath: TextPath; }
+
+ // transform.js
+ interface Element {
+ transform(t: Transform, relative?: boolean): Element;
+ transform(): Transform;
+ untransform(): this;
+ matrixify(): Matrix;
+ toParent(parent: Parent): this;
+ toDoc(): this;
+ }
+ interface Transform {
+ x?: number;
+ y?: number;
+ rotation?: number;
+ cx?: number;
+ cy?: number;
+ scaleX?: number;
+ scaleY?: number;
+ skewX?: number;
+ skewY?: number;
+ matrix?: Matrix; // 1,0,0,1,0,0
+ a?: number; // direct digits of matrix
+ b?: number;
+ c?: number;
+ d?: number;
+ e?: number;
+ f?: number;
+ scale?: number;
+ }
+ export interface Transformation {
+ new (...transform: Transform[]): Transformation;
+ new (source: Transform, inversed?: boolean): Transformation;
+ at(pos: number): Matrix;
+ undo(transform: Transform): this
+ }
+ export interface Translate extends Transformation {new (): Translate}
+ export interface Rotate extends Transformation {new (): Rotate}
+ export interface Scale extends Transformation {new (): Scale}
+ export interface Skew extends Transformation {new (): Skew}
+ interface Library {
+ Transformation: Transformation;
+ Translate: Translate;
+ Rotate: Rotate;
+ Scale: Scale;
+ Skew: Skew;
+ }
+
+ // ungroup.js
+ interface Parent {
+ ungroup(parent: Parent, depth?: number): this;
+ flatten(parent: Parent, depth?: number): this;
+ }
+
+ // use.js
+ export interface Use extends Shape {
+ new (): Use;
+ element(element: Element, file?: string): this;
+ }
+ interface Container {
+ use(element: Element | string, file?: string): Use;
+ }
+ interface Library { Use: Use; }
+
+ // utilities.js
+ interface Library {
+ utils: {
+ map(array: any[], block: Function): any;
+ filter(array: any[], block: Function): any;
+ radians(d: number): number;
+ degrees(r: number): number;
+ filterSVGElements: HTMLElement[]
+ }
+ }
+
+ // viewbox.js
+ type ViewBoxAlias = ViewBoxLike | number[] | string | Element;
+
+ interface ViewBox {
+ new (source: ViewBoxAlias): ViewBox;
+ new (x: number, y: number, width: number, height: number): ViewBox;
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+ zoom?: number;
+ toString(): string;
+ morph(source: ViewBoxAlias): ViewBox;
+ morph(x: number, y: number, width: number, height: number): ViewBox;
+ at(pos:number): ViewBox;
+ }
+ interface Container {
+ viewbox(): ViewBox;
+ viewbox(x: number, y: number, width: number, height: number): this;
+ viewbox(viewbox: ViewBoxLike): this;
+ }
+ interface Library { ViewBox: ViewBox; }
+
+ export interface Animation {
+ stop(): Animation;
+ finish(): Animation;
+ pause(): Animation;
+ play(): Animation;
+ reverse(reversed?: boolean): Animation;
+
+ attr(name: string, value: any, namespace?: string): Animation;
+ attr(obj: Object): Animation;
+ attr(name: string): any;
+ attr(): object;
+
+ viewbox(x: number, y: number, w: number, h: number): Animation;
+
+ move(x: number, y: number, anchor?: boolean): Animation;
+ dmove(x: number, y: number): Animation;
+ x(x: number, anchor?: boolean): Animation;
+ y(y: number, anchor?: boolean): Animation;
+
+ center(x: number, y: number, anchor?: boolean): Animation;
+ cx(x: number, anchor?: boolean): Animation;
+ cy(y: number, anchor?: boolean): Animation;
+
+ size(w: number, h: number, anchor?: boolean): Animation;
+ during(cb: (pos: number) => void): Animation;
+ to(value: number): Animation;
+ after(cb: () => void): Animation;
+
+ delay(delayMS: number): Animation;
+
+ rotate(degrees: number, cx?: number, cy?: number): Animation;
+ skew(skewX: number, skewY?: number, cx?: number, cy?: number): Animation;
+ scale(scaleX: number, scaleY?: number, cx?: number, cy?: number): Animation;
+ translate(x: number, y: number): Animation;
+ transform(t: Transform, relative?: boolean): Animation;
+
+ // TODO style, etc, bbox...
+ }
+}
\ No newline at end of file
diff --git a/node_modules/svg.pathmorphing.js/LICENSE b/node_modules/svg.pathmorphing.js/LICENSE
new file mode 100644
index 0000000..ca43f86
--- /dev/null
+++ b/node_modules/svg.pathmorphing.js/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Fuzzy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/node_modules/svg.pathmorphing.js/README.md b/node_modules/svg.pathmorphing.js/README.md
new file mode 100644
index 0000000..439e1ec
--- /dev/null
+++ b/node_modules/svg.pathmorphing.js/README.md
@@ -0,0 +1,22 @@
+# svg.pathmorphing.js
+
+A plugin for the [svgjs](https://github.com/svgdotjs/svg.js) library to enable path morphing / animation
+
+The code of this plugin will move to the core when it's out of experimental status and shortened (to much space for one feature).
+
+The use is similar to all other animation explained in the svg.js docs:
+
+```javascript
+
+// create path
+var path = draw.path('M150 0 L75 200 L225 200 Z')
+
+// animate path
+path.animate().plot('M100 0 H190 V90 H100 Z')
+
+```
+
+Pretty straight forward, isn't it?
+
+## Dependencies
+This module requires svg.js >= v2.1.1
\ No newline at end of file
diff --git a/node_modules/svg.pathmorphing.js/dist/svg.pathmorphing.js b/node_modules/svg.pathmorphing.js/dist/svg.pathmorphing.js
new file mode 100644
index 0000000..9366850
--- /dev/null
+++ b/node_modules/svg.pathmorphing.js/dist/svg.pathmorphing.js
@@ -0,0 +1,415 @@
+/*!
+* svg.pathmorphing.js - Enables pathmorphing / path animation in svg.js
+* @version 0.1.3
+*
+*
+* @copyright (c) 2018 Ulrich-Matthias Schäfer
+* @license MIT
+*/;
+;(function() {
+"use strict";
+
+SVG.extend(SVG.PathArray, {
+ morph: function(array) {
+
+ var startArr = this.value
+ , destArr = this.parse(array)
+
+ var startOffsetM = 0
+ , destOffsetM = 0
+
+ var startOffsetNextM = false
+ , destOffsetNextM = false
+
+ while(true){
+ // stop if there is no M anymore
+ if(startOffsetM === false && destOffsetM === false) break
+
+ // find the next M in path array
+ startOffsetNextM = findNextM(startArr, startOffsetM === false ? false : startOffsetM+1)
+ destOffsetNextM = findNextM( destArr, destOffsetM === false ? false : destOffsetM+1)
+
+ // We have to add one M to the startArray
+ if(startOffsetM === false){
+ var bbox = new SVG.PathArray(result.start).bbox()
+
+ // when the last block had no bounding box we simply take the first M we got
+ if(bbox.height == 0 || bbox.width == 0){
+ startOffsetM = startArr.push(startArr[0]) - 1
+ }else{
+ // we take the middle of the bbox instead when we got one
+ startOffsetM = startArr.push( ['M', bbox.x + bbox.width/2, bbox.y + bbox.height/2 ] ) - 1
+ }
+ }
+
+ // We have to add one M to the destArray
+ if( destOffsetM === false){
+ var bbox = new SVG.PathArray(result.dest).bbox()
+
+ if(bbox.height == 0 || bbox.width == 0){
+ destOffsetM = destArr.push(destArr[0]) - 1
+ }else{
+ destOffsetM = destArr.push( ['M', bbox.x + bbox.width/2, bbox.y + bbox.height/2 ] ) - 1
+ }
+ }
+
+ // handle block from M to next M
+ var result = handleBlock(startArr, startOffsetM, startOffsetNextM, destArr, destOffsetM, destOffsetNextM)
+
+ // update the arrays to their new values
+ startArr = startArr.slice(0, startOffsetM).concat(result.start, startOffsetNextM === false ? [] : startArr.slice(startOffsetNextM))
+ destArr = destArr.slice(0, destOffsetM).concat(result.dest , destOffsetNextM === false ? [] : destArr.slice( destOffsetNextM))
+
+ // update offsets
+ startOffsetM = startOffsetNextM === false ? false : startOffsetM + result.start.length
+ destOffsetM = destOffsetNextM === false ? false : destOffsetM + result.dest.length
+
+ }
+
+ // copy back arrays
+ this.value = startArr
+ this.destination = new SVG.PathArray()
+ this.destination.value = destArr
+
+ return this
+ }
+})
+
+
+
+// sorry for the long declaration
+// slices out one block (from M to M) and syncronize it so the types and length match
+function handleBlock(startArr, startOffsetM, startOffsetNextM, destArr, destOffsetM, destOffsetNextM, undefined){
+
+ // slice out the block we need
+ var startArrTemp = startArr.slice(startOffsetM, startOffsetNextM || undefined)
+ , destArrTemp = destArr.slice( destOffsetM, destOffsetNextM || undefined)
+
+ var i = 0
+ , posStart = {pos:[0,0], start:[0,0]}
+ , posDest = {pos:[0,0], start:[0,0]}
+
+ do{
+
+ // convert shorthand types to long form
+ startArrTemp[i] = simplyfy.call(posStart, startArrTemp[i])
+ destArrTemp[i] = simplyfy.call(posDest , destArrTemp[i])
+
+ // check if both shape types match
+ // 2 elliptical arc curve commands ('A'), are considered different if the
+ // flags (large-arc-flag, sweep-flag) don't match
+ if(startArrTemp[i][0] != destArrTemp[i][0] || startArrTemp[i][0] == 'M' ||
+ (startArrTemp[i][0] == 'A' &&
+ (startArrTemp[i][4] != destArrTemp[i][4] || startArrTemp[i][5] != destArrTemp[i][5])
+ )
+ ) {
+
+ // if not, convert shapes to beziere
+ Array.prototype.splice.apply(startArrTemp, [i, 1].concat(toBeziere.call(posStart, startArrTemp[i])))
+ Array.prototype.splice.apply(destArrTemp, [i, 1].concat(toBeziere.call(posDest, destArrTemp[i])))
+
+ } else {
+
+ // only update positions otherwise
+ startArrTemp[i] = setPosAndReflection.call(posStart, startArrTemp[i])
+ destArrTemp[i] = setPosAndReflection.call(posDest , destArrTemp[i])
+
+ }
+
+ // we are at the end at both arrays. stop here
+ if(++i == startArrTemp.length && i == destArrTemp.length) break
+
+ // destArray is longer. Add one element
+ if(i == startArrTemp.length){
+ startArrTemp.push([
+ 'C',
+ posStart.pos[0],
+ posStart.pos[1],
+ posStart.pos[0],
+ posStart.pos[1],
+ posStart.pos[0],
+ posStart.pos[1],
+ ])
+ }
+
+ // startArr is longer. Add one element
+ if(i == destArrTemp.length){
+ destArrTemp.push([
+ 'C',
+ posDest.pos[0],
+ posDest.pos[1],
+ posDest.pos[0],
+ posDest.pos[1],
+ posDest.pos[0],
+ posDest.pos[1]
+ ])
+ }
+
+
+ }while(true)
+
+ // return the updated block
+ return {start:startArrTemp, dest:destArrTemp}
+}
+
+// converts shorthand types to long form
+function simplyfy(val){
+
+ switch(val[0]){
+ case 'z': // shorthand line to start
+ case 'Z':
+ val[0] = 'L'
+ val[1] = this.start[0]
+ val[2] = this.start[1]
+ break
+ case 'H': // shorthand horizontal line
+ val[0] = 'L'
+ val[2] = this.pos[1]
+ break
+ case 'V': // shorthand vertical line
+ val[0] = 'L'
+ val[2] = val[1]
+ val[1] = this.pos[0]
+ break
+ case 'T': // shorthand quadratic beziere
+ val[0] = 'Q'
+ val[3] = val[1]
+ val[4] = val[2]
+ val[1] = this.reflection[1]
+ val[2] = this.reflection[0]
+ break
+ case 'S': // shorthand cubic beziere
+ val[0] = 'C'
+ val[6] = val[4]
+ val[5] = val[3]
+ val[4] = val[2]
+ val[3] = val[1]
+ val[2] = this.reflection[1]
+ val[1] = this.reflection[0]
+ break
+ }
+
+ return val
+
+}
+
+// updates reflection point and current position
+function setPosAndReflection(val){
+
+ var len = val.length
+
+ this.pos = [ val[len-2], val[len-1] ]
+
+ if('SCQT'.indexOf(val[0]) != -1)
+ this.reflection = [ 2 * this.pos[0] - val[len-4], 2 * this.pos[1] - val[len-3] ]
+
+ return val
+}
+
+// converts all types to cubic beziere
+function toBeziere(val){
+ var retVal = [val]
+
+ switch(val[0]){
+ case 'M': // special handling for M
+ this.pos = this.start = [val[1], val[2]]
+ return retVal
+ case 'L':
+ val[5] = val[3] = val[1]
+ val[6] = val[4] = val[2]
+ val[1] = this.pos[0]
+ val[2] = this.pos[1]
+ break
+ case 'Q':
+ val[6] = val[4]
+ val[5] = val[3]
+ val[4] = val[4] * 1/3 + val[2] * 2/3
+ val[3] = val[3] * 1/3 + val[1] * 2/3
+ val[2] = this.pos[1] * 1/3 + val[2] * 2/3
+ val[1] = this.pos[0] * 1/3 + val[1] * 2/3
+ break
+ case 'A':
+ retVal = arcToBeziere(this.pos, val)
+ val = retVal[0]
+ break
+ }
+
+ val[0] = 'C'
+ this.pos = [val[5], val[6]]
+ this.reflection = [2 * val[5] - val[3], 2 * val[6] - val[4]]
+
+ return retVal
+
+}
+
+// finds the next position of type M
+function findNextM(arr, offset){
+
+ if(offset === false) return false
+
+ for(var i = offset, len = arr.length;i < len;++i){
+
+ if(arr[i][0] == 'M') return i
+
+ }
+
+ return false
+}
+
+
+
+// Convert an arc segment into equivalent cubic Bezier curves
+// Depending on the arc, up to 4 curves might be used to represent it since a
+// curve gives a good approximation for only a quarter of an ellipse
+// The curves are returned as an array of SVG curve commands:
+// [ ['C', x1, y1, x2, y2, x, y] ... ]
+function arcToBeziere(pos, val) {
+ // Parameters extraction, handle out-of-range parameters as specified in the SVG spec
+ // See: https://www.w3.org/TR/SVG11/implnote.html#ArcOutOfRangeParameters
+ var rx = Math.abs(val[1]), ry = Math.abs(val[2]), xAxisRotation = val[3] % 360
+ , largeArcFlag = val[4], sweepFlag = val[5], x = val[6], y = val[7]
+ , A = new SVG.Point(pos), B = new SVG.Point(x, y)
+ , primedCoord, lambda, mat, k, c, cSquare, t, O, OA, OB, tetaStart, tetaEnd
+ , deltaTeta, nbSectors, f, arcSegPoints, angle, sinAngle, cosAngle, pt, i, il
+ , retVal = [], x1, y1, x2, y2
+
+ // Ensure radii are non-zero
+ if(rx === 0 || ry === 0 || (A.x === B.x && A.y === B.y)) {
+ // treat this arc as a straight line segment
+ return [['C', A.x, A.y, B.x, B.y, B.x, B.y]]
+ }
+
+ // Ensure radii are large enough using the algorithm provided in the SVG spec
+ // See: https://www.w3.org/TR/SVG11/implnote.html#ArcCorrectionOutOfRangeRadii
+ primedCoord = new SVG.Point((A.x-B.x)/2, (A.y-B.y)/2).transform(new SVG.Matrix().rotate(xAxisRotation))
+ lambda = (primedCoord.x * primedCoord.x) / (rx * rx) + (primedCoord.y * primedCoord.y) / (ry * ry)
+ if(lambda > 1) {
+ lambda = Math.sqrt(lambda)
+ rx = lambda*rx
+ ry = lambda*ry
+ }
+
+ // To simplify calculations, we make the arc part of a unit circle (rayon is 1) instead of an ellipse
+ mat = new SVG.Matrix().rotate(xAxisRotation).scale(1/rx, 1/ry).rotate(-xAxisRotation)
+ A = A.transform(mat)
+ B = B.transform(mat)
+
+ // Calculate the horizontal and vertical distance between the initial and final point of the arc
+ k = [B.x-A.x, B.y-A.y]
+
+ // Find the length of the chord formed by A and B
+ cSquare = k[0]*k[0] + k[1]*k[1]
+ c = Math.sqrt(cSquare)
+
+ // Calculate the ratios of the horizontal and vertical distance on the length of the chord
+ k[0] /= c
+ k[1] /= c
+
+ // Calculate the distance between the circle center and the chord midpoint
+ // using this formula: t = sqrt(r^2 - c^2 / 4)
+ // where t is the distance between the cirle center and the chord midpoint,
+ // r is the rayon of the circle and c is the chord length
+ // From: http://www.ajdesigner.com/phpcircle/circle_segment_chord_t.php
+ // Because of the imprecision of floating point numbers, cSquare might end
+ // up being slightly above 4 which would result in a negative radicand
+ // To prevent that, a test is made before computing the square root
+ t = (cSquare < 4) ? Math.sqrt(1 - cSquare/4) : 0
+
+ // For most situations, there are actually two different ellipses that
+ // satisfy the constraints imposed by the points A and B, the radii rx and ry,
+ // and the xAxisRotation
+ // When the flags largeArcFlag and sweepFlag are equal, it means that the
+ // second ellipse is used as a solution
+ // See: https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
+ if(largeArcFlag === sweepFlag) {
+ t *= -1
+ }
+
+ // Calculate the coordinates of the center of the circle from the midpoint of the chord
+ // This is done by multiplying the ratios calculated previously by the distance between
+ // the circle center and the chord midpoint and using these values to go from the midpoint
+ // to the center of the circle
+ // The negative of the vertical distance ratio is used to modify the x coordinate while
+ // the horizontal distance ratio is used to modify the y coordinate
+ // That is because the center of the circle is perpendicular to the chord and perpendicular
+ // lines are negative reciprocals
+ O = new SVG.Point((B.x+A.x)/2 + t*-k[1], (B.y+A.y)/2 + t*k[0])
+ // Move the center of the circle at the origin
+ OA = new SVG.Point(A.x-O.x, A.y-O.y)
+ OB = new SVG.Point(B.x-O.x, B.y-O.y)
+
+ // Calculate the start and end angle
+ tetaStart = Math.acos(OA.x/Math.sqrt(OA.x*OA.x + OA.y*OA.y))
+ if (OA.y < 0) {
+ tetaStart *= -1
+ }
+ tetaEnd = Math.acos(OB.x/Math.sqrt(OB.x*OB.x + OB.y*OB.y))
+ if (OB.y < 0) {
+ tetaEnd *= -1
+ }
+
+ // If sweep-flag is '1', then the arc will be drawn in a "positive-angle" direction,
+ // make sure that the end angle is above the start angle
+ if (sweepFlag && tetaStart > tetaEnd) {
+ tetaEnd += 2*Math.PI
+ }
+ // If sweep-flag is '0', then the arc will be drawn in a "negative-angle" direction,
+ // make sure that the end angle is below the start angle
+ if (!sweepFlag && tetaStart < tetaEnd) {
+ tetaEnd -= 2*Math.PI
+ }
+
+ // Find the number of Bezier curves that are required to represent the arc
+ // A cubic Bezier curve gives a good enough approximation when representing at most a quarter of a circle
+ nbSectors = Math.ceil(Math.abs(tetaStart-tetaEnd) * 2/Math.PI)
+
+ // Calculate the coordinates of the points of all the Bezier curves required to represent the arc
+ // For an in-depth explanation of this part see: http://pomax.github.io/bezierinfo/#circles_cubic
+ arcSegPoints = []
+ angle = tetaStart
+ deltaTeta = (tetaEnd-tetaStart)/nbSectors
+ f = 4*Math.tan(deltaTeta/4)/3
+ for (i = 0; i <= nbSectors; i++) { // The <= is because a Bezier curve have a start and a endpoint
+ cosAngle = Math.cos(angle)
+ sinAngle = Math.sin(angle)
+
+ pt = new SVG.Point(O.x+cosAngle, O.y+sinAngle)
+ arcSegPoints[i] = [new SVG.Point(pt.x+f*sinAngle, pt.y-f*cosAngle), pt, new SVG.Point(pt.x-f*sinAngle, pt.y+f*cosAngle)]
+
+ angle += deltaTeta
+ }
+
+ // Remove the first control point of the first segment point and remove the second control point of the last segment point
+ // These two control points are not used in the approximation of the arc, that is why they are removed
+ arcSegPoints[0][0] = arcSegPoints[0][1].clone()
+ arcSegPoints[arcSegPoints.length-1][2] = arcSegPoints[arcSegPoints.length-1][1].clone()
+
+ // Revert the transformation that was applied to make the arc part of a unit circle instead of an ellipse
+ mat = new SVG.Matrix().rotate(xAxisRotation).scale(rx, ry).rotate(-xAxisRotation)
+ for (i = 0, il = arcSegPoints.length; i < il; i++) {
+ arcSegPoints[i][0] = arcSegPoints[i][0].transform(mat)
+ arcSegPoints[i][1] = arcSegPoints[i][1].transform(mat)
+ arcSegPoints[i][2] = arcSegPoints[i][2].transform(mat)
+ }
+
+
+ // Convert the segments points to SVG curve commands
+ for (i = 1, il = arcSegPoints.length; i < il; i++) {
+ pt = arcSegPoints[i-1][2]
+ x1 = pt.x
+ y1 = pt.y
+
+ pt = arcSegPoints[i][0]
+ x2 = pt.x
+ y2 = pt.y
+
+ pt = arcSegPoints[i][1]
+ x = pt.x
+ y = pt.y
+
+ retVal.push(['C', x1, y1, x2, y2, x, y])
+ }
+
+ return retVal
+}
+}());
diff --git a/node_modules/svg.pathmorphing.js/dist/svg.pathmorphing.min.js b/node_modules/svg.pathmorphing.js/dist/svg.pathmorphing.min.js
new file mode 100644
index 0000000..114c2ee
--- /dev/null
+++ b/node_modules/svg.pathmorphing.js/dist/svg.pathmorphing.min.js
@@ -0,0 +1 @@
+/*! svg.pathmorphing.js v0.1.3 MIT*/;!function(){"use strict";function t(t,r,n,i,o,h,c){for(var l=t.slice(r,n||c),p=i.slice(o,h||c),y=0,x={pos:[0,0],start:[0,0]},f={pos:[0,0],start:[0,0]};;){if(l[y]=s.call(x,l[y]),p[y]=s.call(f,p[y]),l[y][0]!=p[y][0]||"M"==l[y][0]||"A"==l[y][0]&&(l[y][4]!=p[y][4]||l[y][5]!=p[y][5])?(Array.prototype.splice.apply(l,[y,1].concat(a.call(x,l[y]))),Array.prototype.splice.apply(p,[y,1].concat(a.call(f,p[y])))):(l[y]=e.call(x,l[y]),p[y]=e.call(f,p[y])),++y==l.length&&y==p.length)break;y==l.length&&l.push(["C",x.pos[0],x.pos[1],x.pos[0],x.pos[1],x.pos[0],x.pos[1]]),y==p.length&&p.push(["C",f.pos[0],f.pos[1],f.pos[0],f.pos[1],f.pos[0],f.pos[1]])}return{start:l,dest:p}}function s(t){switch(t[0]){case"z":case"Z":t[0]="L",t[1]=this.start[0],t[2]=this.start[1];break;case"H":t[0]="L",t[2]=this.pos[1];break;case"V":t[0]="L",t[2]=t[1],t[1]=this.pos[0];break;case"T":t[0]="Q",t[3]=t[1],t[4]=t[2],t[1]=this.reflection[1],t[2]=this.reflection[0];break;case"S":t[0]="C",t[6]=t[4],t[5]=t[3],t[4]=t[2],t[3]=t[1],t[2]=this.reflection[1],t[1]=this.reflection[0]}return t}function e(t){var s=t.length;return this.pos=[t[s-2],t[s-1]],-1!="SCQT".indexOf(t[0])&&(this.reflection=[2*this.pos[0]-t[s-4],2*this.pos[1]-t[s-3]]),t}function a(t){var s=[t];switch(t[0]){case"M":return this.pos=this.start=[t[1],t[2]],s;case"L":t[5]=t[3]=t[1],t[6]=t[4]=t[2],t[1]=this.pos[0],t[2]=this.pos[1];break;case"Q":t[6]=t[4],t[5]=t[3],t[4]=1*t[4]/3+2*t[2]/3,t[3]=1*t[3]/3+2*t[1]/3,t[2]=1*this.pos[1]/3+2*t[2]/3,t[1]=1*this.pos[0]/3+2*t[1]/3;break;case"A":s=n(this.pos,t),t=s[0]}return t[0]="C",this.pos=[t[5],t[6]],this.reflection=[2*t[5]-t[3],2*t[6]-t[4]],s}function r(t,s){if(!1===s)return!1;for(var e=s,a=t.length;e1&&(a=Math.sqrt(a),m*=a,C*=a),r=(new SVG.Matrix).rotate(q).scale(1/m,1/C).rotate(-q),z=z.transform(r),H=H.transform(r),n=[H.x-z.x,H.y-z.y],o=n[0]*n[0]+n[1]*n[1],i=Math.sqrt(o),n[0]/=i,n[1]/=i,h=o<4?Math.sqrt(1-o/4):0,L===I&&(h*=-1),c=new SVG.Point((H.x+z.x)/2+h*-n[1],(H.y+z.y)/2+h*n[0]),l=new SVG.Point(z.x-c.x,z.y-c.y),p=new SVG.Point(H.x-c.x,H.y-c.y),y=Math.acos(l.x/Math.sqrt(l.x*l.x+l.y*l.y)),l.y<0&&(y*=-1),x=Math.acos(p.x/Math.sqrt(p.x*p.x+p.y*p.y)),p.y<0&&(x*=-1),I&&y>x&&(x+=2*Math.PI),!I&&y= 0.8.0"
+ },
+ "devDependencies": {
+ "del": "^2.2.2",
+ "gulp": "^3.9.1",
+ "gulp-header": "^1.8.8",
+ "gulp-iife": "^0.3.0",
+ "gulp-rename": "^1.2.2",
+ "gulp-standard": "^10.0.0",
+ "gulp-trimlines": "^1.0.1",
+ "gulp-uglify": "^2.1.2",
+ "gulp-wrap-iife": "0.0.1"
+ },
+ "dependencies": {
+ "svg.js": "^2.4.0"
+ }
+}
diff --git a/node_modules/svg.resize.js/LICENSE b/node_modules/svg.resize.js/LICENSE
new file mode 100644
index 0000000..0bf6940
--- /dev/null
+++ b/node_modules/svg.resize.js/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 Ulrich-Matthias Schäfer
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/node_modules/svg.resize.js/README.md b/node_modules/svg.resize.js/README.md
new file mode 100644
index 0000000..c8d5ea6
--- /dev/null
+++ b/node_modules/svg.resize.js/README.md
@@ -0,0 +1,71 @@
+svg.resize.js
+=============
+
+An extension of [svg.js](https://github.com/svgdotjs/svg.js) which allows to resize elements which are selected with [svg.select.js](https://github.com/svgdotjs/svg.select.js)
+
+# Demo
+
+For a demo see http://svgdotjs.github.io/svg.resize.js/
+
+# Get Started
+
+- Install `svg.resize.js` using bower:
+
+ bower install svg.resize.js
+
+- Include the script after svg.js and svg.select.js into your page
+
+
+
+
+
+- Select a rectangle and make it resizeable:
+
+
+
+ var drawing = new SVG('myDrawing').size(500, 500);
+ drawing.rect(50,50).selectize().resize()
+
+# Usage
+
+Activate resizing
+
+ var draw = SVG('drawing');
+ var rect = draw.rect(100,100);
+ rect.selectize().resize();
+
+Deactivate resizing
+
+ rect.resize('stop');
+
+Keep element within constrained box
+
+ var draw = SVG('drawing');
+ var rect = draw.rect(100, 100);
+ var opt = {
+ constraint: {
+ minX: 0,
+ minY: 0,
+ maxX: 200,
+ maxY: 300
+ }
+ };
+ rect.selectize().resize(opt)
+
+
+# Options
+
+- `snapToGrid`: Snaps the shape to a virtual grid while resizing (default `1`)
+- `snapToAngle`: Snaps to an angle when rotating (default `0.1`)
+- `constraint`: Keep element within constrained box (see usage above); The box snaps to the grid defined by `snapToGrid`.
+- `saveAspectRatio`: Save aspect ratio of the element while resizing with left-top, left-bottom, right-top, right-bottom points.
+
+
+# Events
+
+- `resizing`: Fired when changes occur
+- `resizedone`: Fired when resizing is done
+
+# Known Issues
+
+- resize nested svgs does not work
diff --git a/node_modules/svg.resize.js/dist/svg.resize.js b/node_modules/svg.resize.js/dist/svg.resize.js
new file mode 100644
index 0000000..4e00523
--- /dev/null
+++ b/node_modules/svg.resize.js/dist/svg.resize.js
@@ -0,0 +1,493 @@
+/*!
+* svg.resize.js - An extension for svg.js which allows to resize elements which are selected
+* @version 1.4.3
+* https://github.com/svgdotjs/svg.resize.js
+*
+* @copyright [object Object]
+* @license MIT
+*/;
+;(function() {
+"use strict";
+
+;(function () {
+
+ function ResizeHandler(el) {
+
+ el.remember('_resizeHandler', this);
+
+ this.el = el;
+ this.parameters = {};
+ this.lastUpdateCall = null;
+ this.p = el.doc().node.createSVGPoint();
+ }
+
+ ResizeHandler.prototype.transformPoint = function(x, y, m){
+
+ this.p.x = x - (this.offset.x - window.pageXOffset);
+ this.p.y = y - (this.offset.y - window.pageYOffset);
+
+ return this.p.matrixTransform(m || this.m);
+
+ };
+
+ ResizeHandler.prototype._extractPosition = function(event) {
+ // Extract a position from a mouse/touch event.
+ // Returns { x: .., y: .. }
+ return {
+ x: event.clientX != null ? event.clientX : event.touches[0].clientX,
+ y: event.clientY != null ? event.clientY : event.touches[0].clientY
+ }
+ };
+
+ ResizeHandler.prototype.init = function (options) {
+
+ var _this = this;
+
+ this.stop();
+
+ if (options === 'stop') {
+ return;
+ }
+
+ this.options = {};
+
+ // Merge options and defaults
+ for (var i in this.el.resize.defaults) {
+ this.options[i] = this.el.resize.defaults[i];
+ if (typeof options[i] !== 'undefined') {
+ this.options[i] = options[i];
+ }
+ }
+
+ // We listen to all these events which are specifying different edges
+ this.el.on('lt.resize', function(e){ _this.resize(e || window.event); }); // Left-Top
+ this.el.on('rt.resize', function(e){ _this.resize(e || window.event); }); // Right-Top
+ this.el.on('rb.resize', function(e){ _this.resize(e || window.event); }); // Right-Bottom
+ this.el.on('lb.resize', function(e){ _this.resize(e || window.event); }); // Left-Bottom
+
+ this.el.on('t.resize', function(e){ _this.resize(e || window.event); }); // Top
+ this.el.on('r.resize', function(e){ _this.resize(e || window.event); }); // Right
+ this.el.on('b.resize', function(e){ _this.resize(e || window.event); }); // Bottom
+ this.el.on('l.resize', function(e){ _this.resize(e || window.event); }); // Left
+
+ this.el.on('rot.resize', function(e){ _this.resize(e || window.event); }); // Rotation
+
+ this.el.on('point.resize', function(e){ _this.resize(e || window.event); }); // Point-Moving
+
+ // This call ensures, that the plugin reacts to a change of snapToGrid immediately
+ this.update();
+
+ };
+
+ ResizeHandler.prototype.stop = function(){
+ this.el.off('lt.resize');
+ this.el.off('rt.resize');
+ this.el.off('rb.resize');
+ this.el.off('lb.resize');
+
+ this.el.off('t.resize');
+ this.el.off('r.resize');
+ this.el.off('b.resize');
+ this.el.off('l.resize');
+
+ this.el.off('rot.resize');
+
+ this.el.off('point.resize');
+
+ return this;
+ };
+
+ ResizeHandler.prototype.resize = function (event) {
+
+ var _this = this;
+
+ this.m = this.el.node.getScreenCTM().inverse();
+ this.offset = { x: window.pageXOffset, y: window.pageYOffset };
+
+ var txPt = this._extractPosition(event.detail.event);
+ this.parameters = {
+ type: this.el.type, // the type of element
+ p: this.transformPoint(txPt.x, txPt.y),
+ x: event.detail.x, // x-position of the mouse when resizing started
+ y: event.detail.y, // y-position of the mouse when resizing started
+ box: this.el.bbox(), // The bounding-box of the element
+ rotation: this.el.transform().rotation // The current rotation of the element
+ };
+
+ // Add font-size parameter if the element type is text
+ if (this.el.type === "text") {
+ this.parameters.fontSize = this.el.attr()["font-size"];
+ }
+
+ // the i-param in the event holds the index of the point which is moved, when using `deepSelect`
+ if (event.detail.i !== undefined) {
+
+ // get the point array
+ var array = this.el.array().valueOf();
+
+ // Save the index and the point which is moved
+ this.parameters.i = event.detail.i;
+ this.parameters.pointCoords = [array[event.detail.i][0], array[event.detail.i][1]];
+ }
+
+ // Lets check which edge of the bounding-box was clicked and resize the this.el according to this
+ switch (event.type) {
+
+ // Left-Top-Edge
+ case 'lt':
+ // We build a calculating function for every case which gives us the new position of the this.el
+ this.calc = function (diffX, diffY) {
+ // The procedure is always the same
+ // First we snap the edge to the given grid (snapping to 1px grid is normal resizing)
+ var snap = this.snapToGrid(diffX, diffY);
+
+ // Now we check if the new height and width still valid (> 0)
+ if (this.parameters.box.width - snap[0] > 0 && this.parameters.box.height - snap[1] > 0) {
+ // ...if valid, we resize the this.el (which can include moving because the coord-system starts at the left-top and this edge is moving sometimes when resized)
+
+ /*
+ * but first check if the element is text box, so we can change the font size instead of
+ * the width and height
+ */
+
+ if (this.parameters.type === "text") {
+ this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y);
+ this.el.attr("font-size", this.parameters.fontSize - snap[0]);
+ return;
+ }
+
+ snap = this.checkAspectRatio(snap);
+
+ this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y + snap[1]).size(this.parameters.box.width - snap[0], this.parameters.box.height - snap[1]);
+ }
+ };
+ break;
+
+ // Right-Top
+ case 'rt':
+ // s.a.
+ this.calc = function (diffX, diffY) {
+ var snap = this.snapToGrid(diffX, diffY, 1 << 1);
+ if (this.parameters.box.width + snap[0] > 0 && this.parameters.box.height - snap[1] > 0) {
+ if (this.parameters.type === "text") {
+ this.el.move(this.parameters.box.x - snap[0], this.parameters.box.y);
+ this.el.attr("font-size", this.parameters.fontSize + snap[0]);
+ return;
+ }
+
+ snap = this.checkAspectRatio(snap, true);
+
+ this.el.move(this.parameters.box.x, this.parameters.box.y + snap[1]).size(this.parameters.box.width + snap[0], this.parameters.box.height - snap[1]);
+ }
+ };
+ break;
+
+ // Right-Bottom
+ case 'rb':
+ // s.a.
+ this.calc = function (diffX, diffY) {
+ var snap = this.snapToGrid(diffX, diffY, 0);
+ if (this.parameters.box.width + snap[0] > 0 && this.parameters.box.height + snap[1] > 0) {
+ if (this.parameters.type === "text") {
+ this.el.move(this.parameters.box.x - snap[0], this.parameters.box.y);
+ this.el.attr("font-size", this.parameters.fontSize + snap[0]);
+ return;
+ }
+
+ snap = this.checkAspectRatio(snap);
+
+ this.el.move(this.parameters.box.x, this.parameters.box.y).size(this.parameters.box.width + snap[0], this.parameters.box.height + snap[1]);
+ }
+ };
+ break;
+
+ // Left-Bottom
+ case 'lb':
+ // s.a.
+ this.calc = function (diffX, diffY) {
+ var snap = this.snapToGrid(diffX, diffY, 1);
+ if (this.parameters.box.width - snap[0] > 0 && this.parameters.box.height + snap[1] > 0) {
+ if (this.parameters.type === "text") {
+ this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y);
+ this.el.attr("font-size", this.parameters.fontSize - snap[0]);
+ return;
+ }
+
+ snap = this.checkAspectRatio(snap, true);
+
+ this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y).size(this.parameters.box.width - snap[0], this.parameters.box.height + snap[1]);
+ }
+ };
+ break;
+
+ // Top
+ case 't':
+ // s.a.
+ this.calc = function (diffX, diffY) {
+ var snap = this.snapToGrid(diffX, diffY, 1 << 1);
+ if (this.parameters.box.height - snap[1] > 0) {
+ // Disable the font-resizing if it is not from the corner of bounding-box
+ if (this.parameters.type === "text") {
+ return;
+ }
+
+ this.el.move(this.parameters.box.x, this.parameters.box.y + snap[1]).height(this.parameters.box.height - snap[1]);
+ }
+ };
+ break;
+
+ // Right
+ case 'r':
+ // s.a.
+ this.calc = function (diffX, diffY) {
+ var snap = this.snapToGrid(diffX, diffY, 0);
+ if (this.parameters.box.width + snap[0] > 0) {
+ if (this.parameters.type === "text") {
+ return;
+ }
+
+ this.el.move(this.parameters.box.x, this.parameters.box.y).width(this.parameters.box.width + snap[0]);
+ }
+ };
+ break;
+
+ // Bottom
+ case 'b':
+ // s.a.
+ this.calc = function (diffX, diffY) {
+ var snap = this.snapToGrid(diffX, diffY, 0);
+ if (this.parameters.box.height + snap[1] > 0) {
+ if (this.parameters.type === "text") {
+ return;
+ }
+
+ this.el.move(this.parameters.box.x, this.parameters.box.y).height(this.parameters.box.height + snap[1]);
+ }
+ };
+ break;
+
+ // Left
+ case 'l':
+ // s.a.
+ this.calc = function (diffX, diffY) {
+ var snap = this.snapToGrid(diffX, diffY, 1);
+ if (this.parameters.box.width - snap[0] > 0) {
+ if (this.parameters.type === "text") {
+ return;
+ }
+
+ this.el.move(this.parameters.box.x + snap[0], this.parameters.box.y).width(this.parameters.box.width - snap[0]);
+ }
+ };
+ break;
+
+ // Rotation
+ case 'rot':
+ // s.a.
+ this.calc = function (diffX, diffY) {
+
+ // yes this is kinda stupid but we need the mouse coords back...
+ var current = {x: diffX + this.parameters.p.x, y: diffY + this.parameters.p.y};
+
+ // start minus middle
+ var sAngle = Math.atan2((this.parameters.p.y - this.parameters.box.y - this.parameters.box.height / 2), (this.parameters.p.x - this.parameters.box.x - this.parameters.box.width / 2));
+
+ // end minus middle
+ var pAngle = Math.atan2((current.y - this.parameters.box.y - this.parameters.box.height / 2), (current.x - this.parameters.box.x - this.parameters.box.width / 2));
+
+ var angle = this.parameters.rotation + (pAngle - sAngle) * 180 / Math.PI + this.options.snapToAngle / 2;
+
+ // We have to move the element to the center of the box first and change the rotation afterwards
+ // because rotation always works around a rotation-center, which is changed when moving the element
+ // We also set the new rotation center to the center of the box.
+ this.el.center(this.parameters.box.cx, this.parameters.box.cy).rotate(angle - (angle % this.options.snapToAngle), this.parameters.box.cx, this.parameters.box.cy);
+ };
+ break;
+
+ // Moving one single Point (needed when an element is deepSelected which means you can move every single point of the object)
+ case 'point':
+ this.calc = function (diffX, diffY) {
+
+ // Snapping the point to the grid
+ var snap = this.snapToGrid(diffX, diffY, this.parameters.pointCoords[0], this.parameters.pointCoords[1]);
+
+ // Get the point array
+ var array = this.el.array().valueOf();
+
+ // Changing the moved point in the array
+ array[this.parameters.i][0] = this.parameters.pointCoords[0] + snap[0];
+ array[this.parameters.i][1] = this.parameters.pointCoords[1] + snap[1];
+
+ // And plot the new this.el
+ this.el.plot(array);
+ };
+ }
+
+ this.el.fire('resizestart', {dx: this.parameters.x, dy: this.parameters.y, event: event});
+ // When resizing started, we have to register events for...
+ // Touches.
+ SVG.on(window, 'touchmove.resize', function(e) {
+ _this.update(e || window.event);
+ });
+ SVG.on(window, 'touchend.resize', function() {
+ _this.done();
+ });
+ // Mouse.
+ SVG.on(window, 'mousemove.resize', function (e) {
+ _this.update(e || window.event);
+ });
+ SVG.on(window, 'mouseup.resize', function () {
+ _this.done();
+ });
+
+ };
+
+ // The update-function redraws the element every time the mouse is moving
+ ResizeHandler.prototype.update = function (event) {
+
+ if (!event) {
+ if (this.lastUpdateCall) {
+ this.calc(this.lastUpdateCall[0], this.lastUpdateCall[1]);
+ }
+ return;
+ }
+
+ // Calculate the difference between the mouseposition at start and now
+ var txPt = this._extractPosition(event);
+ var p = this.transformPoint(txPt.x, txPt.y);
+
+ var diffX = p.x - this.parameters.p.x,
+ diffY = p.y - this.parameters.p.y;
+
+ this.lastUpdateCall = [diffX, diffY];
+
+ // Calculate the new position and height / width of the element
+ this.calc(diffX, diffY);
+
+ // Emit an event to say we have changed.
+ this.el.fire('resizing', {dx: diffX, dy: diffY, event: event});
+ };
+
+ // Is called on mouseup.
+ // Removes the update-function from the mousemove event
+ ResizeHandler.prototype.done = function () {
+ this.lastUpdateCall = null;
+ SVG.off(window, 'mousemove.resize');
+ SVG.off(window, 'mouseup.resize');
+ SVG.off(window, 'touchmove.resize');
+ SVG.off(window, 'touchend.resize');
+ this.el.fire('resizedone');
+ };
+
+ // The flag is used to determine whether the resizing is used with a left-Point (first bit) and top-point (second bit)
+ // In this cases the temp-values are calculated differently
+ ResizeHandler.prototype.snapToGrid = function (diffX, diffY, flag, pointCoordsY) {
+
+ var temp;
+
+ // If `pointCoordsY` is given, a single Point has to be snapped (deepSelect). That's why we need a different temp-value
+ if (typeof pointCoordsY !== 'undefined') {
+ // Note that flag = pointCoordsX in this case
+ temp = [(flag + diffX) % this.options.snapToGrid, (pointCoordsY + diffY) % this.options.snapToGrid];
+ } else {
+ // We check if the flag is set and if not we set a default-value (both bits set - which means upper-left-edge)
+ flag = flag == null ? 1 | 1 << 1 : flag;
+ temp = [(this.parameters.box.x + diffX + (flag & 1 ? 0 : this.parameters.box.width)) % this.options.snapToGrid, (this.parameters.box.y + diffY + (flag & (1 << 1) ? 0 : this.parameters.box.height)) % this.options.snapToGrid];
+ }
+
+ if(diffX < 0) {
+ temp[0] -= this.options.snapToGrid;
+ }
+ if(diffY < 0) {
+ temp[1] -= this.options.snapToGrid;
+ }
+
+ diffX -= (Math.abs(temp[0]) < this.options.snapToGrid / 2 ?
+ temp[0] :
+ temp[0] - (diffX < 0 ? -this.options.snapToGrid : this.options.snapToGrid));
+ diffY -= (Math.abs(temp[1]) < this.options.snapToGrid / 2 ?
+ temp[1] :
+ temp[1] - (diffY < 0 ? -this.options.snapToGrid : this.options.snapToGrid));
+
+ return this.constraintToBox(diffX, diffY, flag, pointCoordsY);
+
+ };
+
+ // keep element within constrained box
+ ResizeHandler.prototype.constraintToBox = function (diffX, diffY, flag, pointCoordsY) {
+ //return [diffX, diffY]
+ var c = this.options.constraint || {};
+ var orgX, orgY;
+
+ if (typeof pointCoordsY !== 'undefined') {
+ orgX = flag;
+ orgY = pointCoordsY;
+ } else {
+ orgX = this.parameters.box.x + (flag & 1 ? 0 : this.parameters.box.width);
+ orgY = this.parameters.box.y + (flag & (1<<1) ? 0 : this.parameters.box.height);
+ }
+
+ if (typeof c.minX !== 'undefined' && orgX + diffX < c.minX) {
+ diffX = c.minX - orgX;
+ }
+
+ if (typeof c.maxX !== 'undefined' && orgX + diffX > c.maxX) {
+ diffX = c.maxX - orgX;
+ }
+
+ if (typeof c.minY !== 'undefined' && orgY + diffY < c.minY) {
+ diffY = c.minY - orgY;
+ }
+
+ if (typeof c.maxY !== 'undefined' && orgY + diffY > c.maxY) {
+ diffY = c.maxY - orgY;
+ }
+
+ return [diffX, diffY];
+ };
+
+ ResizeHandler.prototype.checkAspectRatio = function (snap, isReverse) {
+ if (!this.options.saveAspectRatio) {
+ return snap;
+ }
+
+ var updatedSnap = snap.slice();
+ var aspectRatio = this.parameters.box.width / this.parameters.box.height;
+ var newW = this.parameters.box.width + snap[0];
+ var newH = this.parameters.box.height - snap[1];
+ var newAspectRatio = newW / newH;
+
+ if (newAspectRatio < aspectRatio) {
+ // Height is too big. Adapt it
+ updatedSnap[1] = newW / aspectRatio - this.parameters.box.height;
+ isReverse && (updatedSnap[1] = -updatedSnap[1]);
+ } else if (newAspectRatio > aspectRatio) {
+ // Width is too big. Adapt it
+ updatedSnap[0] = this.parameters.box.width - newH * aspectRatio;
+ isReverse && (updatedSnap[0] = -updatedSnap[0]);
+ }
+
+ return updatedSnap;
+ };
+
+ SVG.extend(SVG.Element, {
+ // Resize element with mouse
+ resize: function (options) {
+
+ (this.remember('_resizeHandler') || new ResizeHandler(this)).init(options || {});
+
+ return this;
+
+ }
+
+ });
+
+ SVG.Element.prototype.resize.defaults = {
+ snapToAngle: 0.1, // Specifies the speed the rotation is happening when moving the mouse
+ snapToGrid: 1, // Snaps to a grid of `snapToGrid` Pixels
+ constraint: {}, // keep element within constrained box
+ saveAspectRatio: false // Save aspect ratio when resizing using lt, rt, rb or lb points
+ };
+
+}).call(this);
+}());
diff --git a/node_modules/svg.resize.js/dist/svg.resize.min.js b/node_modules/svg.resize.js/dist/svg.resize.min.js
new file mode 100644
index 0000000..cfac5f3
--- /dev/null
+++ b/node_modules/svg.resize.js/dist/svg.resize.min.js
@@ -0,0 +1 @@
+/*! svg.resize.js v1.4.3 MIT*/;!function(){"use strict";(function(){function t(t){t.remember("_resizeHandler",this),this.el=t,this.parameters={},this.lastUpdateCall=null,this.p=t.doc().node.createSVGPoint()}t.prototype.transformPoint=function(t,e,i){return this.p.x=t-(this.offset.x-window.pageXOffset),this.p.y=e-(this.offset.y-window.pageYOffset),this.p.matrixTransform(i||this.m)},t.prototype._extractPosition=function(t){return{x:null!=t.clientX?t.clientX:t.touches[0].clientX,y:null!=t.clientY?t.clientY:t.touches[0].clientY}},t.prototype.init=function(t){var e=this;if(this.stop(),"stop"!==t){this.options={};for(var i in this.el.resize.defaults)this.options[i]=this.el.resize.defaults[i],void 0!==t[i]&&(this.options[i]=t[i]);this.el.on("lt.resize",function(t){e.resize(t||window.event)}),this.el.on("rt.resize",function(t){e.resize(t||window.event)}),this.el.on("rb.resize",function(t){e.resize(t||window.event)}),this.el.on("lb.resize",function(t){e.resize(t||window.event)}),this.el.on("t.resize",function(t){e.resize(t||window.event)}),this.el.on("r.resize",function(t){e.resize(t||window.event)}),this.el.on("b.resize",function(t){e.resize(t||window.event)}),this.el.on("l.resize",function(t){e.resize(t||window.event)}),this.el.on("rot.resize",function(t){e.resize(t||window.event)}),this.el.on("point.resize",function(t){e.resize(t||window.event)}),this.update()}},t.prototype.stop=function(){return this.el.off("lt.resize"),this.el.off("rt.resize"),this.el.off("rb.resize"),this.el.off("lb.resize"),this.el.off("t.resize"),this.el.off("r.resize"),this.el.off("b.resize"),this.el.off("l.resize"),this.el.off("rot.resize"),this.el.off("point.resize"),this},t.prototype.resize=function(t){var e=this;this.m=this.el.node.getScreenCTM().inverse(),this.offset={x:window.pageXOffset,y:window.pageYOffset};var i=this._extractPosition(t.detail.event);if(this.parameters={type:this.el.type,p:this.transformPoint(i.x,i.y),x:t.detail.x,y:t.detail.y,box:this.el.bbox(),rotation:this.el.transform().rotation},"text"===this.el.type&&(this.parameters.fontSize=this.el.attr()["font-size"]),void 0!==t.detail.i){var s=this.el.array().valueOf();this.parameters.i=t.detail.i,this.parameters.pointCoords=[s[t.detail.i][0],s[t.detail.i][1]]}switch(t.type){case"lt":this.calc=function(t,e){var i=this.snapToGrid(t,e);if(this.parameters.box.width-i[0]>0&&this.parameters.box.height-i[1]>0){if("text"===this.parameters.type)return this.el.move(this.parameters.box.x+i[0],this.parameters.box.y),void this.el.attr("font-size",this.parameters.fontSize-i[0]);i=this.checkAspectRatio(i),this.el.move(this.parameters.box.x+i[0],this.parameters.box.y+i[1]).size(this.parameters.box.width-i[0],this.parameters.box.height-i[1])}};break;case"rt":this.calc=function(t,e){var i=this.snapToGrid(t,e,2);if(this.parameters.box.width+i[0]>0&&this.parameters.box.height-i[1]>0){if("text"===this.parameters.type)return this.el.move(this.parameters.box.x-i[0],this.parameters.box.y),void this.el.attr("font-size",this.parameters.fontSize+i[0]);i=this.checkAspectRatio(i,!0),this.el.move(this.parameters.box.x,this.parameters.box.y+i[1]).size(this.parameters.box.width+i[0],this.parameters.box.height-i[1])}};break;case"rb":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.width+i[0]>0&&this.parameters.box.height+i[1]>0){if("text"===this.parameters.type)return this.el.move(this.parameters.box.x-i[0],this.parameters.box.y),void this.el.attr("font-size",this.parameters.fontSize+i[0]);i=this.checkAspectRatio(i),this.el.move(this.parameters.box.x,this.parameters.box.y).size(this.parameters.box.width+i[0],this.parameters.box.height+i[1])}};break;case"lb":this.calc=function(t,e){var i=this.snapToGrid(t,e,1);if(this.parameters.box.width-i[0]>0&&this.parameters.box.height+i[1]>0){if("text"===this.parameters.type)return this.el.move(this.parameters.box.x+i[0],this.parameters.box.y),void this.el.attr("font-size",this.parameters.fontSize-i[0]);i=this.checkAspectRatio(i,!0),this.el.move(this.parameters.box.x+i[0],this.parameters.box.y).size(this.parameters.box.width-i[0],this.parameters.box.height+i[1])}};break;case"t":this.calc=function(t,e){var i=this.snapToGrid(t,e,2);if(this.parameters.box.height-i[1]>0){if("text"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y+i[1]).height(this.parameters.box.height-i[1])}};break;case"r":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.width+i[0]>0){if("text"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y).width(this.parameters.box.width+i[0])}};break;case"b":this.calc=function(t,e){var i=this.snapToGrid(t,e,0);if(this.parameters.box.height+i[1]>0){if("text"===this.parameters.type)return;this.el.move(this.parameters.box.x,this.parameters.box.y).height(this.parameters.box.height+i[1])}};break;case"l":this.calc=function(t,e){var i=this.snapToGrid(t,e,1);if(this.parameters.box.width-i[0]>0){if("text"===this.parameters.type)return;this.el.move(this.parameters.box.x+i[0],this.parameters.box.y).width(this.parameters.box.width-i[0])}};break;case"rot":this.calc=function(t,e){var i={x:t+this.parameters.p.x,y:e+this.parameters.p.y},s=Math.atan2(this.parameters.p.y-this.parameters.box.y-this.parameters.box.height/2,this.parameters.p.x-this.parameters.box.x-this.parameters.box.width/2),r=Math.atan2(i.y-this.parameters.box.y-this.parameters.box.height/2,i.x-this.parameters.box.x-this.parameters.box.width/2),a=this.parameters.rotation+180*(r-s)/Math.PI+this.options.snapToAngle/2;this.el.center(this.parameters.box.cx,this.parameters.box.cy).rotate(a-a%this.options.snapToAngle,this.parameters.box.cx,this.parameters.box.cy)};break;case"point":this.calc=function(t,e){var i=this.snapToGrid(t,e,this.parameters.pointCoords[0],this.parameters.pointCoords[1]),s=this.el.array().valueOf();s[this.parameters.i][0]=this.parameters.pointCoords[0]+i[0],s[this.parameters.i][1]=this.parameters.pointCoords[1]+i[1],this.el.plot(s)}}this.el.fire("resizestart",{dx:this.parameters.x,dy:this.parameters.y,event:t}),SVG.on(window,"touchmove.resize",function(t){e.update(t||window.event)}),SVG.on(window,"touchend.resize",function(){e.done()}),SVG.on(window,"mousemove.resize",function(t){e.update(t||window.event)}),SVG.on(window,"mouseup.resize",function(){e.done()})},t.prototype.update=function(t){if(!t)return void(this.lastUpdateCall&&this.calc(this.lastUpdateCall[0],this.lastUpdateCall[1]));var e=this._extractPosition(t),i=this.transformPoint(e.x,e.y),s=i.x-this.parameters.p.x,r=i.y-this.parameters.p.y;this.lastUpdateCall=[s,r],this.calc(s,r),this.el.fire("resizing",{dx:s,dy:r,event:t})},t.prototype.done=function(){this.lastUpdateCall=null,SVG.off(window,"mousemove.resize"),SVG.off(window,"mouseup.resize"),SVG.off(window,"touchmove.resize"),SVG.off(window,"touchend.resize"),this.el.fire("resizedone")},t.prototype.snapToGrid=function(t,e,i,s){var r;return void 0!==s?r=[(i+t)%this.options.snapToGrid,(s+e)%this.options.snapToGrid]:(i=null==i?3:i,r=[(this.parameters.box.x+t+(1&i?0:this.parameters.box.width))%this.options.snapToGrid,(this.parameters.box.y+e+(2&i?0:this.parameters.box.height))%this.options.snapToGrid]),t<0&&(r[0]-=this.options.snapToGrid),e<0&&(r[1]-=this.options.snapToGrid),t-=Math.abs(r[0])o.maxX&&(t=o.maxX-r),void 0!==o.minY&&a+eo.maxY&&(e=o.maxY-a),[t,e]},t.prototype.checkAspectRatio=function(t,e){if(!this.options.saveAspectRatio)return t;var i=t.slice(),s=this.parameters.box.width/this.parameters.box.height,r=this.parameters.box.width+t[0],a=this.parameters.box.height-t[1],o=r/a;return os&&(i[0]=this.parameters.box.width-a*s,e&&(i[0]=-i[0])),i},SVG.extend(SVG.Element,{resize:function(e){return(this.remember("_resizeHandler")||new t(this)).init(e||{}),this}}),SVG.Element.prototype.resize.defaults={snapToAngle:.1,snapToGrid:1,constraint:{},saveAspectRatio:!1}}).call(this)}();
\ No newline at end of file
diff --git a/node_modules/svg.resize.js/node_modules/svg.select.js/LICENSE b/node_modules/svg.resize.js/node_modules/svg.select.js/LICENSE
new file mode 100644
index 0000000..ca43f86
--- /dev/null
+++ b/node_modules/svg.resize.js/node_modules/svg.select.js/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Fuzzy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/node_modules/svg.resize.js/node_modules/svg.select.js/README.md b/node_modules/svg.resize.js/node_modules/svg.select.js/README.md
new file mode 100644
index 0000000..04b7b31
--- /dev/null
+++ b/node_modules/svg.resize.js/node_modules/svg.select.js/README.md
@@ -0,0 +1,72 @@
+svg.select.js
+=============
+
+An extension of [svg.js](https://github.com/svgdotjs/svg.js) which allows to select elements with mouse
+
+**Note:** Duo to naming conflicts the exported method was renamed from `select()` to `selectize()`.
+
+# Demo
+
+For a demo see http://svgdotjs.github.io/svg.select.js/
+
+# Get Started
+
+- Install `svg.select.js` using bower:
+
+ bower install svg.select.js
+
+- Include the script after svg.js into your page
+
+
+
+
+- Select a rectangle using this simple piece of code:
+
+
+
+ var drawing = new SVG('myDrawing').size(500, 500);
+ drawing.rect(50,50).selectize()
+
+# Usage
+
+Select
+
+ var draw = SVG('drawing');
+ var rect = draw.rect(100,100);
+ rect.selectize();
+
+ // or deepSelect
+ rect.selectize({deepSelect:true});
+
+Unselect
+
+ rect.selectize(false);
+
+ // or deepSelect
+ rect.selectize(false, {deepSelect:true});
+
+
+You can style the selection with the classes
+
+- `svg_select_boundingRect`
+- `svg_select_points`
+- `svg_select_points_lt` - *left top*
+- `svg_select_points_rt` - *right top*
+- `svg_select_points_rb` - *right bottom*
+- `svg_select_points_lb` - *left bottom*
+- `svg_select_points_t` - *top*
+- `svg_select_points_r` - *right*
+- `svg_select_points_b` - *bottom*
+- `svg_select_points_l` - *left*
+- `svg_select_points_rot` - *rotation point*
+- `svg_select_points_point` - *deepSelect points*
+
+
+# Options
+
+- points: Points should be drawn (default `true`)
+- classRect: Classname of the rect from the bounding Box (default `svg_select_boundingRect`)
+- classPoints: Classname/Prefix of the Points (default `svg_select_points`)
+- radius: Radius of the points (default `7`)
+- rotationPoint: Draws the point for doing rotation (default `true`)
+- deepSelect: Only for polygon/polyline/line. Selects the points itself (default `false`)
\ No newline at end of file
diff --git a/node_modules/svg.resize.js/node_modules/svg.select.js/dist/svg.select.css b/node_modules/svg.resize.js/node_modules/svg.select.js/dist/svg.select.css
new file mode 100644
index 0000000..18888ab
--- /dev/null
+++ b/node_modules/svg.resize.js/node_modules/svg.select.js/dist/svg.select.css
@@ -0,0 +1,44 @@
+.svg_select_points_lt{
+ cursor: nw-resize;
+}
+.svg_select_points_rt{
+ cursor: ne-resize;
+}
+.svg_select_points_rb{
+ cursor: se-resize;
+}
+.svg_select_points_lb{
+ cursor: sw-resize;
+}
+.svg_select_points_t{
+ cursor: n-resize;
+}
+.svg_select_points_r{
+ cursor: e-resize;
+}
+.svg_select_points_b{
+ cursor: s-resize;
+}
+.svg_select_points_l{
+ cursor: w-resize;
+}
+
+.svg_select_points_rot{
+ stroke-width:1;
+ stroke:black;
+ fill: #F9FFED;
+}
+
+.svg_select_points_point{
+ cursor: move;
+}
+
+.svg_select_boundingRect{
+ stroke-width:1;
+ fill:gray;
+ stroke-dasharray:10 10;
+ stroke:black;
+ stroke-opacity:0.8;
+ fill-opacity:0.1;
+ pointer-events:none; /* This ons is needed if you want to deselect or drag the shape*/
+}
\ No newline at end of file
diff --git a/node_modules/svg.resize.js/node_modules/svg.select.js/dist/svg.select.js b/node_modules/svg.resize.js/node_modules/svg.select.js/dist/svg.select.js
new file mode 100644
index 0000000..4115b25
--- /dev/null
+++ b/node_modules/svg.resize.js/node_modules/svg.select.js/dist/svg.select.js
@@ -0,0 +1,317 @@
+/*!
+* svg.select.js - An extension of svg.js which allows to select elements with mouse
+* @version 2.1.2
+* https://github.com/svgdotjs/svg.select.js
+*
+* @copyright Ulrich-Matthias Schäfer
+* @license MIT
+*/;
+;(function() {
+"use strict";
+
+function SelectHandler(el) {
+
+ this.el = el;
+ el.remember('_selectHandler', this);
+ this.pointSelection = {isSelected: false};
+ this.rectSelection = {isSelected: false};
+
+}
+
+SelectHandler.prototype.init = function (value, options) {
+
+ var bbox = this.el.bbox();
+ this.options = {};
+
+ // Merging the defaults and the options-object together
+ for (var i in this.el.selectize.defaults) {
+ this.options[i] = this.el.selectize.defaults[i];
+ if (options[i] !== undefined) {
+ this.options[i] = options[i];
+ }
+ }
+
+ this.parent = this.el.parent();
+ this.nested = (this.nested || this.parent.group());
+ this.nested.matrix(new SVG.Matrix(this.el).translate(bbox.x, bbox.y));
+
+ // When deepSelect is enabled and the element is a line/polyline/polygon, draw only points for moving
+ if (this.options.deepSelect && ['line', 'polyline', 'polygon'].indexOf(this.el.type) !== -1) {
+ this.selectPoints(value);
+ } else {
+ this.selectRect(value);
+ }
+
+ this.observe();
+ this.cleanup();
+
+};
+
+SelectHandler.prototype.selectPoints = function (value) {
+
+ this.pointSelection.isSelected = value;
+
+ // When set is already there we dont have to create one
+ if (this.pointSelection.set) {
+ return this;
+ }
+
+ // Create our set of elements
+ this.pointSelection.set = this.parent.set();
+ // draw the circles and mark the element as selected
+ this.drawCircles();
+
+ return this;
+
+};
+
+// create the point-array which contains the 2 points of a line or simply the points-array of polyline/polygon
+SelectHandler.prototype.getPointArray = function () {
+ var bbox = this.el.bbox();
+
+ return this.el.array().valueOf().map(function (el) {
+ return [el[0] - bbox.x, el[1] - bbox.y];
+ });
+};
+
+// The function to draw the circles
+SelectHandler.prototype.drawCircles = function () {
+
+ var _this = this, array = this.getPointArray();
+
+ // go through the array of points
+ for (var i = 0, len = array.length; i < len; ++i) {
+
+ var curriedEvent = (function (k) {
+ return function (ev) {
+ ev = ev || window.event;
+ ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;
+ ev.stopPropagation();
+
+ var x = ev.pageX || ev.touches[0].pageX;
+ var y = ev.pageY || ev.touches[0].pageY;
+ _this.el.fire('point', {x: x, y: y, i: k, event: ev});
+ };
+ })(i);
+
+ // add every point to the set
+ this.pointSelection.set.add(
+ // a circle with our css-classes and a touchstart-event which fires our event for moving points
+ this.nested.circle(this.options.radius)
+ .center(array[i][0], array[i][1])
+ .addClass(this.options.classPoints)
+ .addClass(this.options.classPoints + '_point')
+ .on('touchstart', curriedEvent)
+ .on('mousedown', curriedEvent)
+ );
+ }
+
+};
+
+// every time a circle is moved, we have to update the positions of our circle
+SelectHandler.prototype.updatePointSelection = function () {
+ var array = this.getPointArray();
+
+ this.pointSelection.set.each(function (i) {
+ if (this.cx() === array[i][0] && this.cy() === array[i][1]) {
+ return;
+ }
+ this.center(array[i][0], array[i][1]);
+ });
+};
+
+SelectHandler.prototype.updateRectSelection = function () {
+ var bbox = this.el.bbox();
+
+ this.rectSelection.set.get(0).attr({
+ width: bbox.width,
+ height: bbox.height
+ });
+
+ // set.get(1) is always in the upper left corner. no need to move it
+ if (this.options.points) {
+ this.rectSelection.set.get(2).center(bbox.width, 0);
+ this.rectSelection.set.get(3).center(bbox.width, bbox.height);
+ this.rectSelection.set.get(4).center(0, bbox.height);
+
+ this.rectSelection.set.get(5).center(bbox.width / 2, 0);
+ this.rectSelection.set.get(6).center(bbox.width, bbox.height / 2);
+ this.rectSelection.set.get(7).center(bbox.width / 2, bbox.height);
+ this.rectSelection.set.get(8).center(0, bbox.height / 2);
+ }
+
+ if (this.options.rotationPoint) {
+ if (this.options.points) {
+ this.rectSelection.set.get(9).center(bbox.width / 2, 20);
+ } else {
+ this.rectSelection.set.get(1).center(bbox.width / 2, 20);
+ }
+ }
+};
+
+SelectHandler.prototype.selectRect = function (value) {
+
+ var _this = this, bbox = this.el.bbox();
+
+ this.rectSelection.isSelected = value;
+
+ // when set is already p
+ this.rectSelection.set = this.rectSelection.set || this.parent.set();
+
+ // helperFunction to create a mouse-down function which triggers the event specified in `eventName`
+ function getMoseDownFunc(eventName) {
+ return function (ev) {
+ ev = ev || window.event;
+ ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;
+ ev.stopPropagation();
+
+ var x = ev.pageX || ev.touches[0].pageX;
+ var y = ev.pageY || ev.touches[0].pageY;
+ _this.el.fire(eventName, {x: x, y: y, event: ev});
+ };
+ }
+
+ // create the selection-rectangle and add the css-class
+ if (!this.rectSelection.set.get(0)) {
+ this.rectSelection.set.add(this.nested.rect(bbox.width, bbox.height).addClass(this.options.classRect));
+ }
+
+ // Draw Points at the edges, if enabled
+ if (this.options.points && !this.rectSelection.set.get(1)) {
+ var ename ="touchstart", mname = "mousedown";
+ this.rectSelection.set.add(this.nested.circle(this.options.radius).center(0, 0).attr('class', this.options.classPoints + '_lt').on(mname, getMoseDownFunc('lt')).on(ename, getMoseDownFunc('lt')));
+ this.rectSelection.set.add(this.nested.circle(this.options.radius).center(bbox.width, 0).attr('class', this.options.classPoints + '_rt').on(mname, getMoseDownFunc('rt')).on(ename, getMoseDownFunc('rt')));
+ this.rectSelection.set.add(this.nested.circle(this.options.radius).center(bbox.width, bbox.height).attr('class', this.options.classPoints + '_rb').on(mname, getMoseDownFunc('rb')).on(ename, getMoseDownFunc('rb')));
+ this.rectSelection.set.add(this.nested.circle(this.options.radius).center(0, bbox.height).attr('class', this.options.classPoints + '_lb').on(mname, getMoseDownFunc('lb')).on(ename, getMoseDownFunc('lb')));
+
+ this.rectSelection.set.add(this.nested.circle(this.options.radius).center(bbox.width / 2, 0).attr('class', this.options.classPoints + '_t').on(mname, getMoseDownFunc('t')).on(ename, getMoseDownFunc('t')));
+ this.rectSelection.set.add(this.nested.circle(this.options.radius).center(bbox.width, bbox.height / 2).attr('class', this.options.classPoints + '_r').on(mname, getMoseDownFunc('r')).on(ename, getMoseDownFunc('r')));
+ this.rectSelection.set.add(this.nested.circle(this.options.radius).center(bbox.width / 2, bbox.height).attr('class', this.options.classPoints + '_b').on(mname, getMoseDownFunc('b')).on(ename, getMoseDownFunc('b')));
+ this.rectSelection.set.add(this.nested.circle(this.options.radius).center(0, bbox.height / 2).attr('class', this.options.classPoints + '_l').on(mname, getMoseDownFunc('l')).on(ename, getMoseDownFunc('l')));
+
+ this.rectSelection.set.each(function () {
+ this.addClass(_this.options.classPoints);
+ });
+ }
+
+ // draw rotationPint, if enabled
+ if (this.options.rotationPoint && ((this.options.points && !this.rectSelection.set.get(9)) || (!this.options.points && !this.rectSelection.set.get(1)))) {
+
+ var curriedEvent = function (ev) {
+ ev = ev || window.event;
+ ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;
+ ev.stopPropagation();
+
+ var x = ev.pageX || ev.touches[0].pageX;
+ var y = ev.pageY || ev.touches[0].pageY;
+ _this.el.fire('rot', {x: x, y: y, event: ev});
+ };
+ this.rectSelection.set.add(this.nested.circle(this.options.radius).center(bbox.width / 2, 20).attr('class', this.options.classPoints + '_rot')
+ .on("touchstart", curriedEvent).on("mousedown", curriedEvent));
+
+ }
+
+};
+
+SelectHandler.prototype.handler = function () {
+
+ var bbox = this.el.bbox();
+ this.nested.matrix(new SVG.Matrix(this.el).translate(bbox.x, bbox.y));
+
+ if (this.rectSelection.isSelected) {
+ this.updateRectSelection();
+ }
+
+ if (this.pointSelection.isSelected) {
+ this.updatePointSelection();
+ }
+
+};
+
+SelectHandler.prototype.observe = function () {
+ var _this = this;
+
+ if (MutationObserver) {
+ if (this.rectSelection.isSelected || this.pointSelection.isSelected) {
+ this.observerInst = this.observerInst || new MutationObserver(function () {
+ _this.handler();
+ });
+ this.observerInst.observe(this.el.node, {attributes: true});
+ } else {
+ try {
+ this.observerInst.disconnect();
+ delete this.observerInst;
+ } catch (e) {
+ }
+ }
+ } else {
+ this.el.off('DOMAttrModified.select');
+
+ if (this.rectSelection.isSelected || this.pointSelection.isSelected) {
+ this.el.on('DOMAttrModified.select', function () {
+ _this.handler();
+ });
+ }
+ }
+};
+
+SelectHandler.prototype.cleanup = function () {
+
+ //var _this = this;
+
+ if (!this.rectSelection.isSelected && this.rectSelection.set) {
+ // stop watching the element, remove the selection
+ this.rectSelection.set.each(function () {
+ this.remove();
+ });
+
+ this.rectSelection.set.clear();
+ delete this.rectSelection.set;
+ }
+
+ if (!this.pointSelection.isSelected && this.pointSelection.set) {
+ // Remove all points, clear the set, stop watching the element
+ this.pointSelection.set.each(function () {
+ this.remove();
+ });
+
+ this.pointSelection.set.clear();
+ delete this.pointSelection.set;
+ }
+
+ if (!this.pointSelection.isSelected && !this.rectSelection.isSelected) {
+ this.nested.remove();
+ delete this.nested;
+
+ }
+};
+
+
+SVG.extend(SVG.Element, {
+ // Select element with mouse
+ selectize: function (value, options) {
+
+ // Check the parameters and reassign if needed
+ if (typeof value === 'object') {
+ options = value;
+ value = true;
+ }
+
+ var selectHandler = this.remember('_selectHandler') || new SelectHandler(this);
+
+ selectHandler.init(value === undefined ? true : value, options || {});
+
+ return this;
+
+ }
+});
+
+SVG.Element.prototype.selectize.defaults = {
+ points: true, // If true, points at the edges are drawn. Needed for resize!
+ classRect: 'svg_select_boundingRect', // Css-class added to the rect
+ classPoints: 'svg_select_points', // Css-class added to the points
+ radius: 7, // radius of the points
+ rotationPoint: true, // If true, rotation point is drawn. Needed for rotation!
+ deepSelect: false // If true, moving of single points is possible (only line, polyline, polyon)
+};
+}());
diff --git a/node_modules/svg.resize.js/node_modules/svg.select.js/dist/svg.select.min.css b/node_modules/svg.resize.js/node_modules/svg.select.js/dist/svg.select.min.css
new file mode 100644
index 0000000..d834513
--- /dev/null
+++ b/node_modules/svg.resize.js/node_modules/svg.select.js/dist/svg.select.min.css
@@ -0,0 +1 @@
+.svg_select_points_lt{cursor:nw-resize}.svg_select_points_rt{cursor:ne-resize}.svg_select_points_rb{cursor:se-resize}.svg_select_points_lb{cursor:sw-resize}.svg_select_points_t{cursor:n-resize}.svg_select_points_r{cursor:e-resize}.svg_select_points_b{cursor:s-resize}.svg_select_points_l{cursor:w-resize}.svg_select_points_rot{stroke-width:1;stroke:#000;fill:#f9ffed}.svg_select_points_point{cursor:move}.svg_select_boundingRect{stroke-width:1;fill:gray;stroke-dasharray:10 10;stroke:#000;stroke-opacity:.8;fill-opacity:.1;pointer-events:none}
\ No newline at end of file
diff --git a/node_modules/svg.resize.js/node_modules/svg.select.js/dist/svg.select.min.js b/node_modules/svg.resize.js/node_modules/svg.select.js/dist/svg.select.min.js
new file mode 100644
index 0000000..8fc120d
--- /dev/null
+++ b/node_modules/svg.resize.js/node_modules/svg.select.js/dist/svg.select.min.js
@@ -0,0 +1 @@
+/*! svg.select.js v2.1.2 MIT*/;!function(){"use strict";function t(t){this.el=t,t.remember("_selectHandler",this),this.pointSelection={isSelected:!1},this.rectSelection={isSelected:!1}}t.prototype.init=function(t,e){var i=this.el.bbox();this.options={};for(var s in this.el.selectize.defaults)this.options[s]=this.el.selectize.defaults[s],void 0!==e[s]&&(this.options[s]=e[s]);this.parent=this.el.parent(),this.nested=this.nested||this.parent.group(),this.nested.matrix(new SVG.Matrix(this.el).translate(i.x,i.y)),this.options.deepSelect&&-1!==["line","polyline","polygon"].indexOf(this.el.type)?this.selectPoints(t):this.selectRect(t),this.observe(),this.cleanup()},t.prototype.selectPoints=function(t){return this.pointSelection.isSelected=t,this.pointSelection.set?this:(this.pointSelection.set=this.parent.set(),this.drawCircles(),this)},t.prototype.getPointArray=function(){var t=this.el.bbox();return this.el.array().valueOf().map(function(e){return[e[0]-t.x,e[1]-t.y]})},t.prototype.drawCircles=function(){for(var t=this,e=this.getPointArray(),i=0,s=e.length;i= 0.8.0"
+ },
+ "devDependencies": {
+ "del": "^2.2.2",
+ "gulp": "^3.9.1",
+ "gulp-clean-css": "3.5.0",
+ "gulp-header": "^1.8.8",
+ "gulp-iife": "^0.3.0",
+ "gulp-rename": "^1.2.2",
+ "gulp-standard": "^10.0.0",
+ "gulp-trimlines": "^1.0.1",
+ "gulp-uglify": "^2.1.2",
+ "gulp-wrap-iife": "0.0.1"
+ },
+ "dependencies": {
+ "svg.js": "^2.2.5"
+ }
+}
diff --git a/node_modules/svg.resize.js/package.json b/node_modules/svg.resize.js/package.json
new file mode 100644
index 0000000..f9cde39
--- /dev/null
+++ b/node_modules/svg.resize.js/package.json
@@ -0,0 +1,49 @@
+{
+ "name": "svg.resize.js",
+ "version": "1.4.3",
+ "description": "An extension for svg.js which allows to resize elements which are selected",
+ "keywords": [
+ "svg.js",
+ "resize",
+ "mouse"
+ ],
+ "bugs": "https://github.com/svgdotjs/svg.resize.js/issues",
+ "license": "MIT",
+ "author": {
+ "name": "Ulrich-Matthias Schäfer"
+ },
+ "contributors": {
+ "name": "Ulrich-Matthias Schäfer"
+ },
+ "homepage": "https://github.com/svgdotjs/svg.resize.js",
+ "main": "dist/svg.resize.js",
+ "files": [
+ "dist/"
+ ],
+ "scripts": {
+ "build": "gulp"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/svgdotjs/svg.resize.js.git"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ },
+ "devDependencies": {
+ "del": "^2.2.2",
+ "gulp": "^3.9.1",
+ "gulp-header": "^1.8.12",
+ "gulp-iife": "^0.3.0",
+ "gulp-rename": "^1.3.0",
+ "gulp-standard": "^10.1.2",
+ "gulp-trimlines": "^1.0.1",
+ "gulp-uglify": "^2.1.2",
+ "gulp-wrap-iife": "0.0.1",
+ "natives": "1.1.3"
+ },
+ "dependencies": {
+ "svg.js": "^2.6.5",
+ "svg.select.js": "^2.1.2"
+ }
+}
diff --git a/node_modules/svg.select.js/LICENSE b/node_modules/svg.select.js/LICENSE
new file mode 100644
index 0000000..ca43f86
--- /dev/null
+++ b/node_modules/svg.select.js/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Fuzzy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/node_modules/svg.select.js/README.md b/node_modules/svg.select.js/README.md
new file mode 100644
index 0000000..cc188b2
--- /dev/null
+++ b/node_modules/svg.select.js/README.md
@@ -0,0 +1,100 @@
+svg.select.js
+=============
+
+An extension of [svg.js](https://github.com/svgdotjs/svg.js) which allows to select elements with mouse
+
+**Note:** Duo to naming conflicts the exported method was renamed from `select()` to `selectize()`.
+
+# Demo
+
+For a demo see http://svgdotjs.github.io/svg.select.js/
+
+# Get Started
+
+- Install `svg.select.js` using bower:
+
+ bower install svg.select.js
+
+- Include the script after svg.js into your page
+
+
+
+
+- Select a rectangle using this simple piece of code:
+
+
+
+ var drawing = new SVG('myDrawing').size(500, 500);
+ drawing.rect(50,50).selectize()
+
+# Usage
+
+Select
+
+ var draw = SVG('drawing');
+ var rect = draw.rect(100,100);
+ rect.selectize();
+
+ // or deepSelect
+ rect.selectize({deepSelect:true});
+
+Unselect
+
+ rect.selectize(false);
+
+ // or deepSelect
+ rect.selectize(false, {deepSelect:true});
+
+You can specify which points to be drawn (default all will be drawn)
+
+The list can be an array of strings or a comma separated list / string, representing each position, in correspondence with the classes:
+
+* `lt` - left top
+* `rt` - right top
+* `rb` - right bottom
+* `lb` - left bottom
+* `t` - top
+* `r` - right
+* `b` - bottom
+* `l` - left
+
+Example of drawing only `top` and `right` points:
+
+ rect.selectize({
+ points: ['t', 'r'] // or 't, r'
+ })
+
+There is also an extra option called `pointsExclude` which can be a list of points to be excluded from the `points` list.
+
+So let's say that you need all the points except `top` and `right`:
+
+ rect.selectize({
+ pointsExclude: ['t', 'r'] // or 't, r'
+ })
+
+You can style the selection with the classes
+
+- `svg_select_boundingRect`
+- `svg_select_points`
+- `svg_select_points_lt` - *left top*
+- `svg_select_points_rt` - *right top*
+- `svg_select_points_rb` - *right bottom*
+- `svg_select_points_lb` - *left bottom*
+- `svg_select_points_t` - *top*
+- `svg_select_points_r` - *right*
+- `svg_select_points_b` - *bottom*
+- `svg_select_points_l` - *left*
+- `svg_select_points_rot` - *rotation point*
+- `svg_select_points_point` - *deepSelect points*
+
+
+# Options
+
+- points: Points should be drawn (default `['lt', 'rt', 'rb', 'lb', 't', 'r', 'b', 'l']`)
+- pointsExclude: Same as points option, only thing that this excludes listed points, you can use (default `[]`)
+- classRect: Classname of the rect from the bounding Box (default `svg_select_boundingRect`)
+- classPoints: Classname/Prefix of the Points (default `svg_select_points`)
+- pointSize: Size of the point. Radius for the `pointType: 'circle'` or size of a rect for `pointType: 'rect'` (default `7`)
+- rotationPoint: Draws the point for doing rotation (default `true`)
+- deepSelect: Only for polygon/polyline/line. Selects the points itself (default `false`)
+- pointType: Type of a point, `circle` or `rect` or function (see functions for drawing [circle](src/svg.select.js#L188) or [rect](src/svg.select.js#L194) points) (default `circle`)
\ No newline at end of file
diff --git a/node_modules/svg.select.js/dist/svg.select.css b/node_modules/svg.select.js/dist/svg.select.css
new file mode 100644
index 0000000..18888ab
--- /dev/null
+++ b/node_modules/svg.select.js/dist/svg.select.css
@@ -0,0 +1,44 @@
+.svg_select_points_lt{
+ cursor: nw-resize;
+}
+.svg_select_points_rt{
+ cursor: ne-resize;
+}
+.svg_select_points_rb{
+ cursor: se-resize;
+}
+.svg_select_points_lb{
+ cursor: sw-resize;
+}
+.svg_select_points_t{
+ cursor: n-resize;
+}
+.svg_select_points_r{
+ cursor: e-resize;
+}
+.svg_select_points_b{
+ cursor: s-resize;
+}
+.svg_select_points_l{
+ cursor: w-resize;
+}
+
+.svg_select_points_rot{
+ stroke-width:1;
+ stroke:black;
+ fill: #F9FFED;
+}
+
+.svg_select_points_point{
+ cursor: move;
+}
+
+.svg_select_boundingRect{
+ stroke-width:1;
+ fill:gray;
+ stroke-dasharray:10 10;
+ stroke:black;
+ stroke-opacity:0.8;
+ fill-opacity:0.1;
+ pointer-events:none; /* This ons is needed if you want to deselect or drag the shape*/
+}
\ No newline at end of file
diff --git a/node_modules/svg.select.js/dist/svg.select.js b/node_modules/svg.select.js/dist/svg.select.js
new file mode 100644
index 0000000..1976823
--- /dev/null
+++ b/node_modules/svg.select.js/dist/svg.select.js
@@ -0,0 +1,419 @@
+/*!
+* svg.select.js - An extension of svg.js which allows to select elements with mouse
+* @version 3.0.1
+* https://github.com/svgdotjs/svg.select.js
+*
+* @copyright Ulrich-Matthias Schäfer
+* @license MIT
+*/;
+;(function() {
+"use strict";
+
+function SelectHandler(el) {
+
+ this.el = el;
+ el.remember('_selectHandler', this);
+ this.pointSelection = {isSelected: false};
+ this.rectSelection = {isSelected: false};
+
+ // helper list with position settings of each type of point
+ this.pointsList = {
+ lt: [ 0, 0 ],
+ rt: [ 'width', 0 ],
+ rb: [ 'width', 'height' ],
+ lb: [ 0, 'height' ],
+ t: [ 'width', 0 ],
+ r: [ 'width', 'height' ],
+ b: [ 'width', 'height' ],
+ l: [ 0, 'height' ]
+ };
+
+ // helper function to get point coordinates based on settings above and an object (bbox in our case)
+ this.pointCoord = function (setting, object, isPointCentered) {
+ var coord = typeof setting !== 'string' ? setting : object[setting];
+ // Top, bottom, right and left points are placed in the center of element width/height
+ return isPointCentered ? coord / 2 : coord
+ }
+
+ this.pointCoords = function (point, object) {
+ var settings = this.pointsList[point];
+
+ return {
+ x: this.pointCoord(settings[0], object, (point === 't' || point === 'b')),
+ y: this.pointCoord(settings[1], object, (point === 'r' || point === 'l'))
+ }
+ }
+}
+
+SelectHandler.prototype.init = function (value, options) {
+
+ var bbox = this.el.bbox();
+ this.options = {};
+
+ // store defaults list of points in order to verify users config
+ var points = this.el.selectize.defaults.points;
+
+ // Merging the defaults and the options-object together
+ for (var i in this.el.selectize.defaults) {
+ this.options[i] = this.el.selectize.defaults[i];
+ if (options[i] !== undefined) {
+ this.options[i] = options[i];
+ }
+ }
+
+ // prepare & validate list of points to be added (or excluded)
+ var pointsLists = ['points', 'pointsExclude'];
+
+ for (var i in pointsLists) {
+ var option = this.options[pointsLists[i]];
+
+ if (typeof option === 'string') {
+ if (option.length > 0) {
+ // if set as comma separated string list => convert it into an array
+ option = option.split(/\s*,\s*/i);
+ } else {
+ option = [];
+ }
+ } else if (typeof option === 'boolean' && pointsLists[i] === 'points') {
+ // this is not needed, but let's have it for legacy support
+ option = option ? points : [];
+ }
+
+ this.options[pointsLists[i]] = option;
+ }
+
+ // intersect correct all points options with users config (exclude unwanted points)
+ // ES5 -> NO arrow functions nor Array.includes()
+ this.options.points = [ points, this.options.points ].reduce(
+ function (a, b) {
+ return a.filter(
+ function (c) {
+ return b.indexOf(c) > -1;
+ }
+ )
+ }
+ );
+
+ // exclude pointsExclude, if wanted
+ this.options.points = [ this.options.points, this.options.pointsExclude ].reduce(
+ function (a, b) {
+ return a.filter(
+ function (c) {
+ return b.indexOf(c) < 0;
+ }
+ )
+ }
+ );
+
+ this.parent = this.el.parent();
+ this.nested = (this.nested || this.parent.group());
+ this.nested.matrix(new SVG.Matrix(this.el).translate(bbox.x, bbox.y));
+
+ // When deepSelect is enabled and the element is a line/polyline/polygon, draw only points for moving
+ if (this.options.deepSelect && ['line', 'polyline', 'polygon'].indexOf(this.el.type) !== -1) {
+ this.selectPoints(value);
+ } else {
+ this.selectRect(value);
+ }
+
+ this.observe();
+ this.cleanup();
+
+};
+
+SelectHandler.prototype.selectPoints = function (value) {
+
+ this.pointSelection.isSelected = value;
+
+ // When set is already there we dont have to create one
+ if (this.pointSelection.set) {
+ return this;
+ }
+
+ // Create our set of elements
+ this.pointSelection.set = this.parent.set();
+ // draw the points and mark the element as selected
+ this.drawPoints();
+
+ return this;
+
+};
+
+// create the point-array which contains the 2 points of a line or simply the points-array of polyline/polygon
+SelectHandler.prototype.getPointArray = function () {
+ var bbox = this.el.bbox();
+
+ return this.el.array().valueOf().map(function (el) {
+ return [el[0] - bbox.x, el[1] - bbox.y];
+ });
+};
+
+// Draws a points
+SelectHandler.prototype.drawPoints = function () {
+
+ var _this = this, array = this.getPointArray();
+
+ // go through the array of points
+ for (var i = 0, len = array.length; i < len; ++i) {
+
+ var curriedEvent = (function (k) {
+ return function (ev) {
+ ev = ev || window.event;
+ ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;
+ ev.stopPropagation();
+
+ var x = ev.pageX || ev.touches[0].pageX;
+ var y = ev.pageY || ev.touches[0].pageY;
+ _this.el.fire('point', {x: x, y: y, i: k, event: ev});
+ };
+ })(i);
+
+ // add every point to the set
+ // add css-classes and a touchstart-event which fires our event for moving points
+ var point = this.drawPoint(array[i][0], array[i][1])
+ .addClass(this.options.classPoints)
+ .addClass(this.options.classPoints + '_point')
+ .on('touchstart', curriedEvent)
+ .on('mousedown', curriedEvent)
+ this.pointSelection.set.add(point);
+ }
+};
+
+// The function to draw single point
+SelectHandler.prototype.drawPoint = function (cx, cy) {
+ var pointType = this.options.pointType;
+
+ switch (pointType) {
+ case 'circle':
+ return this.drawCircle(cx, cy);
+ case 'rect':
+ return this.drawRect(cx, cy);
+ default:
+ if (typeof pointType === 'function') {
+ return pointType.call(this, cx, cy);
+ }
+
+ throw new Error('Unknown ' + pointType + ' point type!');
+ }
+};
+
+// The function to draw the circle point
+SelectHandler.prototype.drawCircle = function (cx, cy) {
+ return this.nested.circle(this.options.pointSize)
+ .center(cx, cy);
+};
+
+// The function to draw the rect point
+SelectHandler.prototype.drawRect = function (cx, cy) {
+ return this.nested.rect(this.options.pointSize, this.options.pointSize)
+ .center(cx, cy);
+};
+
+// every time a point is moved, we have to update the positions of our point
+SelectHandler.prototype.updatePointSelection = function () {
+ var array = this.getPointArray();
+
+ this.pointSelection.set.each(function (i) {
+ if (this.cx() === array[i][0] && this.cy() === array[i][1]) {
+ return;
+ }
+ this.center(array[i][0], array[i][1]);
+ });
+};
+
+SelectHandler.prototype.updateRectSelection = function () {
+ var _this = this, bbox = this.el.bbox();
+
+ this.rectSelection.set.get(0).attr({
+ width: bbox.width,
+ height: bbox.height
+ });
+
+ // set.get(1) is always in the upper left corner. no need to move it
+ if (this.options.points.length) {
+ this.options.points.map(function (point, index) {
+ var coords = _this.pointCoords(point, bbox);
+
+ _this.rectSelection.set.get(index + 1).center(coords.x, coords.y);
+ });
+ }
+
+ if (this.options.rotationPoint) {
+ var length = this.rectSelection.set.length();
+
+ this.rectSelection.set.get(length - 1).center(bbox.width / 2, 20);
+ }
+};
+
+SelectHandler.prototype.selectRect = function (value) {
+
+ var _this = this, bbox = this.el.bbox();
+
+ this.rectSelection.isSelected = value;
+
+ // when set is already p
+ this.rectSelection.set = this.rectSelection.set || this.parent.set();
+
+ // helperFunction to create a mouse-down function which triggers the event specified in `eventName`
+ function getMoseDownFunc(eventName) {
+ return function (ev) {
+ ev = ev || window.event;
+ ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;
+ ev.stopPropagation();
+
+ var x = ev.pageX || ev.touches[0].pageX;
+ var y = ev.pageY || ev.touches[0].pageY;
+ _this.el.fire(eventName, {x: x, y: y, event: ev});
+ };
+ }
+
+ // create the selection-rectangle and add the css-class
+ if (!this.rectSelection.set.get(0)) {
+ this.rectSelection.set.add(this.nested.rect(bbox.width, bbox.height).addClass(this.options.classRect));
+ }
+
+ // Draw Points at the edges, if enabled
+ if (this.options.points.length && this.rectSelection.set.length() < 2) {
+ var ename ="touchstart", mname = "mousedown";
+
+ this.options.points.map(function (point, index) {
+ var coords = _this.pointCoords(point, bbox);
+
+ var pointElement = _this.drawPoint(coords.x, coords.y)
+ .attr('class', _this.options.classPoints + '_' + point)
+ .on(mname, getMoseDownFunc(point))
+ .on(ename, getMoseDownFunc(point));
+ _this.rectSelection.set.add(pointElement);
+ });
+
+ this.rectSelection.set.each(function () {
+ this.addClass(_this.options.classPoints);
+ });
+ }
+
+ // draw rotationPint, if enabled
+ if (this.options.rotationPoint && ((this.options.points && !this.rectSelection.set.get(9)) || (!this.options.points && !this.rectSelection.set.get(1)))) {
+
+ var curriedEvent = function (ev) {
+ ev = ev || window.event;
+ ev.preventDefault ? ev.preventDefault() : ev.returnValue = false;
+ ev.stopPropagation();
+
+ var x = ev.pageX || ev.touches[0].pageX;
+ var y = ev.pageY || ev.touches[0].pageY;
+ _this.el.fire('rot', {x: x, y: y, event: ev});
+ };
+
+ var pointElement = this.drawPoint(bbox.width / 2, 20)
+ .attr('class', this.options.classPoints + '_rot')
+ .on("touchstart", curriedEvent)
+ .on("mousedown", curriedEvent);
+ this.rectSelection.set.add(pointElement);
+ }
+
+};
+
+SelectHandler.prototype.handler = function () {
+
+ var bbox = this.el.bbox();
+ this.nested.matrix(new SVG.Matrix(this.el).translate(bbox.x, bbox.y));
+
+ if (this.rectSelection.isSelected) {
+ this.updateRectSelection();
+ }
+
+ if (this.pointSelection.isSelected) {
+ this.updatePointSelection();
+ }
+
+};
+
+SelectHandler.prototype.observe = function () {
+ var _this = this;
+
+ if (MutationObserver) {
+ if (this.rectSelection.isSelected || this.pointSelection.isSelected) {
+ this.observerInst = this.observerInst || new MutationObserver(function () {
+ _this.handler();
+ });
+ this.observerInst.observe(this.el.node, {attributes: true});
+ } else {
+ try {
+ this.observerInst.disconnect();
+ delete this.observerInst;
+ } catch (e) {
+ }
+ }
+ } else {
+ this.el.off('DOMAttrModified.select');
+
+ if (this.rectSelection.isSelected || this.pointSelection.isSelected) {
+ this.el.on('DOMAttrModified.select', function () {
+ _this.handler();
+ });
+ }
+ }
+};
+
+SelectHandler.prototype.cleanup = function () {
+
+ //var _this = this;
+
+ if (!this.rectSelection.isSelected && this.rectSelection.set) {
+ // stop watching the element, remove the selection
+ this.rectSelection.set.each(function () {
+ this.remove();
+ });
+
+ this.rectSelection.set.clear();
+ delete this.rectSelection.set;
+ }
+
+ if (!this.pointSelection.isSelected && this.pointSelection.set) {
+ // Remove all points, clear the set, stop watching the element
+ this.pointSelection.set.each(function () {
+ this.remove();
+ });
+
+ this.pointSelection.set.clear();
+ delete this.pointSelection.set;
+ }
+
+ if (!this.pointSelection.isSelected && !this.rectSelection.isSelected) {
+ this.nested.remove();
+ delete this.nested;
+
+ }
+};
+
+
+SVG.extend(SVG.Element, {
+ // Select element with mouse
+ selectize: function (value, options) {
+
+ // Check the parameters and reassign if needed
+ if (typeof value === 'object') {
+ options = value;
+ value = true;
+ }
+
+ var selectHandler = this.remember('_selectHandler') || new SelectHandler(this);
+
+ selectHandler.init(value === undefined ? true : value, options || {});
+
+ return this;
+
+ }
+});
+
+SVG.Element.prototype.selectize.defaults = {
+ points: ['lt', 'rt', 'rb', 'lb', 't', 'r', 'b', 'l'], // which points to draw, default all
+ pointsExclude: [], // easier option if to exclude few than rewrite all
+ classRect: 'svg_select_boundingRect', // Css-class added to the rect
+ classPoints: 'svg_select_points', // Css-class added to the points
+ pointSize: 7, // size of point
+ rotationPoint: true, // If true, rotation point is drawn. Needed for rotation!
+ deepSelect: false, // If true, moving of single points is possible (only line, polyline, polyon)
+ pointType: 'circle' // Point type: circle or rect, default circle
+};
+}());
diff --git a/node_modules/svg.select.js/dist/svg.select.min.css b/node_modules/svg.select.js/dist/svg.select.min.css
new file mode 100644
index 0000000..d834513
--- /dev/null
+++ b/node_modules/svg.select.js/dist/svg.select.min.css
@@ -0,0 +1 @@
+.svg_select_points_lt{cursor:nw-resize}.svg_select_points_rt{cursor:ne-resize}.svg_select_points_rb{cursor:se-resize}.svg_select_points_lb{cursor:sw-resize}.svg_select_points_t{cursor:n-resize}.svg_select_points_r{cursor:e-resize}.svg_select_points_b{cursor:s-resize}.svg_select_points_l{cursor:w-resize}.svg_select_points_rot{stroke-width:1;stroke:#000;fill:#f9ffed}.svg_select_points_point{cursor:move}.svg_select_boundingRect{stroke-width:1;fill:gray;stroke-dasharray:10 10;stroke:#000;stroke-opacity:.8;fill-opacity:.1;pointer-events:none}
\ No newline at end of file
diff --git a/node_modules/svg.select.js/dist/svg.select.min.js b/node_modules/svg.select.js/dist/svg.select.min.js
new file mode 100644
index 0000000..c705bcb
--- /dev/null
+++ b/node_modules/svg.select.js/dist/svg.select.min.js
@@ -0,0 +1 @@
+/*! svg.select.js v3.0.1 MIT*/;!function(){"use strict";function i(t){(this.el=t).remember("_selectHandler",this),this.pointSelection={isSelected:!1},this.rectSelection={isSelected:!1},this.pointsList={lt:[0,0],rt:["width",0],rb:["width","height"],lb:[0,"height"],t:["width",0],r:["width","height"],b:["width","height"],l:[0,"height"]},this.pointCoord=function(t,e,i){var s="string"!=typeof t?t:e[t];return i?s/2:s},this.pointCoords=function(t,e){var i=this.pointsList[t];return{x:this.pointCoord(i[0],e,"t"===t||"b"===t),y:this.pointCoord(i[1],e,"r"===t||"l"===t)}}}i.prototype.init=function(t,e){var i=this.el.bbox();this.options={};var s=this.el.selectize.defaults.points;for(var n in this.el.selectize.defaults)this.options[n]=this.el.selectize.defaults[n],void 0!==e[n]&&(this.options[n]=e[n]);var o=["points","pointsExclude"];for(var n in o){var r=this.options[o[n]];"string"==typeof r?r=0= 0.8.0"
+ },
+ "devDependencies": {
+ "del": "^3.0.0",
+ "gulp": "^3.9.1",
+ "gulp-clean-css": "3.9.2",
+ "gulp-header": "^2.0.1",
+ "gulp-iife": "^0.3.0",
+ "gulp-rename": "^1.3.0",
+ "gulp-standard": "^10.1.2",
+ "gulp-trimlines": "^1.0.1",
+ "gulp-uglify": "^3.0.0",
+ "gulp-wrap-iife": "0.0.1",
+ "natives": "1.1.3"
+ },
+ "dependencies": {
+ "svg.js": "^2.6.5"
+ }
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..e07883c
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,124 @@
+{
+ "name": "git repo",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "dependencies": {
+ "apexcharts": "^3.46.0"
+ }
+ },
+ "node_modules/@yr/monotone-cubic-spline": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz",
+ "integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==",
+ "license": "MIT"
+ },
+ "node_modules/apexcharts": {
+ "version": "3.46.0",
+ "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.46.0.tgz",
+ "integrity": "sha512-ELAY6vj8JQD7QLktKasTzwm9Wt0qxqfQSo+3QWS7G7I774iK8HCkG1toGsqJH0mkK6PtYBtnSIe66uUcwoCw1w==",
+ "license": "MIT",
+ "dependencies": {
+ "@yr/monotone-cubic-spline": "^1.0.3",
+ "svg.draggable.js": "^2.2.2",
+ "svg.easing.js": "^2.0.0",
+ "svg.filter.js": "^2.0.2",
+ "svg.pathmorphing.js": "^0.1.3",
+ "svg.resize.js": "^1.4.3",
+ "svg.select.js": "^3.0.1"
+ }
+ },
+ "node_modules/svg.draggable.js": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
+ "integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==",
+ "license": "MIT",
+ "dependencies": {
+ "svg.js": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/svg.easing.js": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz",
+ "integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==",
+ "license": "MIT",
+ "dependencies": {
+ "svg.js": ">=2.3.x"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/svg.filter.js": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz",
+ "integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==",
+ "license": "MIT",
+ "dependencies": {
+ "svg.js": "^2.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/svg.js": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz",
+ "integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==",
+ "license": "MIT"
+ },
+ "node_modules/svg.pathmorphing.js": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz",
+ "integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==",
+ "license": "MIT",
+ "dependencies": {
+ "svg.js": "^2.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/svg.resize.js": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz",
+ "integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==",
+ "license": "MIT",
+ "dependencies": {
+ "svg.js": "^2.6.5",
+ "svg.select.js": "^2.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/svg.resize.js/node_modules/svg.select.js": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
+ "integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "svg.js": "^2.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/svg.select.js": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
+ "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
+ "license": "MIT",
+ "dependencies": {
+ "svg.js": "^2.6.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..7242223
--- /dev/null
+++ b/package.json
@@ -0,0 +1,5 @@
+{
+ "dependencies": {
+ "apexcharts": "^3.46.0"
+ }
+}