/* Minification failed. Returning unminified contents.
(193,101-108): run-time error JS1019: Can't have 'break' outside of loop: break a
(193,61-68): run-time error JS1019: Can't have 'break' outside of loop: break a
 */
/* Minification failed. Returning unminified contents.
(189,101-108): run-time error JS1019: Can't have 'break' outside of loop: break a
(189,61-68): run-time error JS1019: Can't have 'break' outside of loop: break a
 */
!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.StackBlur=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){function d(a,b,c,d){if("string"==typeof a)var a=document.getElementById(a);else if(!a instanceof HTMLImageElement)return;var e=a.naturalWidth,g=a.naturalHeight;if("string"==typeof b)var b=document.getElementById(b);else if(!b instanceof HTMLCanvasElement)return;b.style.width=e+"px",b.style.height=g+"px",b.width=e,b.height=g;var i=b.getContext("2d");i.clearRect(0,0,e,g),i.drawImage(a,0,0),isNaN(c)||1>c||(d?f(b,0,0,e,g,c):h(b,0,0,e,g,c))}function e(a,b,c,d,e){if("string"==typeof a)var a=document.getElementById(a);else if(!a instanceof HTMLCanvasElement)return;var f,g=a.getContext("2d");try{try{f=g.getImageData(b,c,d,e)}catch(h){throw new Error("unable to access local image data: "+h)}}catch(h){throw new Error("unable to access image data: "+h)}return f}function f(a,b,c,d,f,h){if(!(isNaN(h)||1>h)){h|=0;var i=e(a,b,c,d,f);i=g(i,b,c,d,f,h),a.getContext("2d").putImageData(i,b,c)}}function g(a,b,c,d,e,f){var g,h,i,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H=a.data,I=f+f+1,J=d-1,K=e-1,L=f+1,M=L*(L+1)/2,N=new j,O=N;for(i=1;I>i;i++)if(O=O.next=new j,i==L)var P=O;O.next=N;var Q=null,R=null;p=o=0;var S=k[f],T=l[f];for(h=0;e>h;h++){for(y=z=A=B=q=r=s=t=0,u=L*(C=H[o]),v=L*(D=H[o+1]),w=L*(E=H[o+2]),x=L*(F=H[o+3]),q+=M*C,r+=M*D,s+=M*E,t+=M*F,O=N,i=0;L>i;i++)O.r=C,O.g=D,O.b=E,O.a=F,O=O.next;for(i=1;L>i;i++)m=o+((i>J?J:i)<<2),q+=(O.r=C=H[m])*(G=L-i),r+=(O.g=D=H[m+1])*G,s+=(O.b=E=H[m+2])*G,t+=(O.a=F=H[m+3])*G,y+=C,z+=D,A+=E,B+=F,O=O.next;for(Q=N,R=P,g=0;d>g;g++)H[o+3]=F=t*S>>T,0!=F?(F=255/F,H[o]=(q*S>>T)*F,H[o+1]=(r*S>>T)*F,H[o+2]=(s*S>>T)*F):H[o]=H[o+1]=H[o+2]=0,q-=u,r-=v,s-=w,t-=x,u-=Q.r,v-=Q.g,w-=Q.b,x-=Q.a,m=p+((m=g+f+1)<J?m:J)<<2,y+=Q.r=H[m],z+=Q.g=H[m+1],A+=Q.b=H[m+2],B+=Q.a=H[m+3],q+=y,r+=z,s+=A,t+=B,Q=Q.next,u+=C=R.r,v+=D=R.g,w+=E=R.b,x+=F=R.a,y-=C,z-=D,A-=E,B-=F,R=R.next,o+=4;p+=d}for(g=0;d>g;g++){for(z=A=B=y=r=s=t=q=0,o=g<<2,u=L*(C=H[o]),v=L*(D=H[o+1]),w=L*(E=H[o+2]),x=L*(F=H[o+3]),q+=M*C,r+=M*D,s+=M*E,t+=M*F,O=N,i=0;L>i;i++)O.r=C,O.g=D,O.b=E,O.a=F,O=O.next;for(n=d,i=1;f>=i;i++)o=n+g<<2,q+=(O.r=C=H[o])*(G=L-i),r+=(O.g=D=H[o+1])*G,s+=(O.b=E=H[o+2])*G,t+=(O.a=F=H[o+3])*G,y+=C,z+=D,A+=E,B+=F,O=O.next,K>i&&(n+=d);for(o=g,Q=N,R=P,h=0;e>h;h++)m=o<<2,H[m+3]=F=t*S>>T,F>0?(F=255/F,H[m]=(q*S>>T)*F,H[m+1]=(r*S>>T)*F,H[m+2]=(s*S>>T)*F):H[m]=H[m+1]=H[m+2]=0,q-=u,r-=v,s-=w,t-=x,u-=Q.r,v-=Q.g,w-=Q.b,x-=Q.a,m=g+((m=h+L)<K?m:K)*d<<2,q+=y+=Q.r=H[m],r+=z+=Q.g=H[m+1],s+=A+=Q.b=H[m+2],t+=B+=Q.a=H[m+3],Q=Q.next,u+=C=R.r,v+=D=R.g,w+=E=R.b,x+=F=R.a,y-=C,z-=D,A-=E,B-=F,R=R.next,o+=d}return a}function h(a,b,c,d,f,g){if(!(isNaN(g)||1>g)){g|=0;var h=e(a,b,c,d,f);h=i(h,b,c,d,f,g),a.getContext("2d").putImageData(h,b,c)}}function i(a,b,c,d,e,f){var g,h,i,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D=a.data,E=f+f+1,F=d-1,G=e-1,H=f+1,I=H*(H+1)/2,J=new j,K=J;for(i=1;E>i;i++)if(K=K.next=new j,i==H)var L=K;K.next=J;var M=null,N=null;p=o=0;var O=k[f],P=l[f];for(h=0;e>h;h++){for(w=x=y=q=r=s=0,t=H*(z=D[o]),u=H*(A=D[o+1]),v=H*(B=D[o+2]),q+=I*z,r+=I*A,s+=I*B,K=J,i=0;H>i;i++)K.r=z,K.g=A,K.b=B,K=K.next;for(i=1;H>i;i++)m=o+((i>F?F:i)<<2),q+=(K.r=z=D[m])*(C=H-i),r+=(K.g=A=D[m+1])*C,s+=(K.b=B=D[m+2])*C,w+=z,x+=A,y+=B,K=K.next;for(M=J,N=L,g=0;d>g;g++)D[o]=q*O>>P,D[o+1]=r*O>>P,D[o+2]=s*O>>P,q-=t,r-=u,s-=v,t-=M.r,u-=M.g,v-=M.b,m=p+((m=g+f+1)<F?m:F)<<2,w+=M.r=D[m],x+=M.g=D[m+1],y+=M.b=D[m+2],q+=w,r+=x,s+=y,M=M.next,t+=z=N.r,u+=A=N.g,v+=B=N.b,w-=z,x-=A,y-=B,N=N.next,o+=4;p+=d}for(g=0;d>g;g++){for(x=y=w=r=s=q=0,o=g<<2,t=H*(z=D[o]),u=H*(A=D[o+1]),v=H*(B=D[o+2]),q+=I*z,r+=I*A,s+=I*B,K=J,i=0;H>i;i++)K.r=z,K.g=A,K.b=B,K=K.next;for(n=d,i=1;f>=i;i++)o=n+g<<2,q+=(K.r=z=D[o])*(C=H-i),r+=(K.g=A=D[o+1])*C,s+=(K.b=B=D[o+2])*C,w+=z,x+=A,y+=B,K=K.next,G>i&&(n+=d);for(o=g,M=J,N=L,h=0;e>h;h++)m=o<<2,D[m]=q*O>>P,D[m+1]=r*O>>P,D[m+2]=s*O>>P,q-=t,r-=u,s-=v,t-=M.r,u-=M.g,v-=M.b,m=g+((m=h+H)<G?m:G)*d<<2,q+=w+=M.r=D[m],r+=x+=M.g=D[m+1],s+=y+=M.b=D[m+2],M=M.next,t+=z=N.r,u+=A=N.g,v+=B=N.b,w-=z,x-=A,y-=B,N=N.next,o+=d}return a}function j(){this.r=0,this.g=0,this.b=0,this.a=0,this.next=null}var k=[512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259],l=[9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24];b.exports={image:d,canvasRGBA:f,canvasRGB:h,imageDataRGBA:g,imageDataRGB:i}},{}]},{},[1])(1)});;
/* MediaMatch v.2.0.2 - Testing css media queries in Javascript. Authors & copyright (c) 2013: WebLinc, David Knight. */

window.matchMedia||(window.matchMedia=function(c){var a=c.document,w=a.documentElement,l=[],t=0,x="",h={},G=/\s*(only|not)?\s*(screen|print|[a-z\-]+)\s*(and)?\s*/i,H=/^\s*\(\s*(-[a-z]+-)?(min-|max-)?([a-z\-]+)\s*(:?\s*([0-9]+(\.[0-9]+)?|portrait|landscape)(px|em|dppx|dpcm|rem|%|in|cm|mm|ex|pt|pc|\/([0-9]+(\.[0-9]+)?))?)?\s*\)\s*$/,y=0,A=function(b){var z=-1!==b.indexOf(",")&&b.split(",")||[b],e=z.length-1,j=e,g=null,d=null,c="",a=0,l=!1,m="",f="",g=null,d=0,f=null,k="",p="",q="",n="",r="",k=!1;if(""===
b)return!0;do{g=z[j-e];l=!1;if(d=g.match(G))c=d[0],a=d.index;if(!d||-1===g.substring(0,a).indexOf("(")&&(a||!d[3]&&c!==d.input))k=!1;else{f=g;l="not"===d[1];a||(m=d[2],f=g.substring(c.length));k=m===x||"all"===m||""===m;g=-1!==f.indexOf(" and ")&&f.split(" and ")||[f];d=g.length-1;if(k&&0<=d&&""!==f){do{f=g[d].match(H);if(!f||!h[f[3]]){k=!1;break}k=f[2];n=p=f[5];q=f[7];r=h[f[3]];q&&(n="px"===q?Number(p):"em"===q||"rem"===q?16*p:f[8]?(p/f[8]).toFixed(2):"dppx"===q?96*p:"dpcm"===q?0.3937*p:Number(p));
k="min-"===k&&n?r>=n:"max-"===k&&n?r<=n:n?r===n:!!r;if(!k)break}while(d--)}if(k)break}}while(e--);return l?!k:k},B=function(){var b=c.innerWidth||w.clientWidth,a=c.innerHeight||w.clientHeight,e=c.screen.width,j=c.screen.height,g=c.screen.colorDepth,d=c.devicePixelRatio;h.width=b;h.height=a;h["aspect-ratio"]=(b/a).toFixed(2);h["device-width"]=e;h["device-height"]=j;h["device-aspect-ratio"]=(e/j).toFixed(2);h.color=g;h["color-index"]=Math.pow(2,g);h.orientation=a>=b?"portrait":"landscape";h.resolution=
d&&96*d||c.screen.deviceXDPI||96;h["device-pixel-ratio"]=d||1},C=function(){clearTimeout(y);y=setTimeout(function(){var b=null,a=t-1,e=a,j=!1;if(0<=a){B();do if(b=l[e-a])if((j=A(b.mql.media))&&!b.mql.matches||!j&&b.mql.matches)if(b.mql.matches=j,b.listeners)for(var j=0,g=b.listeners.length;j<g;j++)b.listeners[j]&&b.listeners[j].call(c,b.mql);while(a--)}},10)},D=a.getElementsByTagName("head")[0],a=a.createElement("style"),E=null,u="screen print speech projection handheld tv braille embossed tty".split(" "),
m=0,I=u.length,s="#mediamatchjs { position: relative; z-index: 0; }",v="",F=c.addEventListener||(v="on")&&c.attachEvent;a.type="text/css";a.id="mediamatchjs";D.appendChild(a);for(E=c.getComputedStyle&&c.getComputedStyle(a)||a.currentStyle;m<I;m++)s+="@media "+u[m]+" { #mediamatchjs { position: relative; z-index: "+m+" } }";a.styleSheet?a.styleSheet.cssText=s:a.textContent=s;x=u[1*E.zIndex||0];D.removeChild(a);B();F(v+"resize",C);F(v+"orientationchange",C);return function(a){var c=t,e={matches:!1,
media:a,addListener:function(a){l[c].listeners||(l[c].listeners=[]);a&&l[c].listeners.push(a)},removeListener:function(a){var b=l[c],d=0,e=0;if(b)for(e=b.listeners.length;d<e;d++)b.listeners[d]===a&&b.listeners.splice(d,1)}};if(""===a)return e.matches=!0,e;e.matches=A(a);t=l.push({mql:e,listeners:null});return e}}(window));;
/*!
 * enquire.js v2.1.2 - Awesome Media Queries in JavaScript
 * Copyright (c) 2014 Nick Williams - http://wicky.nillia.ms/enquire.js
 * License: MIT (http://www.opensource.org/licenses/mit-license.php)
 */

!function(a,b,c){var d=window.matchMedia;"undefined"!=typeof module&&module.exports?module.exports=c(d):"function"==typeof define&&define.amd?define(function(){return b[a]=c(d)}):b[a]=c(d)}("enquire",this,function(a){"use strict";function b(a,b){var c,d=0,e=a.length;for(d;e>d&&(c=b(a[d],d),c!==!1);d++);}function c(a){return"[object Array]"===Object.prototype.toString.apply(a)}function d(a){return"function"==typeof a}function e(a){this.options=a,!a.deferSetup&&this.setup()}function f(b,c){this.query=b,this.isUnconditional=c,this.handlers=[],this.mql=a(b);var d=this;this.listener=function(a){d.mql=a,d.assess()},this.mql.addListener(this.listener)}function g(){if(!a)throw new Error("matchMedia not present, legacy browsers require a polyfill");this.queries={},this.browserIsIncapable=!a("only all").matches}return e.prototype={setup:function(){this.options.setup&&this.options.setup(),this.initialised=!0},on:function(){!this.initialised&&this.setup(),this.options.match&&this.options.match()},off:function(){this.options.unmatch&&this.options.unmatch()},destroy:function(){this.options.destroy?this.options.destroy():this.off()},equals:function(a){return this.options===a||this.options.match===a}},f.prototype={addHandler:function(a){var b=new e(a);this.handlers.push(b),this.matches()&&b.on()},removeHandler:function(a){var c=this.handlers;b(c,function(b,d){return b.equals(a)?(b.destroy(),!c.splice(d,1)):void 0})},matches:function(){return this.mql.matches||this.isUnconditional},clear:function(){b(this.handlers,function(a){a.destroy()}),this.mql.removeListener(this.listener),this.handlers.length=0},assess:function(){var a=this.matches()?"on":"off";b(this.handlers,function(b){b[a]()})}},g.prototype={register:function(a,e,g){var h=this.queries,i=g&&this.browserIsIncapable;return h[a]||(h[a]=new f(a,i)),d(e)&&(e={match:e}),c(e)||(e=[e]),b(e,function(b){d(b)&&(b={match:b}),h[a].addHandler(b)}),this},unregister:function(a,b){var c=this.queries[a];return c&&(b?c.removeHandler(b):(c.clear(),delete this.queries[a])),this}},new g});;
(function(global,factory){if(typeof exports==="object"&&exports){factory(exports)}else if(typeof define==="function"&&define.amd){define(["exports"],factory)}else{factory(global.Mustache={})}})(this,function(mustache){var Object_toString=Object.prototype.toString;var isArray=Array.isArray||function(object){return Object_toString.call(object)==="[object Array]"};function isFunction(object){return typeof object==="function"}function escapeRegExp(string){return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}var RegExp_test=RegExp.prototype.test;function testRegExp(re,string){return RegExp_test.call(re,string)}var nonSpaceRe=/\S/;function isWhitespace(string){return!testRegExp(nonSpaceRe,string)}var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;"};function escapeHtml(string){return String(string).replace(/[&<>"'\/]/g,function(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tags){if(typeof tags==="string")tags=tags.split(spaceRe,2);if(!isArray(tags)||tags.length!==2)throw new Error("Invalid tags: "+tags);openingTagRe=new RegExp(escapeRegExp(tags[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tags[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tags[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i<valueLength;++i){chr=value.charAt(i);if(isWhitespace(chr)){spaces.push(tokens.length)}else{nonSpace=true}tokens.push(["text",chr,start,start+1]);start+=1;if(chr==="\n")stripSpace()}}if(!scanner.scan(openingTagRe))break;hasTag=true;type=scanner.scan(tagRe)||"name";scanner.scan(whiteRe);if(type==="="){value=scanner.scanUntil(equalsRe);scanner.scan(equalsRe);scanner.scanUntil(closingTagRe)}else if(type==="{"){value=scanner.scanUntil(closingCurlyRe);scanner.scan(curlyRe);scanner.scanUntil(closingTagRe);type="&"}else{value=scanner.scanUntil(closingTagRe)}if(!scanner.scan(closingTagRe))throw new Error("Unclosed tag at "+scanner.pos);token=[type,value,start,scanner.pos];tokens.push(token);if(type==="#"||type==="^"){sections.push(token)}else if(type==="/"){openSection=sections.pop();if(!openSection)throw new Error('Unopened section "'+value+'" at '+start);if(openSection[1]!==value)throw new Error('Unclosed section "'+openSection[1]+'" at '+start)}else if(type==="name"||type==="{"||type==="&"){nonSpace=true}else if(type==="="){compileTags(value)}}openSection=sections.pop();if(openSection)throw new Error('Unclosed section "'+openSection[1]+'" at '+scanner.pos);return nestTokens(squashTokens(tokens))}function squashTokens(tokens){var squashedTokens=[];var token,lastToken;for(var i=0,numTokens=tokens.length;i<numTokens;++i){token=tokens[i];if(token){if(token[0]==="text"&&lastToken&&lastToken[0]==="text"){lastToken[1]+=token[1];lastToken[3]=token[3]}else{squashedTokens.push(token);lastToken=token}}}return squashedTokens}function nestTokens(tokens){var nestedTokens=[];var collector=nestedTokens;var sections=[];var token,section;for(var i=0,numTokens=tokens.length;i<numTokens;++i){token=tokens[i];switch(token[0]){case"#":case"^":collector.push(token);sections.push(token);collector=token[4]=[];break;case"/":section=sections.pop();section[5]=token[2];collector=sections.length>0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function(){return this.tail===""};Scanner.prototype.scan=function(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view==null?{}:view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function(view){return new Context(view,this)};Context.prototype.lookup=function(name){var cache=this.cache;var value;if(name in cache){value=cache[name]}else{var context=this,names,index;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index<names.length)value=value[names[index++]]}else if(typeof context.view=="object"){value=context.view[name]}if(value!=null)break;context=context.parent}cache[name]=value}if(isFunction(value))value=value.call(this.view);return value};function Writer(){this.cache={}}Writer.prototype.clearCache=function(){this.cache={}};Writer.prototype.parse=function(template,tags){var cache=this.cache;var tokens=cache[template];if(tokens==null)tokens=cache[template]=parseTemplate(template,tags);return tokens};Writer.prototype.render=function(template,view,partials){var tokens=this.parse(template);var context=view instanceof Context?view:new Context(view);return this.renderTokens(tokens,context,partials,template)};Writer.prototype.renderTokens=function(tokens,context,partials,originalTemplate){var buffer="";var token,symbol,value;for(var i=0,numTokens=tokens.length;i<numTokens;++i){value=undefined;token=tokens[i];symbol=token[0];if(symbol==="#")value=this._renderSection(token,context,partials,originalTemplate);else if(symbol==="^")value=this._renderInverted(token,context,partials,originalTemplate);else if(symbol===">")value=this._renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this._unescapedValue(token,context);else if(symbol==="name")value=this._escapedValue(token,context);else if(symbol==="text")value=this._rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype._renderSection=function(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;j<valueLength;++j){buffer+=this.renderTokens(token[4],context.push(value[j]),partials,originalTemplate)}}else if(typeof value==="object"||typeof value==="string"){buffer+=this.renderTokens(token[4],context.push(value),partials,originalTemplate)}else if(isFunction(value)){if(typeof originalTemplate!=="string")throw new Error("Cannot use higher-order sections without the original template");value=value.call(context.view,originalTemplate.slice(token[3],token[5]),subRender);if(value!=null)buffer+=value}else{buffer+=this.renderTokens(token[4],context,partials,originalTemplate)}return buffer};Writer.prototype._renderInverted=function(token,context,partials,originalTemplate){var value=context.lookup(token[1]);if(!value||isArray(value)&&value.length===0)return this.renderTokens(token[4],context,partials,originalTemplate)};Writer.prototype._renderPartial=function(token,context,partials){if(!partials)return;var value=isFunction(partials)?partials(token[1]):partials[token[1]];if(value!=null)return this.renderTokens(this.parse(value),context,partials,value)};Writer.prototype._unescapedValue=function(token,context){var value=context.lookup(token[1]);if(value!=null)return value};Writer.prototype._escapedValue=function(token,context){var value=context.lookup(token[1]);if(value!=null)return mustache.escape(value)};Writer.prototype._rawValue=function(token){return token[1]};mustache.name="mustache.js";mustache.version="1.1.0";mustache.tags=["{{","}}"];var defaultWriter=new Writer;mustache.clearCache=function(){return defaultWriter.clearCache()};mustache.parse=function(template,tags){return defaultWriter.parse(template,tags)};mustache.render=function(template,view,partials){return defaultWriter.render(template,view,partials)};mustache.to_html=function(template,view,partials,send){var result=mustache.render(template,view,partials);if(isFunction(send)){send(result)}else{return result}};mustache.escape=escapeHtml;mustache.Scanner=Scanner;mustache.Context=Context;mustache.Writer=Writer});;
/*
 AngularJS v1.5.5
 (c) 2010-2016 Google, Inc. http://angularjs.org
 License: MIT
*/
(function(v){'use strict';function O(a){return function(){var b=arguments[0],d;d="["+(a?a+":":"")+b+"] http://errors.angularjs.org/1.5.5/"+(a?a+"/":"")+b;for(b=1;b<arguments.length;b++){d=d+(1==b?"?":"&")+"p"+(b-1)+"=";var c=encodeURIComponent,e;e=arguments[b];e="function"==typeof e?e.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof e?"undefined":"string"!=typeof e?JSON.stringify(e):e;d+=c(e)}return Error(d)}}function ya(a){if(null==a||Va(a))return!1;if(K(a)||F(a)||B&&a instanceof B)return!0;
var b="length"in Object(a)&&a.length;return Q(b)&&(0<=b&&(b-1 in a||a instanceof Array)||"function"==typeof a.item)}function q(a,b,d){var c,e;if(a)if(E(a))for(c in a)"prototype"==c||"length"==c||"name"==c||a.hasOwnProperty&&!a.hasOwnProperty(c)||b.call(d,a[c],c,a);else if(K(a)||ya(a)){var f="object"!==typeof a;c=0;for(e=a.length;c<e;c++)(f||c in a)&&b.call(d,a[c],c,a)}else if(a.forEach&&a.forEach!==q)a.forEach(b,d,a);else if(oc(a))for(c in a)b.call(d,a[c],c,a);else if("function"===typeof a.hasOwnProperty)for(c in a)a.hasOwnProperty(c)&&
b.call(d,a[c],c,a);else for(c in a)ua.call(a,c)&&b.call(d,a[c],c,a);return a}function pc(a,b,d){for(var c=Object.keys(a).sort(),e=0;e<c.length;e++)b.call(d,a[c[e]],c[e]);return c}function qc(a){return function(b,d){a(d,b)}}function Xd(){return++nb}function Nb(a,b,d){for(var c=a.$$hashKey,e=0,f=b.length;e<f;++e){var g=b[e];if(G(g)||E(g))for(var h=Object.keys(g),k=0,l=h.length;k<l;k++){var n=h[k],m=g[n];d&&G(m)?fa(m)?a[n]=new Date(m.valueOf()):Wa(m)?a[n]=new RegExp(m):m.nodeName?a[n]=m.cloneNode(!0):
Ob(m)?a[n]=m.clone():(G(a[n])||(a[n]=K(m)?[]:{}),Nb(a[n],[m],!0)):a[n]=m}}c?a.$$hashKey=c:delete a.$$hashKey;return a}function R(a){return Nb(a,za.call(arguments,1),!1)}function Yd(a){return Nb(a,za.call(arguments,1),!0)}function X(a){return parseInt(a,10)}function Pb(a,b){return R(Object.create(a),b)}function C(){}function Xa(a){return a}function da(a){return function(){return a}}function rc(a){return E(a.toString)&&a.toString!==ma}function y(a){return"undefined"===typeof a}function x(a){return"undefined"!==
typeof a}function G(a){return null!==a&&"object"===typeof a}function oc(a){return null!==a&&"object"===typeof a&&!sc(a)}function F(a){return"string"===typeof a}function Q(a){return"number"===typeof a}function fa(a){return"[object Date]"===ma.call(a)}function E(a){return"function"===typeof a}function Wa(a){return"[object RegExp]"===ma.call(a)}function Va(a){return a&&a.window===a}function Ya(a){return a&&a.$evalAsync&&a.$watch}function Da(a){return"boolean"===typeof a}function Zd(a){return a&&Q(a.length)&&
$d.test(ma.call(a))}function Ob(a){return!(!a||!(a.nodeName||a.prop&&a.attr&&a.find))}function ae(a){var b={};a=a.split(",");var d;for(d=0;d<a.length;d++)b[a[d]]=!0;return b}function va(a){return P(a.nodeName||a[0]&&a[0].nodeName)}function Za(a,b){var d=a.indexOf(b);0<=d&&a.splice(d,1);return d}function qa(a,b){function d(a,b){var d=b.$$hashKey,e;if(K(a)){e=0;for(var f=a.length;e<f;e++)b.push(c(a[e]))}else if(oc(a))for(e in a)b[e]=c(a[e]);else if(a&&"function"===typeof a.hasOwnProperty)for(e in a)a.hasOwnProperty(e)&&
(b[e]=c(a[e]));else for(e in a)ua.call(a,e)&&(b[e]=c(a[e]));d?b.$$hashKey=d:delete b.$$hashKey;return b}function c(a){if(!G(a))return a;var b=f.indexOf(a);if(-1!==b)return g[b];if(Va(a)||Ya(a))throw Aa("cpws");var b=!1,c=e(a);void 0===c&&(c=K(a)?[]:Object.create(sc(a)),b=!0);f.push(a);g.push(c);return b?d(a,c):c}function e(a){switch(ma.call(a)){case "[object Int8Array]":case "[object Int16Array]":case "[object Int32Array]":case "[object Float32Array]":case "[object Float64Array]":case "[object Uint8Array]":case "[object Uint8ClampedArray]":case "[object Uint16Array]":case "[object Uint32Array]":return new a.constructor(c(a.buffer));
case "[object ArrayBuffer]":if(!a.slice){var b=new ArrayBuffer(a.byteLength);(new Uint8Array(b)).set(new Uint8Array(a));return b}return a.slice(0);case "[object Boolean]":case "[object Number]":case "[object String]":case "[object Date]":return new a.constructor(a.valueOf());case "[object RegExp]":return b=new RegExp(a.source,a.toString().match(/[^\/]*$/)[0]),b.lastIndex=a.lastIndex,b;case "[object Blob]":return new a.constructor([a],{type:a.type})}if(E(a.cloneNode))return a.cloneNode(!0)}var f=[],
g=[];if(b){if(Zd(b)||"[object ArrayBuffer]"===ma.call(b))throw Aa("cpta");if(a===b)throw Aa("cpi");K(b)?b.length=0:q(b,function(a,d){"$$hashKey"!==d&&delete b[d]});f.push(a);g.push(b);return d(a,b)}return c(a)}function ha(a,b){if(K(a)){b=b||[];for(var d=0,c=a.length;d<c;d++)b[d]=a[d]}else if(G(a))for(d in b=b||{},a)if("$"!==d.charAt(0)||"$"!==d.charAt(1))b[d]=a[d];return b||a}function pa(a,b){if(a===b)return!0;if(null===a||null===b)return!1;if(a!==a&&b!==b)return!0;var d=typeof a,c;if(d==typeof b&&
"object"==d)if(K(a)){if(!K(b))return!1;if((d=a.length)==b.length){for(c=0;c<d;c++)if(!pa(a[c],b[c]))return!1;return!0}}else{if(fa(a))return fa(b)?pa(a.getTime(),b.getTime()):!1;if(Wa(a))return Wa(b)?a.toString()==b.toString():!1;if(Ya(a)||Ya(b)||Va(a)||Va(b)||K(b)||fa(b)||Wa(b))return!1;d=T();for(c in a)if("$"!==c.charAt(0)&&!E(a[c])){if(!pa(a[c],b[c]))return!1;d[c]=!0}for(c in b)if(!(c in d)&&"$"!==c.charAt(0)&&x(b[c])&&!E(b[c]))return!1;return!0}return!1}function $a(a,b,d){return a.concat(za.call(b,
d))}function tc(a,b){var d=2<arguments.length?za.call(arguments,2):[];return!E(b)||b instanceof RegExp?b:d.length?function(){return arguments.length?b.apply(a,$a(d,arguments,0)):b.apply(a,d)}:function(){return arguments.length?b.apply(a,arguments):b.call(a)}}function be(a,b){var d=b;"string"===typeof a&&"$"===a.charAt(0)&&"$"===a.charAt(1)?d=void 0:Va(b)?d="$WINDOW":b&&v.document===b?d="$DOCUMENT":Ya(b)&&(d="$SCOPE");return d}function ab(a,b){if(!y(a))return Q(b)||(b=b?2:null),JSON.stringify(a,be,
b)}function uc(a){return F(a)?JSON.parse(a):a}function vc(a,b){a=a.replace(ce,"");var d=Date.parse("Jan 01, 1970 00:00:00 "+a)/6E4;return isNaN(d)?b:d}function Qb(a,b,d){d=d?-1:1;var c=a.getTimezoneOffset();b=vc(b,c);d*=b-c;a=new Date(a.getTime());a.setMinutes(a.getMinutes()+d);return a}function wa(a){a=B(a).clone();try{a.empty()}catch(b){}var d=B("<div>").append(a).html();try{return a[0].nodeType===Ma?P(d):d.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+P(b)})}catch(c){return P(d)}}
function wc(a){try{return decodeURIComponent(a)}catch(b){}}function xc(a){var b={};q((a||"").split("&"),function(a){var c,e,f;a&&(e=a=a.replace(/\+/g,"%20"),c=a.indexOf("="),-1!==c&&(e=a.substring(0,c),f=a.substring(c+1)),e=wc(e),x(e)&&(f=x(f)?wc(f):!0,ua.call(b,e)?K(b[e])?b[e].push(f):b[e]=[b[e],f]:b[e]=f))});return b}function Rb(a){var b=[];q(a,function(a,c){K(a)?q(a,function(a){b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))}):b.push(ja(c,!0)+(!0===a?"":"="+ja(a,!0)))});return b.length?b.join("&"):""}
function ob(a){return ja(a,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function ja(a,b){return encodeURIComponent(a).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,b?"%20":"+")}function de(a,b){var d,c,e=Na.length;for(c=0;c<e;++c)if(d=Na[c]+b,F(d=a.getAttribute(d)))return d;return null}function ee(a,b){var d,c,e={};q(Na,function(b){b+="app";!d&&a.hasAttribute&&a.hasAttribute(b)&&(d=a,c=a.getAttribute(b))});
q(Na,function(b){b+="app";var e;!d&&(e=a.querySelector("["+b.replace(":","\\:")+"]"))&&(d=e,c=e.getAttribute(b))});d&&(e.strictDi=null!==de(d,"strict-di"),b(d,c?[c]:[],e))}function yc(a,b,d){G(d)||(d={});d=R({strictDi:!1},d);var c=function(){a=B(a);if(a.injector()){var c=a[0]===v.document?"document":wa(a);throw Aa("btstrpd",c.replace(/</,"&lt;").replace(/>/,"&gt;"));}b=b||[];b.unshift(["$provide",function(b){b.value("$rootElement",a)}]);d.debugInfoEnabled&&b.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);
b.unshift("ng");c=bb(b,d.strictDi);c.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;v&&e.test(v.name)&&(d.debugInfoEnabled=!0,v.name=v.name.replace(e,""));if(v&&!f.test(v.name))return c();v.name=v.name.replace(f,"");ea.resumeBootstrap=function(a){q(a,function(a){b.push(a)});return c()};E(ea.resumeDeferredBootstrap)&&ea.resumeDeferredBootstrap()}function fe(){v.name=
"NG_ENABLE_DEBUG_INFO!"+v.name;v.location.reload()}function ge(a){a=ea.element(a).injector();if(!a)throw Aa("test");return a.get("$$testability")}function zc(a,b){b=b||"_";return a.replace(he,function(a,c){return(c?b:"")+a.toLowerCase()})}function ie(){var a;if(!Ac){var b=pb();(Z=y(b)?v.jQuery:b?v[b]:void 0)&&Z.fn.on?(B=Z,R(Z.fn,{scope:Oa.scope,isolateScope:Oa.isolateScope,controller:Oa.controller,injector:Oa.injector,inheritedData:Oa.inheritedData}),a=Z.cleanData,Z.cleanData=function(b){for(var c,
e=0,f;null!=(f=b[e]);e++)(c=Z._data(f,"events"))&&c.$destroy&&Z(f).triggerHandler("$destroy");a(b)}):B=U;ea.element=B;Ac=!0}}function qb(a,b,d){if(!a)throw Aa("areq",b||"?",d||"required");return a}function Pa(a,b,d){d&&K(a)&&(a=a[a.length-1]);qb(E(a),b,"not a function, got "+(a&&"object"===typeof a?a.constructor.name||"Object":typeof a));return a}function Qa(a,b){if("hasOwnProperty"===a)throw Aa("badname",b);}function Bc(a,b,d){if(!b)return a;b=b.split(".");for(var c,e=a,f=b.length,g=0;g<f;g++)c=
b[g],a&&(a=(e=a)[c]);return!d&&E(a)?tc(e,a):a}function rb(a){for(var b=a[0],d=a[a.length-1],c,e=1;b!==d&&(b=b.nextSibling);e++)if(c||a[e]!==b)c||(c=B(za.call(a,0,e))),c.push(b);return c||a}function T(){return Object.create(null)}function je(a){function b(a,b,c){return a[b]||(a[b]=c())}var d=O("$injector"),c=O("ng");a=b(a,"angular",Object);a.$$minErr=a.$$minErr||O;return b(a,"module",function(){var a={};return function(f,g,h){if("hasOwnProperty"===f)throw c("badname","module");g&&a.hasOwnProperty(f)&&
(a[f]=null);return b(a,f,function(){function a(b,d,e,f){f||(f=c);return function(){f[e||"push"]([b,d,arguments]);return M}}function b(a,d){return function(b,e){e&&E(e)&&(e.$$moduleName=f);c.push([a,d,arguments]);return M}}if(!g)throw d("nomod",f);var c=[],e=[],r=[],N=a("$injector","invoke","push",e),M={_invokeQueue:c,_configBlocks:e,_runBlocks:r,requires:g,name:f,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:a("$provide","value"),constant:a("$provide",
"constant","unshift"),decorator:b("$provide","decorator"),animation:b("$animateProvider","register"),filter:b("$filterProvider","register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider","directive"),component:b("$compileProvider","component"),config:N,run:function(a){r.push(a);return this}};h&&N(h);return M})}})}function ke(a){R(a,{bootstrap:yc,copy:qa,extend:R,merge:Yd,equals:pa,element:B,forEach:q,injector:bb,noop:C,bind:tc,toJson:ab,fromJson:uc,identity:Xa,isUndefined:y,
isDefined:x,isString:F,isFunction:E,isObject:G,isNumber:Q,isElement:Ob,isArray:K,version:le,isDate:fa,lowercase:P,uppercase:sb,callbacks:{counter:0},getTestability:ge,$$minErr:O,$$csp:Ea,reloadWithDebugInfo:fe});Sb=je(v);Sb("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:me});a.provider("$compile",Cc).directive({a:ne,input:Dc,textarea:Dc,form:oe,script:pe,select:qe,style:re,option:se,ngBind:te,ngBindHtml:ue,ngBindTemplate:ve,ngClass:we,ngClassEven:xe,ngClassOdd:ye,ngCloak:ze,ngController:Ae,
ngForm:Be,ngHide:Ce,ngIf:De,ngInclude:Ee,ngInit:Fe,ngNonBindable:Ge,ngPluralize:He,ngRepeat:Ie,ngShow:Je,ngStyle:Ke,ngSwitch:Le,ngSwitchWhen:Me,ngSwitchDefault:Ne,ngOptions:Oe,ngTransclude:Pe,ngModel:Qe,ngList:Re,ngChange:Se,pattern:Ec,ngPattern:Ec,required:Fc,ngRequired:Fc,minlength:Gc,ngMinlength:Gc,maxlength:Hc,ngMaxlength:Hc,ngValue:Te,ngModelOptions:Ue}).directive({ngInclude:Ve}).directive(tb).directive(Ic);a.provider({$anchorScroll:We,$animate:Xe,$animateCss:Ye,$$animateJs:Ze,$$animateQueue:$e,
$$AnimateRunner:af,$$animateAsyncRun:bf,$browser:cf,$cacheFactory:df,$controller:ef,$document:ff,$exceptionHandler:gf,$filter:Jc,$$forceReflow:hf,$interpolate:jf,$interval:kf,$http:lf,$httpParamSerializer:mf,$httpParamSerializerJQLike:nf,$httpBackend:of,$xhrFactory:pf,$location:qf,$log:rf,$parse:sf,$rootScope:tf,$q:uf,$$q:vf,$sce:wf,$sceDelegate:xf,$sniffer:yf,$templateCache:zf,$templateRequest:Af,$$testability:Bf,$timeout:Cf,$window:Df,$$rAF:Ef,$$jqLite:Ff,$$HashMap:Gf,$$cookieReader:Hf})}])}function cb(a){return a.replace(If,
function(a,d,c,e){return e?c.toUpperCase():c}).replace(Jf,"Moz$1")}function Kc(a){a=a.nodeType;return 1===a||!a||9===a}function Lc(a,b){var d,c,e=b.createDocumentFragment(),f=[];if(Tb.test(a)){d=d||e.appendChild(b.createElement("div"));c=(Kf.exec(a)||["",""])[1].toLowerCase();c=ia[c]||ia._default;d.innerHTML=c[1]+a.replace(Lf,"<$1></$2>")+c[2];for(c=c[0];c--;)d=d.lastChild;f=$a(f,d.childNodes);d=e.firstChild;d.textContent=""}else f.push(b.createTextNode(a));e.textContent="";e.innerHTML="";q(f,function(a){e.appendChild(a)});
return e}function Mc(a,b){var d=a.parentNode;d&&d.replaceChild(b,a);b.appendChild(a)}function U(a){if(a instanceof U)return a;var b;F(a)&&(a=V(a),b=!0);if(!(this instanceof U)){if(b&&"<"!=a.charAt(0))throw Ub("nosel");return new U(a)}if(b){b=v.document;var d;a=(d=Mf.exec(a))?[b.createElement(d[1])]:(d=Lc(a,b))?d.childNodes:[]}Nc(this,a)}function Vb(a){return a.cloneNode(!0)}function ub(a,b){b||db(a);if(a.querySelectorAll)for(var d=a.querySelectorAll("*"),c=0,e=d.length;c<e;c++)db(d[c])}function Oc(a,
b,d,c){if(x(c))throw Ub("offargs");var e=(c=vb(a))&&c.events,f=c&&c.handle;if(f)if(b){var g=function(b){var c=e[b];x(d)&&Za(c||[],d);x(d)&&c&&0<c.length||(a.removeEventListener(b,f,!1),delete e[b])};q(b.split(" "),function(a){g(a);wb[a]&&g(wb[a])})}else for(b in e)"$destroy"!==b&&a.removeEventListener(b,f,!1),delete e[b]}function db(a,b){var d=a.ng339,c=d&&eb[d];c&&(b?delete c.data[b]:(c.handle&&(c.events.$destroy&&c.handle({},"$destroy"),Oc(a)),delete eb[d],a.ng339=void 0))}function vb(a,b){var d=
a.ng339,d=d&&eb[d];b&&!d&&(a.ng339=d=++Nf,d=eb[d]={events:{},data:{},handle:void 0});return d}function Wb(a,b,d){if(Kc(a)){var c=x(d),e=!c&&b&&!G(b),f=!b;a=(a=vb(a,!e))&&a.data;if(c)a[b]=d;else{if(f)return a;if(e)return a&&a[b];R(a,b)}}}function xb(a,b){return a.getAttribute?-1<(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+b+" "):!1}function yb(a,b){b&&a.setAttribute&&q(b.split(" "),function(b){a.setAttribute("class",V((" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g,
" ").replace(" "+V(b)+" "," ")))})}function zb(a,b){if(b&&a.setAttribute){var d=(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");q(b.split(" "),function(a){a=V(a);-1===d.indexOf(" "+a+" ")&&(d+=a+" ")});a.setAttribute("class",V(d))}}function Nc(a,b){if(b)if(b.nodeType)a[a.length++]=b;else{var d=b.length;if("number"===typeof d&&b.window!==b){if(d)for(var c=0;c<d;c++)a[a.length++]=b[c]}else a[a.length++]=b}}function Pc(a,b){return Ab(a,"$"+(b||"ngController")+"Controller")}function Ab(a,
b,d){9==a.nodeType&&(a=a.documentElement);for(b=K(b)?b:[b];a;){for(var c=0,e=b.length;c<e;c++)if(x(d=B.data(a,b[c])))return d;a=a.parentNode||11===a.nodeType&&a.host}}function Qc(a){for(ub(a,!0);a.firstChild;)a.removeChild(a.firstChild)}function Bb(a,b){b||ub(a);var d=a.parentNode;d&&d.removeChild(a)}function Of(a,b){b=b||v;if("complete"===b.document.readyState)b.setTimeout(a);else B(b).on("load",a)}function Rc(a,b){var d=Cb[b.toLowerCase()];return d&&Sc[va(a)]&&d}function Pf(a,b){var d=function(c,
d){c.isDefaultPrevented=function(){return c.defaultPrevented};var f=b[d||c.type],g=f?f.length:0;if(g){if(y(c.immediatePropagationStopped)){var h=c.stopImmediatePropagation;c.stopImmediatePropagation=function(){c.immediatePropagationStopped=!0;c.stopPropagation&&c.stopPropagation();h&&h.call(c)}}c.isImmediatePropagationStopped=function(){return!0===c.immediatePropagationStopped};var k=f.specialHandlerWrapper||Qf;1<g&&(f=ha(f));for(var l=0;l<g;l++)c.isImmediatePropagationStopped()||k(a,c,f[l])}};d.elem=
a;return d}function Qf(a,b,d){d.call(a,b)}function Rf(a,b,d){var c=b.relatedTarget;c&&(c===a||Sf.call(a,c))||d.call(a,b)}function Ff(){this.$get=function(){return R(U,{hasClass:function(a,b){a.attr&&(a=a[0]);return xb(a,b)},addClass:function(a,b){a.attr&&(a=a[0]);return zb(a,b)},removeClass:function(a,b){a.attr&&(a=a[0]);return yb(a,b)}})}}function Fa(a,b){var d=a&&a.$$hashKey;if(d)return"function"===typeof d&&(d=a.$$hashKey()),d;d=typeof a;return d="function"==d||"object"==d&&null!==a?a.$$hashKey=
d+":"+(b||Xd)():d+":"+a}function Ra(a,b){if(b){var d=0;this.nextUid=function(){return++d}}q(a,this.put,this)}function Tc(a){a=Function.prototype.toString.call(a).replace(Tf,"");return a.match(Uf)||a.match(Vf)}function Wf(a){return(a=Tc(a))?"function("+(a[1]||"").replace(/[\s\r\n]+/," ")+")":"fn"}function bb(a,b){function d(a){return function(b,c){if(G(b))q(b,qc(a));else return a(b,c)}}function c(a,b){Qa(a,"service");if(E(b)||K(b))b=r.instantiate(b);if(!b.$get)throw Ga("pget",a);return m[a+"Provider"]=
b}function e(a,b){return function(){var c=w.invoke(b,this);if(y(c))throw Ga("undef",a);return c}}function f(a,b,d){return c(a,{$get:!1!==d?e(a,b):b})}function g(a){qb(y(a)||K(a),"modulesToLoad","not an array");var b=[],c;q(a,function(a){function d(a){var b,c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=r.get(e[0]);f[e[1]].apply(f,e[2])}}if(!n.get(a)){n.put(a,!0);try{F(a)?(c=Sb(a),b=b.concat(g(c.requires)).concat(c._runBlocks),d(c._invokeQueue),d(c._configBlocks)):E(a)?b.push(r.invoke(a)):K(a)?b.push(r.invoke(a)):
Pa(a,"module")}catch(e){throw K(a)&&(a=a[a.length-1]),e.message&&e.stack&&-1==e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),Ga("modulerr",a,e.stack||e.message||e);}}});return b}function h(a,c){function d(b,e){if(a.hasOwnProperty(b)){if(a[b]===k)throw Ga("cdep",b+" <- "+l.join(" <- "));return a[b]}try{return l.unshift(b),a[b]=k,a[b]=c(b,e)}catch(f){throw a[b]===k&&delete a[b],f;}finally{l.shift()}}function e(a,c,f){var g=[];a=bb.$$annotate(a,b,f);for(var h=0,k=a.length;h<k;h++){var l=a[h];
if("string"!==typeof l)throw Ga("itkn",l);g.push(c&&c.hasOwnProperty(l)?c[l]:d(l,f))}return g}return{invoke:function(a,b,c,d){"string"===typeof c&&(d=c,c=null);c=e(a,c,d);K(a)&&(a=a[a.length-1]);d=11>=Ca?!1:"function"===typeof a&&/^(?:class\s|constructor\()/.test(Function.prototype.toString.call(a));return d?(c.unshift(null),new (Function.prototype.bind.apply(a,c))):a.apply(b,c)},instantiate:function(a,b,c){var d=K(a)?a[a.length-1]:a;a=e(a,b,c);a.unshift(null);return new (Function.prototype.bind.apply(d,
a))},get:d,annotate:bb.$$annotate,has:function(b){return m.hasOwnProperty(b+"Provider")||a.hasOwnProperty(b)}}}b=!0===b;var k={},l=[],n=new Ra([],!0),m={$provide:{provider:d(c),factory:d(f),service:d(function(a,b){return f(a,["$injector",function(a){return a.instantiate(b)}])}),value:d(function(a,b){return f(a,da(b),!1)}),constant:d(function(a,b){Qa(a,"constant");m[a]=b;N[a]=b}),decorator:function(a,b){var c=r.get(a+"Provider"),d=c.$get;c.$get=function(){var a=w.invoke(d,c);return w.invoke(b,null,
{$delegate:a})}}}},r=m.$injector=h(m,function(a,b){ea.isString(b)&&l.push(b);throw Ga("unpr",l.join(" <- "));}),N={},M=h(N,function(a,b){var c=r.get(a+"Provider",b);return w.invoke(c.$get,c,void 0,a)}),w=M;m.$injectorProvider={$get:da(M)};var p=g(a),w=M.get("$injector");w.strictDi=b;q(p,function(a){a&&w.invoke(a)});return w}function We(){var a=!0;this.disableAutoScrolling=function(){a=!1};this.$get=["$window","$location","$rootScope",function(b,d,c){function e(a){var b=null;Array.prototype.some.call(a,
function(a){if("a"===va(a))return b=a,!0});return b}function f(a){if(a){a.scrollIntoView();var c;c=g.yOffset;E(c)?c=c():Ob(c)?(c=c[0],c="fixed"!==b.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):Q(c)||(c=0);c&&(a=a.getBoundingClientRect().top,b.scrollBy(0,a-c))}else b.scrollTo(0,0)}function g(a){a=F(a)?a:d.hash();var b;a?(b=h.getElementById(a))?f(b):(b=e(h.getElementsByName(a)))?f(b):"top"===a&&f(null):f(null)}var h=b.document;a&&c.$watch(function(){return d.hash()},function(a,b){a===
b&&""===a||Of(function(){c.$evalAsync(g)})});return g}]}function fb(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;K(a)&&(a=a.join(" "));K(b)&&(b=b.join(" "));return a+" "+b}function Xf(a){F(a)&&(a=a.split(" "));var b=T();q(a,function(a){a.length&&(b[a]=!0)});return b}function Ha(a){return G(a)?a:{}}function Yf(a,b,d,c){function e(a){try{a.apply(null,za.call(arguments,1))}finally{if(M--,0===M)for(;w.length;)try{w.pop()()}catch(b){d.error(b)}}}function f(){u=null;g();h()}function g(){p=I();
p=y(p)?null:p;pa(p,L)&&(p=L);L=p}function h(){if(t!==k.url()||H!==p)t=k.url(),H=p,q(J,function(a){a(k.url(),p)})}var k=this,l=a.location,n=a.history,m=a.setTimeout,r=a.clearTimeout,N={};k.isMock=!1;var M=0,w=[];k.$$completeOutstandingRequest=e;k.$$incOutstandingRequestCount=function(){M++};k.notifyWhenNoOutstandingRequests=function(a){0===M?a():w.push(a)};var p,H,t=l.href,z=b.find("base"),u=null,I=c.history?function(){try{return n.state}catch(a){}}:C;g();H=p;k.url=function(b,d,e){y(e)&&(e=null);l!==
a.location&&(l=a.location);n!==a.history&&(n=a.history);if(b){var f=H===e;if(t===b&&(!c.history||f))return k;var h=t&&Ia(t)===Ia(b);t=b;H=e;if(!c.history||h&&f){if(!h||u)u=b;d?l.replace(b):h?(d=l,e=b.indexOf("#"),e=-1===e?"":b.substr(e),d.hash=e):l.href=b;l.href!==b&&(u=b)}else n[d?"replaceState":"pushState"](e,"",b),g(),H=p;return k}return u||l.href.replace(/%27/g,"'")};k.state=function(){return p};var J=[],D=!1,L=null;k.onUrlChange=function(b){if(!D){if(c.history)B(a).on("popstate",f);B(a).on("hashchange",
f);D=!0}J.push(b);return b};k.$$applicationDestroyed=function(){B(a).off("hashchange popstate",f)};k.$$checkUrlChange=h;k.baseHref=function(){var a=z.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};k.defer=function(a,b){var c;M++;c=m(function(){delete N[c];e(a)},b||0);N[c]=!0;return c};k.defer.cancel=function(a){return N[a]?(delete N[a],r(a),e(C),!0):!1}}function cf(){this.$get=["$window","$log","$sniffer","$document",function(a,b,d,c){return new Yf(a,c,b,d)}]}function df(){this.$get=
function(){function a(a,c){function e(a){a!=m&&(r?r==a&&(r=a.n):r=a,f(a.n,a.p),f(a,m),m=a,m.n=null)}function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(a in b)throw O("$cacheFactory")("iid",a);var g=0,h=R({},c,{id:a}),k=T(),l=c&&c.capacity||Number.MAX_VALUE,n=T(),m=null,r=null;return b[a]={put:function(a,b){if(!y(b)){if(l<Number.MAX_VALUE){var c=n[a]||(n[a]={key:a});e(c)}a in k||g++;k[a]=b;g>l&&this.remove(r.key);return b}},get:function(a){if(l<Number.MAX_VALUE){var b=n[a];if(!b)return;e(b)}return k[a]},
remove:function(a){if(l<Number.MAX_VALUE){var b=n[a];if(!b)return;b==m&&(m=b.p);b==r&&(r=b.n);f(b.n,b.p);delete n[a]}a in k&&(delete k[a],g--)},removeAll:function(){k=T();g=0;n=T();m=r=null},destroy:function(){n=h=k=null;delete b[a]},info:function(){return R({},h,{size:g})}}}var b={};a.info=function(){var a={};q(b,function(b,e){a[e]=b.info()});return a};a.get=function(a){return b[a]};return a}}function zf(){this.$get=["$cacheFactory",function(a){return a("templates")}]}function Cc(a,b){function d(a,
b,c){var d=/^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/,e=T();q(a,function(a,f){if(a in n)e[f]=n[a];else{var g=a.match(d);if(!g)throw ga("iscp",b,f,a,c?"controller bindings definition":"isolate scope definition");e[f]={mode:g[1][0],collection:"*"===g[2],optional:"?"===g[3],attrName:g[4]||f};g[4]&&(n[a]=e[f])}});return e}function c(a){var b=a.charAt(0);if(!b||b!==P(b))throw ga("baddir",a);if(a!==a.trim())throw ga("baddir",a);}var e={},f=/^\s*directive\:\s*([\w\-]+)\s+(.*)$/,g=/(([\w\-]+)(?:\:([^;]+))?;?)/,
h=ae("ngSrc,ngSrcset,src,srcset"),k=/^(?:(\^\^?)?(\?)?(\^\^?)?)?/,l=/^(on[a-z]+|formaction)$/,n=T();this.directive=function M(b,d){Qa(b,"directive");F(b)?(c(b),qb(d,"directiveFactory"),e.hasOwnProperty(b)||(e[b]=[],a.factory(b+"Directive",["$injector","$exceptionHandler",function(a,c){var d=[];q(e[b],function(e,f){try{var g=a.invoke(e);E(g)?g={compile:da(g)}:!g.compile&&g.link&&(g.compile=da(g.link));g.priority=g.priority||0;g.index=f;g.name=g.name||b;g.require=g.require||g.controller&&g.name;g.restrict=
g.restrict||"EA";g.$$moduleName=e.$$moduleName;d.push(g)}catch(h){c(h)}});return d}])),e[b].push(d)):q(b,qc(M));return this};this.component=function(a,b){function c(a){function e(b){return E(b)||K(b)?function(c,d){return a.invoke(b,this,{$element:c,$attrs:d})}:b}var f=b.template||b.templateUrl?b.template:"",g={controller:d,controllerAs:Uc(b.controller)||b.controllerAs||"$ctrl",template:e(f),templateUrl:e(b.templateUrl),transclude:b.transclude,scope:{},bindToController:b.bindings||{},restrict:"E",
require:b.require};q(b,function(a,b){"$"===b.charAt(0)&&(g[b]=a)});return g}var d=b.controller||function(){};q(b,function(a,b){"$"===b.charAt(0)&&(c[b]=a,E(d)&&(d[b]=a))});c.$inject=["$injector"];return this.directive(a,c)};this.aHrefSanitizationWhitelist=function(a){return x(a)?(b.aHrefSanitizationWhitelist(a),this):b.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(a){return x(a)?(b.imgSrcSanitizationWhitelist(a),this):b.imgSrcSanitizationWhitelist()};var m=!0;this.debugInfoEnabled=
function(a){return x(a)?(m=a,this):m};var r=10;this.onChangesTtl=function(a){return arguments.length?(r=a,this):r};this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$sce","$animate","$$sanitizeUri",function(a,b,c,n,t,z,u,I,J,D){function L(){try{if(!--qa)throw Z=void 0,ga("infchng",r);u.$apply(function(){for(var a=0,b=Z.length;a<b;++a)Z[a]();Z=void 0})}finally{qa++}}function S(a,b){if(b){var c=Object.keys(b),d,e,f;d=0;for(e=c.length;d<
e;d++)f=c[d],this[f]=b[f]}else this.$attr={};this.$$element=a}function $(a,b,c){na.innerHTML="<span "+b+">";b=na.firstChild.attributes;var d=b[0];b.removeNamedItem(d.name);d.value=c;a.attributes.setNamedItem(d)}function A(a,b){try{a.addClass(b)}catch(c){}}function ba(a,b,c,d,e){a instanceof B||(a=B(a));for(var f=/\S+/,g=0,h=a.length;g<h;g++){var k=a[g];k.nodeType===Ma&&k.nodeValue.match(f)&&Mc(k,a[g]=v.document.createElement("span"))}var l=s(a,b,a,c,d,e);ba.$$addScopeClass(a);var m=null;return function(b,
c,d){qb(b,"scope");e&&e.needsNewScope&&(b=b.$parent.$new());d=d||{};var f=d.parentBoundTranscludeFn,g=d.transcludeControllers;d=d.futureParentElement;f&&f.$$boundTransclude&&(f=f.$$boundTransclude);m||(m=(d=d&&d[0])?"foreignobject"!==va(d)&&ma.call(d).match(/SVG/)?"svg":"html":"html");d="html"!==m?B(ca(m,B("<div>").append(a).html())):c?Oa.clone.call(a):a;if(g)for(var h in g)d.data("$"+h+"Controller",g[h].instance);ba.$$addScopeInfo(d,b);c&&c(d,b);l&&l(b,d,d,f);return d}}function s(a,b,c,d,e,f){function g(a,
c,d,e){var f,k,l,m,n,t,p;if(r)for(p=Array(c.length),m=0;m<h.length;m+=3)f=h[m],p[f]=c[f];else p=c;m=0;for(n=h.length;m<n;)k=p[h[m++]],c=h[m++],f=h[m++],c?(c.scope?(l=a.$new(),ba.$$addScopeInfo(B(k),l)):l=a,t=c.transcludeOnThisElement?ka(a,c.transclude,e):!c.templateOnThisElement&&e?e:!e&&b?ka(a,b):null,c(f,l,k,d,t)):f&&f(a,k.childNodes,void 0,e)}for(var h=[],k,l,m,n,r,t=0;t<a.length;t++){k=new S;l=x(a[t],[],k,0===t?d:void 0,e);(f=l.length?Ba(l,a[t],k,b,c,null,[],[],f):null)&&f.scope&&ba.$$addScopeClass(k.$$element);
k=f&&f.terminal||!(m=a[t].childNodes)||!m.length?null:s(m,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||k)h.push(t,f,k),n=!0,r=r||f;f=null}return n?g:null}function ka(a,b,c){function d(e,f,g,h,k){e||(e=a.$new(!1,k),e.$$transcluded=!0);return b(e,f,{parentBoundTranscludeFn:c,transcludeControllers:g,futureParentElement:h})}var e=d.$$slots=T(),f;for(f in b.$$slots)e[f]=b.$$slots[f]?ka(a,b.$$slots[f],c):null;return d}function x(a,b,c,d,e){var h=c.$attr,k;switch(a.nodeType){case 1:la(b,
xa(va(a)),"E",d,e);for(var l,m,n,t=a.attributes,r=0,p=t&&t.length;r<p;r++){var I=!1,D=!1;l=t[r];k=l.name;m=V(l.value);l=xa(k);if(n=ya.test(l))k=k.replace(Vc,"").substr(8).replace(/_(.)/g,function(a,b){return b.toUpperCase()});(l=l.match(Aa))&&Q(l[1])&&(I=k,D=k.substr(0,k.length-5)+"end",k=k.substr(0,k.length-6));l=xa(k.toLowerCase());h[l]=k;if(n||!c.hasOwnProperty(l))c[l]=m,Rc(a,l)&&(c[l]=!0);fa(a,b,m,l,n);la(b,l,"A",d,e,I,D)}a=a.className;G(a)&&(a=a.animVal);if(F(a)&&""!==a)for(;k=g.exec(a);)l=xa(k[2]),
la(b,l,"C",d,e)&&(c[l]=V(k[3])),a=a.substr(k.index+k[0].length);break;case Ma:if(11===Ca)for(;a.parentNode&&a.nextSibling&&a.nextSibling.nodeType===Ma;)a.nodeValue+=a.nextSibling.nodeValue,a.parentNode.removeChild(a.nextSibling);X(b,a.nodeValue);break;case 8:try{if(k=f.exec(a.nodeValue))l=xa(k[1]),la(b,l,"M",d,e)&&(c[l]=V(k[2]))}catch(J){}}b.sort(Y);return b}function Wc(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ga("uterdir",b,c);1==a.nodeType&&(a.hasAttribute(b)&&
e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return B(d)}function Xc(a,b,c){return function(d,e,f,g,h){e=Wc(e[0],b,c);return a(d,e,f,g,h)}}function Yb(a,b,c,d,e,f){var g;return a?ba(b,c,d,e,f):function(){g||(g=ba(b,c,d,e,f),b=c=f=null);return g.apply(this,arguments)}}function Ba(a,b,d,e,f,g,h,k,l){function m(a,b,c,d){if(a){c&&(a=Xc(a,c,d));a.require=A.require;a.directiveName=M;if(D===A||A.$$isolateScope)a=ha(a,{isolateScope:!0});h.push(a)}if(b){c&&(b=Xc(b,c,d));
b.require=A.require;b.directiveName=M;if(D===A||A.$$isolateScope)b=ha(b,{isolateScope:!0});k.push(b)}}function n(a,c,e,f,g){function l(a,b,c,d){var e;Ya(a)||(d=c,c=b,b=a,a=void 0);H&&(e=u);c||(c=H?z.parent():z);if(d){var f=g.$$slots[d];if(f)return f(a,b,e,c,$);if(y(f))throw ga("noslot",d,wa(z));}else return g(a,b,e,c,$)}var m,t,p,A,w,u,L,z;b===e?(f=d,z=d.$$element):(z=B(e),f=new S(z,d));w=c;D?A=c.$new(!0):r&&(w=c.$parent);g&&(L=l,L.$$boundTransclude=g,L.isSlotFilled=function(a){return!!g.$$slots[a]});
I&&(u=O(z,f,L,I,A,c,D));D&&(ba.$$addScopeInfo(z,A,!0,!(J&&(J===D||J===D.$$originalDirective))),ba.$$addScopeClass(z,!0),A.$$isolateBindings=D.$$isolateBindings,t=ia(c,f,A,A.$$isolateBindings,D),t.removeWatches&&A.$on("$destroy",t.removeWatches));for(m in u){t=I[m];p=u[m];var Xb=t.$$bindings.bindToController;p.bindingInfo=p.identifier&&Xb?ia(w,f,p.instance,Xb,t):{};var M=p();M!==p.instance&&(p.instance=M,z.data("$"+t.name+"Controller",M),p.bindingInfo.removeWatches&&p.bindingInfo.removeWatches(),p.bindingInfo=
ia(w,f,p.instance,Xb,t))}q(I,function(a,b){var c=a.require;a.bindToController&&!K(c)&&G(c)&&R(u[b].instance,gb(b,c,z,u))});q(u,function(a){var b=a.instance;E(b.$onChanges)&&b.$onChanges(a.bindingInfo.initialChanges);E(b.$onInit)&&b.$onInit();E(b.$onDestroy)&&w.$on("$destroy",function(){b.$onDestroy()})});m=0;for(t=h.length;m<t;m++)p=h[m],ja(p,p.isolateScope?A:c,z,f,p.require&&gb(p.directiveName,p.require,z,u),L);var $=c;D&&(D.template||null===D.templateUrl)&&($=A);a&&a($,e.childNodes,void 0,g);for(m=
k.length-1;0<=m;m--)p=k[m],ja(p,p.isolateScope?A:c,z,f,p.require&&gb(p.directiveName,p.require,z,u),L);q(u,function(a){a=a.instance;E(a.$postLink)&&a.$postLink()})}l=l||{};for(var t=-Number.MAX_VALUE,r=l.newScopeDirective,I=l.controllerDirectives,D=l.newIsolateScopeDirective,J=l.templateDirective,w=l.nonTlbTranscludeDirective,u=!1,L=!1,H=l.hasElementTranscludeDirective,z=d.$$element=B(b),A,M,$,s=e,Sa,ka=!1,C=!1,v,F=0,Ba=a.length;F<Ba;F++){A=a[F];var P=A.$$start,Q=A.$$end;P&&(z=Wc(b,P,Q));$=void 0;
if(t>A.priority)break;if(v=A.scope)A.templateUrl||(G(v)?(W("new/isolated scope",D||r,A,z),D=A):W("new/isolated scope",D,A,z)),r=r||A;M=A.name;if(!ka&&(A.replace&&(A.templateUrl||A.template)||A.transclude&&!A.$$tlb)){for(v=F+1;ka=a[v++];)if(ka.transclude&&!ka.$$tlb||ka.replace&&(ka.templateUrl||ka.template)){C=!0;break}ka=!0}!A.templateUrl&&A.controller&&(v=A.controller,I=I||T(),W("'"+M+"' controller",I[M],A,z),I[M]=A);if(v=A.transclude)if(u=!0,A.$$tlb||(W("transclusion",w,A,z),w=A),"element"==v)H=
!0,t=A.priority,$=z,z=d.$$element=B(ba.$$createComment(M,d[M])),b=z[0],da(f,za.call($,0),b),$[0].$$parentNode=$[0].parentNode,s=Yb(C,$,e,t,g&&g.name,{nonTlbTranscludeDirective:w});else{var la=T();$=B(Vb(b)).contents();if(G(v)){$=[];var Y=T(),X=T();q(v,function(a,b){var c="?"===a.charAt(0);a=c?a.substring(1):a;Y[a]=b;la[b]=null;X[b]=c});q(z.contents(),function(a){var b=Y[xa(va(a))];b?(X[b]=!0,la[b]=la[b]||[],la[b].push(a)):$.push(a)});q(X,function(a,b){if(!a)throw ga("reqslot",b);});for(var Z in la)la[Z]&&
(la[Z]=Yb(C,la[Z],e))}z.empty();s=Yb(C,$,e,void 0,void 0,{needsNewScope:A.$$isolateScope||A.$$newScope});s.$$slots=la}if(A.template)if(L=!0,W("template",J,A,z),J=A,v=E(A.template)?A.template(z,d):A.template,v=ta(v),A.replace){g=A;$=Tb.test(v)?Yc(ca(A.templateNamespace,V(v))):[];b=$[0];if(1!=$.length||1!==b.nodeType)throw ga("tplrt",M,"");da(f,z,b);Ba={$attr:{}};v=x(b,[],Ba);var ea=a.splice(F+1,a.length-(F+1));(D||r)&&Zc(v,D,r);a=a.concat(v).concat(ea);U(d,Ba);Ba=a.length}else z.html(v);if(A.templateUrl)L=
!0,W("template",J,A,z),J=A,A.replace&&(g=A),n=aa(a.splice(F,a.length-F),z,d,f,u&&s,h,k,{controllerDirectives:I,newScopeDirective:r!==A&&r,newIsolateScopeDirective:D,templateDirective:J,nonTlbTranscludeDirective:w}),Ba=a.length;else if(A.compile)try{Sa=A.compile(z,d,s),E(Sa)?m(null,Sa,P,Q):Sa&&m(Sa.pre,Sa.post,P,Q)}catch(fa){c(fa,wa(z))}A.terminal&&(n.terminal=!0,t=Math.max(t,A.priority))}n.scope=r&&!0===r.scope;n.transcludeOnThisElement=u;n.templateOnThisElement=L;n.transclude=s;l.hasElementTranscludeDirective=
H;return n}function gb(a,b,c,d){var e;if(F(b)){var f=b.match(k);b=b.substring(f[0].length);var g=f[1]||f[3],f="?"===f[2];"^^"===g?c=c.parent():e=(e=d&&d[b])&&e.instance;if(!e){var h="$"+b+"Controller";e=g?c.inheritedData(h):c.data(h)}if(!e&&!f)throw ga("ctreq",b,a);}else if(K(b))for(e=[],g=0,f=b.length;g<f;g++)e[g]=gb(a,b[g],c,d);else G(b)&&(e={},q(b,function(b,f){e[f]=gb(a,b,c,d)}));return e||null}function O(a,b,c,d,e,f,g){var h=T(),k;for(k in d){var l=d[k],m={$scope:l===g||l.$$isolateScope?e:f,
$element:a,$attrs:b,$transclude:c},n=l.controller;"@"==n&&(n=b[l.name]);m=z(n,m,!0,l.controllerAs);h[l.name]=m;a.data("$"+l.name+"Controller",m.instance)}return h}function Zc(a,b,c){for(var d=0,e=a.length;d<e;d++)a[d]=Pb(a[d],{$$isolateScope:b,$$newScope:c})}function la(b,f,g,h,k,l,m){if(f===k)return null;k=null;if(e.hasOwnProperty(f)){var n;f=a.get(f+"Directive");for(var t=0,r=f.length;t<r;t++)try{if(n=f[t],(y(h)||h>n.priority)&&-1!=n.restrict.indexOf(g)){l&&(n=Pb(n,{$$start:l,$$end:m}));if(!n.$$bindings){var I=
n,D=n,A=n.name,J={isolateScope:null,bindToController:null};G(D.scope)&&(!0===D.bindToController?(J.bindToController=d(D.scope,A,!0),J.isolateScope={}):J.isolateScope=d(D.scope,A,!1));G(D.bindToController)&&(J.bindToController=d(D.bindToController,A,!0));if(G(J.bindToController)){var w=D.controller,z=D.controllerAs;if(!w)throw ga("noctrl",A);if(!Uc(w,z))throw ga("noident",A);}var u=I.$$bindings=J;G(u.isolateScope)&&(n.$$isolateBindings=u.isolateScope)}b.push(n);k=n}}catch(L){c(L)}}return k}function Q(b){if(e.hasOwnProperty(b))for(var c=
a.get(b+"Directive"),d=0,f=c.length;d<f;d++)if(b=c[d],b.multiElement)return!0;return!1}function U(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;q(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});q(b,function(b,f){"class"==f?(A(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function aa(a,b,c,d,e,f,
g,h){var k=[],l,m,t=b[0],p=a.shift(),r=Pb(p,{templateUrl:null,transclude:null,replace:null,$$originalDirective:p}),I=E(p.templateUrl)?p.templateUrl(b,c):p.templateUrl,D=p.templateNamespace;b.empty();n(I).then(function(n){var J,w;n=ta(n);if(p.replace){n=Tb.test(n)?Yc(ca(D,V(n))):[];J=n[0];if(1!=n.length||1!==J.nodeType)throw ga("tplrt",p.name,I);n={$attr:{}};da(d,b,J);var z=x(J,[],n);G(p.scope)&&Zc(z,!0);a=z.concat(a);U(c,n)}else J=t,b.html(n);a.unshift(r);l=Ba(a,J,c,e,b,p,f,g,h);q(d,function(a,c){a==
J&&(d[c]=b[0])});for(m=s(b[0].childNodes,e);k.length;){n=k.shift();w=k.shift();var u=k.shift(),L=k.shift(),z=b[0];if(!n.$$destroyed){if(w!==t){var S=w.className;h.hasElementTranscludeDirective&&p.replace||(z=Vb(J));da(u,B(w),z);A(B(z),S)}w=l.transcludeOnThisElement?ka(n,l.transclude,L):L;l(m,n,z,d,w)}}k=null});return function(a,b,c,d,e){a=e;b.$$destroyed||(k?k.push(b,c,d,a):(l.transcludeOnThisElement&&(a=ka(b,l.transclude,e)),l(m,b,c,d,a)))}}function Y(a,b){var c=b.priority-a.priority;return 0!==
c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function W(a,b,c,d){function e(a){return a?" (module: "+a+")":""}if(b)throw ga("multidir",b.name,e(b.$$moduleName),c.name,e(c.$$moduleName),a,wa(d));}function X(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=a.parent();var b=!!a.length;b&&ba.$$addBindingClass(a);return function(a,c){var e=c.parent();b||ba.$$addBindingClass(e);ba.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function ca(a,b){a=
P(a||"html");switch(a){case "svg":case "math":var c=v.document.createElement("div");c.innerHTML="<"+a+">"+b+"</"+a+">";return c.childNodes[0].childNodes;default:return b}}function ea(a,b){if("srcdoc"==b)return I.HTML;var c=va(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return I.RESOURCE_URL}function fa(a,c,d,e,f){var g=ea(a,e);f=h[e]||f;var k=b(d,!0,g,f);if(k){if("multiple"===e&&"select"===va(a))throw ga("selmulti",wa(a));c.push({priority:100,compile:function(){return{pre:function(a,
c,h){c=h.$$observers||(h.$$observers=T());if(l.test(e))throw ga("nodomevents");var m=h[e];m!==d&&(k=m&&b(m,!0,g,f),d=m);k&&(h[e]=k(a),(c[e]||(c[e]=[])).$$inter=!0,(h.$$observers&&h.$$observers[e].$$scope||a).$watch(k,function(a,b){"class"===e&&a!=b?h.$updateClass(a,b):h.$set(e,a)}))}}}})}}function da(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g<h;g++)if(a[g]==d){a[g++]=c;h=g+e-1;for(var k=a.length;g<k;g++,h++)h<k?a[g]=a[h]:delete a[g];a.length-=e-1;a.context===d&&(a.context=
c);break}f&&f.replaceChild(c,d);a=v.document.createDocumentFragment();for(g=0;g<e;g++)a.appendChild(b[g]);B.hasData(d)&&(B.data(c,B.data(d)),B(d).off("$destroy"));B.cleanData(a.querySelectorAll("*"));for(g=1;g<e;g++)delete b[g];b[0]=c;b.length=1}function ha(a,b){return R(function(){return a.apply(null,arguments)},a,b)}function ja(a,b,d,e,f,g){try{a(b,d,e,f,g)}catch(h){c(h,wa(d))}}function ia(a,c,d,e,f){function g(b,c,e){E(d.$onChanges)&&c!==e&&(Z||(a.$$postDigest(L),Z=[]),m||(m={},Z.push(h)),m[b]&&
(e=m[b].previousValue),m[b]=new Db(e,c))}function h(){d.$onChanges(m);m=void 0}var k=[],l={},m;q(e,function(e,h){var m=e.attrName,n=e.optional,p,r,I,D;switch(e.mode){case "@":n||ua.call(c,m)||(d[h]=c[m]=void 0);c.$observe(m,function(a){if(F(a)||Da(a))g(h,a,d[h]),d[h]=a});c.$$observers[m].$$scope=a;p=c[m];F(p)?d[h]=b(p)(a):Da(p)&&(d[h]=p);l[h]=new Db(Zb,d[h]);break;case "=":if(!ua.call(c,m)){if(n)break;c[m]=void 0}if(n&&!c[m])break;r=t(c[m]);D=r.literal?pa:function(a,b){return a===b||a!==a&&b!==b};
I=r.assign||function(){p=d[h]=r(a);throw ga("nonassign",c[m],m,f.name);};p=d[h]=r(a);n=function(b){D(b,d[h])||(D(b,p)?I(a,b=d[h]):d[h]=b);return p=b};n.$stateful=!0;n=e.collection?a.$watchCollection(c[m],n):a.$watch(t(c[m],n),null,r.literal);k.push(n);break;case "<":if(!ua.call(c,m)){if(n)break;c[m]=void 0}if(n&&!c[m])break;r=t(c[m]);d[h]=r(a);l[h]=new Db(Zb,d[h]);n=a.$watch(r,function(a,b){a===b&&(b=d[h]);g(h,a,b);d[h]=a},r.literal);k.push(n);break;case "&":r=c.hasOwnProperty(m)?t(c[m]):C;if(r===
C&&n)break;d[h]=function(b){return r(a,b)}}});return{initialChanges:l,removeWatches:k.length&&function(){for(var a=0,b=k.length;a<b;++a)k[a]()}}}var oa=/^\w/,na=v.document.createElement("div"),qa=r,Z;S.prototype={$normalize:xa,$addClass:function(a){a&&0<a.length&&J.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&J.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=$c(a,b);c&&c.length&&J.addClass(this.$$element,c);(c=$c(b,a))&&c.length&&J.removeClass(this.$$element,
c)},$set:function(a,b,d,e){var f=Rc(this.$$element[0],a),g=ad[a],h=a;f?(this.$$element.prop(a,b),e=f):g&&(this[g]=b,h=g);this[a]=b;e?this.$attr[a]=e:(e=this.$attr[a])||(this.$attr[a]=e=zc(a,"-"));f=va(this.$$element);if("a"===f&&("href"===a||"xlinkHref"===a)||"img"===f&&"src"===a)this[a]=b=D(b,"src"===a);else if("img"===f&&"srcset"===a){for(var f="",g=V(b),k=/(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/,k=/\s/.test(g)?k:/(,)/,g=g.split(k),k=Math.floor(g.length/2),l=0;l<k;l++)var m=2*l,f=f+D(V(g[m]),!0),f=
f+(" "+V(g[m+1]));g=V(g[2*l]).split(/\s/);f+=D(V(g[0]),!0);2===g.length&&(f+=" "+V(g[1]));this[a]=b=f}!1!==d&&(null===b||y(b)?this.$$element.removeAttr(e):oa.test(e)?this.$$element.attr(e,b):$(this.$$element[0],e,b));(a=this.$$observers)&&q(a[h],function(a){try{a(b)}catch(d){c(d)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers=T()),e=d[a]||(d[a]=[]);e.push(b);u.$evalAsync(function(){e.$$inter||!c.hasOwnProperty(a)||y(c[a])||b(c[a])});return function(){Za(e,b)}}};var ra=b.startSymbol(),
sa=b.endSymbol(),ta="{{"==ra&&"}}"==sa?Xa:function(a){return a.replace(/\{\{/g,ra).replace(/}}/g,sa)},ya=/^ngAttr[A-Z]/,Aa=/^(.+)Start$/;ba.$$addBindingInfo=m?function(a,b){var c=a.data("$binding")||[];K(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:C;ba.$$addBindingClass=m?function(a){A(a,"ng-binding")}:C;ba.$$addScopeInfo=m?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope",b)}:C;ba.$$addScopeClass=m?function(a,b){A(a,b?"ng-isolate-scope":"ng-scope")}:C;ba.$$createComment=
function(a,b){var c="";m&&(c=" "+(a||"")+": "+(b||"")+" ");return v.document.createComment(c)};return ba}]}function Db(a,b){this.previousValue=a;this.currentValue=b}function xa(a){return cb(a.replace(Vc,""))}function $c(a,b){var d="",c=a.split(/\s+/),e=b.split(/\s+/),f=0;a:for(;f<c.length;f++){for(var g=c[f],h=0;h<e.length;h++)if(g==e[h])continue a;d+=(0<d.length?" ":"")+g}return d}function Yc(a){a=B(a);var b=a.length;if(1>=b)return a;for(;b--;)8===a[b].nodeType&&Zf.call(a,b,1);return a}function Uc(a,
b){if(b&&F(b))return b;if(F(a)){var d=bd.exec(a);if(d)return d[3]}}function ef(){var a={},b=!1;this.has=function(b){return a.hasOwnProperty(b)};this.register=function(b,c){Qa(b,"controller");G(b)?R(a,b):a[b]=c};this.allowGlobals=function(){b=!0};this.$get=["$injector","$window",function(d,c){function e(a,b,c,d){if(!a||!G(a.$scope))throw O("$controller")("noscp",d,b);a.$scope[b]=c}return function(f,g,h,k){var l,n,m;h=!0===h;k&&F(k)&&(m=k);if(F(f)){k=f.match(bd);if(!k)throw $f("ctrlfmt",f);n=k[1];m=
m||k[3];f=a.hasOwnProperty(n)?a[n]:Bc(g.$scope,n,!0)||(b?Bc(c,n,!0):void 0);Pa(f,n,!0)}if(h)return h=(K(f)?f[f.length-1]:f).prototype,l=Object.create(h||null),m&&e(g,m,l,n||f.name),R(function(){var a=d.invoke(f,l,g,n);a!==l&&(G(a)||E(a))&&(l=a,m&&e(g,m,l,n||f.name));return l},{instance:l,identifier:m});l=d.instantiate(f,g,n);m&&e(g,m,l,n||f.name);return l}}]}function ff(){this.$get=["$window",function(a){return B(a.document)}]}function gf(){this.$get=["$log",function(a){return function(b,d){a.error.apply(a,
arguments)}}]}function $b(a){return G(a)?fa(a)?a.toISOString():ab(a):a}function mf(){this.$get=function(){return function(a){if(!a)return"";var b=[];pc(a,function(a,c){null===a||y(a)||(K(a)?q(a,function(a){b.push(ja(c)+"="+ja($b(a)))}):b.push(ja(c)+"="+ja($b(a))))});return b.join("&")}}}function nf(){this.$get=function(){return function(a){function b(a,e,f){null===a||y(a)||(K(a)?q(a,function(a,c){b(a,e+"["+(G(a)?c:"")+"]")}):G(a)&&!fa(a)?pc(a,function(a,c){b(a,e+(f?"":"[")+c+(f?"":"]"))}):d.push(ja(e)+
"="+ja($b(a))))}if(!a)return"";var d=[];b(a,"",!0);return d.join("&")}}}function ac(a,b){if(F(a)){var d=a.replace(ag,"").trim();if(d){var c=b("Content-Type");(c=c&&0===c.indexOf(cd))||(c=(c=d.match(bg))&&cg[c[0]].test(d));c&&(a=uc(d))}}return a}function dd(a){var b=T(),d;F(a)?q(a.split("\n"),function(a){d=a.indexOf(":");var e=P(V(a.substr(0,d)));a=V(a.substr(d+1));e&&(b[e]=b[e]?b[e]+", "+a:a)}):G(a)&&q(a,function(a,d){var f=P(d),g=V(a);f&&(b[f]=b[f]?b[f]+", "+g:g)});return b}function ed(a){var b;
return function(d){b||(b=dd(a));return d?(d=b[P(d)],void 0===d&&(d=null),d):b}}function fd(a,b,d,c){if(E(c))return c(a,b,d);q(c,function(c){a=c(a,b,d)});return a}function lf(){var a=this.defaults={transformResponse:[ac],transformRequest:[function(a){return G(a)&&"[object File]"!==ma.call(a)&&"[object Blob]"!==ma.call(a)&&"[object FormData]"!==ma.call(a)?ab(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:ha(bc),put:ha(bc),patch:ha(bc)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",
paramSerializer:"$httpParamSerializer"},b=!1;this.useApplyAsync=function(a){return x(a)?(b=!!a,this):b};var d=!0;this.useLegacyPromiseExtensions=function(a){return x(a)?(d=!!a,this):d};var c=this.interceptors=[];this.$get=["$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector",function(e,f,g,h,k,l){function n(b){function c(a){var b=R({},a);b.data=fd(a.data,a.headers,a.status,f.transformResponse);a=a.status;return 200<=a&&300>a?b:k.reject(b)}function e(a,b){var c,d={};q(a,function(a,
e){E(a)?(c=a(b),null!=c&&(d[e]=c)):d[e]=a});return d}if(!G(b))throw O("$http")("badreq",b);if(!F(b.url))throw O("$http")("badreq",b.url);var f=R({method:"get",transformRequest:a.transformRequest,transformResponse:a.transformResponse,paramSerializer:a.paramSerializer},b);f.headers=function(b){var c=a.headers,d=R({},b.headers),f,g,h,c=R({},c.common,c[P(b.method)]);a:for(f in c){g=P(f);for(h in d)if(P(h)===g)continue a;d[f]=c[f]}return e(d,ha(b))}(b);f.method=sb(f.method);f.paramSerializer=F(f.paramSerializer)?
l.get(f.paramSerializer):f.paramSerializer;var g=[function(b){var d=b.headers,e=fd(b.data,ed(d),void 0,b.transformRequest);y(e)&&q(d,function(a,b){"content-type"===P(b)&&delete d[b]});y(b.withCredentials)&&!y(a.withCredentials)&&(b.withCredentials=a.withCredentials);return m(b,e).then(c,c)},void 0],h=k.when(f);for(q(M,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){b=g.shift();var n=g.shift(),
h=h.then(b,n)}d?(h.success=function(a){Pa(a,"fn");h.then(function(b){a(b.data,b.status,b.headers,f)});return h},h.error=function(a){Pa(a,"fn");h.then(null,function(b){a(b.data,b.status,b.headers,f)});return h}):(h.success=gd("success"),h.error=gd("error"));return h}function m(c,d){function g(a){if(a){var c={};q(a,function(a,d){c[d]=function(c){function d(){a(c)}b?h.$applyAsync(d):h.$$phase?d():h.$apply(d)}});return c}}function l(a,c,d,e){function f(){m(c,a,d,e)}L&&(200<=a&&300>a?L.put(A,[a,c,dd(d),
e]):L.remove(A));b?h.$applyAsync(f):(f(),h.$$phase||h.$apply())}function m(a,b,d,e){b=-1<=b?b:0;(200<=b&&300>b?J.resolve:J.reject)({data:a,status:b,headers:ed(d),config:c,statusText:e})}function u(a){m(a.data,a.status,ha(a.headers()),a.statusText)}function I(){var a=n.pendingRequests.indexOf(c);-1!==a&&n.pendingRequests.splice(a,1)}var J=k.defer(),D=J.promise,L,S,M=c.headers,A=r(c.url,c.paramSerializer(c.params));n.pendingRequests.push(c);D.then(I,I);!c.cache&&!a.cache||!1===c.cache||"GET"!==c.method&&
"JSONP"!==c.method||(L=G(c.cache)?c.cache:G(a.cache)?a.cache:N);L&&(S=L.get(A),x(S)?S&&E(S.then)?S.then(u,u):K(S)?m(S[1],S[0],ha(S[2]),S[3]):m(S,200,{},"OK"):L.put(A,D));y(S)&&((S=hd(c.url)?f()[c.xsrfCookieName||a.xsrfCookieName]:void 0)&&(M[c.xsrfHeaderName||a.xsrfHeaderName]=S),e(c.method,A,d,l,M,c.timeout,c.withCredentials,c.responseType,g(c.eventHandlers),g(c.uploadEventHandlers)));return D}function r(a,b){0<b.length&&(a+=(-1==a.indexOf("?")?"?":"&")+b);return a}var N=g("$http");a.paramSerializer=
F(a.paramSerializer)?l.get(a.paramSerializer):a.paramSerializer;var M=[];q(c,function(a){M.unshift(F(a)?l.get(a):l.invoke(a))});n.pendingRequests=[];(function(a){q(arguments,function(a){n[a]=function(b,c){return n(R({},c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){q(arguments,function(a){n[a]=function(b,c,d){return n(R({},d||{},{method:a,url:b,data:c}))}})})("post","put","patch");n.defaults=a;return n}]}function pf(){this.$get=function(){return function(){return new v.XMLHttpRequest}}}
function of(){this.$get=["$browser","$window","$document","$xhrFactory",function(a,b,d,c){return dg(a,c,a.defer,b.angular.callbacks,d[0])}]}function dg(a,b,d,c,e){function f(a,b,d){var f=e.createElement("script"),n=null;f.type="text/javascript";f.src=a;f.async=!0;n=function(a){f.removeEventListener("load",n,!1);f.removeEventListener("error",n,!1);e.body.removeChild(f);f=null;var g=-1,N="unknown";a&&("load"!==a.type||c[b].called||(a={type:"error"}),N=a.type,g="error"===a.type?404:200);d&&d(g,N)};f.addEventListener("load",
n,!1);f.addEventListener("error",n,!1);e.body.appendChild(f);return n}return function(e,h,k,l,n,m,r,N,M,w){function p(){z&&z();u&&u.abort()}function H(b,c,e,f,g){x(J)&&d.cancel(J);z=u=null;b(c,e,f,g);a.$$completeOutstandingRequest(C)}a.$$incOutstandingRequestCount();h=h||a.url();if("jsonp"==P(e)){var t="_"+(c.counter++).toString(36);c[t]=function(a){c[t].data=a;c[t].called=!0};var z=f(h.replace("JSON_CALLBACK","angular.callbacks."+t),t,function(a,b){H(l,a,c[t].data,"",b);c[t]=C})}else{var u=b(e,h);
u.open(e,h,!0);q(n,function(a,b){x(a)&&u.setRequestHeader(b,a)});u.onload=function(){var a=u.statusText||"",b="response"in u?u.response:u.responseText,c=1223===u.status?204:u.status;0===c&&(c=b?200:"file"==ra(h).protocol?404:0);H(l,c,b,u.getAllResponseHeaders(),a)};e=function(){H(l,-1,null,null,"")};u.onerror=e;u.onabort=e;q(M,function(a,b){u.addEventListener(b,a)});q(w,function(a,b){u.upload.addEventListener(b,a)});r&&(u.withCredentials=!0);if(N)try{u.responseType=N}catch(I){if("json"!==N)throw I;
}u.send(y(k)?null:k)}if(0<m)var J=d(p,m);else m&&E(m.then)&&m.then(p)}}function jf(){var a="{{",b="}}";this.startSymbol=function(b){return b?(a=b,this):a};this.endSymbol=function(a){return a?(b=a,this):b};this.$get=["$parse","$exceptionHandler","$sce",function(d,c,e){function f(a){return"\\\\\\"+a}function g(c){return c.replace(m,a).replace(r,b)}function h(a,b,c,d){var e;return e=a.$watch(function(a){e();return d(a)},b,c)}function k(f,k,m,r){function H(a){try{var b=a;a=m?e.getTrusted(m,b):e.valueOf(b);
var d;if(r&&!x(a))d=a;else if(null==a)d="";else{switch(typeof a){case "string":break;case "number":a=""+a;break;default:a=ab(a)}d=a}return d}catch(g){c(Ja.interr(f,g))}}if(!f.length||-1===f.indexOf(a)){var t;k||(k=g(f),t=da(k),t.exp=f,t.expressions=[],t.$$watchDelegate=h);return t}r=!!r;var z,u,I=0,J=[],D=[];t=f.length;for(var L=[],S=[];I<t;)if(-1!=(z=f.indexOf(a,I))&&-1!=(u=f.indexOf(b,z+l)))I!==z&&L.push(g(f.substring(I,z))),I=f.substring(z+l,u),J.push(I),D.push(d(I,H)),I=u+n,S.push(L.length),L.push("");
else{I!==t&&L.push(g(f.substring(I)));break}m&&1<L.length&&Ja.throwNoconcat(f);if(!k||J.length){var q=function(a){for(var b=0,c=J.length;b<c;b++){if(r&&y(a[b]))return;L[S[b]]=a[b]}return L.join("")};return R(function(a){var b=0,d=J.length,e=Array(d);try{for(;b<d;b++)e[b]=D[b](a);return q(e)}catch(g){c(Ja.interr(f,g))}},{exp:f,expressions:J,$$watchDelegate:function(a,b){var c;return a.$watchGroup(D,function(d,e){var f=q(d);E(b)&&b.call(this,f,d!==e?c:f,a);c=f})}})}}var l=a.length,n=b.length,m=new RegExp(a.replace(/./g,
f),"g"),r=new RegExp(b.replace(/./g,f),"g");k.startSymbol=function(){return a};k.endSymbol=function(){return b};return k}]}function kf(){this.$get=["$rootScope","$window","$q","$$q","$browser",function(a,b,d,c,e){function f(f,k,l,n){function m(){r?f.apply(null,N):f(p)}var r=4<arguments.length,N=r?za.call(arguments,4):[],q=b.setInterval,w=b.clearInterval,p=0,H=x(n)&&!n,t=(H?c:d).defer(),z=t.promise;l=x(l)?l:0;z.$$intervalId=q(function(){H?e.defer(m):a.$evalAsync(m);t.notify(p++);0<l&&p>=l&&(t.resolve(p),
w(z.$$intervalId),delete g[z.$$intervalId]);H||a.$apply()},k);g[z.$$intervalId]=t;return z}var g={};f.cancel=function(a){return a&&a.$$intervalId in g?(g[a.$$intervalId].reject("canceled"),b.clearInterval(a.$$intervalId),delete g[a.$$intervalId],!0):!1};return f}]}function cc(a){a=a.split("/");for(var b=a.length;b--;)a[b]=ob(a[b]);return a.join("/")}function id(a,b){var d=ra(a);b.$$protocol=d.protocol;b.$$host=d.hostname;b.$$port=X(d.port)||eg[d.protocol]||null}function jd(a,b){var d="/"!==a.charAt(0);
d&&(a="/"+a);var c=ra(a);b.$$path=decodeURIComponent(d&&"/"===c.pathname.charAt(0)?c.pathname.substring(1):c.pathname);b.$$search=xc(c.search);b.$$hash=decodeURIComponent(c.hash);b.$$path&&"/"!=b.$$path.charAt(0)&&(b.$$path="/"+b.$$path)}function na(a,b){if(0===b.indexOf(a))return b.substr(a.length)}function Ia(a){var b=a.indexOf("#");return-1==b?a:a.substr(0,b)}function hb(a){return a.replace(/(#.+)|#$/,"$1")}function dc(a,b,d){this.$$html5=!0;d=d||"";id(a,this);this.$$parse=function(a){var d=na(b,
a);if(!F(d))throw Eb("ipthprfx",a,b);jd(d,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Rb(this.$$search),d=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=cc(this.$$path)+(a?"?"+a:"")+d;this.$$absUrl=b+this.$$url.substr(1)};this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;x(f=na(a,c))?(g=f,g=x(f=na(d,f))?b+(na("/",f)||f):a+g):x(f=na(b,c))?g=b+f:b==c+"/"&&(g=b);g&&this.$$parse(g);return!!g}}function ec(a,b,d){id(a,this);
this.$$parse=function(c){var e=na(a,c)||na(b,c),f;y(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",y(e)&&(a=c,this.replace())):(f=na(d,e),y(f)&&(f=e));jd(f,this);c=this.$$path;var e=a,g=/^\/[A-Z]:(\/.*)/;0===f.indexOf(e)&&(f=f.replace(e,""));g.exec(f)||(c=(f=g.exec(c))?f[1]:c);this.$$path=c;this.$$compose()};this.$$compose=function(){var b=Rb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=cc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+(this.$$url?d+this.$$url:"")};this.$$parseLinkUrl=
function(b,d){return Ia(a)==Ia(b)?(this.$$parse(b),!0):!1}}function kd(a,b,d){this.$$html5=!0;ec.apply(this,arguments);this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;a==Ia(c)?f=c:(g=na(b,c))?f=a+d+g:b===c+"/"&&(f=b);f&&this.$$parse(f);return!!f};this.$$compose=function(){var b=Rb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=cc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+d+this.$$url}}function Fb(a){return function(){return this[a]}}function ld(a,
b){return function(d){if(y(d))return this[a];this[a]=b(d);this.$$compose();return this}}function qf(){var a="",b={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(b){return x(b)?(a=b,this):a};this.html5Mode=function(a){return Da(a)?(b.enabled=a,this):G(a)?(Da(a.enabled)&&(b.enabled=a.enabled),Da(a.requireBase)&&(b.requireBase=a.requireBase),Da(a.rewriteLinks)&&(b.rewriteLinks=a.rewriteLinks),this):b};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",function(d,
c,e,f,g){function h(a,b,d){var e=l.url(),f=l.$$state;try{c.url(a,b,d),l.$$state=c.state()}catch(g){throw l.url(e),l.$$state=f,g;}}function k(a,b){d.$broadcast("$locationChangeSuccess",l.absUrl(),a,l.$$state,b)}var l,n;n=c.baseHref();var m=c.url(),r;if(b.enabled){if(!n&&b.requireBase)throw Eb("nobase");r=m.substring(0,m.indexOf("/",m.indexOf("//")+2))+(n||"/");n=e.history?dc:kd}else r=Ia(m),n=ec;var N=r.substr(0,Ia(r).lastIndexOf("/")+1);l=new n(r,N,"#"+a);l.$$parseLinkUrl(m,m);l.$$state=c.state();
var q=/^\s*(javascript|mailto):/i;f.on("click",function(a){if(b.rewriteLinks&&!a.ctrlKey&&!a.metaKey&&!a.shiftKey&&2!=a.which&&2!=a.button){for(var e=B(a.target);"a"!==va(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;var h=e.prop("href"),k=e.attr("href")||e.attr("xlink:href");G(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=ra(h.animVal).href);q.test(h)||!h||e.attr("target")||a.isDefaultPrevented()||!l.$$parseLinkUrl(h,k)||(a.preventDefault(),l.absUrl()!=c.url()&&(d.$apply(),g.angular["ff-684208-preventDefault"]=
!0))}});hb(l.absUrl())!=hb(m)&&c.url(l.absUrl(),!0);var w=!0;c.onUrlChange(function(a,b){y(na(N,a))?g.location.href=a:(d.$evalAsync(function(){var c=l.absUrl(),e=l.$$state,f;a=hb(a);l.$$parse(a);l.$$state=b;f=d.$broadcast("$locationChangeStart",a,c,b,e).defaultPrevented;l.absUrl()===a&&(f?(l.$$parse(c),l.$$state=e,h(c,!1,e)):(w=!1,k(c,e)))}),d.$$phase||d.$digest())});d.$watch(function(){var a=hb(c.url()),b=hb(l.absUrl()),f=c.state(),g=l.$$replace,m=a!==b||l.$$html5&&e.history&&f!==l.$$state;if(w||
m)w=!1,d.$evalAsync(function(){var b=l.absUrl(),c=d.$broadcast("$locationChangeStart",b,a,l.$$state,f).defaultPrevented;l.absUrl()===b&&(c?(l.$$parse(a),l.$$state=f):(m&&h(b,g,f===l.$$state?null:l.$$state),k(a,f)))});l.$$replace=!1});return l}]}function rf(){var a=!0,b=this;this.debugEnabled=function(b){return x(b)?(a=b,this):a};this.$get=["$window",function(d){function c(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&
(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=d.console||{},e=b[a]||b.log||C;a=!1;try{a=!!e.apply}catch(k){}return a?function(){var a=[];q(arguments,function(b){a.push(c(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){a&&c.apply(b,arguments)}}()}}]}function Ta(a,b){if("__defineGetter__"===a||"__defineSetter__"===a||"__lookupGetter__"===a||"__lookupSetter__"===
a||"__proto__"===a)throw ca("isecfld",b);return a}function fg(a){return a+""}function sa(a,b){if(a){if(a.constructor===a)throw ca("isecfn",b);if(a.window===a)throw ca("isecwindow",b);if(a.children&&(a.nodeName||a.prop&&a.attr&&a.find))throw ca("isecdom",b);if(a===Object)throw ca("isecobj",b);}return a}function md(a,b){if(a){if(a.constructor===a)throw ca("isecfn",b);if(a===gg||a===hg||a===ig)throw ca("isecff",b);}}function Gb(a,b){if(a&&(a===(0).constructor||a===(!1).constructor||a==="".constructor||
a==={}.constructor||a===[].constructor||a===Function.constructor))throw ca("isecaf",b);}function jg(a,b){return"undefined"!==typeof a?a:b}function nd(a,b){return"undefined"===typeof a?b:"undefined"===typeof b?a:a+b}function aa(a,b){var d,c;switch(a.type){case s.Program:d=!0;q(a.body,function(a){aa(a.expression,b);d=d&&a.expression.constant});a.constant=d;break;case s.Literal:a.constant=!0;a.toWatch=[];break;case s.UnaryExpression:aa(a.argument,b);a.constant=a.argument.constant;a.toWatch=a.argument.toWatch;
break;case s.BinaryExpression:aa(a.left,b);aa(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.left.toWatch.concat(a.right.toWatch);break;case s.LogicalExpression:aa(a.left,b);aa(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.constant?[]:[a];break;case s.ConditionalExpression:aa(a.test,b);aa(a.alternate,b);aa(a.consequent,b);a.constant=a.test.constant&&a.alternate.constant&&a.consequent.constant;a.toWatch=a.constant?[]:[a];break;case s.Identifier:a.constant=
!1;a.toWatch=[a];break;case s.MemberExpression:aa(a.object,b);a.computed&&aa(a.property,b);a.constant=a.object.constant&&(!a.computed||a.property.constant);a.toWatch=[a];break;case s.CallExpression:d=a.filter?!b(a.callee.name).$stateful:!1;c=[];q(a.arguments,function(a){aa(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=a.filter&&!b(a.callee.name).$stateful?c:[a];break;case s.AssignmentExpression:aa(a.left,b);aa(a.right,b);a.constant=a.left.constant&&a.right.constant;
a.toWatch=[a];break;case s.ArrayExpression:d=!0;c=[];q(a.elements,function(a){aa(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=c;break;case s.ObjectExpression:d=!0;c=[];q(a.properties,function(a){aa(a.value,b);d=d&&a.value.constant;a.value.constant||c.push.apply(c,a.value.toWatch)});a.constant=d;a.toWatch=c;break;case s.ThisExpression:a.constant=!1;a.toWatch=[];break;case s.LocalsExpression:a.constant=!1,a.toWatch=[]}}function od(a){if(1==a.length){a=a[0].expression;
var b=a.toWatch;return 1!==b.length?b:b[0]!==a?b:void 0}}function pd(a){return a.type===s.Identifier||a.type===s.MemberExpression}function qd(a){if(1===a.body.length&&pd(a.body[0].expression))return{type:s.AssignmentExpression,left:a.body[0].expression,right:{type:s.NGValueParameter},operator:"="}}function rd(a){return 0===a.body.length||1===a.body.length&&(a.body[0].expression.type===s.Literal||a.body[0].expression.type===s.ArrayExpression||a.body[0].expression.type===s.ObjectExpression)}function sd(a,
b){this.astBuilder=a;this.$filter=b}function td(a,b){this.astBuilder=a;this.$filter=b}function Hb(a){return"constructor"==a}function fc(a){return E(a.valueOf)?a.valueOf():kg.call(a)}function sf(){var a=T(),b=T(),d={"true":!0,"false":!1,"null":null,undefined:void 0},c,e;this.addLiteral=function(a,b){d[a]=b};this.setIdentifierFns=function(a,b){c=a;e=b;return this};this.$get=["$filter",function(f){function g(c,d,e){var g,k,D;e=e||H;switch(typeof c){case "string":D=c=c.trim();var q=e?b:a;g=q[D];if(!g){":"===
c.charAt(0)&&":"===c.charAt(1)&&(k=!0,c=c.substring(2));g=e?p:w;var S=new gc(g);g=(new hc(S,f,g)).parse(c);g.constant?g.$$watchDelegate=r:k?g.$$watchDelegate=g.literal?m:n:g.inputs&&(g.$$watchDelegate=l);e&&(g=h(g));q[D]=g}return N(g,d);case "function":return N(c,d);default:return N(C,d)}}function h(a){function b(c,d,e,f){var g=H;H=!0;try{return a(c,d,e,f)}finally{H=g}}if(!a)return a;b.$$watchDelegate=a.$$watchDelegate;b.assign=h(a.assign);b.constant=a.constant;b.literal=a.literal;for(var c=0;a.inputs&&
c<a.inputs.length;++c)a.inputs[c]=h(a.inputs[c]);b.inputs=a.inputs;return b}function k(a,b){return null==a||null==b?a===b:"object"===typeof a&&(a=fc(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function l(a,b,c,d,e){var f=d.inputs,g;if(1===f.length){var h=k,f=f[0];return a.$watch(function(a){var b=f(a);k(b,h)||(g=d(a,void 0,void 0,[b]),h=b&&fc(b));return g},b,c,e)}for(var l=[],m=[],n=0,r=f.length;n<r;n++)l[n]=k,m[n]=null;return a.$watch(function(a){for(var b=!1,c=0,e=f.length;c<e;c++){var h=f[c](a);
if(b||(b=!k(h,l[c])))m[c]=h,l[c]=h&&fc(h)}b&&(g=d(a,void 0,void 0,m));return g},b,c,e)}function n(a,b,c,d){var e,f;return e=a.$watch(function(a){return d(a)},function(a,c,d){f=a;E(b)&&b.apply(this,arguments);x(a)&&d.$$postDigest(function(){x(f)&&e()})},c)}function m(a,b,c,d){function e(a){var b=!0;q(a,function(a){x(a)||(b=!1)});return b}var f,g;return f=a.$watch(function(a){return d(a)},function(a,c,d){g=a;E(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(g)&&f()})},c)}function r(a,b,c,d){var e;
return e=a.$watch(function(a){e();return d(a)},b,c)}function N(a,b){if(!b)return a;var c=a.$$watchDelegate,d=!1,c=c!==m&&c!==n?function(c,e,f,g){f=d&&g?g[0]:a(c,e,f,g);return b(f,c,e)}:function(c,d,e,f){e=a(c,d,e,f);c=b(e,c,d);return x(e)?c:e};a.$$watchDelegate&&a.$$watchDelegate!==l?c.$$watchDelegate=a.$$watchDelegate:b.$stateful||(c.$$watchDelegate=l,d=!a.inputs,c.inputs=a.inputs?a.inputs:[a]);return c}var M=Ea().noUnsafeEval,w={csp:M,expensiveChecks:!1,literals:qa(d),isIdentifierStart:E(c)&&c,
isIdentifierContinue:E(e)&&e},p={csp:M,expensiveChecks:!0,literals:qa(d),isIdentifierStart:E(c)&&c,isIdentifierContinue:E(e)&&e},H=!1;g.$$runningExpensiveChecks=function(){return H};return g}]}function uf(){this.$get=["$rootScope","$exceptionHandler",function(a,b){return ud(function(b){a.$evalAsync(b)},b)}]}function vf(){this.$get=["$browser","$exceptionHandler",function(a,b){return ud(function(b){a.defer(b)},b)}]}function ud(a,b){function d(){this.$$state={status:0}}function c(a,b){return function(c){b.call(a,
c)}}function e(c){!c.processScheduled&&c.pending&&(c.processScheduled=!0,a(function(){var a,d,e;e=c.pending;c.processScheduled=!1;c.pending=void 0;for(var f=0,g=e.length;f<g;++f){d=e[f][0];a=e[f][c.status];try{E(a)?d.resolve(a(c.value)):1===c.status?d.resolve(c.value):d.reject(c.value)}catch(h){d.reject(h),b(h)}}}))}function f(){this.promise=new d}var g=O("$q",TypeError);R(d.prototype,{then:function(a,b,c){if(y(a)&&y(b)&&y(c))return this;var d=new f;this.$$state.pending=this.$$state.pending||[];this.$$state.pending.push([d,
a,b,c]);0<this.$$state.status&&e(this.$$state);return d.promise},"catch":function(a){return this.then(null,a)},"finally":function(a,b){return this.then(function(b){return k(b,!0,a)},function(b){return k(b,!1,a)},b)}});R(f.prototype,{resolve:function(a){this.promise.$$state.status||(a===this.promise?this.$$reject(g("qcycle",a)):this.$$resolve(a))},$$resolve:function(a){function d(a){k||(k=!0,h.$$resolve(a))}function f(a){k||(k=!0,h.$$reject(a))}var g,h=this,k=!1;try{if(G(a)||E(a))g=a&&a.then;E(g)?
(this.promise.$$state.status=-1,g.call(a,d,f,c(this,this.notify))):(this.promise.$$state.value=a,this.promise.$$state.status=1,e(this.promise.$$state))}catch(l){f(l),b(l)}},reject:function(a){this.promise.$$state.status||this.$$reject(a)},$$reject:function(a){this.promise.$$state.value=a;this.promise.$$state.status=2;e(this.promise.$$state)},notify:function(c){var d=this.promise.$$state.pending;0>=this.promise.$$state.status&&d&&d.length&&a(function(){for(var a,e,f=0,g=d.length;f<g;f++){e=d[f][0];
a=d[f][3];try{e.notify(E(a)?a(c):c)}catch(h){b(h)}}})}});var h=function(a,b){var c=new f;b?c.resolve(a):c.reject(a);return c.promise},k=function(a,b,c){var d=null;try{E(c)&&(d=c())}catch(e){return h(e,!1)}return d&&E(d.then)?d.then(function(){return h(a,b)},function(a){return h(a,!1)}):h(a,b)},l=function(a,b,c,d){var e=new f;e.resolve(a);return e.promise.then(b,c,d)},n=function(a){if(!E(a))throw g("norslvr",a);var b=new f;a(function(a){b.resolve(a)},function(a){b.reject(a)});return b.promise};n.prototype=
d.prototype;n.defer=function(){var a=new f;a.resolve=c(a,a.resolve);a.reject=c(a,a.reject);a.notify=c(a,a.notify);return a};n.reject=function(a){var b=new f;b.reject(a);return b.promise};n.when=l;n.resolve=l;n.all=function(a){var b=new f,c=0,d=K(a)?[]:{};q(a,function(a,e){c++;l(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise};return n}function Ef(){this.$get=["$window","$timeout",function(a,
b){var d=a.requestAnimationFrame||a.webkitRequestAnimationFrame,c=a.cancelAnimationFrame||a.webkitCancelAnimationFrame||a.webkitCancelRequestAnimationFrame,e=!!d,f=e?function(a){var b=d(a);return function(){c(b)}}:function(a){var c=b(a,16.66,!1);return function(){b.cancel(c)}};f.supported=e;return f}]}function tf(){function a(a){function b(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++nb;this.$$ChildScope=
null}b.prototype=a;return b}var b=10,d=O("$rootScope"),c=null,e=null;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$exceptionHandler","$parse","$browser",function(f,g,h){function k(a){a.currentScope.$$destroyed=!0}function l(a){9===Ca&&(a.$$childHead&&l(a.$$childHead),a.$$nextSibling&&l(a.$$nextSibling));a.$parent=a.$$nextSibling=a.$$prevSibling=a.$$childHead=a.$$childTail=a.$root=a.$$watchers=null}function n(){this.$id=++nb;this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=
this.$$prevSibling=this.$$childHead=this.$$childTail=null;this.$root=this;this.$$destroyed=!1;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$$isolateBindings=null}function m(a){if(H.$$phase)throw d("inprog",H.$$phase);H.$$phase=a}function r(a,b){do a.$$watchersCount+=b;while(a=a.$parent)}function N(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function s(){}function w(){for(;u.length;)try{u.shift()()}catch(a){f(a)}e=
null}function p(){null===e&&(e=h.defer(function(){H.$apply(w)}))}n.prototype={constructor:n,$new:function(b,c){var d;c=c||this;b?(d=new n,d.$root=this.$root):(this.$$ChildScope||(this.$$ChildScope=a(this)),d=new this.$$ChildScope);d.$parent=c;d.$$prevSibling=c.$$childTail;c.$$childHead?(c.$$childTail.$$nextSibling=d,c.$$childTail=d):c.$$childHead=c.$$childTail=d;(b||c!=this)&&d.$on("$destroy",k);return d},$watch:function(a,b,d,e){var f=g(a);if(f.$$watchDelegate)return f.$$watchDelegate(this,b,d,f,
a);var h=this,k=h.$$watchers,l={fn:b,last:s,get:f,exp:e||a,eq:!!d};c=null;E(b)||(l.fn=C);k||(k=h.$$watchers=[]);k.unshift(l);r(this,1);return function(){0<=Za(k,l)&&r(h,-1);c=null}},$watchGroup:function(a,b){function c(){h=!1;k?(k=!1,b(e,e,g)):b(e,d,g)}var d=Array(a.length),e=Array(a.length),f=[],g=this,h=!1,k=!0;if(!a.length){var l=!0;g.$evalAsync(function(){l&&b(e,e,g)});return function(){l=!1}}if(1===a.length)return this.$watch(a[0],function(a,c,f){e[0]=a;d[0]=c;b(e,a===c?e:d,f)});q(a,function(a,
b){var k=g.$watch(a,function(a,f){e[b]=a;d[b]=f;h||(h=!0,g.$evalAsync(c))});f.push(k)});return function(){for(;f.length;)f.shift()()}},$watchCollection:function(a,b){function c(a){e=a;var b,d,g,h;if(!y(e)){if(G(e))if(ya(e))for(f!==m&&(f=m,t=f.length=0,l++),a=e.length,t!==a&&(l++,f.length=t=a),b=0;b<a;b++)h=f[b],g=e[b],d=h!==h&&g!==g,d||h===g||(l++,f[b]=g);else{f!==r&&(f=r={},t=0,l++);a=0;for(b in e)ua.call(e,b)&&(a++,g=e[b],h=f[b],b in f?(d=h!==h&&g!==g,d||h===g||(l++,f[b]=g)):(t++,f[b]=g,l++));if(t>
a)for(b in l++,f)ua.call(e,b)||(t--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$stateful=!0;var d=this,e,f,h,k=1<b.length,l=0,n=g(a,c),m=[],r={},p=!0,t=0;return this.$watch(n,function(){p?(p=!1,b(e,e,d)):b(e,h,d);if(k)if(G(e))if(ya(e)){h=Array(e.length);for(var a=0;a<e.length;a++)h[a]=e[a]}else for(a in h={},e)ua.call(e,a)&&(h[a]=e[a]);else h=e})},$digest:function(){var a,g,k,l,n,r,p,q,N=b,u,x=[],y,v;m("$digest");h.$$checkUrlChange();this===H&&null!==e&&(h.defer.cancel(e),w());c=null;do{q=!1;
for(u=this;t.length;){try{v=t.shift(),v.scope.$eval(v.expression,v.locals)}catch(C){f(C)}c=null}a:do{if(r=u.$$watchers)for(p=r.length;p--;)try{if(a=r[p])if(n=a.get,(g=n(u))!==(k=a.last)&&!(a.eq?pa(g,k):"number"===typeof g&&"number"===typeof k&&isNaN(g)&&isNaN(k)))q=!0,c=a,a.last=a.eq?qa(g,null):g,l=a.fn,l(g,k===s?g:k,u),5>N&&(y=4-N,x[y]||(x[y]=[]),x[y].push({msg:E(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):a.exp,newVal:g,oldVal:k}));else if(a===c){q=!1;break a}}catch(F){f(F)}if(!(r=u.$$watchersCount&&
u.$$childHead||u!==this&&u.$$nextSibling))for(;u!==this&&!(r=u.$$nextSibling);)u=u.$parent}while(u=r);if((q||t.length)&&!N--)throw H.$$phase=null,d("infdig",b,x);}while(q||t.length);for(H.$$phase=null;z.length;)try{z.shift()()}catch(B){f(B)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this===H&&h.$$applicationDestroyed();r(this,-this.$$watchersCount);for(var b in this.$$listenerCount)N(this,this.$$listenerCount[b],b);a&&a.$$childHead==
this&&(a.$$childHead=this.$$nextSibling);a&&a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=C;this.$on=this.$watch=this.$watchGroup=function(){return C};this.$$listeners={};this.$$nextSibling=null;l(this)}},$eval:function(a,b){return g(a)(this,b)},$evalAsync:function(a,b){H.$$phase||
t.length||h.defer(function(){t.length&&H.$digest()});t.push({scope:this,expression:g(a),locals:b})},$$postDigest:function(a){z.push(a)},$apply:function(a){try{m("$apply");try{return this.$eval(a)}finally{H.$$phase=null}}catch(b){f(b)}finally{try{H.$digest()}catch(c){throw f(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&u.push(b);a=g(a);p()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=
0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=c.indexOf(b);-1!==d&&(c[d]=null,N(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,g=!1,h={name:a,targetScope:e,stopPropagation:function(){g=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=$a([h],arguments,1),l,n;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(n=d.length;l<n;l++)if(d[l])try{d[l].apply(null,k)}catch(m){f(m)}else d.splice(l,1),l--,n--;if(g)return h.currentScope=null,h;e=e.$parent}while(e);
h.currentScope=null;return h},$broadcast:function(a,b){var c=this,d=this,e={name:a,targetScope:this,preventDefault:function(){e.defaultPrevented=!0},defaultPrevented:!1};if(!this.$$listenerCount[a])return e;for(var g=$a([e],arguments,1),h,k;c=d;){e.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,g)}catch(l){f(l)}else d.splice(h,1),h--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}e.currentScope=
null;return e}};var H=new n,t=H.$$asyncQueue=[],z=H.$$postDigestQueue=[],u=H.$$applyAsyncQueue=[];return H}]}function me(){var a=/^\s*(https?|ftp|mailto|tel|file):/,b=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.aHrefSanitizationWhitelist=function(b){return x(b)?(a=b,this):a};this.imgSrcSanitizationWhitelist=function(a){return x(a)?(b=a,this):b};this.$get=function(){return function(d,c){var e=c?b:a,f;f=ra(d).href;return""===f||f.match(e)?d:"unsafe:"+f}}}function lg(a){if("self"===a)return a;
if(F(a)){if(-1<a.indexOf("***"))throw ta("iwcard",a);a=vd(a).replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return new RegExp("^"+a+"$")}if(Wa(a))return new RegExp("^"+a.source+"$");throw ta("imatcher");}function wd(a){var b=[];x(a)&&q(a,function(a){b.push(lg(a))});return b}function xf(){this.SCE_CONTEXTS=oa;var a=["self"],b=[];this.resourceUrlWhitelist=function(b){arguments.length&&(a=wd(b));return a};this.resourceUrlBlacklist=function(a){arguments.length&&(b=wd(a));return b};this.$get=["$injector",
function(d){function c(a,b){return"self"===a?hd(b):!!a.exec(b.href)}function e(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var f=function(a){throw ta("unsafe");};d.has("$sanitize")&&(f=d.get("$sanitize"));var g=e(),h={};h[oa.HTML]=e(g);h[oa.CSS]=e(g);h[oa.URL]=e(g);h[oa.JS]=e(g);h[oa.RESOURCE_URL]=
e(h[oa.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw ta("icontext",a,b);if(null===b||y(b)||""===b)return b;if("string"!==typeof b)throw ta("itype",a);return new c(b)},getTrusted:function(d,e){if(null===e||y(e)||""===e)return e;var g=h.hasOwnProperty(d)?h[d]:null;if(g&&e instanceof g)return e.$$unwrapTrustedValue();if(d===oa.RESOURCE_URL){var g=ra(e.toString()),m,r,q=!1;m=0;for(r=a.length;m<r;m++)if(c(a[m],g)){q=!0;break}if(q)for(m=0,r=b.length;m<r;m++)if(c(b[m],
g)){q=!1;break}if(q)return e;throw ta("insecurl",e.toString());}if(d===oa.HTML)return f(e);throw ta("unsafe");},valueOf:function(a){return a instanceof g?a.$$unwrapTrustedValue():a}}}]}function wf(){var a=!0;this.enabled=function(b){arguments.length&&(a=!!b);return a};this.$get=["$parse","$sceDelegate",function(b,d){if(a&&8>Ca)throw ta("iequirks");var c=ha(oa);c.isEnabled=function(){return a};c.trustAs=d.trustAs;c.getTrusted=d.getTrusted;c.valueOf=d.valueOf;a||(c.trustAs=c.getTrusted=function(a,b){return b},
c.valueOf=Xa);c.parseAs=function(a,d){var e=b(d);return e.literal&&e.constant?e:b(d,function(b){return c.getTrusted(a,b)})};var e=c.parseAs,f=c.getTrusted,g=c.trustAs;q(oa,function(a,b){var d=P(b);c[cb("parse_as_"+d)]=function(b){return e(a,b)};c[cb("get_trusted_"+d)]=function(b){return f(a,b)};c[cb("trust_as_"+d)]=function(b){return g(a,b)}});return c}]}function yf(){this.$get=["$window","$document",function(a,b){var d={},c=!(a.chrome&&a.chrome.app&&a.chrome.app.runtime)&&a.history&&a.history.pushState,
e=X((/android (\d+)/.exec(P((a.navigator||{}).userAgent))||[])[1]),f=/Boxee/i.test((a.navigator||{}).userAgent),g=b[0]||{},h,k=/^(Moz|webkit|ms)(?=[A-Z])/,l=g.body&&g.body.style,n=!1,m=!1;if(l){for(var r in l)if(n=k.exec(r)){h=n[0];h=h.substr(0,1).toUpperCase()+h.substr(1);break}h||(h="WebkitOpacity"in l&&"webkit");n=!!("transition"in l||h+"Transition"in l);m=!!("animation"in l||h+"Animation"in l);!e||n&&m||(n=F(l.webkitTransition),m=F(l.webkitAnimation))}return{history:!(!c||4>e||f),hasEvent:function(a){if("input"===
a&&11>=Ca)return!1;if(y(d[a])){var b=g.createElement("div");d[a]="on"+a in b}return d[a]},csp:Ea(),vendorPrefix:h,transitions:n,animations:m,android:e}}]}function Af(){var a;this.httpOptions=function(b){return b?(a=b,this):a};this.$get=["$templateCache","$http","$q","$sce",function(b,d,c,e){function f(g,h){f.totalPendingRequests++;F(g)&&b.get(g)||(g=e.getTrustedResourceUrl(g));var k=d.defaults&&d.defaults.transformResponse;K(k)?k=k.filter(function(a){return a!==ac}):k===ac&&(k=null);return d.get(g,
R({cache:b,transformResponse:k},a))["finally"](function(){f.totalPendingRequests--}).then(function(a){b.put(g,a.data);return a.data},function(a){if(!h)throw mg("tpload",g,a.status,a.statusText);return c.reject(a)})}f.totalPendingRequests=0;return f}]}function Bf(){this.$get=["$rootScope","$browser","$location",function(a,b,d){return{findBindings:function(a,b,d){a=a.getElementsByClassName("ng-binding");var g=[];q(a,function(a){var c=ea.element(a).data("$binding");c&&q(c,function(c){d?(new RegExp("(^|\\s)"+
vd(b)+"(\\s|\\||$)")).test(c)&&g.push(a):-1!=c.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,d){for(var g=["ng-","data-ng-","ng\\:"],h=0;h<g.length;++h){var k=a.querySelectorAll("["+g[h]+"model"+(d?"=":"*=")+'"'+b+'"]');if(k.length)return k}},getLocation:function(){return d.url()},setLocation:function(b){b!==d.url()&&(d.url(b),a.$digest())},whenStable:function(a){b.notifyWhenNoOutstandingRequests(a)}}}]}function Cf(){this.$get=["$rootScope","$browser","$q","$$q","$exceptionHandler",
function(a,b,d,c,e){function f(f,k,l){E(f)||(l=k,k=f,f=C);var n=za.call(arguments,3),m=x(l)&&!l,r=(m?c:d).defer(),q=r.promise,s;s=b.defer(function(){try{r.resolve(f.apply(null,n))}catch(b){r.reject(b),e(b)}finally{delete g[q.$$timeoutId]}m||a.$apply()},k);q.$$timeoutId=s;g[s]=r;return q}var g={};f.cancel=function(a){return a&&a.$$timeoutId in g?(g[a.$$timeoutId].reject("canceled"),delete g[a.$$timeoutId],b.defer.cancel(a.$$timeoutId)):!1};return f}]}function ra(a){Ca&&(Y.setAttribute("href",a),a=
Y.href);Y.setAttribute("href",a);return{href:Y.href,protocol:Y.protocol?Y.protocol.replace(/:$/,""):"",host:Y.host,search:Y.search?Y.search.replace(/^\?/,""):"",hash:Y.hash?Y.hash.replace(/^#/,""):"",hostname:Y.hostname,port:Y.port,pathname:"/"===Y.pathname.charAt(0)?Y.pathname:"/"+Y.pathname}}function hd(a){a=F(a)?ra(a):a;return a.protocol===xd.protocol&&a.host===xd.host}function Df(){this.$get=da(v)}function yd(a){function b(a){try{return decodeURIComponent(a)}catch(b){return a}}var d=a[0]||{},
c={},e="";return function(){var a,g,h,k,l;a=d.cookie||"";if(a!==e)for(e=a,a=e.split("; "),c={},h=0;h<a.length;h++)g=a[h],k=g.indexOf("="),0<k&&(l=b(g.substring(0,k)),y(c[l])&&(c[l]=b(g.substring(k+1))));return c}}function Hf(){this.$get=yd}function Jc(a){function b(d,c){if(G(d)){var e={};q(d,function(a,c){e[c]=b(c,a)});return e}return a.factory(d+"Filter",c)}this.register=b;this.$get=["$injector",function(a){return function(b){return a.get(b+"Filter")}}];b("currency",zd);b("date",Ad);b("filter",ng);
b("json",og);b("limitTo",pg);b("lowercase",qg);b("number",Bd);b("orderBy",Cd);b("uppercase",rg)}function ng(){return function(a,b,d){if(!ya(a)){if(null==a)return a;throw O("filter")("notarray",a);}var c;switch(ic(b)){case "function":break;case "boolean":case "null":case "number":case "string":c=!0;case "object":b=sg(b,d,c);break;default:return a}return Array.prototype.filter.call(a,b)}}function sg(a,b,d){var c=G(a)&&"$"in a;!0===b?b=pa:E(b)||(b=function(a,b){if(y(a))return!1;if(null===a||null===b)return a===
b;if(G(b)||G(a)&&!rc(a))return!1;a=P(""+a);b=P(""+b);return-1!==a.indexOf(b)});return function(e){return c&&!G(e)?Ka(e,a.$,b,!1):Ka(e,a,b,d)}}function Ka(a,b,d,c,e){var f=ic(a),g=ic(b);if("string"===g&&"!"===b.charAt(0))return!Ka(a,b.substring(1),d,c);if(K(a))return a.some(function(a){return Ka(a,b,d,c)});switch(f){case "object":var h;if(c){for(h in a)if("$"!==h.charAt(0)&&Ka(a[h],b,d,!0))return!0;return e?!1:Ka(a,b,d,!1)}if("object"===g){for(h in b)if(e=b[h],!E(e)&&!y(e)&&(f="$"===h,!Ka(f?a:a[h],
e,d,f,f)))return!1;return!0}return d(a,b);case "function":return!1;default:return d(a,b)}}function ic(a){return null===a?"null":typeof a}function zd(a){var b=a.NUMBER_FORMATS;return function(a,c,e){y(c)&&(c=b.CURRENCY_SYM);y(e)&&(e=b.PATTERNS[1].maxFrac);return null==a?a:Dd(a,b.PATTERNS[1],b.GROUP_SEP,b.DECIMAL_SEP,e).replace(/\u00A4/g,c)}}function Bd(a){var b=a.NUMBER_FORMATS;return function(a,c){return null==a?a:Dd(a,b.PATTERNS[0],b.GROUP_SEP,b.DECIMAL_SEP,c)}}function tg(a){var b=0,d,c,e,f,g;-1<
(c=a.indexOf(Ed))&&(a=a.replace(Ed,""));0<(e=a.search(/e/i))?(0>c&&(c=e),c+=+a.slice(e+1),a=a.substring(0,e)):0>c&&(c=a.length);for(e=0;a.charAt(e)==jc;e++);if(e==(g=a.length))d=[0],c=1;else{for(g--;a.charAt(g)==jc;)g--;c-=e;d=[];for(f=0;e<=g;e++,f++)d[f]=+a.charAt(e)}c>Fd&&(d=d.splice(0,Fd-1),b=c-1,c=1);return{d:d,e:b,i:c}}function ug(a,b,d,c){var e=a.d,f=e.length-a.i;b=y(b)?Math.min(Math.max(d,f),c):+b;d=b+a.i;c=e[d];if(0<d){e.splice(Math.max(a.i,d));for(var g=d;g<e.length;g++)e[g]=0}else for(f=
Math.max(0,f),a.i=1,e.length=Math.max(1,d=b+1),e[0]=0,g=1;g<d;g++)e[g]=0;if(5<=c)if(0>d-1){for(c=0;c>d;c--)e.unshift(0),a.i++;e.unshift(1);a.i++}else e[d-1]++;for(;f<Math.max(0,b);f++)e.push(0);if(b=e.reduceRight(function(a,b,c,d){b+=a;d[c]=b%10;return Math.floor(b/10)},0))e.unshift(b),a.i++}function Dd(a,b,d,c,e){if(!F(a)&&!Q(a)||isNaN(a))return"";var f=!isFinite(a),g=!1,h=Math.abs(a)+"",k="";if(f)k="\u221e";else{g=tg(h);ug(g,e,b.minFrac,b.maxFrac);k=g.d;h=g.i;e=g.e;f=[];for(g=k.reduce(function(a,
b){return a&&!b},!0);0>h;)k.unshift(0),h++;0<h?f=k.splice(h):(f=k,k=[0]);h=[];for(k.length>=b.lgSize&&h.unshift(k.splice(-b.lgSize).join(""));k.length>b.gSize;)h.unshift(k.splice(-b.gSize).join(""));k.length&&h.unshift(k.join(""));k=h.join(d);f.length&&(k+=c+f.join(""));e&&(k+="e+"+e)}return 0>a&&!g?b.negPre+k+b.negSuf:b.posPre+k+b.posSuf}function Ib(a,b,d,c){var e="";if(0>a||c&&0>=a)c?a=-a+1:(a=-a,e="-");for(a=""+a;a.length<b;)a=jc+a;d&&(a=a.substr(a.length-b));return e+a}function W(a,b,d,c,e){d=
d||0;return function(f){f=f["get"+a]();if(0<d||f>-d)f+=d;0===f&&-12==d&&(f=12);return Ib(f,b,c,e)}}function ib(a,b,d){return function(c,e){var f=c["get"+a](),g=sb((d?"STANDALONE":"")+(b?"SHORT":"")+a);return e[g][f]}}function Gd(a){var b=(new Date(a,0,1)).getDay();return new Date(a,0,(4>=b?5:12)-b)}function Hd(a){return function(b){var d=Gd(b.getFullYear());b=+new Date(b.getFullYear(),b.getMonth(),b.getDate()+(4-b.getDay()))-+d;b=1+Math.round(b/6048E5);return Ib(b,a)}}function kc(a,b){return 0>=a.getFullYear()?
b.ERAS[0]:b.ERAS[1]}function Ad(a){function b(a){var b;if(b=a.match(d)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,k=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=X(b[9]+b[10]),g=X(b[9]+b[11]));h.call(a,X(b[1]),X(b[2])-1,X(b[3]));f=X(b[4]||0)-f;g=X(b[5]||0)-g;h=X(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));k.call(a,f,g,h,b)}return a}var d=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,d,f){var g="",h=
[],k,l;d=d||"mediumDate";d=a.DATETIME_FORMATS[d]||d;F(c)&&(c=vg.test(c)?X(c):b(c));Q(c)&&(c=new Date(c));if(!fa(c)||!isFinite(c.getTime()))return c;for(;d;)(l=wg.exec(d))?(h=$a(h,l,1),d=h.pop()):(h.push(d),d=null);var n=c.getTimezoneOffset();f&&(n=vc(f,n),c=Qb(c,f,!0));q(h,function(b){k=xg[b];g+=k?k(c,a.DATETIME_FORMATS,n):"''"===b?"'":b.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function og(){return function(a,b){y(b)&&(b=2);return ab(a,b)}}function pg(){return function(a,b,d){b=Infinity===
Math.abs(Number(b))?Number(b):X(b);if(isNaN(b))return a;Q(a)&&(a=a.toString());if(!K(a)&&!F(a))return a;d=!d||isNaN(d)?0:X(d);d=0>d?Math.max(0,a.length+d):d;return 0<=b?a.slice(d,d+b):0===d?a.slice(b,a.length):a.slice(Math.max(0,d+b),d)}}function Cd(a){function b(b,d){d=d?-1:1;return b.map(function(b){var c=1,h=Xa;if(E(b))h=b;else if(F(b)){if("+"==b.charAt(0)||"-"==b.charAt(0))c="-"==b.charAt(0)?-1:1,b=b.substring(1);if(""!==b&&(h=a(b),h.constant))var k=h(),h=function(a){return a[k]}}return{get:h,
descending:c*d}})}function d(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}return function(a,e,f){if(null==a)return a;if(!ya(a))throw O("orderBy")("notarray",a);K(e)||(e=[e]);0===e.length&&(e=["+"]);var g=b(e,f);g.push({get:function(){return{}},descending:f?-1:1});a=Array.prototype.map.call(a,function(a,b){return{value:a,predicateValues:g.map(function(c){var e=c.get(a);c=typeof e;if(null===e)c="string",e="null";else if("string"===c)e=e.toLowerCase();else if("object"===
c)a:{if("function"===typeof e.valueOf&&(e=e.valueOf(),d(e)))break a;if(rc(e)&&(e=e.toString(),d(e)))break a;e=b}return{value:e,type:c}})}});a.sort(function(a,b){for(var c=0,d=0,e=g.length;d<e;++d){var c=a.predicateValues[d],f=b.predicateValues[d],q=0;c.type===f.type?c.value!==f.value&&(q=c.value<f.value?-1:1):q=c.type<f.type?-1:1;if(c=q*g[d].descending)break}return c});return a=a.map(function(a){return a.value})}}function La(a){E(a)&&(a={link:a});a.restrict=a.restrict||"AC";return da(a)}function Id(a,
b,d,c,e){var f=this,g=[];f.$error={};f.$$success={};f.$pending=void 0;f.$name=e(b.name||b.ngForm||"")(d);f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;f.$submitted=!1;f.$$parentForm=Jb;f.$rollbackViewValue=function(){q(g,function(a){a.$rollbackViewValue()})};f.$commitViewValue=function(){q(g,function(a){a.$commitViewValue()})};f.$addControl=function(a){Qa(a.$name,"input");g.push(a);a.$name&&(f[a.$name]=a);a.$$parentForm=f};f.$$renameControl=function(a,b){var c=a.$name;f[c]===a&&delete f[c];
f[b]=a;a.$name=b};f.$removeControl=function(a){a.$name&&f[a.$name]===a&&delete f[a.$name];q(f.$pending,function(b,c){f.$setValidity(c,null,a)});q(f.$error,function(b,c){f.$setValidity(c,null,a)});q(f.$$success,function(b,c){f.$setValidity(c,null,a)});Za(g,a);a.$$parentForm=Jb};Jd({ctrl:this,$element:a,set:function(a,b,c){var d=a[b];d?-1===d.indexOf(c)&&d.push(c):a[b]=[c]},unset:function(a,b,c){var d=a[b];d&&(Za(d,c),0===d.length&&delete a[b])},$animate:c});f.$setDirty=function(){c.removeClass(a,Ua);
c.addClass(a,Kb);f.$dirty=!0;f.$pristine=!1;f.$$parentForm.$setDirty()};f.$setPristine=function(){c.setClass(a,Ua,Kb+" ng-submitted");f.$dirty=!1;f.$pristine=!0;f.$submitted=!1;q(g,function(a){a.$setPristine()})};f.$setUntouched=function(){q(g,function(a){a.$setUntouched()})};f.$setSubmitted=function(){c.addClass(a,"ng-submitted");f.$submitted=!0;f.$$parentForm.$setSubmitted()}}function lc(a){a.$formatters.push(function(b){return a.$isEmpty(b)?b:b.toString()})}function jb(a,b,d,c,e,f){var g=P(b[0].type);
if(!e.android){var h=!1;b.on("compositionstart",function(){h=!0});b.on("compositionend",function(){h=!1;l()})}var k,l=function(a){k&&(f.defer.cancel(k),k=null);if(!h){var e=b.val();a=a&&a.type;"password"===g||d.ngTrim&&"false"===d.ngTrim||(e=V(e));(c.$viewValue!==e||""===e&&c.$$hasNativeValidators)&&c.$setViewValue(e,a)}};if(e.hasEvent("input"))b.on("input",l);else{var n=function(a,b,c){k||(k=f.defer(function(){k=null;b&&b.value===c||l(a)}))};b.on("keydown",function(a){var b=a.keyCode;91===b||15<
b&&19>b||37<=b&&40>=b||n(a,this,this.value)});if(e.hasEvent("paste"))b.on("paste cut",n)}b.on("change",l);if(Kd[g]&&c.$$hasNativeValidators&&g===d.type)b.on("keydown wheel mousedown",function(a){if(!k){var b=this.validity,c=b.badInput,d=b.typeMismatch;k=f.defer(function(){k=null;b.badInput===c&&b.typeMismatch===d||l(a)})}});c.$render=function(){var a=c.$isEmpty(c.$viewValue)?"":c.$viewValue;b.val()!==a&&b.val(a)}}function Lb(a,b){return function(d,c){var e,f;if(fa(d))return d;if(F(d)){'"'==d.charAt(0)&&
'"'==d.charAt(d.length-1)&&(d=d.substring(1,d.length-1));if(yg.test(d))return new Date(d);a.lastIndex=0;if(e=a.exec(d))return e.shift(),f=c?{yyyy:c.getFullYear(),MM:c.getMonth()+1,dd:c.getDate(),HH:c.getHours(),mm:c.getMinutes(),ss:c.getSeconds(),sss:c.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},q(e,function(a,c){c<b.length&&(f[b[c]]=+a)}),new Date(f.yyyy,f.MM-1,f.dd,f.HH,f.mm,f.ss||0,1E3*f.sss||0)}return NaN}}function kb(a,b,d,c){return function(e,f,g,h,k,l,n){function m(a){return a&&
!(a.getTime&&a.getTime()!==a.getTime())}function r(a){return x(a)&&!fa(a)?d(a)||void 0:a}Ld(e,f,g,h);jb(e,f,g,h,k,l);var q=h&&h.$options&&h.$options.timezone,s;h.$$parserName=a;h.$parsers.push(function(a){if(h.$isEmpty(a))return null;if(b.test(a))return a=d(a,s),q&&(a=Qb(a,q)),a});h.$formatters.push(function(a){if(a&&!fa(a))throw lb("datefmt",a);if(m(a))return(s=a)&&q&&(s=Qb(s,q,!0)),n("date")(a,c,q);s=null;return""});if(x(g.min)||g.ngMin){var w;h.$validators.min=function(a){return!m(a)||y(w)||d(a)>=
w};g.$observe("min",function(a){w=r(a);h.$validate()})}if(x(g.max)||g.ngMax){var p;h.$validators.max=function(a){return!m(a)||y(p)||d(a)<=p};g.$observe("max",function(a){p=r(a);h.$validate()})}}}function Ld(a,b,d,c){(c.$$hasNativeValidators=G(b[0].validity))&&c.$parsers.push(function(a){var c=b.prop("validity")||{};return c.badInput||c.typeMismatch?void 0:a})}function Md(a,b,d,c,e){if(x(c)){a=a(c);if(!a.constant)throw lb("constexpr",d,c);return a(b)}return e}function mc(a,b){a="ngClass"+a;return["$animate",
function(d){function c(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],n=0;n<b.length;n++)if(e==b[n])continue a;c.push(e)}return c}function e(a){var b=[];return K(a)?(q(a,function(a){b=b.concat(e(a))}),b):F(a)?a.split(" "):G(a)?(q(a,function(a,c){a&&(b=b.concat(c.split(" ")))}),b):a}return{restrict:"AC",link:function(f,g,h){function k(a){a=l(a,1);h.$addClass(a)}function l(a,b){var c=g.data("$classCounts")||T(),d=[];q(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});g.data("$classCounts",
c);return d.join(" ")}function n(a,b){var e=c(b,a),f=c(a,b),e=l(e,1),f=l(f,-1);e&&e.length&&d.addClass(g,e);f&&f.length&&d.removeClass(g,f)}function m(a){if(!0===b||f.$index%2===b){var c=e(a||[]);if(!r)k(c);else if(!pa(a,r)){var d=e(r);n(d,c)}}r=K(a)?a.map(function(a){return ha(a)}):ha(a)}var r;f.$watch(h[a],m,!0);h.$observe("class",function(b){m(f.$eval(h[a]))});"ngClass"!==a&&f.$watch("$index",function(c,d){var g=c&1;if(g!==(d&1)){var m=e(f.$eval(h[a]));g===b?k(m):(g=l(m,-1),h.$removeClass(g))}})}}}]}
function Jd(a){function b(a,b){b&&!f[a]?(k.addClass(e,a),f[a]=!0):!b&&f[a]&&(k.removeClass(e,a),f[a]=!1)}function d(a,c){a=a?"-"+zc(a,"-"):"";b(mb+a,!0===c);b(Nd+a,!1===c)}var c=a.ctrl,e=a.$element,f={},g=a.set,h=a.unset,k=a.$animate;f[Nd]=!(f[mb]=e.hasClass(mb));c.$setValidity=function(a,e,f){y(e)?(c.$pending||(c.$pending={}),g(c.$pending,a,f)):(c.$pending&&h(c.$pending,a,f),Od(c.$pending)&&(c.$pending=void 0));Da(e)?e?(h(c.$error,a,f),g(c.$$success,a,f)):(g(c.$error,a,f),h(c.$$success,a,f)):(h(c.$error,
a,f),h(c.$$success,a,f));c.$pending?(b(Pd,!0),c.$valid=c.$invalid=void 0,d("",null)):(b(Pd,!1),c.$valid=Od(c.$error),c.$invalid=!c.$valid,d("",c.$valid));e=c.$pending&&c.$pending[a]?void 0:c.$error[a]?!1:c.$$success[a]?!0:null;d(a,e);c.$$parentForm.$setValidity(a,e,c)}}function Od(a){if(a)for(var b in a)if(a.hasOwnProperty(b))return!1;return!0}var zg=/^\/(.+)\/([a-z]*)$/,ua=Object.prototype.hasOwnProperty,P=function(a){return F(a)?a.toLowerCase():a},sb=function(a){return F(a)?a.toUpperCase():a},Ca,
B,Z,za=[].slice,Zf=[].splice,Ag=[].push,ma=Object.prototype.toString,sc=Object.getPrototypeOf,Aa=O("ng"),ea=v.angular||(v.angular={}),Sb,nb=0;Ca=v.document.documentMode;C.$inject=[];Xa.$inject=[];var K=Array.isArray,$d=/^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/,V=function(a){return F(a)?a.trim():a},vd=function(a){return a.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08")},Ea=function(){if(!x(Ea.rules)){var a=v.document.querySelector("[ng-csp]")||
v.document.querySelector("[data-ng-csp]");if(a){var b=a.getAttribute("ng-csp")||a.getAttribute("data-ng-csp");Ea.rules={noUnsafeEval:!b||-1!==b.indexOf("no-unsafe-eval"),noInlineStyle:!b||-1!==b.indexOf("no-inline-style")}}else{a=Ea;try{new Function(""),b=!1}catch(d){b=!0}a.rules={noUnsafeEval:b,noInlineStyle:!1}}}return Ea.rules},pb=function(){if(x(pb.name_))return pb.name_;var a,b,d=Na.length,c,e;for(b=0;b<d;++b)if(c=Na[b],a=v.document.querySelector("["+c.replace(":","\\:")+"jq]")){e=a.getAttribute(c+
"jq");break}return pb.name_=e},ce=/:/g,Na=["ng-","data-ng-","ng:","x-ng-"],he=/[A-Z]/g,Ac=!1,Ma=3,le={full:"1.5.5",major:1,minor:5,dot:5,codeName:"material-conspiration"};U.expando="ng339";var eb=U.cache={},Nf=1;U._data=function(a){return this.cache[a[this.expando]]||{}};var If=/([\:\-\_]+(.))/g,Jf=/^moz([A-Z])/,wb={mouseleave:"mouseout",mouseenter:"mouseover"},Ub=O("jqLite"),Mf=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Tb=/<|&#?\w+;/,Kf=/<([\w:-]+)/,Lf=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,
ia={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ia.optgroup=ia.option;ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead;ia.th=ia.td;var Sf=v.Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)},Oa=U.prototype={ready:function(a){function b(){d||(d=!0,a())}var d=!1;"complete"===
v.document.readyState?v.setTimeout(b):(this.on("DOMContentLoaded",b),U(v).on("load",b))},toString:function(){var a=[];q(this,function(b){a.push(""+b)});return"["+a.join(", ")+"]"},eq:function(a){return 0<=a?B(this[a]):B(this[this.length+a])},length:0,push:Ag,sort:[].sort,splice:[].splice},Cb={};q("multiple selected checked disabled readOnly required open".split(" "),function(a){Cb[P(a)]=a});var Sc={};q("input select option textarea button form details".split(" "),function(a){Sc[a]=!0});var ad={ngMinlength:"minlength",
ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};q({data:Wb,removeData:db,hasData:function(a){for(var b in eb[a.ng339])return!0;return!1},cleanData:function(a){for(var b=0,d=a.length;b<d;b++)db(a[b])}},function(a,b){U[b]=a});q({data:Wb,inheritedData:Ab,scope:function(a){return B.data(a,"$scope")||Ab(a.parentNode||a,["$isolateScope","$scope"])},isolateScope:function(a){return B.data(a,"$isolateScope")||B.data(a,"$isolateScopeNoTemplate")},controller:Pc,injector:function(a){return Ab(a,
"$injector")},removeAttr:function(a,b){a.removeAttribute(b)},hasClass:xb,css:function(a,b,d){b=cb(b);if(x(d))a.style[b]=d;else return a.style[b]},attr:function(a,b,d){var c=a.nodeType;if(c!==Ma&&2!==c&&8!==c)if(c=P(b),Cb[c])if(x(d))d?(a[b]=!0,a.setAttribute(b,c)):(a[b]=!1,a.removeAttribute(c));else return a[b]||(a.attributes.getNamedItem(b)||C).specified?c:void 0;else if(x(d))a.setAttribute(b,d);else if(a.getAttribute)return a=a.getAttribute(b,2),null===a?void 0:a},prop:function(a,b,d){if(x(d))a[b]=
d;else return a[b]},text:function(){function a(a,d){if(y(d)){var c=a.nodeType;return 1===c||c===Ma?a.textContent:""}a.textContent=d}a.$dv="";return a}(),val:function(a,b){if(y(b)){if(a.multiple&&"select"===va(a)){var d=[];q(a.options,function(a){a.selected&&d.push(a.value||a.text)});return 0===d.length?null:d}return a.value}a.value=b},html:function(a,b){if(y(b))return a.innerHTML;ub(a,!0);a.innerHTML=b},empty:Qc},function(a,b){U.prototype[b]=function(b,c){var e,f,g=this.length;if(a!==Qc&&y(2==a.length&&
a!==xb&&a!==Pc?b:c)){if(G(b)){for(e=0;e<g;e++)if(a===Wb)a(this[e],b);else for(f in b)a(this[e],f,b[f]);return this}e=a.$dv;g=y(e)?Math.min(g,1):g;for(f=0;f<g;f++){var h=a(this[f],b,c);e=e?e+h:h}return e}for(e=0;e<g;e++)a(this[e],b,c);return this}});q({removeData:db,on:function(a,b,d,c){if(x(c))throw Ub("onargs");if(Kc(a)){c=vb(a,!0);var e=c.events,f=c.handle;f||(f=c.handle=Pf(a,e));c=0<=b.indexOf(" ")?b.split(" "):[b];for(var g=c.length,h=function(b,c,g){var h=e[b];h||(h=e[b]=[],h.specialHandlerWrapper=
c,"$destroy"===b||g||a.addEventListener(b,f,!1));h.push(d)};g--;)b=c[g],wb[b]?(h(wb[b],Rf),h(b,void 0,!0)):h(b)}},off:Oc,one:function(a,b,d){a=B(a);a.on(b,function e(){a.off(b,d);a.off(b,e)});a.on(b,d)},replaceWith:function(a,b){var d,c=a.parentNode;ub(a);q(new U(b),function(b){d?c.insertBefore(b,d.nextSibling):c.replaceChild(b,a);d=b})},children:function(a){var b=[];q(a.childNodes,function(a){1===a.nodeType&&b.push(a)});return b},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,
b){var d=a.nodeType;if(1===d||11===d){b=new U(b);for(var d=0,c=b.length;d<c;d++)a.appendChild(b[d])}},prepend:function(a,b){if(1===a.nodeType){var d=a.firstChild;q(new U(b),function(b){a.insertBefore(b,d)})}},wrap:function(a,b){Mc(a,B(b).eq(0).clone()[0])},remove:Bb,detach:function(a){Bb(a,!0)},after:function(a,b){var d=a,c=a.parentNode;b=new U(b);for(var e=0,f=b.length;e<f;e++){var g=b[e];c.insertBefore(g,d.nextSibling);d=g}},addClass:zb,removeClass:yb,toggleClass:function(a,b,d){b&&q(b.split(" "),
function(b){var e=d;y(e)&&(e=!xb(a,b));(e?zb:yb)(a,b)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){return a.nextElementSibling},find:function(a,b){return a.getElementsByTagName?a.getElementsByTagName(b):[]},clone:Vb,triggerHandler:function(a,b,d){var c,e,f=b.type||b,g=vb(a);if(g=(g=g&&g.events)&&g[f])c={preventDefault:function(){this.defaultPrevented=!0},isDefaultPrevented:function(){return!0===this.defaultPrevented},stopImmediatePropagation:function(){this.immediatePropagationStopped=
!0},isImmediatePropagationStopped:function(){return!0===this.immediatePropagationStopped},stopPropagation:C,type:f,target:a},b.type&&(c=R(c,b)),b=ha(g),e=d?[c].concat(d):[c],q(b,function(b){c.isImmediatePropagationStopped()||b.apply(a,e)})}},function(a,b){U.prototype[b]=function(b,c,e){for(var f,g=0,h=this.length;g<h;g++)y(f)?(f=a(this[g],b,c,e),x(f)&&(f=B(f))):Nc(f,a(this[g],b,c,e));return x(f)?f:this};U.prototype.bind=U.prototype.on;U.prototype.unbind=U.prototype.off});Ra.prototype={put:function(a,
b){this[Fa(a,this.nextUid)]=b},get:function(a){return this[Fa(a,this.nextUid)]},remove:function(a){var b=this[a=Fa(a,this.nextUid)];delete this[a];return b}};var Gf=[function(){this.$get=[function(){return Ra}]}],Uf=/^([^\(]+?)=>/,Vf=/^[^\(]*\(\s*([^\)]*)\)/m,Bg=/,/,Cg=/^\s*(_?)(\S+?)\1\s*$/,Tf=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Ga=O("$injector");bb.$$annotate=function(a,b,d){var c;if("function"===typeof a){if(!(c=a.$inject)){c=[];if(a.length){if(b)throw F(d)&&d||(d=a.name||Wf(a)),Ga("strictdi",d);
b=Tc(a);q(b[1].split(Bg),function(a){a.replace(Cg,function(a,b,d){c.push(d)})})}a.$inject=c}}else K(a)?(b=a.length-1,Pa(a[b],"fn"),c=a.slice(0,b)):Pa(a,"fn",!0);return c};var Qd=O("$animate"),Ze=function(){this.$get=C},$e=function(){var a=new Ra,b=[];this.$get=["$$AnimateRunner","$rootScope",function(d,c){function e(a,b,c){var d=!1;b&&(b=F(b)?b.split(" "):K(b)?b:[],q(b,function(b){b&&(d=!0,a[b]=c)}));return d}function f(){q(b,function(b){var c=a.get(b);if(c){var d=Xf(b.attr("class")),e="",f="";q(c,
function(a,b){a!==!!d[b]&&(a?e+=(e.length?" ":"")+b:f+=(f.length?" ":"")+b)});q(b,function(a){e&&zb(a,e);f&&yb(a,f)});a.remove(b)}});b.length=0}return{enabled:C,on:C,off:C,pin:C,push:function(g,h,k,l){l&&l();k=k||{};k.from&&g.css(k.from);k.to&&g.css(k.to);if(k.addClass||k.removeClass)if(h=k.addClass,l=k.removeClass,k=a.get(g)||{},h=e(k,h,!0),l=e(k,l,!1),h||l)a.put(g,k),b.push(g),1===b.length&&c.$$postDigest(f);g=new d;g.complete();return g}}}]},Xe=["$provide",function(a){var b=this;this.$$registeredAnimations=
Object.create(null);this.register=function(d,c){if(d&&"."!==d.charAt(0))throw Qd("notcsel",d);var e=d+"-animation";b.$$registeredAnimations[d.substr(1)]=e;a.factory(e,c)};this.classNameFilter=function(a){if(1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?a:null)&&/(\s+|\/)ng-animate(\s+|\/)/.test(this.$$classNameFilter.toString()))throw Qd("nongcls","ng-animate");return this.$$classNameFilter};this.$get=["$$animateQueue",function(a){function b(a,c,d){if(d){var h;a:{for(h=0;h<d.length;h++){var k=
d[h];if(1===k.nodeType){h=k;break a}}h=void 0}!h||h.parentNode||h.previousElementSibling||(d=null)}d?d.after(a):c.prepend(a)}return{on:a.on,off:a.off,pin:a.pin,enabled:a.enabled,cancel:function(a){a.end&&a.end()},enter:function(e,f,g,h){f=f&&B(f);g=g&&B(g);f=f||g.parent();b(e,f,g);return a.push(e,"enter",Ha(h))},move:function(e,f,g,h){f=f&&B(f);g=g&&B(g);f=f||g.parent();b(e,f,g);return a.push(e,"move",Ha(h))},leave:function(b,c){return a.push(b,"leave",Ha(c),function(){b.remove()})},addClass:function(b,
c,g){g=Ha(g);g.addClass=fb(g.addclass,c);return a.push(b,"addClass",g)},removeClass:function(b,c,g){g=Ha(g);g.removeClass=fb(g.removeClass,c);return a.push(b,"removeClass",g)},setClass:function(b,c,g,h){h=Ha(h);h.addClass=fb(h.addClass,c);h.removeClass=fb(h.removeClass,g);return a.push(b,"setClass",h)},animate:function(b,c,g,h,k){k=Ha(k);k.from=k.from?R(k.from,c):c;k.to=k.to?R(k.to,g):g;k.tempClasses=fb(k.tempClasses,h||"ng-inline-animate");return a.push(b,"animate",k)}}}]}],bf=function(){this.$get=
["$$rAF",function(a){function b(b){d.push(b);1<d.length||a(function(){for(var a=0;a<d.length;a++)d[a]();d=[]})}var d=[];return function(){var a=!1;b(function(){a=!0});return function(d){a?d():b(d)}}}]},af=function(){this.$get=["$q","$sniffer","$$animateAsyncRun","$document","$timeout",function(a,b,d,c,e){function f(a){this.setHost(a);var b=d();this._doneCallbacks=[];this._tick=function(a){var d=c[0];d&&d.hidden?e(a,0,!1):b(a)};this._state=0}f.chain=function(a,b){function c(){if(d===a.length)b(!0);
else a[d](function(a){!1===a?b(!1):(d++,c())})}var d=0;c()};f.all=function(a,b){function c(f){e=e&&f;++d===a.length&&b(e)}var d=0,e=!0;q(a,function(a){a.done(c)})};f.prototype={setHost:function(a){this.host=a||{}},done:function(a){2===this._state?a():this._doneCallbacks.push(a)},progress:C,getPromise:function(){if(!this.promise){var b=this;this.promise=a(function(a,c){b.done(function(b){!1===b?c():a()})})}return this.promise},then:function(a,b){return this.getPromise().then(a,b)},"catch":function(a){return this.getPromise()["catch"](a)},
"finally":function(a){return this.getPromise()["finally"](a)},pause:function(){this.host.pause&&this.host.pause()},resume:function(){this.host.resume&&this.host.resume()},end:function(){this.host.end&&this.host.end();this._resolve(!0)},cancel:function(){this.host.cancel&&this.host.cancel();this._resolve(!1)},complete:function(a){var b=this;0===b._state&&(b._state=1,b._tick(function(){b._resolve(a)}))},_resolve:function(a){2!==this._state&&(q(this._doneCallbacks,function(b){b(a)}),this._doneCallbacks.length=
0,this._state=2)}};return f}]},Ye=function(){this.$get=["$$rAF","$q","$$AnimateRunner",function(a,b,d){return function(b,e){function f(){a(function(){g.addClass&&(b.addClass(g.addClass),g.addClass=null);g.removeClass&&(b.removeClass(g.removeClass),g.removeClass=null);g.to&&(b.css(g.to),g.to=null);h||k.complete();h=!0});return k}var g=e||{};g.$$prepared||(g=qa(g));g.cleanupStyles&&(g.from=g.to=null);g.from&&(b.css(g.from),g.from=null);var h,k=new d;return{start:f,end:f}}}]},ga=O("$compile"),Zb=new function(){};
Cc.$inject=["$provide","$$sanitizeUriProvider"];Db.prototype.isFirstChange=function(){return this.previousValue===Zb};var Vc=/^((?:x|data)[\:\-_])/i,$f=O("$controller"),bd=/^(\S+)(\s+as\s+([\w$]+))?$/,hf=function(){this.$get=["$document",function(a){return function(b){b?!b.nodeType&&b instanceof B&&(b=b[0]):b=a[0].body;return b.offsetWidth+1}}]},cd="application/json",bc={"Content-Type":cd+";charset=utf-8"},bg=/^\[|^\{(?!\{)/,cg={"[":/]$/,"{":/}$/},ag=/^\)\]\}',?\n/,Dg=O("$http"),gd=function(a){return function(){throw Dg("legacy",
a);}},Ja=ea.$interpolateMinErr=O("$interpolate");Ja.throwNoconcat=function(a){throw Ja("noconcat",a);};Ja.interr=function(a,b){return Ja("interr",a,b.toString())};var Eg=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,eg={http:80,https:443,ftp:21},Eb=O("$location"),Fg={$$html5:!1,$$replace:!1,absUrl:Fb("$$absUrl"),url:function(a){if(y(a))return this.$$url;var b=Eg.exec(a);(b[1]||""===a)&&this.path(decodeURIComponent(b[1]));(b[2]||b[1]||""===a)&&this.search(b[3]||"");this.hash(b[5]||"");return this},protocol:Fb("$$protocol"),
host:Fb("$$host"),port:Fb("$$port"),path:ld("$$path",function(a){a=null!==a?a.toString():"";return"/"==a.charAt(0)?a:"/"+a}),search:function(a,b){switch(arguments.length){case 0:return this.$$search;case 1:if(F(a)||Q(a))a=a.toString(),this.$$search=xc(a);else if(G(a))a=qa(a,{}),q(a,function(b,c){null==b&&delete a[c]}),this.$$search=a;else throw Eb("isrcharg");break;default:y(b)||null===b?delete this.$$search[a]:this.$$search[a]=b}this.$$compose();return this},hash:ld("$$hash",function(a){return null!==
a?a.toString():""}),replace:function(){this.$$replace=!0;return this}};q([kd,ec,dc],function(a){a.prototype=Object.create(Fg);a.prototype.state=function(b){if(!arguments.length)return this.$$state;if(a!==dc||!this.$$html5)throw Eb("nostate");this.$$state=y(b)?null:b;return this}});var ca=O("$parse"),gg=Function.prototype.call,hg=Function.prototype.apply,ig=Function.prototype.bind,Mb=T();q("+ - * / % === !== == != < > <= >= && || ! = |".split(" "),function(a){Mb[a]=!0});var Gg={n:"\n",f:"\f",r:"\r",
t:"\t",v:"\v","'":"'",'"':'"'},gc=function(a){this.options=a};gc.prototype={constructor:gc,lex:function(a){this.text=a;this.index=0;for(this.tokens=[];this.index<this.text.length;)if(a=this.text.charAt(this.index),'"'===a||"'"===a)this.readString(a);else if(this.isNumber(a)||"."===a&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdentifierStart(this.peekMultichar()))this.readIdent();else if(this.is(a,"(){}[].,;:?"))this.tokens.push({index:this.index,text:a}),this.index++;else if(this.isWhitespace(a))this.index++;
else{var b=a+this.peek(),d=b+this.peek(2),c=Mb[b],e=Mb[d];Mb[a]||c||e?(a=e?d:c?b:a,this.tokens.push({index:this.index,text:a,operator:!0}),this.index+=a.length):this.throwError("Unexpected next character ",this.index,this.index+1)}return this.tokens},is:function(a,b){return-1!==b.indexOf(a)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a&&"string"===typeof a},isWhitespace:function(a){return" "===a||"\r"===a||
"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdentifierStart:function(a){return this.options.isIdentifierStart?this.options.isIdentifierStart(a,this.codePointAt(a)):this.isValidIdentifierStart(a)},isValidIdentifierStart:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isIdentifierContinue:function(a){return this.options.isIdentifierContinue?this.options.isIdentifierContinue(a,this.codePointAt(a)):this.isValidIdentifierContinue(a)},isValidIdentifierContinue:function(a,b){return this.isValidIdentifierStart(a,
b)||this.isNumber(a)},codePointAt:function(a){return 1===a.length?a.charCodeAt(0):(a.charCodeAt(0)<<10)+a.charCodeAt(1)-56613888},peekMultichar:function(){var a=this.text.charAt(this.index),b=this.peek();if(!b)return a;var d=a.charCodeAt(0),c=b.charCodeAt(0);return 55296<=d&&56319>=d&&56320<=c&&57343>=c?a+b:a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,b,d){d=d||this.index;b=x(b)?"s "+b+"-"+this.index+" ["+this.text.substring(b,d)+"]":" "+d;throw ca("lexerr",
a,b,this.text);},readNumber:function(){for(var a="",b=this.index;this.index<this.text.length;){var d=P(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var c=this.peek();if("e"==d&&this.isExpOperator(c))a+=d;else if(this.isExpOperator(d)&&c&&this.isNumber(c)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||c&&this.isNumber(c)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}this.tokens.push({index:b,text:a,constant:!0,value:Number(a)})},
readIdent:function(){var a=this.index;for(this.index+=this.peekMultichar().length;this.index<this.text.length;){var b=this.peekMultichar();if(!this.isIdentifierContinue(b))break;this.index+=b.length}this.tokens.push({index:a,text:this.text.slice(a,this.index),identifier:!0})},readString:function(a){var b=this.index;this.index++;for(var d="",c=a,e=!1;this.index<this.text.length;){var f=this.text.charAt(this.index),c=c+f;if(e)"u"===f?(e=this.text.substring(this.index+1,this.index+5),e.match(/[\da-f]{4}/i)||
this.throwError("Invalid unicode escape [\\u"+e+"]"),this.index+=4,d+=String.fromCharCode(parseInt(e,16))):d+=Gg[f]||f,e=!1;else if("\\"===f)e=!0;else{if(f===a){this.index++;this.tokens.push({index:b,text:c,constant:!0,value:d});return}d+=f}this.index++}this.throwError("Unterminated quote",b)}};var s=function(a,b){this.lexer=a;this.options=b};s.Program="Program";s.ExpressionStatement="ExpressionStatement";s.AssignmentExpression="AssignmentExpression";s.ConditionalExpression="ConditionalExpression";
s.LogicalExpression="LogicalExpression";s.BinaryExpression="BinaryExpression";s.UnaryExpression="UnaryExpression";s.CallExpression="CallExpression";s.MemberExpression="MemberExpression";s.Identifier="Identifier";s.Literal="Literal";s.ArrayExpression="ArrayExpression";s.Property="Property";s.ObjectExpression="ObjectExpression";s.ThisExpression="ThisExpression";s.LocalsExpression="LocalsExpression";s.NGValueParameter="NGValueParameter";s.prototype={ast:function(a){this.text=a;this.tokens=this.lexer.lex(a);
a=this.program();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);return a},program:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.expressionStatement()),!this.expect(";"))return{type:s.Program,body:a}},expressionStatement:function(){return{type:s.ExpressionStatement,expression:this.filterChain()}},filterChain:function(){for(var a=this.expression();this.expect("|");)a=this.filter(a);return a},expression:function(){return this.assignment()},
assignment:function(){var a=this.ternary();this.expect("=")&&(a={type:s.AssignmentExpression,left:a,right:this.assignment(),operator:"="});return a},ternary:function(){var a=this.logicalOR(),b,d;return this.expect("?")&&(b=this.expression(),this.consume(":"))?(d=this.expression(),{type:s.ConditionalExpression,test:a,alternate:b,consequent:d}):a},logicalOR:function(){for(var a=this.logicalAND();this.expect("||");)a={type:s.LogicalExpression,operator:"||",left:a,right:this.logicalAND()};return a},logicalAND:function(){for(var a=
this.equality();this.expect("&&");)a={type:s.LogicalExpression,operator:"&&",left:a,right:this.equality()};return a},equality:function(){for(var a=this.relational(),b;b=this.expect("==","!=","===","!==");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.relational()};return a},relational:function(){for(var a=this.additive(),b;b=this.expect("<",">","<=",">=");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(),
b;b=this.expect("+","-");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),b;b=this.expect("*","/","%");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+","-","!"))?{type:s.UnaryExpression,operator:a.text,prefix:!0,argument:this.unary()}:this.primary()},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")):
this.expect("[")?a=this.arrayDeclaration():this.expect("{")?a=this.object():this.selfReferential.hasOwnProperty(this.peek().text)?a=qa(this.selfReferential[this.consume().text]):this.options.literals.hasOwnProperty(this.peek().text)?a={type:s.Literal,value:this.options.literals[this.consume().text]}:this.peek().identifier?a=this.identifier():this.peek().constant?a=this.constant():this.throwError("not a primary expression",this.peek());for(var b;b=this.expect("(","[",".");)"("===b.text?(a={type:s.CallExpression,
callee:a,arguments:this.parseArguments()},this.consume(")")):"["===b.text?(a={type:s.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===b.text?a={type:s.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var b={type:s.CallExpression,callee:this.identifier(),arguments:a,filter:!0};this.expect(":");)a.push(this.expression());return b},parseArguments:function(){var a=[];if(")"!==
this.peekToken().text){do a.push(this.expression());while(this.expect(","))}return a},identifier:function(){var a=this.consume();a.identifier||this.throwError("is not a valid identifier",a);return{type:s.Identifier,name:a.text}},constant:function(){return{type:s.Literal,value:this.consume().value}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;a.push(this.expression())}while(this.expect(","))}this.consume("]");return{type:s.ArrayExpression,elements:a}},
object:function(){var a=[],b;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;b={type:s.Property,kind:"init"};this.peek().constant?b.key=this.constant():this.peek().identifier?b.key=this.identifier():this.throwError("invalid key",this.peek());this.consume(":");b.value=this.expression();a.push(b)}while(this.expect(","))}this.consume("}");return{type:s.ObjectExpression,properties:a}},throwError:function(a,b){throw ca("syntax",b.text,a,b.index+1,this.text,this.text.substring(b.index));},consume:function(a){if(0===
this.tokens.length)throw ca("ueoe",this.text);var b=this.expect(a);b||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return b},peekToken:function(){if(0===this.tokens.length)throw ca("ueoe",this.text);return this.tokens[0]},peek:function(a,b,d,c){return this.peekAhead(0,a,b,d,c)},peekAhead:function(a,b,d,c,e){if(this.tokens.length>a){a=this.tokens[a];var f=a.text;if(f===b||f===d||f===c||f===e||!(b||d||c||e))return a}return!1},expect:function(a,b,d,c){return(a=this.peek(a,b,d,c))?
(this.tokens.shift(),a):!1},selfReferential:{"this":{type:s.ThisExpression},$locals:{type:s.LocalsExpression}}};sd.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.state={nextId:0,filters:{},expensiveChecks:b,fn:{vars:[],body:[],own:{}},assign:{vars:[],body:[],own:{}},inputs:[]};aa(c,d.$filter);var e="",f;this.stage="assign";if(f=qd(c))this.state.computing="assign",e=this.nextId(),this.recurse(f,e),this.return_(e),e="fn.assign="+this.generateFunction("assign","s,v,l");f=od(c.body);
d.stage="inputs";q(f,function(a,b){var c="fn"+b;d.state[c]={vars:[],body:[],own:{}};d.state.computing=c;var e=d.nextId();d.recurse(a,e);d.return_(e);d.state.inputs.push(c);a.watchId=b});this.state.computing="fn";this.stage="main";this.recurse(c);e='"'+this.USE+" "+this.STRICT+'";\n'+this.filterPrefix()+"var fn="+this.generateFunction("fn","s,l,a,i")+e+this.watchFns()+"return fn;";e=(new Function("$filter","ensureSafeMemberName","ensureSafeObject","ensureSafeFunction","getStringValue","ensureSafeAssignContext",
"ifDefined","plus","text",e))(this.$filter,Ta,sa,md,fg,Gb,jg,nd,a);this.state=this.stage=void 0;e.literal=rd(c);e.constant=c.constant;return e},USE:"use",STRICT:"strict",watchFns:function(){var a=[],b=this.state.inputs,d=this;q(b,function(b){a.push("var "+b+"="+d.generateFunction(b,"s"))});b.length&&a.push("fn.inputs=["+b.join(",")+"];");return a.join("")},generateFunction:function(a,b){return"function("+b+"){"+this.varsPrefix(a)+this.body(a)+"};"},filterPrefix:function(){var a=[],b=this;q(this.state.filters,
function(d,c){a.push(d+"=$filter("+b.escape(c)+")")});return a.length?"var "+a.join(",")+";":""},varsPrefix:function(a){return this.state[a].vars.length?"var "+this.state[a].vars.join(",")+";":""},body:function(a){return this.state[a].body.join("")},recurse:function(a,b,d,c,e,f){var g,h,k=this,l,n;c=c||C;if(!f&&x(a.watchId))b=b||this.nextId(),this.if_("i",this.lazyAssign(b,this.computedMember("i",a.watchId)),this.lazyRecurse(a,b,d,c,e,!0));else switch(a.type){case s.Program:q(a.body,function(b,c){k.recurse(b.expression,
void 0,void 0,function(a){h=a});c!==a.body.length-1?k.current().body.push(h,";"):k.return_(h)});break;case s.Literal:n=this.escape(a.value);this.assign(b,n);c(n);break;case s.UnaryExpression:this.recurse(a.argument,void 0,void 0,function(a){h=a});n=a.operator+"("+this.ifDefined(h,0)+")";this.assign(b,n);c(n);break;case s.BinaryExpression:this.recurse(a.left,void 0,void 0,function(a){g=a});this.recurse(a.right,void 0,void 0,function(a){h=a});n="+"===a.operator?this.plus(g,h):"-"===a.operator?this.ifDefined(g,
0)+a.operator+this.ifDefined(h,0):"("+g+")"+a.operator+"("+h+")";this.assign(b,n);c(n);break;case s.LogicalExpression:b=b||this.nextId();k.recurse(a.left,b);k.if_("&&"===a.operator?b:k.not(b),k.lazyRecurse(a.right,b));c(b);break;case s.ConditionalExpression:b=b||this.nextId();k.recurse(a.test,b);k.if_(b,k.lazyRecurse(a.alternate,b),k.lazyRecurse(a.consequent,b));c(b);break;case s.Identifier:b=b||this.nextId();d&&(d.context="inputs"===k.stage?"s":this.assign(this.nextId(),this.getHasOwnProperty("l",
a.name)+"?l:s"),d.computed=!1,d.name=a.name);Ta(a.name);k.if_("inputs"===k.stage||k.not(k.getHasOwnProperty("l",a.name)),function(){k.if_("inputs"===k.stage||"s",function(){e&&1!==e&&k.if_(k.not(k.nonComputedMember("s",a.name)),k.lazyAssign(k.nonComputedMember("s",a.name),"{}"));k.assign(b,k.nonComputedMember("s",a.name))})},b&&k.lazyAssign(b,k.nonComputedMember("l",a.name)));(k.state.expensiveChecks||Hb(a.name))&&k.addEnsureSafeObject(b);c(b);break;case s.MemberExpression:g=d&&(d.context=this.nextId())||
this.nextId();b=b||this.nextId();k.recurse(a.object,g,void 0,function(){k.if_(k.notNull(g),function(){e&&1!==e&&k.addEnsureSafeAssignContext(g);if(a.computed)h=k.nextId(),k.recurse(a.property,h),k.getStringValue(h),k.addEnsureSafeMemberName(h),e&&1!==e&&k.if_(k.not(k.computedMember(g,h)),k.lazyAssign(k.computedMember(g,h),"{}")),n=k.ensureSafeObject(k.computedMember(g,h)),k.assign(b,n),d&&(d.computed=!0,d.name=h);else{Ta(a.property.name);e&&1!==e&&k.if_(k.not(k.nonComputedMember(g,a.property.name)),
k.lazyAssign(k.nonComputedMember(g,a.property.name),"{}"));n=k.nonComputedMember(g,a.property.name);if(k.state.expensiveChecks||Hb(a.property.name))n=k.ensureSafeObject(n);k.assign(b,n);d&&(d.computed=!1,d.name=a.property.name)}},function(){k.assign(b,"undefined")});c(b)},!!e);break;case s.CallExpression:b=b||this.nextId();a.filter?(h=k.filter(a.callee.name),l=[],q(a.arguments,function(a){var b=k.nextId();k.recurse(a,b);l.push(b)}),n=h+"("+l.join(",")+")",k.assign(b,n),c(b)):(h=k.nextId(),g={},l=
[],k.recurse(a.callee,h,g,function(){k.if_(k.notNull(h),function(){k.addEnsureSafeFunction(h);q(a.arguments,function(a){k.recurse(a,k.nextId(),void 0,function(a){l.push(k.ensureSafeObject(a))})});g.name?(k.state.expensiveChecks||k.addEnsureSafeObject(g.context),n=k.member(g.context,g.name,g.computed)+"("+l.join(",")+")"):n=h+"("+l.join(",")+")";n=k.ensureSafeObject(n);k.assign(b,n)},function(){k.assign(b,"undefined")});c(b)}));break;case s.AssignmentExpression:h=this.nextId();g={};if(!pd(a.left))throw ca("lval");
this.recurse(a.left,void 0,g,function(){k.if_(k.notNull(g.context),function(){k.recurse(a.right,h);k.addEnsureSafeObject(k.member(g.context,g.name,g.computed));k.addEnsureSafeAssignContext(g.context);n=k.member(g.context,g.name,g.computed)+a.operator+h;k.assign(b,n);c(b||n)})},1);break;case s.ArrayExpression:l=[];q(a.elements,function(a){k.recurse(a,k.nextId(),void 0,function(a){l.push(a)})});n="["+l.join(",")+"]";this.assign(b,n);c(n);break;case s.ObjectExpression:l=[];q(a.properties,function(a){k.recurse(a.value,
k.nextId(),void 0,function(b){l.push(k.escape(a.key.type===s.Identifier?a.key.name:""+a.key.value)+":"+b)})});n="{"+l.join(",")+"}";this.assign(b,n);c(n);break;case s.ThisExpression:this.assign(b,"s");c("s");break;case s.LocalsExpression:this.assign(b,"l");c("l");break;case s.NGValueParameter:this.assign(b,"v"),c("v")}},getHasOwnProperty:function(a,b){var d=a+"."+b,c=this.current().own;c.hasOwnProperty(d)||(c[d]=this.nextId(!1,a+"&&("+this.escape(b)+" in "+a+")"));return c[d]},assign:function(a,b){if(a)return this.current().body.push(a,
"=",b,";"),a},filter:function(a){this.state.filters.hasOwnProperty(a)||(this.state.filters[a]=this.nextId(!0));return this.state.filters[a]},ifDefined:function(a,b){return"ifDefined("+a+","+this.escape(b)+")"},plus:function(a,b){return"plus("+a+","+b+")"},return_:function(a){this.current().body.push("return ",a,";")},if_:function(a,b,d){if(!0===a)b();else{var c=this.current().body;c.push("if(",a,"){");b();c.push("}");d&&(c.push("else{"),d(),c.push("}"))}},not:function(a){return"!("+a+")"},notNull:function(a){return a+
"!=null"},nonComputedMember:function(a,b){var d=/[^$_a-zA-Z0-9]/g;return/[$_a-zA-Z][$_a-zA-Z0-9]*/.test(b)?a+"."+b:a+'["'+b.replace(d,this.stringEscapeFn)+'"]'},computedMember:function(a,b){return a+"["+b+"]"},member:function(a,b,d){return d?this.computedMember(a,b):this.nonComputedMember(a,b)},addEnsureSafeObject:function(a){this.current().body.push(this.ensureSafeObject(a),";")},addEnsureSafeMemberName:function(a){this.current().body.push(this.ensureSafeMemberName(a),";")},addEnsureSafeFunction:function(a){this.current().body.push(this.ensureSafeFunction(a),
";")},addEnsureSafeAssignContext:function(a){this.current().body.push(this.ensureSafeAssignContext(a),";")},ensureSafeObject:function(a){return"ensureSafeObject("+a+",text)"},ensureSafeMemberName:function(a){return"ensureSafeMemberName("+a+",text)"},ensureSafeFunction:function(a){return"ensureSafeFunction("+a+",text)"},getStringValue:function(a){this.assign(a,"getStringValue("+a+")")},ensureSafeAssignContext:function(a){return"ensureSafeAssignContext("+a+",text)"},lazyRecurse:function(a,b,d,c,e,f){var g=
this;return function(){g.recurse(a,b,d,c,e,f)}},lazyAssign:function(a,b){var d=this;return function(){d.assign(a,b)}},stringEscapeRegex:/[^ a-zA-Z0-9]/g,stringEscapeFn:function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)},escape:function(a){if(F(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(Q(a))return a.toString();if(!0===a)return"true";if(!1===a)return"false";if(null===a)return"null";if("undefined"===typeof a)return"undefined";throw ca("esc");},nextId:function(a,
b){var d="v"+this.state.nextId++;a||this.current().vars.push(d+(b?"="+b:""));return d},current:function(){return this.state[this.state.computing]}};td.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.expression=a;this.expensiveChecks=b;aa(c,d.$filter);var e,f;if(e=qd(c))f=this.recurse(e);e=od(c.body);var g;e&&(g=[],q(e,function(a,b){var c=d.recurse(a);a.input=c;g.push(c);a.watchId=b}));var h=[];q(c.body,function(a){h.push(d.recurse(a.expression))});e=0===c.body.length?C:1===
c.body.length?h[0]:function(a,b){var c;q(h,function(d){c=d(a,b)});return c};f&&(e.assign=function(a,b,c){return f(a,c,b)});g&&(e.inputs=g);e.literal=rd(c);e.constant=c.constant;return e},recurse:function(a,b,d){var c,e,f=this,g;if(a.input)return this.inputs(a.input,a.watchId);switch(a.type){case s.Literal:return this.value(a.value,b);case s.UnaryExpression:return e=this.recurse(a.argument),this["unary"+a.operator](e,b);case s.BinaryExpression:return c=this.recurse(a.left),e=this.recurse(a.right),
this["binary"+a.operator](c,e,b);case s.LogicalExpression:return c=this.recurse(a.left),e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case s.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),b);case s.Identifier:return Ta(a.name,f.expression),f.identifier(a.name,f.expensiveChecks||Hb(a.name),b,d,f.expression);case s.MemberExpression:return c=this.recurse(a.object,!1,!!d),a.computed||(Ta(a.property.name,f.expression),
e=a.property.name),a.computed&&(e=this.recurse(a.property)),a.computed?this.computedMember(c,e,b,d,f.expression):this.nonComputedMember(c,e,f.expensiveChecks,b,d,f.expression);case s.CallExpression:return g=[],q(a.arguments,function(a){g.push(f.recurse(a))}),a.filter&&(e=this.$filter(a.callee.name)),a.filter||(e=this.recurse(a.callee,!0)),a.filter?function(a,c,d,f){for(var m=[],r=0;r<g.length;++r)m.push(g[r](a,c,d,f));a=e.apply(void 0,m,f);return b?{context:void 0,name:void 0,value:a}:a}:function(a,
c,d,n){var m=e(a,c,d,n),r;if(null!=m.value){sa(m.context,f.expression);md(m.value,f.expression);r=[];for(var q=0;q<g.length;++q)r.push(sa(g[q](a,c,d,n),f.expression));r=sa(m.value.apply(m.context,r),f.expression)}return b?{value:r}:r};case s.AssignmentExpression:return c=this.recurse(a.left,!0,1),e=this.recurse(a.right),function(a,d,g,n){var m=c(a,d,g,n);a=e(a,d,g,n);sa(m.value,f.expression);Gb(m.context);m.context[m.name]=a;return b?{value:a}:a};case s.ArrayExpression:return g=[],q(a.elements,function(a){g.push(f.recurse(a))}),
function(a,c,d,e){for(var f=[],r=0;r<g.length;++r)f.push(g[r](a,c,d,e));return b?{value:f}:f};case s.ObjectExpression:return g=[],q(a.properties,function(a){g.push({key:a.key.type===s.Identifier?a.key.name:""+a.key.value,value:f.recurse(a.value)})}),function(a,c,d,e){for(var f={},r=0;r<g.length;++r)f[g[r].key]=g[r].value(a,c,d,e);return b?{value:f}:f};case s.ThisExpression:return function(a){return b?{value:a}:a};case s.LocalsExpression:return function(a,c){return b?{value:c}:c};case s.NGValueParameter:return function(a,
c,d){return b?{value:d}:d}}},"unary+":function(a,b){return function(d,c,e,f){d=a(d,c,e,f);d=x(d)?+d:0;return b?{value:d}:d}},"unary-":function(a,b){return function(d,c,e,f){d=a(d,c,e,f);d=x(d)?-d:0;return b?{value:d}:d}},"unary!":function(a,b){return function(d,c,e,f){d=!a(d,c,e,f);return b?{value:d}:d}},"binary+":function(a,b,d){return function(c,e,f,g){var h=a(c,e,f,g);c=b(c,e,f,g);h=nd(h,c);return d?{value:h}:h}},"binary-":function(a,b,d){return function(c,e,f,g){var h=a(c,e,f,g);c=b(c,e,f,g);
h=(x(h)?h:0)-(x(c)?c:0);return d?{value:h}:h}},"binary*":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)*b(c,e,f,g);return d?{value:c}:c}},"binary/":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)/b(c,e,f,g);return d?{value:c}:c}},"binary%":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)%b(c,e,f,g);return d?{value:c}:c}},"binary===":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)===b(c,e,f,g);return d?{value:c}:c}},"binary!==":function(a,b,d){return function(c,e,f,g){c=a(c,
e,f,g)!==b(c,e,f,g);return d?{value:c}:c}},"binary==":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)==b(c,e,f,g);return d?{value:c}:c}},"binary!=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)!=b(c,e,f,g);return d?{value:c}:c}},"binary<":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)<b(c,e,f,g);return d?{value:c}:c}},"binary>":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>b(c,e,f,g);return d?{value:c}:c}},"binary<=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,
g)<=b(c,e,f,g);return d?{value:c}:c}},"binary>=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>=b(c,e,f,g);return d?{value:c}:c}},"binary&&":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)&&b(c,e,f,g);return d?{value:c}:c}},"binary||":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)||b(c,e,f,g);return d?{value:c}:c}},"ternary?:":function(a,b,d,c){return function(e,f,g,h){e=a(e,f,g,h)?b(e,f,g,h):d(e,f,g,h);return c?{value:e}:e}},value:function(a,b){return function(){return b?{context:void 0,
name:void 0,value:a}:a}},identifier:function(a,b,d,c,e){return function(f,g,h,k){f=g&&a in g?g:f;c&&1!==c&&f&&!f[a]&&(f[a]={});g=f?f[a]:void 0;b&&sa(g,e);return d?{context:f,name:a,value:g}:g}},computedMember:function(a,b,d,c,e){return function(f,g,h,k){var l=a(f,g,h,k),n,m;null!=l&&(n=b(f,g,h,k),n+="",Ta(n,e),c&&1!==c&&(Gb(l),l&&!l[n]&&(l[n]={})),m=l[n],sa(m,e));return d?{context:l,name:n,value:m}:m}},nonComputedMember:function(a,b,d,c,e,f){return function(g,h,k,l){g=a(g,h,k,l);e&&1!==e&&(Gb(g),
g&&!g[b]&&(g[b]={}));h=null!=g?g[b]:void 0;(d||Hb(b))&&sa(h,f);return c?{context:g,name:b,value:h}:h}},inputs:function(a,b){return function(d,c,e,f){return f?f[b]:a(d,c,e)}}};var hc=function(a,b,d){this.lexer=a;this.$filter=b;this.options=d;this.ast=new s(a,d);this.astCompiler=d.csp?new td(this.ast,b):new sd(this.ast,b)};hc.prototype={constructor:hc,parse:function(a){return this.astCompiler.compile(a,this.options.expensiveChecks)}};var kg=Object.prototype.valueOf,ta=O("$sce"),oa={HTML:"html",CSS:"css",
URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},mg=O("$compile"),Y=v.document.createElement("a"),xd=ra(v.location.href);yd.$inject=["$document"];Jc.$inject=["$provide"];var Fd=22,Ed=".",jc="0";zd.$inject=["$locale"];Bd.$inject=["$locale"];var xg={yyyy:W("FullYear",4,0,!1,!0),yy:W("FullYear",2,0,!0,!0),y:W("FullYear",1,0,!1,!0),MMMM:ib("Month"),MMM:ib("Month",!0),MM:W("Month",2,1),M:W("Month",1,1),LLLL:ib("Month",!1,!0),dd:W("Date",2),d:W("Date",1),HH:W("Hours",2),H:W("Hours",1),hh:W("Hours",2,-12),
h:W("Hours",1,-12),mm:W("Minutes",2),m:W("Minutes",1),ss:W("Seconds",2),s:W("Seconds",1),sss:W("Milliseconds",3),EEEE:ib("Day"),EEE:ib("Day",!0),a:function(a,b){return 12>a.getHours()?b.AMPMS[0]:b.AMPMS[1]},Z:function(a,b,d){a=-1*d;return a=(0<=a?"+":"")+(Ib(Math[0<a?"floor":"ceil"](a/60),2)+Ib(Math.abs(a%60),2))},ww:Hd(2),w:Hd(1),G:kc,GG:kc,GGG:kc,GGGG:function(a,b){return 0>=a.getFullYear()?b.ERANAMES[0]:b.ERANAMES[1]}},wg=/((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
vg=/^\-?\d+$/;Ad.$inject=["$locale"];var qg=da(P),rg=da(sb);Cd.$inject=["$parse"];var ne=da({restrict:"E",compile:function(a,b){if(!b.href&&!b.xlinkHref)return function(a,b){if("a"===b[0].nodeName.toLowerCase()){var e="[object SVGAnimatedString]"===ma.call(b.prop("href"))?"xlink:href":"href";b.on("click",function(a){b.attr(e)||a.preventDefault()})}}}}),tb={};q(Cb,function(a,b){function d(a,d,e){a.$watch(e[c],function(a){e.$set(b,!!a)})}if("multiple"!=a){var c=xa("ng-"+b),e=d;"checked"===a&&(e=function(a,
b,e){e.ngModel!==e[c]&&d(a,b,e)});tb[c]=function(){return{restrict:"A",priority:100,link:e}}}});q(ad,function(a,b){tb[b]=function(){return{priority:100,link:function(a,c,e){if("ngPattern"===b&&"/"==e.ngPattern.charAt(0)&&(c=e.ngPattern.match(zg))){e.$set("ngPattern",new RegExp(c[1],c[2]));return}a.$watch(e[b],function(a){e.$set(b,a)})}}}});q(["src","srcset","href"],function(a){var b=xa("ng-"+a);tb[b]=function(){return{priority:99,link:function(d,c,e){var f=a,g=a;"href"===a&&"[object SVGAnimatedString]"===
ma.call(c.prop("href"))&&(g="xlinkHref",e.$attr[g]="xlink:href",f=null);e.$observe(b,function(b){b?(e.$set(g,b),Ca&&f&&c.prop(f,e[g])):"href"===a&&e.$set(g,null)})}}}});var Jb={$addControl:C,$$renameControl:function(a,b){a.$name=b},$removeControl:C,$setValidity:C,$setDirty:C,$setPristine:C,$setSubmitted:C};Id.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var Rd=function(a){return["$timeout","$parse",function(b,d){function c(a){return""===a?d('this[""]').assign:d(a).assign||C}return{name:"form",
restrict:a?"EAC":"E",require:["form","^^?form"],controller:Id,compile:function(d,f){d.addClass(Ua).addClass(mb);var g=f.name?"name":a&&f.ngForm?"ngForm":!1;return{pre:function(a,d,e,f){var m=f[0];if(!("action"in e)){var r=function(b){a.$apply(function(){m.$commitViewValue();m.$setSubmitted()});b.preventDefault()};d[0].addEventListener("submit",r,!1);d.on("$destroy",function(){b(function(){d[0].removeEventListener("submit",r,!1)},0,!1)})}(f[1]||m.$$parentForm).$addControl(m);var q=g?c(m.$name):C;g&&
(q(a,m),e.$observe(g,function(b){m.$name!==b&&(q(a,void 0),m.$$parentForm.$$renameControl(m,b),q=c(m.$name),q(a,m))}));d.on("$destroy",function(){m.$$parentForm.$removeControl(m);q(a,void 0);R(m,Jb)})}}}}}]},oe=Rd(),Be=Rd(!0),yg=/^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/,Hg=/^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i,Ig=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,
Jg=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/,Sd=/^(\d{4,})-(\d{2})-(\d{2})$/,Td=/^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,nc=/^(\d{4,})-W(\d\d)$/,Ud=/^(\d{4,})-(\d\d)$/,Vd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Kd=T();q(["date","datetime-local","month","time","week"],function(a){Kd[a]=!0});var Wd={text:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);lc(c)},date:kb("date",Sd,Lb(Sd,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":kb("datetimelocal",Td,Lb(Td,"yyyy MM dd HH mm ss sss".split(" ")),
"yyyy-MM-ddTHH:mm:ss.sss"),time:kb("time",Vd,Lb(Vd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:kb("week",nc,function(a,b){if(fa(a))return a;if(F(a)){nc.lastIndex=0;var d=nc.exec(a);if(d){var c=+d[1],e=+d[2],f=d=0,g=0,h=0,k=Gd(c),e=7*(e-1);b&&(d=b.getHours(),f=b.getMinutes(),g=b.getSeconds(),h=b.getMilliseconds());return new Date(c,0,k.getDate()+e,d,f,g,h)}}return NaN},"yyyy-Www"),month:kb("month",Ud,Lb(Ud,["yyyy","MM"]),"yyyy-MM"),number:function(a,b,d,c,e,f){Ld(a,b,d,c);jb(a,b,d,c,e,f);c.$$parserName=
"number";c.$parsers.push(function(a){if(c.$isEmpty(a))return null;if(Jg.test(a))return parseFloat(a)});c.$formatters.push(function(a){if(!c.$isEmpty(a)){if(!Q(a))throw lb("numfmt",a);a=a.toString()}return a});if(x(d.min)||d.ngMin){var g;c.$validators.min=function(a){return c.$isEmpty(a)||y(g)||a>=g};d.$observe("min",function(a){x(a)&&!Q(a)&&(a=parseFloat(a,10));g=Q(a)&&!isNaN(a)?a:void 0;c.$validate()})}if(x(d.max)||d.ngMax){var h;c.$validators.max=function(a){return c.$isEmpty(a)||y(h)||a<=h};d.$observe("max",
function(a){x(a)&&!Q(a)&&(a=parseFloat(a,10));h=Q(a)&&!isNaN(a)?a:void 0;c.$validate()})}},url:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);lc(c);c.$$parserName="url";c.$validators.url=function(a,b){var d=a||b;return c.$isEmpty(d)||Hg.test(d)}},email:function(a,b,d,c,e,f){jb(a,b,d,c,e,f);lc(c);c.$$parserName="email";c.$validators.email=function(a,b){var d=a||b;return c.$isEmpty(d)||Ig.test(d)}},radio:function(a,b,d,c){y(d.name)&&b.attr("name",++nb);b.on("click",function(a){b[0].checked&&c.$setViewValue(d.value,
a&&a.type)});c.$render=function(){b[0].checked=d.value==c.$viewValue};d.$observe("value",c.$render)},checkbox:function(a,b,d,c,e,f,g,h){var k=Md(h,a,"ngTrueValue",d.ngTrueValue,!0),l=Md(h,a,"ngFalseValue",d.ngFalseValue,!1);b.on("click",function(a){c.$setViewValue(b[0].checked,a&&a.type)});c.$render=function(){b[0].checked=c.$viewValue};c.$isEmpty=function(a){return!1===a};c.$formatters.push(function(a){return pa(a,k)});c.$parsers.push(function(a){return a?k:l})},hidden:C,button:C,submit:C,reset:C,
file:C},Dc=["$browser","$sniffer","$filter","$parse",function(a,b,d,c){return{restrict:"E",require:["?ngModel"],link:{pre:function(e,f,g,h){h[0]&&(Wd[P(g.type)]||Wd.text)(e,f,g,h[0],b,a,d,c)}}}}],Kg=/^(true|false|\d+)$/,Te=function(){return{restrict:"A",priority:100,compile:function(a,b){return Kg.test(b.ngValue)?function(a,b,e){e.$set("value",a.$eval(e.ngValue))}:function(a,b,e){a.$watch(e.ngValue,function(a){e.$set("value",a)})}}}},te=["$compile",function(a){return{restrict:"AC",compile:function(b){a.$$addBindingClass(b);
return function(b,c,e){a.$$addBindingInfo(c,e.ngBind);c=c[0];b.$watch(e.ngBind,function(a){c.textContent=y(a)?"":a})}}}}],ve=["$interpolate","$compile",function(a,b){return{compile:function(d){b.$$addBindingClass(d);return function(c,d,f){c=a(d.attr(f.$attr.ngBindTemplate));b.$$addBindingInfo(d,c.expressions);d=d[0];f.$observe("ngBindTemplate",function(a){d.textContent=y(a)?"":a})}}}}],ue=["$sce","$parse","$compile",function(a,b,d){return{restrict:"A",compile:function(c,e){var f=b(e.ngBindHtml),g=
b(e.ngBindHtml,function(a){return(a||"").toString()});d.$$addBindingClass(c);return function(b,c,e){d.$$addBindingInfo(c,e.ngBindHtml);b.$watch(g,function(){c.html(a.getTrustedHtml(f(b))||"")})}}}}],Se=da({restrict:"A",require:"ngModel",link:function(a,b,d,c){c.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),we=mc("",!0),ye=mc("Odd",0),xe=mc("Even",1),ze=La({compile:function(a,b){b.$set("ngCloak",void 0);a.removeClass("ng-cloak")}}),Ae=[function(){return{restrict:"A",scope:!0,controller:"@",
priority:500}}],Ic={},Lg={blur:!0,focus:!0};q("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var b=xa("ng-"+a);Ic[b]=["$parse","$rootScope",function(d,c){return{restrict:"A",compile:function(e,f){var g=d(f[b],null,!0);return function(b,d){d.on(a,function(d){var e=function(){g(b,{$event:d})};Lg[a]&&c.$$phase?b.$evalAsync(e):b.$apply(e)})}}}}]});var De=["$animate","$compile",function(a,
b){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(d,c,e,f,g){var h,k,l;d.$watch(e.ngIf,function(d){d?k||g(function(d,f){k=f;d[d.length++]=b.$$createComment("end ngIf",e.ngIf);h={clone:d};a.enter(d,c.parent(),c)}):(l&&(l.remove(),l=null),k&&(k.$destroy(),k=null),h&&(l=rb(h.clone),a.leave(l).then(function(){l=null}),h=null))})}}}],Ee=["$templateRequest","$anchorScroll","$animate",function(a,b,d){return{restrict:"ECA",priority:400,terminal:!0,
transclude:"element",controller:ea.noop,compile:function(c,e){var f=e.ngInclude||e.src,g=e.onload||"",h=e.autoscroll;return function(c,e,n,m,r){var q=0,s,w,p,y=function(){w&&(w.remove(),w=null);s&&(s.$destroy(),s=null);p&&(d.leave(p).then(function(){w=null}),w=p,p=null)};c.$watch(f,function(f){var n=function(){!x(h)||h&&!c.$eval(h)||b()},u=++q;f?(a(f,!0).then(function(a){if(!c.$$destroyed&&u===q){var b=c.$new();m.template=a;a=r(b,function(a){y();d.enter(a,null,e).then(n)});s=b;p=a;s.$emit("$includeContentLoaded",
f);c.$eval(g)}},function(){c.$$destroyed||u!==q||(y(),c.$emit("$includeContentError",f))}),c.$emit("$includeContentRequested",f)):(y(),m.template=null)})}}}}],Ve=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(b,d,c,e){ma.call(d[0]).match(/SVG/)?(d.empty(),a(Lc(e.template,v.document).childNodes)(b,function(a){d.append(a)},{futureParentElement:d})):(d.html(e.template),a(d.contents())(b))}}}],Fe=La({priority:450,compile:function(){return{pre:function(a,
b,d){a.$eval(d.ngInit)}}}}),Re=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,b,d,c){var e=b.attr(d.$attr.ngList)||", ",f="false"!==d.ngTrim,g=f?V(e):e;c.$parsers.push(function(a){if(!y(a)){var b=[];a&&q(a.split(g),function(a){a&&b.push(f?V(a):a)});return b}});c.$formatters.push(function(a){if(K(a))return a.join(e)});c.$isEmpty=function(a){return!a||!a.length}}}},mb="ng-valid",Nd="ng-invalid",Ua="ng-pristine",Kb="ng-dirty",Pd="ng-pending",lb=O("ngModel"),Mg=["$scope",
"$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,b,d,c,e,f,g,h,k,l){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=void 0;this.$validators={};this.$asyncValidators={};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0;this.$touched=!1;this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending=void 0;this.$name=l(d.name||"",!1)(a);
this.$$parentForm=Jb;var n=e(d.ngModel),m=n.assign,r=n,s=m,v=null,w,p=this;this.$$setOptions=function(a){if((p.$options=a)&&a.getterSetter){var b=e(d.ngModel+"()"),f=e(d.ngModel+"($$$p)");r=function(a){var c=n(a);E(c)&&(c=b(a));return c};s=function(a,b){E(n(a))?f(a,{$$$p:b}):m(a,b)}}else if(!n.assign)throw lb("nonassign",d.ngModel,wa(c));};this.$render=C;this.$isEmpty=function(a){return y(a)||""===a||null===a||a!==a};this.$$updateEmptyClasses=function(a){p.$isEmpty(a)?(f.removeClass(c,"ng-not-empty"),
f.addClass(c,"ng-empty")):(f.removeClass(c,"ng-empty"),f.addClass(c,"ng-not-empty"))};var H=0;Jd({ctrl:this,$element:c,set:function(a,b){a[b]=!0},unset:function(a,b){delete a[b]},$animate:f});this.$setPristine=function(){p.$dirty=!1;p.$pristine=!0;f.removeClass(c,Kb);f.addClass(c,Ua)};this.$setDirty=function(){p.$dirty=!0;p.$pristine=!1;f.removeClass(c,Ua);f.addClass(c,Kb);p.$$parentForm.$setDirty()};this.$setUntouched=function(){p.$touched=!1;p.$untouched=!0;f.setClass(c,"ng-untouched","ng-touched")};
this.$setTouched=function(){p.$touched=!0;p.$untouched=!1;f.setClass(c,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){g.cancel(v);p.$viewValue=p.$$lastCommittedViewValue;p.$render()};this.$validate=function(){if(!Q(p.$modelValue)||!isNaN(p.$modelValue)){var a=p.$$rawModelValue,b=p.$valid,c=p.$modelValue,d=p.$options&&p.$options.allowInvalid;p.$$runValidators(a,p.$$lastCommittedViewValue,function(e){d||b===e||(p.$modelValue=e?a:void 0,p.$modelValue!==c&&p.$$writeModelToScope())})}};
this.$$runValidators=function(a,b,c){function d(){var c=!0;q(p.$validators,function(d,e){var g=d(a,b);c=c&&g;f(e,g)});return c?!0:(q(p.$asyncValidators,function(a,b){f(b,null)}),!1)}function e(){var c=[],d=!0;q(p.$asyncValidators,function(e,g){var h=e(a,b);if(!h||!E(h.then))throw lb("nopromise",h);f(g,void 0);c.push(h.then(function(){f(g,!0)},function(){d=!1;f(g,!1)}))});c.length?k.all(c).then(function(){g(d)},C):g(!0)}function f(a,b){h===H&&p.$setValidity(a,b)}function g(a){h===H&&c(a)}H++;var h=
H;(function(){var a=p.$$parserName||"parse";if(y(w))f(a,null);else return w||(q(p.$validators,function(a,b){f(b,null)}),q(p.$asyncValidators,function(a,b){f(b,null)})),f(a,w),w;return!0})()?d()?e():g(!1):g(!1)};this.$commitViewValue=function(){var a=p.$viewValue;g.cancel(v);if(p.$$lastCommittedViewValue!==a||""===a&&p.$$hasNativeValidators)p.$$updateEmptyClasses(a),p.$$lastCommittedViewValue=a,p.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var b=p.$$lastCommittedViewValue;
if(w=y(b)?void 0:!0)for(var c=0;c<p.$parsers.length;c++)if(b=p.$parsers[c](b),y(b)){w=!1;break}Q(p.$modelValue)&&isNaN(p.$modelValue)&&(p.$modelValue=r(a));var d=p.$modelValue,e=p.$options&&p.$options.allowInvalid;p.$$rawModelValue=b;e&&(p.$modelValue=b,p.$modelValue!==d&&p.$$writeModelToScope());p.$$runValidators(b,p.$$lastCommittedViewValue,function(a){e||(p.$modelValue=a?b:void 0,p.$modelValue!==d&&p.$$writeModelToScope())})};this.$$writeModelToScope=function(){s(a,p.$modelValue);q(p.$viewChangeListeners,
function(a){try{a()}catch(c){b(c)}})};this.$setViewValue=function(a,b){p.$viewValue=a;p.$options&&!p.$options.updateOnDefault||p.$$debounceViewValueCommit(b)};this.$$debounceViewValueCommit=function(b){var c=0,d=p.$options;d&&x(d.debounce)&&(d=d.debounce,Q(d)?c=d:Q(d[b])?c=d[b]:Q(d["default"])&&(c=d["default"]));g.cancel(v);c?v=g(function(){p.$commitViewValue()},c):h.$$phase?p.$commitViewValue():a.$apply(function(){p.$commitViewValue()})};a.$watch(function(){var b=r(a);if(b!==p.$modelValue&&(p.$modelValue===
p.$modelValue||b===b)){p.$modelValue=p.$$rawModelValue=b;w=void 0;for(var c=p.$formatters,d=c.length,e=b;d--;)e=c[d](e);p.$viewValue!==e&&(p.$$updateEmptyClasses(e),p.$viewValue=p.$$lastCommittedViewValue=e,p.$render(),p.$$runValidators(b,e,C))}return b})}],Qe=["$rootScope",function(a){return{restrict:"A",require:["ngModel","^?form","^?ngModelOptions"],controller:Mg,priority:1,compile:function(b){b.addClass(Ua).addClass("ng-untouched").addClass(mb);return{pre:function(a,b,e,f){var g=f[0];b=f[1]||
g.$$parentForm;g.$$setOptions(f[2]&&f[2].$options);b.$addControl(g);e.$observe("name",function(a){g.$name!==a&&g.$$parentForm.$$renameControl(g,a)});a.$on("$destroy",function(){g.$$parentForm.$removeControl(g)})},post:function(b,c,e,f){var g=f[0];if(g.$options&&g.$options.updateOn)c.on(g.$options.updateOn,function(a){g.$$debounceViewValueCommit(a&&a.type)});c.on("blur",function(){g.$touched||(a.$$phase?b.$evalAsync(g.$setTouched):b.$apply(g.$setTouched))})}}}}}],Ng=/(\s+|^)default(\s+|$)/,Ue=function(){return{restrict:"A",
controller:["$scope","$attrs",function(a,b){var d=this;this.$options=qa(a.$eval(b.ngModelOptions));x(this.$options.updateOn)?(this.$options.updateOnDefault=!1,this.$options.updateOn=V(this.$options.updateOn.replace(Ng,function(){d.$options.updateOnDefault=!0;return" "}))):this.$options.updateOnDefault=!0}]}},Ge=La({terminal:!0,priority:1E3}),Og=O("ngOptions"),Pg=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,
Oe=["$compile","$document","$parse",function(a,b,d){function c(a,b,c){function e(a,b,c,d,f){this.selectValue=a;this.viewValue=b;this.label=c;this.group=d;this.disabled=f}function f(a){var b;if(!q&&ya(a))b=a;else{b=[];for(var c in a)a.hasOwnProperty(c)&&"$"!==c.charAt(0)&&b.push(c)}return b}var m=a.match(Pg);if(!m)throw Og("iexp",a,wa(b));var r=m[5]||m[7],q=m[6];a=/ as /.test(m[0])&&m[1];var s=m[9];b=d(m[2]?m[1]:r);var w=a&&d(a)||b,p=s&&d(s),v=s?function(a,b){return p(c,b)}:function(a){return Fa(a)},
t=function(a,b){return v(a,L(a,b))},z=d(m[2]||m[1]),u=d(m[3]||""),y=d(m[4]||""),x=d(m[8]),D={},L=q?function(a,b){D[q]=b;D[r]=a;return D}:function(a){D[r]=a;return D};return{trackBy:s,getTrackByValue:t,getWatchables:d(x,function(a){var b=[];a=a||[];for(var d=f(a),e=d.length,g=0;g<e;g++){var h=a===d?g:d[g],l=a[h],h=L(l,h),l=v(l,h);b.push(l);if(m[2]||m[1])l=z(c,h),b.push(l);m[4]&&(h=y(c,h),b.push(h))}return b}),getOptions:function(){for(var a=[],b={},d=x(c)||[],g=f(d),h=g.length,m=0;m<h;m++){var p=d===
g?m:g[m],q=L(d[p],p),r=w(c,q),p=v(r,q),D=z(c,q),N=u(c,q),q=y(c,q),r=new e(p,r,D,N,q);a.push(r);b[p]=r}return{items:a,selectValueMap:b,getOptionFromViewValue:function(a){return b[t(a)]},getViewValueFromOption:function(a){return s?ea.copy(a.viewValue):a.viewValue}}}}}var e=v.document.createElement("option"),f=v.document.createElement("optgroup");return{restrict:"A",terminal:!0,require:["select","ngModel"],link:{pre:function(a,b,c,d){d[0].registerOption=C},post:function(d,h,k,l){function n(a,b){a.element=
b;b.disabled=a.disabled;a.label!==b.label&&(b.label=a.label,b.textContent=a.label);a.value!==b.value&&(b.value=a.selectValue)}function m(){var a=u&&r.readValue();if(u)for(var b=u.items.length-1;0<=b;b--){var c=u.items[b];c.group?Bb(c.element.parentNode):Bb(c.element)}u=I.getOptions();var d={};t&&h.prepend(w);u.items.forEach(function(a){var b;if(x(a.group)){b=d[a.group];b||(b=f.cloneNode(!1),E.appendChild(b),b.label=a.group,d[a.group]=b);var c=e.cloneNode(!1)}else b=E,c=e.cloneNode(!1);b.appendChild(c);
n(a,c)});h[0].appendChild(E);s.$render();s.$isEmpty(a)||(b=r.readValue(),(I.trackBy||v?pa(a,b):a===b)||(s.$setViewValue(b),s.$render()))}var r=l[0],s=l[1],v=k.multiple,w;l=0;for(var p=h.children(),y=p.length;l<y;l++)if(""===p[l].value){w=p.eq(l);break}var t=!!w,z=B(e.cloneNode(!1));z.val("?");var u,I=c(k.ngOptions,h,d),E=b[0].createDocumentFragment();v?(s.$isEmpty=function(a){return!a||0===a.length},r.writeValue=function(a){u.items.forEach(function(a){a.element.selected=!1});a&&a.forEach(function(a){if(a=
u.getOptionFromViewValue(a))a.element.selected=!0})},r.readValue=function(){var a=h.val()||[],b=[];q(a,function(a){(a=u.selectValueMap[a])&&!a.disabled&&b.push(u.getViewValueFromOption(a))});return b},I.trackBy&&d.$watchCollection(function(){if(K(s.$viewValue))return s.$viewValue.map(function(a){return I.getTrackByValue(a)})},function(){s.$render()})):(r.writeValue=function(a){var b=u.getOptionFromViewValue(a);b?(h[0].value!==b.selectValue&&(z.remove(),t||w.remove(),h[0].value=b.selectValue,b.element.selected=
!0),b.element.setAttribute("selected","selected")):null===a||t?(z.remove(),t||h.prepend(w),h.val(""),w.prop("selected",!0),w.attr("selected",!0)):(t||w.remove(),h.prepend(z),h.val("?"),z.prop("selected",!0),z.attr("selected",!0))},r.readValue=function(){var a=u.selectValueMap[h.val()];return a&&!a.disabled?(t||w.remove(),z.remove(),u.getViewValueFromOption(a)):null},I.trackBy&&d.$watch(function(){return I.getTrackByValue(s.$viewValue)},function(){s.$render()}));t?(w.remove(),a(w)(d),w.removeClass("ng-scope")):
w=B(e.cloneNode(!1));h.empty();m();d.$watchCollection(I.getWatchables,m)}}}}],He=["$locale","$interpolate","$log",function(a,b,d){var c=/{}/g,e=/^when(Minus)?(.+)$/;return{link:function(f,g,h){function k(a){g.text(a||"")}var l=h.count,n=h.$attr.when&&g.attr(h.$attr.when),m=h.offset||0,r=f.$eval(n)||{},s={},v=b.startSymbol(),w=b.endSymbol(),p=v+l+"-"+m+w,x=ea.noop,t;q(h,function(a,b){var c=e.exec(b);c&&(c=(c[1]?"-":"")+P(c[2]),r[c]=g.attr(h.$attr[b]))});q(r,function(a,d){s[d]=b(a.replace(c,p))});f.$watch(l,
function(b){var c=parseFloat(b),e=isNaN(c);e||c in r||(c=a.pluralCat(c-m));c===t||e&&Q(t)&&isNaN(t)||(x(),e=s[c],y(e)?(null!=b&&d.debug("ngPluralize: no rule defined for '"+c+"' in "+n),x=C,k()):x=f.$watch(e,k),t=c)})}}}],Ie=["$parse","$animate","$compile",function(a,b,d){var c=O("ngRepeat"),e=function(a,b,c,d,e,n,m){a[c]=d;e&&(a[e]=n);a.$index=b;a.$first=0===b;a.$last=b===m-1;a.$middle=!(a.$first||a.$last);a.$odd=!(a.$even=0===(b&1))};return{restrict:"A",multiElement:!0,transclude:"element",priority:1E3,
terminal:!0,$$tlb:!0,compile:function(f,g){var h=g.ngRepeat,k=d.$$createComment("end ngRepeat",h),l=h.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);if(!l)throw c("iexp",h);var n=l[1],m=l[2],r=l[3],s=l[4],l=n.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);if(!l)throw c("iidexp",n);var v=l[3]||l[1],w=l[2];if(r&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(r)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(r)))throw c("badident",
r);var p,y,t,z,u={$id:Fa};s?p=a(s):(t=function(a,b){return Fa(b)},z=function(a){return a});return function(a,d,f,g,l){p&&(y=function(b,c,d){w&&(u[w]=b);u[v]=c;u.$index=d;return p(a,u)});var n=T();a.$watchCollection(m,function(f){var g,m,p=d[0],s,u=T(),x,D,E,C,F,B,G;r&&(a[r]=f);if(ya(f))F=f,m=y||t;else for(G in m=y||z,F=[],f)ua.call(f,G)&&"$"!==G.charAt(0)&&F.push(G);x=F.length;G=Array(x);for(g=0;g<x;g++)if(D=f===F?g:F[g],E=f[D],C=m(D,E,g),n[C])B=n[C],delete n[C],u[C]=B,G[g]=B;else{if(u[C])throw q(G,
function(a){a&&a.scope&&(n[a.id]=a)}),c("dupes",h,C,E);G[g]={id:C,scope:void 0,clone:void 0};u[C]=!0}for(s in n){B=n[s];C=rb(B.clone);b.leave(C);if(C[0].parentNode)for(g=0,m=C.length;g<m;g++)C[g].$$NG_REMOVED=!0;B.scope.$destroy()}for(g=0;g<x;g++)if(D=f===F?g:F[g],E=f[D],B=G[g],B.scope){s=p;do s=s.nextSibling;while(s&&s.$$NG_REMOVED);B.clone[0]!=s&&b.move(rb(B.clone),null,p);p=B.clone[B.clone.length-1];e(B.scope,g,v,E,w,D,x)}else l(function(a,c){B.scope=c;var d=k.cloneNode(!1);a[a.length++]=d;b.enter(a,
null,p);p=d;B.clone=a;u[B.id]=B;e(B.scope,g,v,E,w,D,x)});n=u})}}}}],Je=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngShow,function(b){a[b?"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],Ce=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngHide,function(b){a[b?"addClass":"removeClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],Ke=La(function(a,b,d){a.$watch(d.ngStyle,function(a,
d){d&&a!==d&&q(d,function(a,c){b.css(c,"")});a&&b.css(a)},!0)}),Le=["$animate","$compile",function(a,b){return{require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(d,c,e,f){var g=[],h=[],k=[],l=[],n=function(a,b){return function(){a.splice(b,1)}};d.$watch(e.ngSwitch||e.on,function(c){var d,e;d=0;for(e=k.length;d<e;++d)a.cancel(k[d]);d=k.length=0;for(e=l.length;d<e;++d){var s=rb(h[d].clone);l[d].$destroy();(k[d]=a.leave(s)).then(n(k,d))}h.length=0;l.length=0;(g=f.cases["!"+
c]||f.cases["?"])&&q(g,function(c){c.transclude(function(d,e){l.push(e);var f=c.element;d[d.length++]=b.$$createComment("end ngSwitchWhen");h.push({clone:d});a.enter(d,f.parent(),f)})})})}}}],Me=La({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){c.cases["!"+d.ngSwitchWhen]=c.cases["!"+d.ngSwitchWhen]||[];c.cases["!"+d.ngSwitchWhen].push({transclude:e,element:b})}}),Ne=La({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,
b,d,c,e){c.cases["?"]=c.cases["?"]||[];c.cases["?"].push({transclude:e,element:b})}}),Qg=O("ngTransclude"),Pe=La({restrict:"EAC",link:function(a,b,d,c,e){d.ngTransclude===d.$attr.ngTransclude&&(d.ngTransclude="");if(!e)throw Qg("orphan",wa(b));e(function(a){a.length&&(b.empty(),b.append(a))},null,d.ngTransclude||d.ngTranscludeSlot)}}),pe=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(b,d){"text/ng-template"==d.type&&a.put(d.id,b[0].text)}}}],Rg={$setViewValue:C,$render:C},
Sg=["$element","$scope",function(a,b){var d=this,c=new Ra;d.ngModelCtrl=Rg;d.unknownOption=B(v.document.createElement("option"));d.renderUnknownOption=function(b){b="? "+Fa(b)+" ?";d.unknownOption.val(b);a.prepend(d.unknownOption);a.val(b)};b.$on("$destroy",function(){d.renderUnknownOption=C});d.removeUnknownOption=function(){d.unknownOption.parent()&&d.unknownOption.remove()};d.readValue=function(){d.removeUnknownOption();return a.val()};d.writeValue=function(b){d.hasOption(b)?(d.removeUnknownOption(),
a.val(b),""===b&&d.emptyOption.prop("selected",!0)):null==b&&d.emptyOption?(d.removeUnknownOption(),a.val("")):d.renderUnknownOption(b)};d.addOption=function(a,b){if(8!==b[0].nodeType){Qa(a,'"option value"');""===a&&(d.emptyOption=b);var g=c.get(a)||0;c.put(a,g+1);d.ngModelCtrl.$render();b[0].hasAttribute("selected")&&(b[0].selected=!0)}};d.removeOption=function(a){var b=c.get(a);b&&(1===b?(c.remove(a),""===a&&(d.emptyOption=void 0)):c.put(a,b-1))};d.hasOption=function(a){return!!c.get(a)};d.registerOption=
function(a,b,c,h,k){if(h){var l;c.$observe("value",function(a){x(l)&&d.removeOption(l);l=a;d.addOption(a,b)})}else k?a.$watch(k,function(a,e){c.$set("value",a);e!==a&&d.removeOption(e);d.addOption(a,b)}):d.addOption(c.value,b);b.on("$destroy",function(){d.removeOption(c.value);d.ngModelCtrl.$render()})}}],qe=function(){return{restrict:"E",require:["select","?ngModel"],controller:Sg,priority:1,link:{pre:function(a,b,d,c){var e=c[1];if(e){var f=c[0];f.ngModelCtrl=e;b.on("change",function(){a.$apply(function(){e.$setViewValue(f.readValue())})});
if(d.multiple){f.readValue=function(){var a=[];q(b.find("option"),function(b){b.selected&&a.push(b.value)});return a};f.writeValue=function(a){var c=new Ra(a);q(b.find("option"),function(a){a.selected=x(c.get(a.value))})};var g,h=NaN;a.$watch(function(){h!==e.$viewValue||pa(g,e.$viewValue)||(g=ha(e.$viewValue),e.$render());h=e.$viewValue});e.$isEmpty=function(a){return!a||0===a.length}}}},post:function(a,b,d,c){var e=c[1];if(e){var f=c[0];e.$render=function(){f.writeValue(e.$viewValue)}}}}}},se=["$interpolate",
function(a){return{restrict:"E",priority:100,compile:function(b,d){if(x(d.value))var c=a(d.value,!0);else{var e=a(b.text(),!0);e||d.$set("value",b.text())}return function(a,b,d){var k=b.parent();(k=k.data("$selectController")||k.parent().data("$selectController"))&&k.registerOption(a,b,d,c,e)}}}}],re=da({restrict:"E",terminal:!1}),Fc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){c&&(d.required=!0,c.$validators.required=function(a,b){return!d.required||!c.$isEmpty(b)},d.$observe("required",
function(){c.$validate()}))}}},Ec=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e,f=d.ngPattern||d.pattern;d.$observe("pattern",function(a){F(a)&&0<a.length&&(a=new RegExp("^"+a+"$"));if(a&&!a.test)throw O("ngPattern")("noregexp",f,a,wa(b));e=a||void 0;c.$validate()});c.$validators.pattern=function(a,b){return c.$isEmpty(b)||y(e)||e.test(b)}}}}},Hc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=-1;d.$observe("maxlength",function(a){a=
X(a);e=isNaN(a)?-1:a;c.$validate()});c.$validators.maxlength=function(a,b){return 0>e||c.$isEmpty(b)||b.length<=e}}}}},Gc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=0;d.$observe("minlength",function(a){e=X(a)||0;c.$validate()});c.$validators.minlength=function(a,b){return c.$isEmpty(b)||b.length>=e}}}}};v.angular.bootstrap?v.console&&console.log("WARNING: Tried to load angular more than once."):(ie(),ke(ea),ea.module("ngLocale",[],["$provide",function(a){function b(a){a+=
"";var b=a.indexOf(".");return-1==b?0:a.length-b-1}a.value("$locale",{DATETIME_FORMATS:{AMPMS:["AM","PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"],FIRSTDAYOFWEEK:6,MONTH:"January February March April May June July August September October November December".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),STANDALONEMONTH:"January February March April May June July August September October November December".split(" "),
WEEKENDRANGE:[5,6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a","short":"M/d/yy h:mm a",shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3,maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",localeID:"en_US",pluralCat:function(a,
c){var e=a|0,f=c;void 0===f&&(f=Math.min(b(a),3));Math.pow(10,f);return 1==e&&0==f?"one":"other"}})}]),B(v.document).ready(function(){ee(v.document,yc)}))})(window);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
//# sourceMappingURL=angular.min.js.map
;
/*
 AngularJS v1.5.5
 (c) 2010-2016 Google, Inc. http://angularjs.org
 License: MIT
*/
(function(n,e){'use strict';function B(a){var c=[];w(c,e.noop).chars(a);return c.join("")}function h(a,c){var b={},d=a.split(","),l;for(l=0;l<d.length;l++)b[c?e.lowercase(d[l]):d[l]]=!0;return b}function C(a,c){null===a||void 0===a?a="":"string"!==typeof a&&(a=""+a);g.innerHTML=a;var b=5;do{if(0===b)throw x("uinput");b--;n.document.documentMode&&t(g);a=g.innerHTML;g.innerHTML=a}while(a!==g.innerHTML);for(b=g.firstChild;b;){switch(b.nodeType){case 1:c.start(b.nodeName.toLowerCase(),D(b.attributes));
break;case 3:c.chars(b.textContent)}var d;if(!(d=b.firstChild)&&(1==b.nodeType&&c.end(b.nodeName.toLowerCase()),d=b.nextSibling,!d))for(;null==d;){b=b.parentNode;if(b===g)break;d=b.nextSibling;1==b.nodeType&&c.end(b.nodeName.toLowerCase())}b=d}for(;b=g.firstChild;)g.removeChild(b)}function D(a){for(var c={},b=0,d=a.length;b<d;b++){var l=a[b];c[l.name]=l.value}return c}function y(a){return a.replace(/&/g,"&amp;").replace(E,function(a){var b=a.charCodeAt(0);a=a.charCodeAt(1);return"&#"+(1024*(b-55296)+
(a-56320)+65536)+";"}).replace(F,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"&lt;").replace(/>/g,"&gt;")}function w(a,c){var b=!1,d=e.bind(a,a.push);return{start:function(a,f){a=e.lowercase(a);!b&&G[a]&&(b=a);b||!0!==u[a]||(d("<"),d(a),e.forEach(f,function(b,f){var g=e.lowercase(f),h="img"===a&&"src"===g||"background"===g;!0!==H[g]||!0===z[g]&&!c(b,h)||(d(" "),d(f),d('="'),d(y(b)),d('"'))}),d(">"))},end:function(a){a=e.lowercase(a);b||!0!==u[a]||!0===A[a]||(d("</"),d(a),d(">"));a==
b&&(b=!1)},chars:function(a){b||d(y(a))}}}function t(a){if(a.nodeType===n.Node.ELEMENT_NODE)for(var c=a.attributes,b=0,d=c.length;b<d;b++){var e=c[b],f=e.name.toLowerCase();if("xmlns:ns1"===f||0===f.indexOf("ns1:"))a.removeAttributeNode(e),b--,d--}(c=a.firstChild)&&t(c);(c=a.nextSibling)&&t(c)}var x=e.$$minErr("$sanitize"),E=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,F=/([^\#-~ |!])/g,A=h("area,br,col,hr,img,wbr"),q=h("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),k=h("rp,rt"),v=e.extend({},k,q),q=e.extend({},
q,h("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul")),k=e.extend({},k,h("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")),I=h("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,stop,svg,switch,text,title,tspan"),
G=h("script,style"),u=e.extend({},A,q,k,v),z=h("background,cite,href,longdesc,src,xlink:href"),v=h("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,valign,value,vspace,width"),k=h("accent-height,accumulate,additive,alphabetic,arabic-form,ascent,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan",
!0),H=e.extend({},z,k,v),g;(function(a){if(a.document&&a.document.implementation)a=a.document.implementation.createHTMLDocument("inert");else throw x("noinert");var c=(a.documentElement||a.getDocumentElement()).getElementsByTagName("body");1===c.length?g=c[0]:(c=a.createElement("html"),g=a.createElement("body"),c.appendChild(g),a.appendChild(c))})(n);e.module("ngSanitize",[]).provider("$sanitize",function(){var a=!1;this.$get=["$$sanitizeUri",function(c){a&&e.extend(u,I);return function(a){var d=
[];C(a,w(d,function(a,b){return!/^unsafe:/.test(c(a,b))}));return d.join("")}}];this.enableSvg=function(c){return e.isDefined(c)?(a=c,this):a}});e.module("ngSanitize").filter("linky",["$sanitize",function(a){var c=/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,b=/^mailto:/i,d=e.$$minErr("linky"),g=e.isString;return function(f,h,m){function k(a){a&&p.push(B(a))}function q(a,b){var c;p.push("<a ");e.isFunction(m)&&(m=m(a));if(e.isObject(m))for(c in m)p.push(c+
'="'+m[c]+'" ');else m={};!e.isDefined(h)||"target"in m||p.push('target="',h,'" ');p.push('href="',a.replace(/"/g,"&quot;"),'">');k(b);p.push("</a>")}if(null==f||""===f)return f;if(!g(f))throw d("notstring",f);for(var r=f,p=[],s,n;f=r.match(c);)s=f[0],f[2]||f[4]||(s=(f[3]?"http://":"mailto:")+s),n=f.index,k(r.substr(0,n)),q(s,f[0].replace(b,"")),r=r.substring(n+f[0].length);k(r);return a(p.join(""))}}])})(window,window.angular);
//# sourceMappingURL=angular-sanitize.min.js.map;
/*!
 * jQuery Cookie Plugin v1.4.1
 * https://github.com/carhartl/jquery-cookie
 *
 * Copyright 2013 Klaus Hartl
 * Released under the MIT license
 */
(function (factory) {
	if (typeof define === 'function' && define.amd) {
		// AMD
		define(['jquery'], factory);
	} else if (typeof exports === 'object') {
		// CommonJS
		factory(require('jquery'));
	} else {
		// Browser globals
		factory(jQuery);
	}
}(function ($) {

	var pluses = /\+/g;

	function encode(s) {
		return config.raw ? s : encodeURIComponent(s);
	}

	function decode(s) {
		return config.raw ? s : decodeURIComponent(s);
	}

	function stringifyCookieValue(value) {
		return encode(config.json ? JSON.stringify(value) : String(value));
	}

	function parseCookieValue(s) {
		if (s.indexOf('"') === 0) {
			// This is a quoted cookie as according to RFC2068, unescape...
			s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
		}

		try {
			// Replace server-side written pluses with spaces.
			// If we can't decode the cookie, ignore it, it's unusable.
			// If we can't parse the cookie, ignore it, it's unusable.
			s = decodeURIComponent(s.replace(pluses, ' '));
			return config.json ? JSON.parse(s) : s;
		} catch(e) {}
	}

	function read(s, converter) {
		var value = config.raw ? s : parseCookieValue(s);
		return $.isFunction(converter) ? converter(value) : value;
	}

	var config = $.cookie = function (key, value, options) {

		// Write

		if (value !== undefined && !$.isFunction(value)) {
			options = $.extend({}, config.defaults, options);

			if (typeof options.expires === 'number') {
				var days = options.expires, t = options.expires = new Date();
				t.setTime(+t + days * 864e+5);
			}

			return (document.cookie = [
				encode(key), '=', stringifyCookieValue(value),
				options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
				options.path    ? '; path=' + options.path : '',
				options.domain  ? '; domain=' + options.domain : '',
				options.secure  ? '; secure' : ''
			].join(''));
		}

		// Read

		var result = key ? undefined : {};

		// To prevent the for loop in the first place assign an empty array
		// in case there are no cookies at all. Also prevents odd result when
		// calling $.cookie().
		var cookies = document.cookie ? document.cookie.split('; ') : [];

		for (var i = 0, l = cookies.length; i < l; i++) {
			var parts = cookies[i].split('=');
			var name = decode(parts.shift());
			var cookie = parts.join('=');

			if (key && key === name) {
				// If second argument (value) is a function it's a converter...
				result = read(cookie, value);
				break;
			}

			// Prevent storing a cookie that we couldn't decode.
			if (!key && (cookie = read(cookie)) !== undefined) {
				result[name] = cookie;
			}
		}

		return result;
	};

	config.defaults = {};

	$.removeCookie = function (key, options) {
		if ($.cookie(key) === undefined) {
			return false;
		}

		// Must not alter options, thus extending a fresh object...
		$.cookie(key, '', $.extend({}, options, { expires: -1 }));
		return !$.cookie(key);
	};

}));
;
/**!
 * trunk8 v1.3.3
 * https://github.com/rviscomi/trunk8
 * 
 * Copyright 2012 Rick Viscomi
 * Released under the MIT License.
 * 
 * Date: September 26, 2012
 */

(function ($) {
	var methods,
		utils,
		SIDES = {
			/* cen...ter */
			center: 'center',
			/* ...left */
			left: 'left',
			/* right... */
			right: 'right'
		},
		WIDTH = {
			auto: 'auto'
		};
	
	function trunk8(element) {
		this.$element = $(element);
		this.original_text = $.trim(this.$element.html());
		this.settings = $.extend({}, $.fn.trunk8.defaults);
	}
	
	trunk8.prototype.updateSettings = function (options) {
		this.settings = $.extend(this.settings, options);
	};

	function stripHTML(html) {
		var tmp = document.createElement("DIV");
		tmp.innerHTML = html;
		
		if (typeof tmp.textContent != 'undefined') {
			return tmp.textContent;
		}

		return tmp.innerText
	}

	function getHtmlArr(str) {
		/* Builds an array of strings and designated */
		/* HTML tags around them. */
		if (stripHTML(str) === str) {
			return str.split(/\s/g);
		}
		var allResults = [],
			reg = /<([a-z]+)([^<]*)(?:>(.*?(?!<\1>))<\/\1>|\s+\/>)(['.?!,]*)|((?:[^<>\s])+['.?!,]*\w?|<br\s?\/?>)/ig,
			outArr = reg.exec(str),
			lastI,
			ind;
		while (outArr && lastI !== reg.lastIndex) {
			lastI = reg.lastIndex;
			if (outArr[5]) {
				allResults.push(outArr[5]);
			} else if (outArr[1]) {
				allResults.push({
					tag: outArr[1],
					attribs: outArr[2],
					content: outArr[3],
					after: outArr[4]
				});
			}
			outArr = reg.exec(str);
		}
		for (ind = 0; ind < allResults.length; ind++) {
			if (typeof allResults[ind] !== 'string' &&
					allResults[ind].content) {
				allResults[ind].content = getHtmlArr(allResults[ind].content);
			}
		}
		return allResults;
	}

	function rebuildHtmlFromBite(bite, htmlObject, fill) {
		// Take the processed bite after binary-search
		// truncated and re-build the original HTML
		// tags around the processed string.
		bite = bite.replace(fill, '');

		var biteHelper = function(contentArr, tagInfo) {
				var retStr = '',
					content,
					biteContent,
					biteLength,
					nextWord,
					i;
				for (i = 0; i < contentArr.length; i++) {
					content = contentArr[i];
					biteLength = $.trim(bite).split(' ').length;
					if ($.trim(bite).length) {
						if (typeof content === 'string') {
							if (!/<br\s*\/?>/i.test(content)) {
								if (biteLength === 1 && $.trim(bite).length <= content.length) {
									content = bite;
									// We want the fill to go inside of the last HTML
									// element if the element is a container.
									if (tagInfo === 'p' || tagInfo === 'div') {
										content += fill;
									}
									bite = '';
								} else {
									bite = bite.replace(content, '');
								}
							}
							retStr += $.trim(content) + ((i === contentArr.length-1 || biteLength <= 1) ? '' : ' ');
						} else {
							biteContent = biteHelper(content.content, content.tag);
							if (content.after) bite = bite.replace(content.after, '');
							if (biteContent) {
								if (!content.after) content.after = ' ';
								retStr += '<'+content.tag+content.attribs+'>'+biteContent+'</'+content.tag+'>' + content.after;
							}
						}
					}
				}
				return retStr;
			},
			htmlResults = biteHelper(htmlObject);

		// Add fill if doesn't exist. This will place it outside the HTML elements.
		if (htmlResults.slice(htmlResults.length - fill.length) === fill) {
			htmlResults += fill;
		}

		return htmlResults;
	}

	function truncate() {
		var data = this.data('trunk8'),
			settings = data.settings,
			width = settings.width,
			side = settings.side,
			fill = settings.fill,
			parseHTML = settings.parseHTML,
			line_height = utils.getLineHeight(this) * settings.lines,
			str = data.original_text,
			length = str.length,
			max_bite = '',
			lower, upper,
			bite_size,
			bite,
			text,
			htmlObject;
		
		/* Reset the field to the original string. */
		this.html(str);
		text = this.text();

		/* If string has HTML and parse HTML is set, build */
		/* the data struct to house the tags */
		if (parseHTML && stripHTML(str) !== str) {
			htmlObject = getHtmlArr(str);
			str = stripHTML(str);
			length = str.length;
		}

		if (width === WIDTH.auto) {
			/* Assuming there is no "overflow: hidden". */
			if (this.height() <= line_height) {
				/* Text is already at the optimal trunkage. */
				return;
			}

			/* Binary search technique for finding the optimal trunkage. */
			/* Find the maximum bite without overflowing. */
			lower = 0;
			upper = length - 1;

			while (lower <= upper) {
				bite_size = lower + ((upper - lower) >> 1);
				
				bite = utils.eatStr(str, side, length - bite_size, fill);

				if (parseHTML && htmlObject) {
					bite = rebuildHtmlFromBite(bite, htmlObject, fill);
				}
				
				this.html(bite);

				/* Check for overflow. */
				if (this.height() > line_height) {
					upper = bite_size - 1;
				}
				else {
					lower = bite_size + 1;

					/* Save the bigger bite. */
					max_bite = (max_bite.length > bite.length) ? max_bite : bite;
				}
			}

			/* Reset the content to eliminate possible existing scroll bars. */
			this.html('');
			
			/* Display the biggest bite. */
			this.html(max_bite);
			
			if (settings.tooltip) {
				this.attr('title', text);
			}
		}
		else if (!isNaN(width)) {
			bite_size = length - width;

			bite = utils.eatStr(str, side, bite_size, fill);

			this.html(bite);
			
			if (settings.tooltip) {
				this.attr('title', str);
			}
		}
		else {
			$.error('Invalid width "' + width + '".');
			return;
		}
		settings.onTruncate();
	}

	methods = {
		init: function (options) {
			return this.each(function () {
				var $this = $(this),
					data = $this.data('trunk8');
				
				if (!data) {
					$this.data('trunk8', (data = new trunk8(this)));
				}
				
				data.updateSettings(options);
				
				truncate.call($this);
			});
		},

		/** Updates the text value of the elements while maintaining truncation. */
		update: function (new_string) {
			return this.each(function () {
				var $this = $(this);
				
				/* Update text. */
				if (new_string) {
					$this.data('trunk8').original_text = new_string;
				}

				/* Truncate accordingly. */
				truncate.call($this);
			});
		},
		
		revert: function () {
			return this.each(function () {
				/* Get original text. */
				var text = $(this).data('trunk8').original_text;
				
				/* Revert element to original text. */
				$(this).html(text);
			});
		},

		/** Returns this instance's settings object. NOT CHAINABLE. */
		getSettings: function () {
			return $(this.get(0)).data('trunk8').settings;
		}
	};

	utils = {
		/** Replaces [bite_size] [side]-most chars in [str] with [fill]. */
		eatStr: function (str, side, bite_size, fill) {
			var length = str.length,
				key = utils.eatStr.generateKey.apply(null, arguments),
				half_length,
				half_bite_size;

			/* If the result is already in the cache, return it. */
			if (utils.eatStr.cache[key]) {
				return utils.eatStr.cache[key];
			}
			
			/* Common error handling. */
			if ((typeof str !== 'string') || (length === 0)) {
				$.error('Invalid source string "' + str + '".');
			}
			if ((bite_size < 0) || (bite_size > length)) {
				$.error('Invalid bite size "' + bite_size + '".');
			}
			else if (bite_size === 0) {
				/* No bite should show no truncation. */
				return str;
			}
			if (typeof (fill + '') !== 'string') {
				$.error('Fill unable to be converted to a string.');
			}

			/* Compute the result, store it in the cache, and return it. */
			switch (side) {
				case SIDES.right:
					/* str... */
					return utils.eatStr.cache[key] =
							$.trim(str.substr(0, length - bite_size)) + fill;
					
				case SIDES.left:
					/* ...str */
					return utils.eatStr.cache[key] =
							fill + $.trim(str.substr(bite_size));
					
				case SIDES.center:
					/* Bit-shift to the right by one === Math.floor(x / 2) */
					half_length = length >> 1; // halve the length
					half_bite_size = bite_size >> 1; // halve the bite_size

					/* st...r */
					return utils.eatStr.cache[key] =
							$.trim(utils.eatStr(str.substr(0, length - half_length), SIDES.right, bite_size - half_bite_size, '')) +
							fill +
							$.trim(utils.eatStr(str.substr(length - half_length), SIDES.left, half_bite_size, ''));
					
				default:
					$.error('Invalid side "' + side + '".');
			}
		},
		
		getLineHeight: function (elem) {
				var floats = $(elem).css('float');
				if (floats !== 'none') {
					$(elem).css('float', 'none');
				}
				var pos = $(elem).css('position');
				if (pos === 'absolute') {
					$(elem).css('position', 'static');
				}
	
				var html = $(elem).html(),
				wrapper_id = 'line-height-test',
				line_height;
	
				/* Set the content to a small single character and wrap. */
				$(elem).html('i').wrap('<div id="' + wrapper_id + '" />');
	
				/* Calculate the line height by measuring the wrapper.*/
				line_height = $('#' + wrapper_id).innerHeight();
	
				/* Remove the wrapper and reset the content. */
				$(elem).html(html).css({ 'float': floats, 'position': pos }).unwrap();
	
				return line_height;
			}
	};

	utils.eatStr.cache = {};
	utils.eatStr.generateKey = function () {
		return Array.prototype.join.call(arguments, '');
	};
	
	$.fn.trunk8 = function (method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else if (typeof method === 'object' || !method) {
			return methods.init.apply(this, arguments);
		}
		else {
			$.error('Method ' + method + ' does not exist on jQuery.trunk8');
		}
	};
	
	/* Default trunk8 settings. */
	$.fn.trunk8.defaults = {
		fill: '&hellip;',
		lines: 1,
		side: SIDES.right,
		tooltip: true,
		width: WIDTH.auto,
		parseHTML: false,
		onTruncate: function () {}
	};
})(jQuery);
;
/*

Uniform v2.1.2
Copyright © 2009 Josh Pyles / Pixelmatrix Design LLC
http://pixelmatrixdesign.com

Requires jQuery 1.3 or newer

Much thanks to Thomas Reynolds and Buck Wilson for their help and advice on
this.

Disabling text selection is made possible by Mathias Bynens
<http://mathiasbynens.be/> and his noSelect plugin.
<https://github.com/mathiasbynens/jquery-noselect>, which is embedded.

Also, thanks to David Kaneda and Eugene Bond for their contributions to the
plugin.

Tyler Akins has also rewritten chunks of the plugin, helped close many issues,
and ensured version 2 got out the door.

License:
MIT License - http://www.opensource.org/licenses/mit-license.php

Enjoy!

*/
/*global jQuery, document, navigator*/

(function (wind, $, undef) {
    "use strict";

    /**
     * Use .prop() if jQuery supports it, otherwise fall back to .attr()
     *
     * @param jQuery $el jQuery'd element on which we're calling attr/prop
     * @param ... All other parameters are passed to jQuery's function
     * @return The result from jQuery
     */
    function attrOrProp($el) {
        var args = Array.prototype.slice.call(arguments, 1);

        if ($el.prop) {
            // jQuery 1.6+
            return $el.prop.apply($el, args);
        }

        // jQuery 1.5 and below
        return $el.attr.apply($el, args);
    }

    /**
     * For backwards compatibility with older jQuery libraries, only bind
     * one thing at a time.  Also, this function adds our namespace to
     * events in one consistent location, shrinking the minified code.
     *
     * The properties on the events object are the names of the events
     * that we are supposed to add to.  It can be a space separated list.
     * The namespace will be added automatically.
     *
     * @param jQuery $el
     * @param Object options Uniform options for this element
     * @param Object events Events to bind, properties are event names
     */
    function bindMany($el, options, events) {
        var name, namespaced;

        for (name in events) {
            if (events.hasOwnProperty(name)) {
                namespaced = name.replace(/ |$/g, options.eventNamespace);
                $el.bind(namespaced, events[name]);
            }
        }
    }

    /**
     * Bind the hover, active, focus, and blur UI updates
     *
     * @param jQuery $el Original element
     * @param jQuery $target Target for the events (our div/span)
     * @param Object options Uniform options for the element $target
     */
    function bindUi($el, $target, options) {
        bindMany($el, options, {
            focus: function () {
                $target.addClass(options.focusClass);
            },
            blur: function () {
                $target.removeClass(options.focusClass);
                $target.removeClass(options.activeClass);
            },
            mouseenter: function () {
                $target.addClass(options.hoverClass);
            },
            mouseleave: function () {
                $target.removeClass(options.hoverClass);
                $target.removeClass(options.activeClass);
            },
            "mousedown touchbegin": function () {
                if (!$el.is(":disabled")) {
                    $target.addClass(options.activeClass);
                }
            },
            "mouseup touchend": function () {
                $target.removeClass(options.activeClass);
            }
        });
    }

    /**
     * Remove the hover, focus, active classes.
     *
     * @param jQuery $el Element with classes
     * @param Object options Uniform options for the element
     */
    function classClearStandard($el, options) {
        $el.removeClass(options.hoverClass + " " + options.focusClass + " " + options.activeClass);
    }

    /**
     * Add or remove a class, depending on if it's "enabled"
     *
     * @param jQuery $el Element that has the class added/removed
     * @param String className Class or classes to add/remove
     * @param Boolean enabled True to add the class, false to remove
     */
    function classUpdate($el, className, enabled) {
        if (enabled) {
            $el.addClass(className);
        } else {
            $el.removeClass(className);
        }
    }

    /**
     * Updating the "checked" property can be a little tricky.  This
     * changed in jQuery 1.6 and now we can pass booleans to .prop().
     * Prior to that, one either adds an attribute ("checked=checked") or
     * removes the attribute.
     *
     * @param jQuery $tag Our Uniform span/div
     * @param jQuery $el Original form element
     * @param Object options Uniform options for this element
     */
    function classUpdateChecked($tag, $el, options) {
        var c = "checked",
            isChecked = $el.is(":" + c);

        if ($el.prop) {
            // jQuery 1.6+
            $el.prop(c, isChecked);
        } else {
            // jQuery 1.5 and below
            if (isChecked) {
                $el.attr(c, c);
            } else {
                $el.removeAttr(c);
            }
        }

        classUpdate($tag, options.checkedClass, isChecked);
    }

    /**
     * Set or remove the "disabled" class for disabled elements, based on
     * if the element is detected to be disabled.
     *
     * @param jQuery $tag Our Uniform span/div
     * @param jQuery $el Original form element
     * @param Object options Uniform options for this element
     */
    function classUpdateDisabled($tag, $el, options) {
        classUpdate($tag, options.disabledClass, $el.is(":disabled"));
    }

    /**
     * Wrap an element inside of a container or put the container next
     * to the element.  See the code for examples of the different methods.
     *
     * Returns the container that was added to the HTML.
     *
     * @param jQuery $el Element to wrap
     * @param jQuery $container Add this new container around/near $el
     * @param String method One of "after", "before" or "wrap"
     * @return $container after it has been cloned for adding to $el
     */
    function divSpanWrap($el, $container, method) {
        switch (method) {
        case "after":
            // Result:  <element /> <container />
            $el.after($container);
            return $el.next();
        case "before":
            // Result:  <container /> <element />
            $el.before($container);
            return $el.prev();
        case "wrap":
            // Result:  <container> <element /> </container>
            $el.wrap($container);
            return $el.parent();
        }

        return null;
    }


    /**
     * Create a div/span combo for uniforming an element
     *
     * @param jQuery $el Element to wrap
     * @param Object options Options for the element, set by the user
     * @param Object divSpanConfig Options for how we wrap the div/span
     * @return Object Contains the div and span as properties
     */
    function divSpan($el, options, divSpanConfig) {
        var $div, $span, id;

        if (!divSpanConfig) {
            divSpanConfig = {};
        }

        divSpanConfig = $.extend({
            bind: {},
            divClass: null,
            divWrap: "wrap",
            spanClass: null,
            spanHtml: null,
            spanWrap: "wrap"
        }, divSpanConfig);

        $div = $('<div />');
        $span = $('<span />');

        // Automatically hide this div/span if the element is hidden.
        // Do not hide if the element is hidden because a parent is hidden.
        if (options.autoHide && $el.is(':hidden') && $el.css('display') === 'none') {
            $div.hide();
        }

        if (divSpanConfig.divClass) {
            $div.addClass(divSpanConfig.divClass);
        }

        if (options.wrapperClass) {
            $div.addClass(options.wrapperClass);
        }

        if (divSpanConfig.spanClass) {
            $span.addClass(divSpanConfig.spanClass);
        }

        id = attrOrProp($el, 'id');

        if (options.useID && id) {
            attrOrProp($div, 'id', options.idPrefix + '-' + id);
        }

        if (divSpanConfig.spanHtml) {
            $span.html(divSpanConfig.spanHtml);
        }

        $div = divSpanWrap($el, $div, divSpanConfig.divWrap);
        $span = divSpanWrap($el, $span, divSpanConfig.spanWrap);
        classUpdateDisabled($div, $el, options);
        return {
            div: $div,
            span: $span
        };
    }


    /**
     * Wrap an element with a span to apply a global wrapper class
     *
     * @param jQuery $el Element to wrap
     * @param object options
     * @return jQuery Wrapper element
     */
    function wrapWithWrapperClass($el, options) {
        var $span;

        if (!options.wrapperClass) {
            return null;
        }

        $span = $('<span />').addClass(options.wrapperClass);
        $span = divSpanWrap($el, $span, "wrap");
        return $span;
    }


    /**
     * Test if high contrast mode is enabled.
     *
     * In high contrast mode, background images can not be set and
     * they are always returned as 'none'.
     *
     * @return boolean True if in high contrast mode
     */
    function highContrast() {
        var c, $div, el, rgb;

        // High contrast mode deals with white and black
        rgb = 'rgb(120,2,153)';
        $div = $('<div style="width:0;height:0;color:' + rgb + '">');
        $('body').append($div);
        el = $div.get(0);

        // $div.css() will get the style definition, not
        // the actually displaying style
        if (wind.getComputedStyle) {
            c = wind.getComputedStyle(el, '').color;
        } else {
            c = (el.currentStyle || el.style || {}).color;
        }

        $div.remove();
        return c.replace(/ /g, '') !== rgb;
    }


    /**
     * Change text into safe HTML
     *
     * @param String text
     * @return String HTML version
     */
    function htmlify(text) {
        if (!text) {
            return "";
        }

        return $('<span />').text(text).html();
    }

    /**
     * If not MSIE, return false.
     * If it is, return the version number.
     *
     * @return false|number
     */
    function isMsie() {
        return navigator.cpuClass && !navigator.product;
    }

    /**
     * Return true if this version of IE allows styling
     *
     * @return boolean
     */
    function isMsieSevenOrNewer() {
        if (wind.XMLHttpRequest !== undefined) {
            return true;
        }

        return false;
    }

    /**
     * Test if the element is a multiselect
     *
     * @param jQuery $el Element
     * @return boolean true/false
     */
    function isMultiselect($el) {
        var elSize;

        if ($el[0].multiple) {
            return true;
        }

        elSize = attrOrProp($el, "size");

        if (!elSize || elSize <= 1) {
            return false;
        }

        return true;
    }

    /**
     * Meaningless utility function.  Used mostly for improving minification.
     *
     * @return false
     */
    function returnFalse() {
        return false;
    }

    /**
     * noSelect plugin, very slightly modified
     * http://mths.be/noselect v1.0.3
     *
     * @param jQuery $elem Element that we don't want to select
     * @param Object options Uniform options for the element
     */
    function noSelect($elem, options) {
        var none = 'none';
        bindMany($elem, options, {
            'selectstart dragstart mousedown': returnFalse
        });

        $elem.css({
            MozUserSelect: none,
            msUserSelect: none,
            webkitUserSelect: none,
            userSelect: none
        });
    }

    /**
     * Updates the filename tag based on the value of the real input
     * element.
     *
     * @param jQuery $el Actual form element
     * @param jQuery $filenameTag Span/div to update
     * @param Object options Uniform options for this element
     */
    function setFilename($el, $filenameTag, options) {
        var filename = $el.val();

        if (filename === "") {
            filename = options.fileDefaultHtml;
        } else {
            filename = filename.split(/[\/\\]+/);
            filename = filename[(filename.length - 1)];
        }

        $filenameTag.text(filename);
    }


    /**
     * Function from jQuery to swap some CSS values, run a callback,
     * then restore the CSS.  Modified to pass JSLint and handle undefined
     * values with 'use strict'.
     *
     * @param jQuery $el Element
     * @param object newCss CSS values to swap out
     * @param Function callback Function to run
     */
    function swap($elements, newCss, callback) {
        var restore, item;

        restore = [];

        $elements.each(function () {
            var name;

            for (name in newCss) {
                if (Object.prototype.hasOwnProperty.call(newCss, name)) {
                    restore.push({
                        el: this,
                        name: name,
                        old: this.style[name]
                    });

                    this.style[name] = newCss[name];
                }
            }
        });

        callback();

        while (restore.length) {
            item = restore.pop();
            item.el.style[item.name] = item.old;
        }
    }


    /**
     * The browser doesn't provide sizes of elements that are not visible.
     * This will clone an element and add it to the DOM for calculations.
     *
     * @param jQuery $el
     * @param String method
     */
    function sizingInvisible($el, callback) {
        var targets;

        // We wish to target ourselves and any parents as long as
        // they are not visible
        targets = $el.parents();
        targets.push($el[0]);
        targets = targets.not(':visible');
        swap(targets, {
            visibility: "hidden",
            display: "block",
            position: "absolute"
        }, callback);
    }


    /**
     * Standard way to unwrap the div/span combination from an element
     *
     * @param jQuery $el Element that we wish to preserve
     * @param Object options Uniform options for the element
     * @return Function This generated function will perform the given work
     */
    function unwrapUnwrapUnbindFunction($el, options) {
        return function () {
            $el.unwrap().unwrap().unbind(options.eventNamespace);
        };
    }

    var allowStyling = true,  // False if IE6 or other unsupported browsers
        highContrastTest = false,  // Was the high contrast test ran?
        uniformHandlers = [  // Objects that take care of "unification"
            {
                // Buttons
                match: function ($el) {
                    return $el.is("a, button, :submit, :reset, input[type='button']");
                },
                apply: function ($el, options) {
                    var $div, defaultSpanHtml, ds, getHtml, doingClickEvent;
                    defaultSpanHtml = options.submitDefaultHtml;

                    if ($el.is(":reset")) {
                        defaultSpanHtml = options.resetDefaultHtml;
                    }

                    if ($el.is("a, button")) {
                        // Use the HTML inside the tag
                        getHtml = function () {
                            return $el.html() || defaultSpanHtml;
                        };
                    } else {
                        // Use the value property of the element
                        getHtml = function () {
                            return htmlify(attrOrProp($el, "value")) || defaultSpanHtml;
                        };
                    }

                    ds = divSpan($el, options, {
                        divClass: options.buttonClass,
                        spanHtml: getHtml()
                    });
                    $div = ds.div;
                    bindUi($el, $div, options);
                    doingClickEvent = false;
                    bindMany($div, options, {
                        "click touchend": function () {
                            var ev, res, target, href;

                            if (doingClickEvent) {
                                return;
                            }

                            if ($el.is(':disabled')) {
                                return;
                            }

                            doingClickEvent = true;

                            if ($el[0].dispatchEvent) {
                                ev = document.createEvent("MouseEvents");
                                ev.initEvent("click", true, true);
                                res = $el[0].dispatchEvent(ev);

                                if ($el.is('a') && res) {
                                    target = attrOrProp($el, 'target');
                                    href = attrOrProp($el, 'href');

                                    if (!target || target === '_self') {
                                        document.location.href = href;
                                    } else {
                                        wind.open(href, target);
                                    }
                                }
                            } else {
                                $el.click();
                            }

                            doingClickEvent = false;
                        }
                    });
                    noSelect($div, options);
                    return {
                        remove: function () {
                            // Move $el out
                            $div.after($el);

                            // Remove div and span
                            $div.remove();

                            // Unbind events
                            $el.unbind(options.eventNamespace);
                            return $el;
                        },
                        update: function () {
                            classClearStandard($div, options);
                            classUpdateDisabled($div, $el, options);
                            $el.detach();
                            ds.span.html(getHtml()).append($el);
                        }
                    };
                }
            },
            {
                // Checkboxes
                match: function ($el) {
                    return $el.is(":checkbox");
                },
                apply: function ($el, options) {
                    var ds, $div, $span;
                    ds = divSpan($el, options, {
                        divClass: options.checkboxClass
                    });
                    $div = ds.div;
                    $span = ds.span;

                    // Add focus classes, toggling, active, etc.
                    bindUi($el, $div, options);
                    bindMany($el, options, {
                        "click touchend": function () {
                            classUpdateChecked($span, $el, options);
                        }
                    });
                    classUpdateChecked($span, $el, options);
                    return {
                        remove: unwrapUnwrapUnbindFunction($el, options),
                        update: function () {
                            classClearStandard($div, options);
                            $span.removeClass(options.checkedClass);
                            classUpdateChecked($span, $el, options);
                            classUpdateDisabled($div, $el, options);
                        }
                    };
                }
            },
            {
                // File selection / uploads
                match: function ($el) {
                    return $el.is(":file");
                },
                apply: function ($el, options) {
                    var ds, $div, $filename, $button;

                    // The "span" is the button
                    ds = divSpan($el, options, {
                        divClass: options.fileClass,
                        spanClass: options.fileButtonClass,
                        spanHtml: options.fileButtonHtml,
                        spanWrap: "after"
                    });
                    $div = ds.div;
                    $button = ds.span;
                    $filename = $("<span />").html(options.fileDefaultHtml);
                    $filename.addClass(options.filenameClass);
                    $filename = divSpanWrap($el, $filename, "after");

                    // Set the size
                    if (!attrOrProp($el, "size")) {
                        attrOrProp($el, "size", $div.width() / 10);
                    }

                    // Actions
                    function filenameUpdate() {
                        setFilename($el, $filename, options);
                    }

                    bindUi($el, $div, options);

                    // Account for input saved across refreshes
                    filenameUpdate();

                    // IE7 doesn't fire onChange until blur or second fire.
                    if (isMsie()) {
                        // IE considers browser chrome blocking I/O, so it
                        // suspends tiemouts until after the file has
                        // been selected.
                        bindMany($el, options, {
                            click: function () {
                                $el.trigger("change");
                                setTimeout(filenameUpdate, 0);
                            }
                        });
                    } else {
                        // All other browsers behave properly
                        bindMany($el, options, {
                            change: filenameUpdate
                        });
                    }

                    noSelect($filename, options);
                    noSelect($button, options);
                    return {
                        remove: function () {
                            // Remove filename and button
                            $filename.remove();
                            $button.remove();

                            // Unwrap parent div, remove events
                            return $el.unwrap().unbind(options.eventNamespace);
                        },
                        update: function () {
                            classClearStandard($div, options);
                            setFilename($el, $filename, options);
                            classUpdateDisabled($div, $el, options);
                        }
                    };
                }
            },
            {
                // Input fields (text)
                match: function ($el) {
                    if ($el.is("input")) {
                        var t = (" " + attrOrProp($el, "type") + " ").toLowerCase(),
                            allowed = " color date datetime datetime-local email month number password search tel text time url week ";
                        return allowed.indexOf(t) >= 0;
                    }

                    return false;
                },
                apply: function ($el, options) {
                    var elType, $wrapper;

                    elType = attrOrProp($el, "type");
                    $el.addClass(options.inputClass);
                    $wrapper = wrapWithWrapperClass($el, options);
                    bindUi($el, $el, options);

                    if (options.inputAddTypeAsClass) {
                        $el.addClass(elType);
                    }

                    return {
                        remove: function () {
                            $el.removeClass(options.inputClass);

                            if (options.inputAddTypeAsClass) {
                                $el.removeClass(elType);
                            }

                            if ($wrapper) {
                                $el.unwrap();
                            }
                        },
                        update: returnFalse
                    };
                }
            },
            {
                // Radio buttons
                match: function ($el) {
                    return $el.is(":radio");
                },
                apply: function ($el, options) {
                    var ds, $div, $span;
                    ds = divSpan($el, options, {
                        divClass: options.radioClass
                    });
                    $div = ds.div;
                    $span = ds.span;

                    // Add classes for focus, handle active, checked
                    bindUi($el, $div, options);
                    bindMany($el, options, {
                        "click touchend": function () {
                            // Find all radios with the same name, then update
                            // them with $.uniform.update() so the right
                            // per-element options are used
                            $.uniform.update($(':radio[name="' + attrOrProp($el, "name") + '"]'));
                        }
                    });
                    classUpdateChecked($span, $el, options);
                    return {
                        remove: unwrapUnwrapUnbindFunction($el, options),
                        update: function () {
                            classClearStandard($div, options);
                            classUpdateChecked($span, $el, options);
                            classUpdateDisabled($div, $el, options);
                        }
                    };
                }
            },
            {
                // Select lists, but do not style multiselects here
                match: function ($el) {
                    if ($el.is("select") && !isMultiselect($el)) {
                        return true;
                    }

                    return false;
                },
                apply: function ($el, options) {
                    var ds, $div, $span, origElemWidth;

                    if (options.selectAutoWidth) {
                        sizingInvisible($el, function () {
                            origElemWidth = $el.width();
                        });
                    }

                    ds = divSpan($el, options, {
                        divClass: options.selectClass,
                        spanHtml: ($el.find(":selected:first") || $el.find("option:first")).html(),
                        spanWrap: "before"
                    });
                    $div = ds.div;
                    $span = ds.span;

                    if (options.selectAutoWidth) {
                        // Use the width of the select and adjust the
                        // span and div accordingly
                        //console.log(origElemWidth + spanPad);
                        sizingInvisible($el, function () {
                            // Force "display: block" - related to bug #287

                            swap($([ $span[0], $div[0] ]), {
                                display: "block"
                            }, function () {
                                var spanPad;
                                spanPad = $span.outerWidth() - $span.width();
                                $div.width(origElemWidth + spanPad);
                                $span.width(origElemWidth);
                            });
                        });
                    } else {
                        // Force the select to fill the size of the div
                        $div.addClass('fixedWidth');
                    }

                    // Take care of events
                    bindUi($el, $div, options);
                    bindMany($el, options, {
                        change: function () {
                            $span.html($el.find(":selected").html());
                            $div.removeClass(options.activeClass);

                            // Joel - added this in to aid the
                            sizingInvisible($el, function () {
                                // Force "display: block" - related to bug #287

                                swap($([ $span[0], $div[0] ]), {
                                    display: "block"
                                }, function () {
                                    var spanPad;
                                    spanPad = $span.outerWidth() - $span.width();
                                    $div.width(origElemWidth + spanPad);
                                    $span.width(origElemWidth);
                                });
                            });
                        },
                        "click touchend": function () {
                            // IE7 and IE8 may not update the value right
                            // until after click event - issue #238
                            var selHtml = $el.find(":selected").html();

                            if ($span.html() !== selHtml) {
                                // Change was detected
                                // Fire the change event on the select tag
                                $el.trigger('change');
                            }
                        },
                        keyup: function () {
                            $span.html($el.find(":selected").html());
                        }
                    });
                    noSelect($span, options);
                    return {
                        remove: function () {
                            // Remove sibling span
                            $span.remove();

                            // Unwrap parent div
                            $el.unwrap().unbind(options.eventNamespace);
                            return $el;
                        },
                        update: function () {
                            if (options.selectAutoWidth) {
                                // Easier to remove and reapply formatting
                                $.uniform.restore($el);
                                $el.uniform(options);
                            } else {
                                classClearStandard($div, options);

                                // Reset current selected text
                                $span.html($el.find(":selected").html());
                                classUpdateDisabled($div, $el, options);
                            }
                        }
                    };
                }
            },
            {
                // Select lists - multiselect lists only
                match: function ($el) {
                    if ($el.is("select") && isMultiselect($el)) {
                        return true;
                    }

                    return false;
                },
                apply: function ($el, options) {
                    var $wrapper;

                    $el.addClass(options.selectMultiClass);
                    $wrapper = wrapWithWrapperClass($el, options);
                    bindUi($el, $el, options);

                    return {
                        remove: function () {
                            $el.removeClass(options.selectMultiClass);

                            if ($wrapper) {
                                $el.unwrap();
                            }
                        },
                        update: returnFalse
                    };
                }
            },
            {
                // Textareas
                match: function ($el) {
                    return $el.is("textarea");
                },
                apply: function ($el, options) {
                    var $wrapper;

                    $el.addClass(options.textareaClass);
                    $wrapper = wrapWithWrapperClass($el, options);
                    bindUi($el, $el, options);

                    return {
                        remove: function () {
                            $el.removeClass(options.textareaClass);

                            if ($wrapper) {
                                $el.unwrap();
                            }
                        },
                        update: returnFalse
                    };
                }
            }
        ];

    // IE6 can't be styled - can't set opacity on select
    if (isMsie() && !isMsieSevenOrNewer()) {
        allowStyling = false;
    }

    $.uniform = {
        // Default options that can be overridden globally or when uniformed
        // globally:  $.uniform.defaults.fileButtonHtml = "Pick A File";
        // on uniform:  $('input').uniform({fileButtonHtml: "Pick a File"});
        defaults: {
            activeClass: "active",
            autoHide: true,
            buttonClass: "button",
            checkboxClass: "checker",
            checkedClass: "checked",
            disabledClass: "disabled",
            eventNamespace: ".uniform",
            fileButtonClass: "action",
            fileButtonHtml: "Choose File",
            fileClass: "uploader",
            fileDefaultHtml: "No file selected",
            filenameClass: "filename",
            focusClass: "focus",
            hoverClass: "hover",
            idPrefix: "uniform",
            inputAddTypeAsClass: true,
            inputClass: "uniform-input",
            radioClass: "radio",
            resetDefaultHtml: "Reset",
            resetSelector: false,  // We'll use our own function when you don't specify one
            selectAutoWidth: true,
            selectClass: "has-uniform",
            selectMultiClass: "uniform-multiselect",
            submitDefaultHtml: "Submit",  // Only text allowed
            textareaClass: "uniform",
            useID: true,
            wrapperClass: null
        },

        // All uniformed elements - DOM objects
        elements: []
    };

    $.fn.uniform = function (options) {
        var el = this;
        options = $.extend({}, $.uniform.defaults, options);

        // If we are in high contrast mode, do not allow styling
        if (!highContrastTest) {
            highContrastTest = true;

            if (highContrast()) {
                allowStyling = false;
            }
        }

        // Only uniform on browsers that work
        if (!allowStyling) {
            return this;
        }

        // Code for specifying a reset button
        if (options.resetSelector) {
            $(options.resetSelector).mouseup(function () {
                wind.setTimeout(function () {
                    $.uniform.update(el);
                }, 10);
            });
        }

        return this.each(function () {
            var $el = $(this), i, handler, callbacks;

            // Avoid uniforming elements already uniformed - just update
            if ($el.data("uniformed")) {
                $.uniform.update($el);
                return;
            }

            // See if we have any handler for this type of element
            for (i = 0; i < uniformHandlers.length; i = i + 1) {
                handler = uniformHandlers[i];

                if (handler.match($el, options)) {
                    callbacks = handler.apply($el, options);
                    $el.data("uniformed", callbacks);

                    // Store element in our global array
                    $.uniform.elements.push($el.get(0));
                    return;
                }
            }

            // Could not style this element
        });
    };

    $.uniform.restore = $.fn.uniform.restore = function (elem) {
        if (elem === undef) {
            elem = $.uniform.elements;
        }

        $(elem).each(function () {
            var $el = $(this), index, elementData;
            elementData = $el.data("uniformed");

            // Skip elements that are not uniformed
            if (!elementData) {
                return;
            }

            // Unbind events, remove additional markup that was added
            elementData.remove();

            // Remove item from list of uniformed elements
            index = $.inArray(this, $.uniform.elements);

            if (index >= 0) {
                $.uniform.elements.splice(index, 1);
            }

            $el.removeData("uniformed");
        });
    };

    $.uniform.update = $.fn.uniform.update = function (elem) {
        if (elem === undef) {
            elem = $.uniform.elements;
        }

        $(elem).each(function () {
            var $el = $(this), elementData;
            elementData = $el.data("uniformed");

            // Skip elements that are not uniformed
            if (!elementData) {
                return;
            }

            elementData.update($el, elementData.options);
        });
    };
}(this, jQuery));
;
/*! Tiny Pub/Sub - v0.7.0 - 2013-01-29
* https://github.com/cowboy/jquery-tiny-pubsub
* Copyright (c) 2013 "Cowboy" Ben Alman; Licensed MIT */
(function(n){var u=n({});n.subscribe=function(){u.on.apply(u,arguments)},n.unsubscribe=function(){u.off.apply(u,arguments)},n.publish=function(){u.trigger.apply(u,arguments)}})(jQuery);;
/*! Magnific Popup - v0.9.9 - 2014-09-06
* http://dimsemenov.com/plugins/magnific-popup/
* Copyright (c) 2014 Dmitry Semenov; */
; (function ($) {

    /*>>core*/
    /**
     *
     * Magnific Popup Core JS file
     *
     */


    /**
     * Private static constants
     */
    var CLOSE_EVENT = 'Close',
        BEFORE_CLOSE_EVENT = 'BeforeClose',
        AFTER_CLOSE_EVENT = 'AfterClose',
        BEFORE_APPEND_EVENT = 'BeforeAppend',
        MARKUP_PARSE_EVENT = 'MarkupParse',
        OPEN_EVENT = 'Open',
        CHANGE_EVENT = 'Change',
        NS = 'mfp',
        EVENT_NS = '.' + NS,
        READY_CLASS = 'mfp-ready',
        REMOVING_CLASS = 'mfp-removing',
        PREVENT_CLOSE_CLASS = 'mfp-prevent-close';


    /**
     * Private vars
     */
    var mfp, // As we have only one instance of MagnificPopup object, we define it locally to not to use 'this'
        MagnificPopup = function () { },
        _isJQ = !!(window.jQuery),
        _prevStatus,
        _window = $(window),
        _body,
        _document,
        _prevContentType,
        _wrapClasses,
        _currPopupType;


    /**
     * Private functions
     */
    var _mfpOn = function (name, f) {
        mfp.ev.on(NS + name + EVENT_NS, f);
    },
        _getEl = function (className, appendTo, html, raw) {
            var el = document.createElement('div');
            el.className = 'mfp-' + className;
            if (html) {
                el.innerHTML = html;
            }
            if (!raw) {
                el = $(el);
                if (appendTo) {
                    el.appendTo(appendTo);
                }
            } else if (appendTo) {
                appendTo.appendChild(el);
            }
            return el;
        },
        _mfpTrigger = function (e, data) {
            mfp.ev.triggerHandler(NS + e, data);

            if (mfp.st.callbacks) {
                // converts "mfpEventName" to "eventName" callback and triggers it if it's present
                e = e.charAt(0).toLowerCase() + e.slice(1);
                if (mfp.st.callbacks[e]) {
                    mfp.st.callbacks[e].apply(mfp, $.isArray(data) ? data : [data]);
                }
            }
        },
        _getCloseBtn = function (type) {
            if (type !== _currPopupType || !mfp.currTemplate.closeBtn) {
                mfp.currTemplate.closeBtn = $(mfp.st.closeMarkup.replace('%title%', mfp.st.tClose));
                _currPopupType = type;
            }
            return mfp.currTemplate.closeBtn;
        },
        // Initialize Magnific Popup only when called at least once
        _checkInstance = function () {
            if (!$.magnificPopup.instance) {
                mfp = new MagnificPopup();
                mfp.init();
                $.magnificPopup.instance = mfp;
            }
        },
        // CSS transition detection, http://stackoverflow.com/questions/7264899/detect-css-transitions-using-javascript-and-without-modernizr
        supportsTransitions = function () {
            var s = document.createElement('p').style, // 's' for style. better to create an element if body yet to exist
                v = ['ms', 'O', 'Moz', 'Webkit']; // 'v' for vendor

            if (s['transition'] !== undefined) {
                return true;
            }

            while (v.length) {
                if (v.pop() + 'Transition' in s) {
                    return true;
                }
            }

            return false;
        };



    /**
     * Public functions
     */
    MagnificPopup.prototype = {

        constructor: MagnificPopup,

        /**
         * Initializes Magnific Popup plugin.
         * This function is triggered only once when $.fn.magnificPopup or $.magnificPopup is executed
         */
        init: function () {
            var appVersion = navigator.appVersion;
            mfp.isIE7 = appVersion.indexOf("MSIE 7.") !== -1;
            mfp.isIE8 = appVersion.indexOf("MSIE 8.") !== -1;
            mfp.isLowIE = mfp.isIE7 || mfp.isIE8;
            mfp.isAndroid = (/android/gi).test(appVersion);
            mfp.isIOS = (/iphone|ipad|ipod/gi).test(appVersion);
            mfp.supportsTransition = supportsTransitions();

            // We disable fixed positioned lightbox on devices that don't handle it nicely.
            // If you know a better way of detecting this - let me know.
            mfp.probablyMobile = (mfp.isAndroid || mfp.isIOS || /(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent));
            _document = $(document);

            mfp.popupsCache = {};
        },

        /**
         * Opens popup
         * @param  data [description]
         */
        open: function (data) {

            if (!_body) {
                _body = $(document.body);
            }

            var i;

            if (data.isObj === false) {
                // convert jQuery collection to array to avoid conflicts later
                mfp.items = data.items.toArray();

                mfp.index = 0;
                var items = data.items,
                    item;
                for (i = 0; i < items.length; i++) {
                    item = items[i];
                    if (item.parsed) {
                        item = item.el[0];
                    }
                    if (item === data.el[0]) {
                        mfp.index = i;
                        break;
                    }
                }
            } else {
                mfp.items = $.isArray(data.items) ? data.items : [data.items];
                mfp.index = data.index || 0;
            }


            //$.publish('popup_object_focus', mfp.items[mfp.index]); // joel publish a popup event to grab the trigger item


            // if popup is already opened - we just update the content
            if (mfp.isOpen) {
                mfp.updateItemHTML();
                return;
            }

            mfp.types = [];
            _wrapClasses = '';
            if (data.mainEl && data.mainEl.length) {
                mfp.ev = data.mainEl.eq(0);
            } else {
                mfp.ev = _document;
            }

            if (data.key) {
                if (!mfp.popupsCache[data.key]) {
                    mfp.popupsCache[data.key] = {};
                }
                mfp.currTemplate = mfp.popupsCache[data.key];
            } else {
                mfp.currTemplate = {};
            }



            mfp.st = $.extend(true, {}, $.magnificPopup.defaults, data);
            mfp.fixedContentPos = mfp.st.fixedContentPos === 'auto' ? !mfp.probablyMobile : mfp.st.fixedContentPos;

            if (mfp.st.modal) {
                mfp.st.closeOnContentClick = false;
                mfp.st.closeOnBgClick = false;
                mfp.st.showCloseBtn = false;
                mfp.st.enableEscapeKey = false;
            }


            // Building markup
            // main containers are created only once
            if (!mfp.bgOverlay) {

                // Dark overlay
                mfp.bgOverlay = _getEl('bg').on('click' + EVENT_NS, function () {
                    mfp.close();
                });

                mfp.wrap = _getEl('wrap').attr('tabindex', -1).on('click' + EVENT_NS, function (e) {
                    if (mfp._checkIfClose(e.target)) {
                        mfp.close();
                    }
                });

                mfp.container = _getEl('container', mfp.wrap);
            }

            mfp.contentContainer = _getEl('content');
            if (mfp.st.preloader) {
                mfp.preloader = _getEl('preloader', mfp.container, mfp.st.tLoading);
            }


            // Initializing modules
            var modules = $.magnificPopup.modules;
            for (i = 0; i < modules.length; i++) {
                var n = modules[i];
                n = n.charAt(0).toUpperCase() + n.slice(1);
                mfp['init' + n].call(mfp);
            }
            _mfpTrigger('BeforeOpen');


            if (mfp.st.showCloseBtn) {
                // Close button
                if (!mfp.st.closeBtnInside) {
                    mfp.wrap.append(_getCloseBtn());
                } else {
                    _mfpOn(MARKUP_PARSE_EVENT, function (e, template, values, item) {
                        values.close_replaceWith = _getCloseBtn(item.type);
                    });
                    _wrapClasses += ' mfp-close-btn-in';
                }
            }

            if (mfp.st.alignTop) {
                _wrapClasses += ' mfp-align-top';
            }



            if (mfp.fixedContentPos) {
                mfp.wrap.css({
                    overflow: mfp.st.overflowY,
                    overflowX: 'hidden',
                    overflowY: mfp.st.overflowY
                });
            } else {
                mfp.wrap.css({
                    top: _window.scrollTop(),
                    position: 'absolute'
                });
            }
            if (mfp.st.fixedBgPos === false || (mfp.st.fixedBgPos === 'auto' && !mfp.fixedContentPos)) {
                mfp.bgOverlay.css({
                    height: _document.height(),
                    position: 'absolute'
                });
            }



            if (mfp.st.enableEscapeKey) {
                // Close on ESC key
                _document.on('keyup' + EVENT_NS, function (e) {
                    if (e.keyCode === 27) {
                        mfp.close();
                    }
                });
            }

            _window.on('resize' + EVENT_NS, function () {
                mfp.updateSize();
            });


            if (!mfp.st.closeOnContentClick) {
                _wrapClasses += ' mfp-auto-cursor';
            }

            if (_wrapClasses)
                mfp.wrap.addClass(_wrapClasses);


            // this triggers recalculation of layout, so we get it once to not to trigger twice
            var windowHeight = mfp.wH = _window.height();


            var windowStyles = {};

            if (mfp.fixedContentPos) {
                if (mfp._hasScrollBar(windowHeight)) {
                    var s = mfp._getScrollbarSize();
                    if (s) {
                        windowStyles.marginRight = s;
                    }
                }
            }

            if (mfp.fixedContentPos) {
                if (!mfp.isIE7) {
                    windowStyles.overflow = 'hidden';
                } else {
                    // ie7 double-scroll bug
                    $('body, html').css('overflow', 'hidden');
                }
            }



            var classesToadd = mfp.st.mainClass;
            if (mfp.isIE7) {
                classesToadd += ' mfp-ie7';
            }
            if (classesToadd) {
                mfp._addClassToMFP(classesToadd);
            }

            // add content
            mfp.updateItemHTML();

            _mfpTrigger('BuildControls');

            // remove scrollbar, add margin e.t.c
            $('html').css(windowStyles);

            // add everything to DOM
            mfp.bgOverlay.add(mfp.wrap).prependTo(mfp.st.prependTo || _body);

            // Save last focused element
            mfp._lastFocusedEl = document.activeElement;

            // Wait for next cycle to allow CSS transition
            setTimeout(function () {

                if (mfp.content) {
                    mfp._addClassToMFP(READY_CLASS);
                    mfp._setFocus();
                } else {
                    // if content is not defined (not loaded e.t.c) we add class only for BG
                    mfp.bgOverlay.addClass(READY_CLASS);
                }

                // Trap the focus in popup
                _document.on('focusin' + EVENT_NS, mfp._onFocusIn);

            }, 16);

            mfp.isOpen = true;
            mfp.updateSize(windowHeight);
            _mfpTrigger(OPEN_EVENT);

            return data;
        },

        /**
         * Closes the popup
         */
        close: function () {
            if (!mfp.isOpen) return;
            _mfpTrigger(BEFORE_CLOSE_EVENT);

            mfp.isOpen = false;
            // for CSS3 animation
            if (mfp.st.removalDelay && !mfp.isLowIE && mfp.supportsTransition) {
                mfp._addClassToMFP(REMOVING_CLASS);
                setTimeout(function () {
                    mfp._close();
                }, mfp.st.removalDelay);
            } else {
                mfp._close();
            }
        },

        /**
         * Helper for close() function
         */
        _close: function () {
            _mfpTrigger(CLOSE_EVENT);

            var classesToRemove = REMOVING_CLASS + ' ' + READY_CLASS + ' ';

            mfp.bgOverlay.detach();
            mfp.wrap.detach();
            mfp.container.empty();

            if (mfp.st.mainClass) {
                classesToRemove += mfp.st.mainClass + ' ';
            }

            mfp._removeClassFromMFP(classesToRemove);

            if (mfp.fixedContentPos) {
                var windowStyles = { marginRight: '' };
                if (mfp.isIE7) {
                    $('body, html').css('overflow', '');
                } else {
                    windowStyles.overflow = '';
                }
                $('html').css(windowStyles);
            }

            _document.off('keyup' + EVENT_NS + ' focusin' + EVENT_NS);
            mfp.ev.off(EVENT_NS);

            // clean up DOM elements that aren't removed
            mfp.wrap.attr('class', 'mfp-wrap').removeAttr('style');
            mfp.bgOverlay.attr('class', 'mfp-bg');
            mfp.container.attr('class', 'mfp-container');

            // remove close button from target element
            if (mfp.st.showCloseBtn &&
            (!mfp.st.closeBtnInside || mfp.currTemplate[mfp.currItem.type] === true)) {
                if (mfp.currTemplate.closeBtn)
                    mfp.currTemplate.closeBtn.detach();
            }


            if (mfp._lastFocusedEl) {
                $(mfp._lastFocusedEl).focus(); // put tab focus back
            }
            mfp.currItem = null;
            mfp.content = null;
            mfp.currTemplate = null;
            mfp.prevHeight = 0;

            _mfpTrigger(AFTER_CLOSE_EVENT);
        },

        updateSize: function (winHeight) {

            if (mfp.isIOS) {
                // fixes iOS nav bars https://github.com/dimsemenov/Magnific-Popup/issues/2
                var zoomLevel = document.documentElement.clientWidth / window.innerWidth;
                var height = window.innerHeight * zoomLevel;
                mfp.wrap.css('height', height);
                mfp.wH = height;
            } else {
                mfp.wH = winHeight || _window.height();
            }
            // Fixes #84: popup incorrectly positioned with position:relative on body
            if (!mfp.fixedContentPos) {
                mfp.wrap.css('height', mfp.wH);
            }

            _mfpTrigger('Resize');

        },

        /**
         * Set content of popup based on current index
         */
        updateItemHTML: function () {
            var item = mfp.items[mfp.index];

            // Detach and perform modifications
            mfp.contentContainer.detach();

            if (mfp.content)
                mfp.content.detach();

            if (!item.parsed) {
                item = mfp.parseEl(mfp.index);
            }

            var type = item.type;

            _mfpTrigger('BeforeChange', [mfp.currItem ? mfp.currItem.type : '', type]);
            // BeforeChange event works like so:
            // _mfpOn('BeforeChange', function(e, prevType, newType) { });

            mfp.currItem = item;





            if (!mfp.currTemplate[type]) {
                var markup = mfp.st[type] ? mfp.st[type].markup : false;

                // allows to modify markup
                _mfpTrigger('FirstMarkupParse', markup);

                if (markup) {
                    mfp.currTemplate[type] = $(markup);
                } else {
                    // if there is no markup found we just define that template is parsed
                    mfp.currTemplate[type] = true;
                }
            }

            if (_prevContentType && _prevContentType !== item.type) {
                mfp.container.removeClass('mfp-' + _prevContentType + '-holder');
            }

            var newContent = mfp['get' + type.charAt(0).toUpperCase() + type.slice(1)](item, mfp.currTemplate[type]);
            mfp.appendContent(newContent, type);

            item.preloaded = true;

            _mfpTrigger(CHANGE_EVENT, item);
            _prevContentType = item.type;

            // Append container back after its content changed
            mfp.container.prepend(mfp.contentContainer);

            _mfpTrigger('AfterChange');
        },


        /**
         * Set HTML content of popup
         */
        appendContent: function (newContent, type) {
            mfp.content = newContent;

            if (newContent) {
                if (mfp.st.showCloseBtn && mfp.st.closeBtnInside &&
                    mfp.currTemplate[type] === true) {
                    // if there is no markup, we just append close button element inside
                    if (!mfp.content.find('.mfp-close').length) {
                        mfp.content.append(_getCloseBtn());
                    }
                } else {
                    mfp.content = newContent;
                }
            } else {
                mfp.content = '';
            }

            _mfpTrigger(BEFORE_APPEND_EVENT);
            mfp.container.addClass('mfp-' + type + '-holder');

            mfp.contentContainer.append(mfp.content);
        },




        /**
         * Creates Magnific Popup data object based on given data
         * @param  {int} index Index of item to parse
         */
        parseEl: function (index) {
            var item = mfp.items[index],
                type;

            if (item.tagName) {
                item = { el: $(item) };
            } else {
                type = item.type;
                item = { data: item, src: item.src };
            }

            if (item.el) {
                var types = mfp.types;

                // check for 'mfp-TYPE' class
                for (var i = 0; i < types.length; i++) {
                    if (item.el.hasClass('mfp-' + types[i])) {
                        type = types[i];
                        break;
                    }
                }

                item.src = item.el.attr('data-mfp-src');
                if (!item.src) {
                    item.src = item.el.attr('href');
                }
            }

            item.type = type || mfp.st.type || 'inline';
            item.index = index;
            item.parsed = true;
            mfp.items[index] = item;
            _mfpTrigger('ElementParse', item);

            return mfp.items[index];
        },


        /**
         * Initializes single popup or a group of popups
         */
        addGroup: function (el, options) {
            var eHandler = function (e) {
                e.mfpEl = this;
                mfp._openClick(e, el, options);
            };

            if (!options) {
                options = {};
            }

            var eName = 'click.magnificPopup';
            options.mainEl = el;

            if (options.items) {
                options.isObj = true;
                el.off(eName).on(eName, eHandler);
            } else {
                options.isObj = false;
                if (options.delegate) {
                    el.off(eName).on(eName, options.delegate, eHandler);
                } else {
                    options.items = el;
                    el.off(eName).on(eName, eHandler);
                }
            }
        },
        _openClick: function (e, el, options) {
            var midClick = options.midClick !== undefined ? options.midClick : $.magnificPopup.defaults.midClick;


            if (!midClick && (e.which === 2 || e.ctrlKey || e.metaKey)) {
                return;
            }

            var disableOn = options.disableOn !== undefined ? options.disableOn : $.magnificPopup.defaults.disableOn;

            if (disableOn) {
                if ($.isFunction(disableOn)) {
                    if (!disableOn.call(mfp)) {
                        return true;
                    }
                } else { // else it's number
                    if (_window.width() < disableOn) {
                        return true;
                    }
                }
            }

            if (e.type) {
                e.preventDefault();

                // This will prevent popup from closing if element is inside and popup is already opened
                if (mfp.isOpen) {
                    e.stopPropagation();
                }
            }


            options.el = $(e.mfpEl);
            if (options.delegate) {
                options.items = el.find(options.delegate);
            }
            mfp.open(options);
        },


        /**
         * Updates text on preloader
         */
        updateStatus: function (status, text) {

            if (mfp.preloader) {
                if (_prevStatus !== status) {
                    mfp.container.removeClass('mfp-s-' + _prevStatus);
                }

                if (!text && status === 'loading') {
                    text = mfp.st.tLoading;
                }

                var data = {
                    status: status,
                    text: text
                };
                // allows to modify status
                _mfpTrigger('UpdateStatus', data);

                status = data.status;
                text = data.text;

                mfp.preloader.html(text);

                mfp.preloader.find('a').on('click', function (e) {
                    e.stopImmediatePropagation();
                });

                mfp.container.addClass('mfp-s-' + status);
                _prevStatus = status;
            }
        },


        /*
            "Private" helpers that aren't private at all
         */
        // Check to close popup or not
        // "target" is an element that was clicked
        _checkIfClose: function (target) {

            if ($(target).hasClass(PREVENT_CLOSE_CLASS)) {
                return;
            }

            var closeOnContent = mfp.st.closeOnContentClick;
            var closeOnBg = mfp.st.closeOnBgClick;

            if (closeOnContent && closeOnBg) {
                return true;
            } else {

                // We close the popup if click is on close button or on preloader. Or if there is no content.
                if (!mfp.content || $(target).hasClass('mfp-close') || (mfp.preloader && target === mfp.preloader[0])) {
                    return true;
                }

                // if click is outside the content
                if ((target !== mfp.content[0] && !$.contains(mfp.content[0], target))) {
                    if (closeOnBg) {
                        // last check, if the clicked element is in DOM, (in case it's removed onclick)
                        if ($.contains(document, target)) {
                            return true;
                        }
                    }
                } else if (closeOnContent) {
                    return true;
                }

            }
            return false;
        },
        _addClassToMFP: function (cName) {
            mfp.bgOverlay.addClass(cName);
            mfp.wrap.addClass(cName);
        },
        _removeClassFromMFP: function (cName) {
            this.bgOverlay.removeClass(cName);
            mfp.wrap.removeClass(cName);
        },
        _hasScrollBar: function (winHeight) {
            return ((mfp.isIE7 ? _document.height() : document.body.scrollHeight) > (winHeight || _window.height()));
        },
        _setFocus: function () {
            (mfp.st.focus ? mfp.content.find(mfp.st.focus).eq(0) : mfp.wrap).focus();
        },
        _onFocusIn: function (e) {
            if (e.target !== mfp.wrap[0] && !$.contains(mfp.wrap[0], e.target)) {
                mfp._setFocus();
                return false;
            }
        },
        _parseMarkup: function (template, values, item) {
            var arr;
            if (item.data) {
                values = $.extend(item.data, values);
            }
            _mfpTrigger(MARKUP_PARSE_EVENT, [template, values, item]);

            $.each(values, function (key, value) {
                if (value === undefined || value === false) {
                    return true;
                }
                arr = key.split('_');
                if (arr.length > 1) {
                    var el = template.find(EVENT_NS + '-' + arr[0]);

                    if (el.length > 0) {
                        var attr = arr[1];
                        if (attr === 'replaceWith') {
                            if (el[0] !== value[0]) {
                                el.replaceWith(value);
                            }
                        } else if (attr === 'img') {
                            if (el.is('img')) {
                                el.attr('src', value);
                            } else {
                                el.replaceWith('<img src="' + value + '" class="' + el.attr('class') + '" />');
                            }
                        } else {
                            el.attr(arr[1], value);
                        }
                    }

                } else {
                    template.find(EVENT_NS + '-' + key).html(value);
                }
            });
        },

        _getScrollbarSize: function () {
            // thx David
            if (mfp.scrollbarSize === undefined) {
                var scrollDiv = document.createElement("div");
                scrollDiv.style.cssText = 'width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;';
                document.body.appendChild(scrollDiv);
                mfp.scrollbarSize = scrollDiv.offsetWidth - scrollDiv.clientWidth;
                document.body.removeChild(scrollDiv);
            }
            return mfp.scrollbarSize;
        }

    }; /* MagnificPopup core prototype end */




    /**
     * Public static functions
     */
    $.magnificPopup = {
        instance: null,
        proto: MagnificPopup.prototype,
        modules: [],

        open: function (options, index) {
            _checkInstance();

            if (!options) {
                options = {};
            } else {
                options = $.extend(true, {}, options);
            }


            options.isObj = true;
            options.index = index || 0;
            return this.instance.open(options);
        },

        close: function () {
            return $.magnificPopup.instance && $.magnificPopup.instance.close();
        },

        registerModule: function (name, module) {
            if (module.options) {
                $.magnificPopup.defaults[name] = module.options;
            }
            $.extend(this.proto, module.proto);
            this.modules.push(name);
        },

        defaults: {

            // Info about options is in docs:
            // http://dimsemenov.com/plugins/magnific-popup/documentation.html#options

            disableOn: 0,

            key: null,

            midClick: false,

            mainClass: '',

            preloader: true,

            focus: '', // CSS selector of input to focus after popup is opened

            closeOnContentClick: false,

            closeOnBgClick: true,

            closeBtnInside: true,

            showCloseBtn: true,

            enableEscapeKey: true,

            modal: false,

            alignTop: false,

            removalDelay: 0,

            prependTo: null,

            fixedContentPos: 'auto',

            fixedBgPos: 'auto',

            overflowY: 'auto',

            closeMarkup: '<button title="%title%" type="button" class="mfp-close">&times;</button>',

            tClose: 'Close (Esc)',

            tLoading: 'Loading...'

        }
    };



    $.fn.magnificPopup = function (options) {
        _checkInstance();

        var jqEl = $(this);

        // We call some API method of first param is a string
        if (typeof options === "string") {

            if (options === 'open') {
                var items,
                    itemOpts = _isJQ ? jqEl.data('magnificPopup') : jqEl[0].magnificPopup,
                    index = parseInt(arguments[1], 10) || 0;

                if (itemOpts.items) {
                    items = itemOpts.items[index];
                } else {
                    items = jqEl;
                    if (itemOpts.delegate) {
                        items = items.find(itemOpts.delegate);
                    }
                    items = items.eq(index);
                }
                mfp._openClick({ mfpEl: items }, jqEl, itemOpts);
            } else {
                if (mfp.isOpen)
                    mfp[options].apply(mfp, Array.prototype.slice.call(arguments, 1));
            }

        } else {
            // clone options obj
            options = $.extend(true, {}, options);

            /*
             * As Zepto doesn't support .data() method for objects
             * and it works only in normal browsers
             * we assign "options" object directly to the DOM element. FTW!
             */
            if (_isJQ) {
                jqEl.data('magnificPopup', options);
            } else {
                jqEl[0].magnificPopup = options;
            }

            mfp.addGroup(jqEl, options);

        }
        return jqEl;
    };


    //Quick benchmark
    /*
    var start = performance.now(),
        i,
        rounds = 1000;
    
    for(i = 0; i < rounds; i++) {
    
    }
    console.log('Test #1:', performance.now() - start);
    
    start = performance.now();
    for(i = 0; i < rounds; i++) {
    
    }
    console.log('Test #2:', performance.now() - start);
    */


    /*>>core*/

    /*>>inline*/

    var INLINE_NS = 'inline',
        _hiddenClass,
        _inlinePlaceholder,
        _lastInlineElement,
        _putInlineElementsBack = function () {
            if (_lastInlineElement) {
                _inlinePlaceholder.after(_lastInlineElement.addClass(_hiddenClass)).detach();
                _lastInlineElement = null;
            }
        };

    $.magnificPopup.registerModule(INLINE_NS, {
        options: {
            hiddenClass: 'hide', // will be appended with `mfp-` prefix
            markup: '',
            tNotFound: 'Content not found'
        },
        proto: {

            initInline: function () {
                mfp.types.push(INLINE_NS);

                _mfpOn(CLOSE_EVENT + '.' + INLINE_NS, function () {
                    _putInlineElementsBack();
                });
            },

            getInline: function (item, template) {

                _putInlineElementsBack();

                if (item.src) {
                    var inlineSt = mfp.st.inline,
                        el = $(item.src);

                    if (el.length) {

                        // If target element has parent - we replace it with placeholder and put it back after popup is closed
                        var parent = el[0].parentNode;
                        if (parent && parent.tagName) {
                            if (!_inlinePlaceholder) {
                                _hiddenClass = inlineSt.hiddenClass;
                                _inlinePlaceholder = _getEl(_hiddenClass);
                                _hiddenClass = 'mfp-' + _hiddenClass;
                            }
                            // replace target inline element with placeholder
                            _lastInlineElement = el.after(_inlinePlaceholder).detach().removeClass(_hiddenClass);
                        }

                        mfp.updateStatus('ready');
                    } else {
                        mfp.updateStatus('error', inlineSt.tNotFound);
                        el = $('<div>');
                    }

                    item.inlineElement = el;
                    return el;
                }

                mfp.updateStatus('ready');
                mfp._parseMarkup(template, {}, item);
                return template;
            }
        }
    });

    /*>>inline*/

    /*>>ajax*/
    var AJAX_NS = 'ajax',
        _ajaxCur,
        _removeAjaxCursor = function () {
            if (_ajaxCur) {
                _body.removeClass(_ajaxCur);
            }
        },
        _destroyAjaxRequest = function () {
            _removeAjaxCursor();
            if (mfp.req) {
                mfp.req.abort();
            }
        };

    $.magnificPopup.registerModule(AJAX_NS, {

        options: {
            settings: null,
            cursor: 'mfp-ajax-cur',
            tError: '<a href="%url%">The content</a> could not be loaded.'
        },

        proto: {
            initAjax: function () {
                mfp.types.push(AJAX_NS);
                _ajaxCur = mfp.st.ajax.cursor;

                _mfpOn(CLOSE_EVENT + '.' + AJAX_NS, _destroyAjaxRequest);
                _mfpOn('BeforeChange.' + AJAX_NS, _destroyAjaxRequest);
            },
            getAjax: function (item) {

                if (_ajaxCur)
                    _body.addClass(_ajaxCur);

                mfp.updateStatus('loading');

                var opts = $.extend({
                    url: item.src,
                    success: function (data, textStatus, jqXHR) {
                        var temp = {
                            data: data,
                            xhr: jqXHR
                        };

                        _mfpTrigger('ParseAjax', temp);

                        mfp.appendContent($(temp.data), AJAX_NS);

                        item.finished = true;

                        _removeAjaxCursor();

                        mfp._setFocus();

                        setTimeout(function () {
                            mfp.wrap.addClass(READY_CLASS);
                        }, 16);

                        mfp.updateStatus('ready');

                        _mfpTrigger('AjaxContentAdded');

                    },
                    error: function () {
                        _removeAjaxCursor();
                        item.finished = item.loadError = true;
                        mfp.updateStatus('error', mfp.st.ajax.tError.replace('%url%', item.src));
                    }
                }, mfp.st.ajax.settings);

                mfp.req = $.ajax(opts);

                return '';
            }
        }
    });







    /*>>ajax*/

    /*>>image*/
    var _imgInterval,
        _getTitle = function (item) {
            if (item.data && item.data.title !== undefined)
                return item.data.title;

            var src = mfp.st.image.titleSrc;

            if (src) {
                if ($.isFunction(src)) {
                    return src.call(mfp, item);
                } else if (item.el) {
                    return item.el.attr(src) || '';
                }
            }
            return '';
        };

    $.magnificPopup.registerModule('image', {

        options: {
            markup: '<div class="mfp-figure">' +
                        '<div class="mfp-close"></div>' +
                        '<figure>' +
                            '<div class="mfp-img"></div>' +
                            '<figcaption>' +
                                '<div class="mfp-bottom-bar">' +
                                    '<div class="mfp-title"></div>' +
                                    '<div class="mfp-counter"></div>' +
                                '</div>' +
                            '</figcaption>' +
                        '</figure>' +
                    '</div>',
            cursor: 'mfp-zoom-out-cur',
            titleSrc: 'title',
            verticalFit: true,
            tError: '<a href="%url%">The image</a> could not be loaded.'
        },

        proto: {
            initImage: function () {
                var imgSt = mfp.st.image,
                    ns = '.image';

                mfp.types.push('image');

                _mfpOn(OPEN_EVENT + ns, function () {
                    if (mfp.currItem.type === 'image' && imgSt.cursor) {
                        _body.addClass(imgSt.cursor);
                    }
                });

                _mfpOn(CLOSE_EVENT + ns, function () {
                    if (imgSt.cursor) {
                        _body.removeClass(imgSt.cursor);
                    }
                    _window.off('resize' + EVENT_NS);
                });

                _mfpOn('Resize' + ns, mfp.resizeImage);
                if (mfp.isLowIE) {
                    _mfpOn('AfterChange', mfp.resizeImage);
                }
            },
            resizeImage: function () {
                var item = mfp.currItem;
                if (!item || !item.img) return;

                if (mfp.st.image.verticalFit) {
                    var decr = 0;
                    // fix box-sizing in ie7/8
                    if (mfp.isLowIE) {
                        decr = parseInt(item.img.css('padding-top'), 10) + parseInt(item.img.css('padding-bottom'), 10);
                    }
                    item.img.css('max-height', mfp.wH - decr);
                }
            },
            _onImageHasSize: function (item) {
                if (item.img) {

                    item.hasSize = true;

                    if (_imgInterval) {
                        clearInterval(_imgInterval);
                    }

                    item.isCheckingImgSize = false;

                    _mfpTrigger('ImageHasSize', item);

                    if (item.imgHidden) {
                        if (mfp.content)
                            mfp.content.removeClass('mfp-loading');

                        item.imgHidden = false;
                    }

                }
            },

            /**
             * Function that loops until the image has size to display elements that rely on it asap
             */
            findImageSize: function (item) {

                var counter = 0,
                    img = item.img[0],
                    mfpSetInterval = function (delay) {

                        if (_imgInterval) {
                            clearInterval(_imgInterval);
                        }
                        // decelerating interval that checks for size of an image
                        _imgInterval = setInterval(function () {
                            if (img.naturalWidth > 0) {
                                mfp._onImageHasSize(item);
                                return;
                            }

                            if (counter > 200) {
                                clearInterval(_imgInterval);
                            }

                            counter++;
                            if (counter === 3) {
                                mfpSetInterval(10);
                            } else if (counter === 40) {
                                mfpSetInterval(50);
                            } else if (counter === 100) {
                                mfpSetInterval(500);
                            }
                        }, delay);
                    };

                mfpSetInterval(1);
            },

            getImage: function (item, template) {

                var guard = 0,

                    // image load complete handler
                    onLoadComplete = function () {
                        if (item) {
                            if (item.img[0].complete) {
                                item.img.off('.mfploader');

                                if (item === mfp.currItem) {
                                    mfp._onImageHasSize(item);

                                    mfp.updateStatus('ready');
                                }

                                item.hasSize = true;
                                item.loaded = true;

                                _mfpTrigger('ImageLoadComplete');

                            }
                            else {
                                // if image complete check fails 200 times (20 sec), we assume that there was an error.
                                guard++;
                                if (guard < 200) {
                                    setTimeout(onLoadComplete, 100);
                                } else {
                                    onLoadError();
                                }
                            }
                        }
                    },

                    // image error handler
                    onLoadError = function () {
                        if (item) {
                            item.img.off('.mfploader');
                            if (item === mfp.currItem) {
                                mfp._onImageHasSize(item);
                                mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src));
                            }

                            item.hasSize = true;
                            item.loaded = true;
                            item.loadError = true;
                        }
                    },
                    imgSt = mfp.st.image;


                var el = template.find('.mfp-img');
                if (el.length) {
                    var img = document.createElement('img');
                    img.className = 'mfp-img';
                    item.img = $(img).on('load.mfploader', onLoadComplete).on('error.mfploader', onLoadError);
                    img.src = item.src;

                    // without clone() "error" event is not firing when IMG is replaced by new IMG
                    // TODO: find a way to avoid such cloning
                    if (el.is('img')) {
                        item.img = item.img.clone();
                    }

                    img = item.img[0];
                    if (img.naturalWidth > 0) {
                        item.hasSize = true;
                    } else if (!img.width) {
                        item.hasSize = false;
                    }
                }

                mfp._parseMarkup(template, {
                    title: _getTitle(item),
                    img_replaceWith: item.img
                }, item);

                mfp.resizeImage();

                if (item.hasSize) {
                    if (_imgInterval) clearInterval(_imgInterval);

                    if (item.loadError) {
                        template.addClass('mfp-loading');
                        mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src));
                    } else {
                        template.removeClass('mfp-loading');
                        mfp.updateStatus('ready');
                    }
                    return template;
                }

                mfp.updateStatus('loading');
                item.loading = true;

                if (!item.hasSize) {
                    item.imgHidden = true;
                    template.addClass('mfp-loading');
                    mfp.findImageSize(item);
                }

                return template;
            }
        }
    });



    /*>>image*/

    /*>>zoom*/
    var hasMozTransform,
        getHasMozTransform = function () {
            if (hasMozTransform === undefined) {
                hasMozTransform = document.createElement('p').style.MozTransform !== undefined;
            }
            return hasMozTransform;
        };

    $.magnificPopup.registerModule('zoom', {

        options: {
            enabled: false,
            easing: 'ease-in-out',
            duration: 300,
            opener: function (element) {
                return element.is('img') ? element : element.find('img');
            }
        },

        proto: {

            initZoom: function () {
                var zoomSt = mfp.st.zoom,
                    ns = '.zoom',
                    image;

                if (!zoomSt.enabled || !mfp.supportsTransition) {
                    return;
                }

                var duration = zoomSt.duration,
                    getElToAnimate = function (image) {
                        var newImg = image.clone().removeAttr('style').removeAttr('class').addClass('mfp-animated-image'),
                            transition = 'all ' + (zoomSt.duration / 1000) + 's ' + zoomSt.easing,
                            cssObj = {
                                position: 'fixed',
                                zIndex: 9999,
                                left: 0,
                                top: 0,
                                '-webkit-backface-visibility': 'hidden'
                            },
                            t = 'transition';

                        cssObj['-webkit-' + t] = cssObj['-moz-' + t] = cssObj['-o-' + t] = cssObj[t] = transition;

                        newImg.css(cssObj);
                        return newImg;
                    },
                    showMainContent = function () {
                        mfp.content.css('visibility', 'visible');
                    },
                    openTimeout,
                    animatedImg;

                _mfpOn('BuildControls' + ns, function () {
                    if (mfp._allowZoom()) {

                        clearTimeout(openTimeout);
                        mfp.content.css('visibility', 'hidden');

                        // Basically, all code below does is clones existing image, puts in on top of the current one and animated it

                        image = mfp._getItemToZoom();

                        if (!image) {
                            showMainContent();
                            return;
                        }

                        animatedImg = getElToAnimate(image);

                        animatedImg.css(mfp._getOffset());

                        mfp.wrap.append(animatedImg);

                        openTimeout = setTimeout(function () {
                            animatedImg.css(mfp._getOffset(true));
                            openTimeout = setTimeout(function () {

                                showMainContent();

                                setTimeout(function () {
                                    animatedImg.remove();
                                    image = animatedImg = null;
                                    _mfpTrigger('ZoomAnimationEnded');
                                }, 16); // avoid blink when switching images

                            }, duration); // this timeout equals animation duration

                        }, 16); // by adding this timeout we avoid short glitch at the beginning of animation


                        // Lots of timeouts...
                    }
                });
                _mfpOn(BEFORE_CLOSE_EVENT + ns, function () {
                    if (mfp._allowZoom()) {

                        clearTimeout(openTimeout);

                        mfp.st.removalDelay = duration;

                        if (!image) {
                            image = mfp._getItemToZoom();
                            if (!image) {
                                return;
                            }
                            animatedImg = getElToAnimate(image);
                        }


                        animatedImg.css(mfp._getOffset(true));
                        mfp.wrap.append(animatedImg);
                        mfp.content.css('visibility', 'hidden');

                        setTimeout(function () {
                            animatedImg.css(mfp._getOffset());
                        }, 16);
                    }

                });

                _mfpOn(CLOSE_EVENT + ns, function () {
                    if (mfp._allowZoom()) {
                        showMainContent();
                        if (animatedImg) {
                            animatedImg.remove();
                        }
                        image = null;
                    }
                });
            },

            _allowZoom: function () {
                return mfp.currItem.type === 'image';
            },

            _getItemToZoom: function () {
                if (mfp.currItem.hasSize) {
                    return mfp.currItem.img;
                } else {
                    return false;
                }
            },

            // Get element postion relative to viewport
            _getOffset: function (isLarge) {
                var el;
                if (isLarge) {
                    el = mfp.currItem.img;
                } else {
                    el = mfp.st.zoom.opener(mfp.currItem.el || mfp.currItem);
                }

                var offset = el.offset();
                var paddingTop = parseInt(el.css('padding-top'), 10);
                var paddingBottom = parseInt(el.css('padding-bottom'), 10);
                offset.top -= ($(window).scrollTop() - paddingTop);


                /*
    
                Animating left + top + width/height looks glitchy in Firefox, but perfect in Chrome. And vice-versa.
    
                 */
                var obj = {
                    width: el.width(),
                    // fix Zepto height+padding issue
                    height: (_isJQ ? el.innerHeight() : el[0].offsetHeight) - paddingBottom - paddingTop
                };

                // I hate to do this, but there is no another option
                if (getHasMozTransform()) {
                    obj['-moz-transform'] = obj['transform'] = 'translate(' + offset.left + 'px,' + offset.top + 'px)';
                } else {
                    obj.left = offset.left;
                    obj.top = offset.top;
                }
                return obj;
            }

        }
    });



    /*>>zoom*/

    /*>>iframe*/

    var IFRAME_NS = 'iframe',
        _emptyPage = '//about:blank',

        _fixIframeBugs = function (isShowing) {
            if (mfp.currTemplate[IFRAME_NS]) {
                var el = mfp.currTemplate[IFRAME_NS].find('iframe');
                if (el.length) {
                    // reset src after the popup is closed to avoid "video keeps playing after popup is closed" bug
                    if (!isShowing) {
                        el[0].src = _emptyPage;
                    }

                    // IE8 black screen bug fix
                    if (mfp.isIE8) {
                        el.css('display', isShowing ? 'block' : 'none');
                    }
                }
            }
        };

    $.magnificPopup.registerModule(IFRAME_NS, {

        options: {
            markup: '<div class="mfp-iframe-scaler">' +
                        '<div class="mfp-close"></div>' +
                        '<iframe class="mfp-iframe" src="//about:blank" frameborder="0" allowfullscreen></iframe>' +
                    '</div>',

            srcAction: 'iframe_src',

            // we don't care and support only one default type of URL by default
            patterns: {
                youtube: {
                    index: 'youtube.com',
                    id: 'v=',
                    src: '//www.youtube.com/embed/%id%?autoplay=1'
                },
                vimeo: {
                    index: 'vimeo.com/',
                    id: '/',
                    src: '//player.vimeo.com/video/%id%?autoplay=1'
                },
                gmaps: {
                    index: '//maps.google.',
                    src: '%id%&output=embed'
                }
            }
        },

        proto: {
            initIframe: function () {
                mfp.types.push(IFRAME_NS);

                _mfpOn('BeforeChange', function (e, prevType, newType) {
                    if (prevType !== newType) {
                        if (prevType === IFRAME_NS) {
                            _fixIframeBugs(); // iframe if removed
                        } else if (newType === IFRAME_NS) {
                            _fixIframeBugs(true); // iframe is showing
                        }
                    }// else {
                    // iframe source is switched, don't do anything
                    //}
                });

                _mfpOn(CLOSE_EVENT + '.' + IFRAME_NS, function () {
                    _fixIframeBugs();
                });
            },

            getIframe: function (item, template) {
                var embedSrc = item.src;
                var iframeSt = mfp.st.iframe;

                $.each(iframeSt.patterns, function () {
                    if (embedSrc.indexOf(this.index) > -1) {
                        if (this.id) {
                            if (typeof this.id === 'string') {
                                embedSrc = embedSrc.substr(embedSrc.lastIndexOf(this.id) + this.id.length, embedSrc.length);
                            } else {
                                embedSrc = this.id.call(this, embedSrc);
                            }
                        }
                        embedSrc = this.src.replace('%id%', embedSrc);
                        return false; // break;
                    }
                });

                var dataObj = {};
                if (iframeSt.srcAction) {
                    dataObj[iframeSt.srcAction] = embedSrc;
                }
                mfp._parseMarkup(template, dataObj, item);

                mfp.updateStatus('ready');

                return template;
            }
        }
    });



    /*>>iframe*/

    /*>>gallery*/
    /**
     * Get looped index depending on number of slides
     */
    var _getLoopedId = function (index) {
        var numSlides = mfp.items.length;
        if (index > numSlides - 1) {
            return index - numSlides;
        } else if (index < 0) {
            return numSlides + index;
        }
        return index;
    },
        _replaceCurrTotal = function (text, curr, total) {
            return text.replace(/%curr%/gi, curr + 1).replace(/%total%/gi, total);
        };

    $.magnificPopup.registerModule('gallery', {

        options: {
            enabled: false,
            arrowMarkup: '<button title="%title%" type="button" class="mfp-arrow mfp-arrow-%dir%"></button>',
            preload: [0, 2],
            navigateByImgClick: true,
            arrows: true,

            tPrev: 'Previous (Left arrow key)',
            tNext: 'Next (Right arrow key)',
            tCounter: '%curr% of %total%'
        },

        proto: {
            initGallery: function () {

                var gSt = mfp.st.gallery,
                    ns = '.mfp-gallery',
                    supportsFastClick = Boolean($.fn.mfpFastClick);

                mfp.direction = true; // true - next, false - prev

                if (!gSt || !gSt.enabled) return false;

                _wrapClasses += ' mfp-gallery';

                _mfpOn(OPEN_EVENT + ns, function () {

                    if (gSt.navigateByImgClick) {
                        mfp.wrap.on('click' + ns, '.mfp-img', function () {
                            if (mfp.items.length > 1) {
                                mfp.next();
                                return false;
                            }
                        });
                    }

                    _document.on('keydown' + ns, function (e) {
                        if (e.keyCode === 37) {
                            mfp.prev();
                        } else if (e.keyCode === 39) {
                            mfp.next();
                        }
                    });
                });

                _mfpOn('UpdateStatus' + ns, function (e, data) {
                    if (data.text) {
                        data.text = _replaceCurrTotal(data.text, mfp.currItem.index, mfp.items.length);
                    }
                });

                _mfpOn(MARKUP_PARSE_EVENT + ns, function (e, element, values, item) {
                    var l = mfp.items.length;
                    values.counter = l > 1 ? _replaceCurrTotal(gSt.tCounter, item.index, l) : '';
                });

                _mfpOn('BuildControls' + ns, function () {
                    if (mfp.items.length > 1 && gSt.arrows && !mfp.arrowLeft) {
                        var markup = gSt.arrowMarkup,
                            arrowLeft = mfp.arrowLeft = $(markup.replace(/%title%/gi, gSt.tPrev).replace(/%dir%/gi, 'left')).addClass(PREVENT_CLOSE_CLASS),
                            arrowRight = mfp.arrowRight = $(markup.replace(/%title%/gi, gSt.tNext).replace(/%dir%/gi, 'right')).addClass(PREVENT_CLOSE_CLASS);

                        var eName = supportsFastClick ? 'mfpFastClick' : 'click';
                        arrowLeft[eName](function () {
                            mfp.prev();
                        });
                        arrowRight[eName](function () {
                            mfp.next();
                        });

                        // Polyfill for :before and :after (adds elements with classes mfp-a and mfp-b)
                        if (mfp.isIE7) {
                            _getEl('b', arrowLeft[0], false, true);
                            _getEl('a', arrowLeft[0], false, true);
                            _getEl('b', arrowRight[0], false, true);
                            _getEl('a', arrowRight[0], false, true);
                        }

                        mfp.container.append(arrowLeft.add(arrowRight));
                    }
                });

                _mfpOn(CHANGE_EVENT + ns, function () {
                    if (mfp._preloadTimeout) clearTimeout(mfp._preloadTimeout);

                    mfp._preloadTimeout = setTimeout(function () {
                        mfp.preloadNearbyImages();
                        mfp._preloadTimeout = null;
                    }, 16);
                });


                _mfpOn(CLOSE_EVENT + ns, function () {
                    _document.off(ns);
                    mfp.wrap.off('click' + ns);

                    if (mfp.arrowLeft && supportsFastClick) {
                        mfp.arrowLeft.add(mfp.arrowRight).destroyMfpFastClick();
                    }
                    mfp.arrowRight = mfp.arrowLeft = null;
                });

            },
            next: function () {
                mfp.direction = true;
                mfp.index = _getLoopedId(mfp.index + 1);
                mfp.updateItemHTML();
            },
            prev: function () {
                mfp.direction = false;
                mfp.index = _getLoopedId(mfp.index - 1);
                mfp.updateItemHTML();
            },
            goTo: function (newIndex) {
                mfp.direction = (newIndex >= mfp.index);
                mfp.index = newIndex;
                mfp.updateItemHTML();
            },
            preloadNearbyImages: function () {
                var p = mfp.st.gallery.preload,
                    preloadBefore = Math.min(p[0], mfp.items.length),
                    preloadAfter = Math.min(p[1], mfp.items.length),
                    i;

                for (i = 1; i <= (mfp.direction ? preloadAfter : preloadBefore) ; i++) {
                    mfp._preloadItem(mfp.index + i);
                }
                for (i = 1; i <= (mfp.direction ? preloadBefore : preloadAfter) ; i++) {
                    mfp._preloadItem(mfp.index - i);
                }
            },
            _preloadItem: function (index) {
                index = _getLoopedId(index);

                if (mfp.items[index].preloaded) {
                    return;
                }

                var item = mfp.items[index];
                if (!item.parsed) {
                    item = mfp.parseEl(index);
                }

                _mfpTrigger('LazyLoad', item);

                if (item.type === 'image') {
                    item.img = $('<img class="mfp-img" />').on('load.mfploader', function () {
                        item.hasSize = true;
                    }).on('error.mfploader', function () {
                        item.hasSize = true;
                        item.loadError = true;
                        _mfpTrigger('LazyLoadError', item);
                    }).attr('src', item.src);
                }


                item.preloaded = true;
            }
        }
    });

    /*
    Touch Support that might be implemented some day
    
    addSwipeGesture: function() {
        var startX,
            moved,
            multipleTouches;
    
            return;
    
        var namespace = '.mfp',
            addEventNames = function(pref, down, move, up, cancel) {
                mfp._tStart = pref + down + namespace;
                mfp._tMove = pref + move + namespace;
                mfp._tEnd = pref + up + namespace;
                mfp._tCancel = pref + cancel + namespace;
            };
    
        if(window.navigator.msPointerEnabled) {
            addEventNames('MSPointer', 'Down', 'Move', 'Up', 'Cancel');
        } else if('ontouchstart' in window) {
            addEventNames('touch', 'start', 'move', 'end', 'cancel');
        } else {
            return;
        }
        _window.on(mfp._tStart, function(e) {
            var oE = e.originalEvent;
            multipleTouches = moved = false;
            startX = oE.pageX || oE.changedTouches[0].pageX;
        }).on(mfp._tMove, function(e) {
            if(e.originalEvent.touches.length > 1) {
                multipleTouches = e.originalEvent.touches.length;
            } else {
                //e.preventDefault();
                moved = true;
            }
        }).on(mfp._tEnd + ' ' + mfp._tCancel, function(e) {
            if(moved && !multipleTouches) {
                var oE = e.originalEvent,
                    diff = startX - (oE.pageX || oE.changedTouches[0].pageX);
    
                if(diff > 20) {
                    mfp.next();
                } else if(diff < -20) {
                    mfp.prev();
                }
            }
        });
    },
    */


    /*>>gallery*/

    /*>>retina*/

    var RETINA_NS = 'retina';

    $.magnificPopup.registerModule(RETINA_NS, {
        options: {
            replaceSrc: function (item) {
                return item.src.replace(/\.\w+$/, function (m) { return '@2x' + m; });
            },
            ratio: 1 // Function or number.  Set to 1 to disable.
        },
        proto: {
            initRetina: function () {
                if (window.devicePixelRatio > 1) {

                    var st = mfp.st.retina,
                        ratio = st.ratio;

                    ratio = !isNaN(ratio) ? ratio : ratio();

                    if (ratio > 1) {
                        _mfpOn('ImageHasSize' + '.' + RETINA_NS, function (e, item) {
                            item.img.css({
                                'max-width': item.img[0].naturalWidth / ratio,
                                'width': '100%'
                            });
                        });
                        _mfpOn('ElementParse' + '.' + RETINA_NS, function (e, item) {
                            item.src = st.replaceSrc(item, ratio);
                        });
                    }
                }

            }
        }
    });

    /*>>retina*/

    /*>>fastclick*/
    /**
     * FastClick event implementation. (removes 300ms delay on touch devices)
     * Based on https://developers.google.com/mobile/articles/fast_buttons
     *
     * You may use it outside the Magnific Popup by calling just:
     *
     * $('.your-el').mfpFastClick(function() {
     *     console.log('Clicked!');
     * });
     *
     * To unbind:
     * $('.your-el').destroyMfpFastClick();
     *
     *
     * Note that it's a very basic and simple implementation, it blocks ghost click on the same element where it was bound.
     * If you need something more advanced, use plugin by FT Labs https://github.com/ftlabs/fastclick
     *
     */

    (function () {
        var ghostClickDelay = 1000,
            supportsTouch = 'ontouchstart' in window,
            unbindTouchMove = function () {
                _window.off('touchmove' + ns + ' touchend' + ns);
            },
            eName = 'mfpFastClick',
            ns = '.' + eName;


        // As Zepto.js doesn't have an easy way to add custom events (like jQuery), so we implement it in this way
        $.fn.mfpFastClick = function (callback) {

            return $(this).each(function () {

                var elem = $(this),
                    lock;

                if (supportsTouch) {

                    var timeout,
                        startX,
                        startY,
                        pointerMoved,
                        point,
                        numPointers;

                    elem.on('touchstart' + ns, function (e) {
                        pointerMoved = false;
                        numPointers = 1;

                        point = e.originalEvent ? e.originalEvent.touches[0] : e.touches[0];
                        startX = point.clientX;
                        startY = point.clientY;

                        _window.on('touchmove' + ns, function (e) {
                            point = e.originalEvent ? e.originalEvent.touches : e.touches;
                            numPointers = point.length;
                            point = point[0];
                            if (Math.abs(point.clientX - startX) > 10 ||
                                Math.abs(point.clientY - startY) > 10) {
                                pointerMoved = true;
                                unbindTouchMove();
                            }
                        }).on('touchend' + ns, function (e) {
                            unbindTouchMove();
                            if (pointerMoved || numPointers > 1) {
                                return;
                            }
                            lock = true;
                            e.preventDefault();
                            clearTimeout(timeout);
                            timeout = setTimeout(function () {
                                lock = false;
                            }, ghostClickDelay);
                            callback();
                        });
                    });

                }

                elem.on('click' + ns, function () {
                    if (!lock) {
                        callback();
                    }
                });
            });
        };

        $.fn.destroyMfpFastClick = function () {
            $(this).off('touchstart' + ns + ' click' + ns);
            if (supportsTouch) _window.off('touchmove' + ns + ' touchend' + ns);
        };
    })();

    /*>>fastclick*/
    _checkInstance();
})(window.jQuery || window.Zepto);
;
/*
== malihu jquery custom scrollbar plugin == 
Version: 3.1.3 
Plugin URI: http://manos.malihu.gr/jquery-custom-content-scroller 
Author: malihu
Author URI: http://manos.malihu.gr
License: MIT License (MIT)
*/

/*
Copyright Manos Malihutsakis (email: manos@malihu.gr)

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.
*/

/*
The code below is fairly long, fully commented and should be normally used in development. 
For production, use either the minified jquery.mCustomScrollbar.min.js script or 
the production-ready jquery.mCustomScrollbar.concat.min.js which contains the plugin 
and dependencies (minified). 
*/

(function(factory){
	if(typeof module!=="undefined" && module.exports){
		module.exports=factory;
	}else{
		factory(jQuery,window,document);
	}
}(function($){
(function(init){
	var _rjs=typeof define==="function" && define.amd, /* RequireJS */
		_njs=typeof module !== "undefined" && module.exports, /* NodeJS */
		_dlp=("https:"==document.location.protocol) ? "https:" : "http:", /* location protocol */
		_url="cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.13/jquery.mousewheel.min.js";
	if(!_rjs){
		if(_njs){
			require("jquery-mousewheel")($);
		}else{
			/* load jquery-mousewheel plugin (via CDN) if it's not present or not loaded via RequireJS 
			(works when mCustomScrollbar fn is called on window load) */
			$.event.special.mousewheel || $("head").append(decodeURI("%3Cscript src="+_dlp+"//"+_url+"%3E%3C/script%3E"));
		}
	}
	init();
}(function(){
	
	/* 
	----------------------------------------
	PLUGIN NAMESPACE, PREFIX, DEFAULT SELECTOR(S) 
	----------------------------------------
	*/
	
	var pluginNS="mCustomScrollbar",
		pluginPfx="mCS",
		defaultSelector=".mCustomScrollbar",
	
	
		
	
	
	/* 
	----------------------------------------
	DEFAULT OPTIONS 
	----------------------------------------
	*/
	
		defaults={
			/*
			set element/content width/height programmatically 
			values: boolean, pixels, percentage 
				option						default
				-------------------------------------
				setWidth					false
				setHeight					false
			*/
			/*
			set the initial css top property of content  
			values: string (e.g. "-100px", "10%" etc.)
			*/
			setTop:0,
			/*
			set the initial css left property of content  
			values: string (e.g. "-100px", "10%" etc.)
			*/
			setLeft:0,
			/* 
			scrollbar axis (vertical and/or horizontal scrollbars) 
			values (string): "y", "x", "yx"
			*/
			axis:"y",
			/*
			position of scrollbar relative to content  
			values (string): "inside", "outside" ("outside" requires elements with position:relative)
			*/
			scrollbarPosition:"inside",
			/*
			scrolling inertia
			values: integer (milliseconds)
			*/
			scrollInertia:950,
			/* 
			auto-adjust scrollbar dragger length
			values: boolean
			*/
			autoDraggerLength:true,
			/*
			auto-hide scrollbar when idle 
			values: boolean
				option						default
				-------------------------------------
				autoHideScrollbar			false
			*/
			/*
			auto-expands scrollbar on mouse-over and dragging
			values: boolean
				option						default
				-------------------------------------
				autoExpandScrollbar			false
			*/
			/*
			always show scrollbar, even when there's nothing to scroll 
			values: integer (0=disable, 1=always show dragger rail and buttons, 2=always show dragger rail, dragger and buttons), boolean
			*/
			alwaysShowScrollbar:0,
			/*
			scrolling always snaps to a multiple of this number in pixels
			values: integer, array ([y,x])
				option						default
				-------------------------------------
				snapAmount					null
			*/
			/*
			when snapping, snap with this number in pixels as an offset 
			values: integer
			*/
			snapOffset:0,
			/* 
			mouse-wheel scrolling
			*/
			mouseWheel:{
				/* 
				enable mouse-wheel scrolling
				values: boolean
				*/
				enable:true,
				/* 
				scrolling amount in pixels
				values: "auto", integer 
				*/
				scrollAmount:"auto",
				/* 
				mouse-wheel scrolling axis 
				the default scrolling direction when both vertical and horizontal scrollbars are present 
				values (string): "y", "x" 
				*/
				axis:"y",
				/* 
				prevent the default behaviour which automatically scrolls the parent element(s) when end of scrolling is reached 
				values: boolean
					option						default
					-------------------------------------
					preventDefault				null
				*/
				/*
				the reported mouse-wheel delta value. The number of lines (translated to pixels) one wheel notch scrolls.  
				values: "auto", integer 
				"auto" uses the default OS/browser value 
				*/
				deltaFactor:"auto",
				/*
				normalize mouse-wheel delta to -1 or 1 (disables mouse-wheel acceleration) 
				values: boolean
					option						default
					-------------------------------------
					normalizeDelta				null
				*/
				/*
				invert mouse-wheel scrolling direction 
				values: boolean
					option						default
					-------------------------------------
					invert						null
				*/
				/*
				the tags that disable mouse-wheel when cursor is over them
				*/
				disableOver:["select","option","keygen","datalist","textarea"]
			},
			/* 
			scrollbar buttons
			*/
			scrollButtons:{ 
				/*
				enable scrollbar buttons
				values: boolean
					option						default
					-------------------------------------
					enable						null
				*/
				/*
				scrollbar buttons scrolling type 
				values (string): "stepless", "stepped"
				*/
				scrollType:"stepless",
				/*
				scrolling amount in pixels
				values: "auto", integer 
				*/
				scrollAmount:"auto"
				/*
				tabindex of the scrollbar buttons
				values: false, integer
					option						default
					-------------------------------------
					tabindex					null
				*/
			},
			/* 
			keyboard scrolling
			*/
			keyboard:{ 
				/*
				enable scrolling via keyboard
				values: boolean
				*/
				enable:true,
				/*
				keyboard scrolling type 
				values (string): "stepless", "stepped"
				*/
				scrollType:"stepless",
				/*
				scrolling amount in pixels
				values: "auto", integer 
				*/
				scrollAmount:"auto"
			},
			/*
			enable content touch-swipe scrolling 
			values: boolean, integer, string (number)
			integer values define the axis-specific minimum amount required for scrolling momentum
			*/
			contentTouchScroll:25,
			/*
			enable/disable document (default) touch-swipe scrolling 
			*/
			documentTouchScroll:true,
			/*
			advanced option parameters
			*/
			advanced:{
				/*
				auto-expand content horizontally (for "x" or "yx" axis) 
				values: boolean, integer (the value 2 forces the non scrollHeight/scrollWidth method, the value 3 forces the scrollHeight/scrollWidth method)
					option						default
					-------------------------------------
					autoExpandHorizontalScroll	null
				*/
				/*
				auto-scroll to elements with focus
				*/
				autoScrollOnFocus:"input,textarea,select,button,datalist,keygen,a[tabindex],area,object,[contenteditable='true']",
				/*
				auto-update scrollbars on content, element or viewport resize 
				should be true for fluid layouts/elements, adding/removing content dynamically, hiding/showing elements, content with images etc. 
				values: boolean
				*/
				updateOnContentResize:true,
				/*
				auto-update scrollbars each time each image inside the element is fully loaded 
				values: "auto", boolean
				*/
				updateOnImageLoad:"auto",
				/*
				auto-update scrollbars based on the amount and size changes of specific selectors 
				useful when you need to update the scrollbar(s) automatically, each time a type of element is added, removed or changes its size 
				values: boolean, string (e.g. "ul li" will auto-update scrollbars each time list-items inside the element are changed) 
				a value of true (boolean) will auto-update scrollbars each time any element is changed
					option						default
					-------------------------------------
					updateOnSelectorChange		null
				*/
				/*
				extra selectors that'll allow scrollbar dragging upon mousemove/up, pointermove/up, touchend etc. (e.g. "selector-1, selector-2")
					option						default
					-------------------------------------
					extraDraggableSelectors		null
				*/
				/*
				extra selectors that'll release scrollbar dragging upon mouseup, pointerup, touchend etc. (e.g. "selector-1, selector-2")
					option						default
					-------------------------------------
					releaseDraggableSelectors	null
				*/
				/*
				auto-update timeout 
				values: integer (milliseconds)
				*/
				autoUpdateTimeout:60
			},
			/* 
			scrollbar theme 
			values: string (see CSS/plugin URI for a list of ready-to-use themes)
			*/
			theme:"light",
			/*
			user defined callback functions
			*/
			callbacks:{
				/*
				Available callbacks: 
					callback					default
					-------------------------------------
					onCreate					null
					onInit						null
					onScrollStart				null
					onScroll					null
					onTotalScroll				null
					onTotalScrollBack			null
					whileScrolling				null
					onOverflowY					null
					onOverflowX					null
					onOverflowYNone				null
					onOverflowXNone				null
					onImageLoad					null
					onSelectorChange			null
					onBeforeUpdate				null
					onUpdate					null
				*/
				onTotalScrollOffset:0,
				onTotalScrollBackOffset:0,
				alwaysTriggerOffsets:true
			}
			/*
			add scrollbar(s) on all elements matching the current selector, now and in the future 
			values: boolean, string 
			string values: "on" (enable), "once" (disable after first invocation), "off" (disable)
			liveSelector values: string (selector)
				option						default
				-------------------------------------
				live						false
				liveSelector				null
			*/
		},
	
	
	
	
	
	/* 
	----------------------------------------
	VARS, CONSTANTS 
	----------------------------------------
	*/
	
		totalInstances=0, /* plugin instances amount */
		liveTimers={}, /* live option timers */
		oldIE=(window.attachEvent && !window.addEventListener) ? 1 : 0, /* detect IE < 9 */
		touchActive=false,touchable, /* global touch vars (for touch and pointer events) */
		/* general plugin classes */
		classes=[
			"mCSB_dragger_onDrag","mCSB_scrollTools_onDrag","mCS_img_loaded","mCS_disabled","mCS_destroyed","mCS_no_scrollbar",
			"mCS-autoHide","mCS-dir-rtl","mCS_no_scrollbar_y","mCS_no_scrollbar_x","mCS_y_hidden","mCS_x_hidden","mCSB_draggerContainer",
			"mCSB_buttonUp","mCSB_buttonDown","mCSB_buttonLeft","mCSB_buttonRight"
		],
		
	
	
	
	
	/* 
	----------------------------------------
	METHODS 
	----------------------------------------
	*/
	
		methods={
			
			/* 
			plugin initialization method 
			creates the scrollbar(s), plugin data object and options
			----------------------------------------
			*/
			
			init:function(options){
				
				var options=$.extend(true,{},defaults,options),
					selector=_selector.call(this); /* validate selector */
				
				/* 
				if live option is enabled, monitor for elements matching the current selector and 
				apply scrollbar(s) when found (now and in the future) 
				*/
				if(options.live){
					var liveSelector=options.liveSelector || this.selector || defaultSelector, /* live selector(s) */
						$liveSelector=$(liveSelector); /* live selector(s) as jquery object */
					if(options.live==="off"){
						/* 
						disable live if requested 
						usage: $(selector).mCustomScrollbar({live:"off"}); 
						*/
						removeLiveTimers(liveSelector);
						return;
					}
					liveTimers[liveSelector]=setTimeout(function(){
						/* call mCustomScrollbar fn on live selector(s) every half-second */
						$liveSelector.mCustomScrollbar(options);
						if(options.live==="once" && $liveSelector.length){
							/* disable live after first invocation */
							removeLiveTimers(liveSelector);
						}
					},500);
				}else{
					removeLiveTimers(liveSelector);
				}
				
				/* options backward compatibility (for versions < 3.0.0) and normalization */
				options.setWidth=(options.set_width) ? options.set_width : options.setWidth;
				options.setHeight=(options.set_height) ? options.set_height : options.setHeight;
				options.axis=(options.horizontalScroll) ? "x" : _findAxis(options.axis);
				options.scrollInertia=options.scrollInertia>0 && options.scrollInertia<17 ? 17 : options.scrollInertia;
				if(typeof options.mouseWheel!=="object" &&  options.mouseWheel==true){ /* old school mouseWheel option (non-object) */
					options.mouseWheel={enable:true,scrollAmount:"auto",axis:"y",preventDefault:false,deltaFactor:"auto",normalizeDelta:false,invert:false}
				}
				options.mouseWheel.scrollAmount=!options.mouseWheelPixels ? options.mouseWheel.scrollAmount : options.mouseWheelPixels;
				options.mouseWheel.normalizeDelta=!options.advanced.normalizeMouseWheelDelta ? options.mouseWheel.normalizeDelta : options.advanced.normalizeMouseWheelDelta;
				options.scrollButtons.scrollType=_findScrollButtonsType(options.scrollButtons.scrollType); 
				
				_theme(options); /* theme-specific options */
				
				/* plugin constructor */
				return $(selector).each(function(){
					
					var $this=$(this);
					
					if(!$this.data(pluginPfx)){ /* prevent multiple instantiations */
					
						/* store options and create objects in jquery data */
						$this.data(pluginPfx,{
							idx:++totalInstances, /* instance index */
							opt:options, /* options */
							scrollRatio:{y:null,x:null}, /* scrollbar to content ratio */
							overflowed:null, /* overflowed axis */
							contentReset:{y:null,x:null}, /* object to check when content resets */
							bindEvents:false, /* object to check if events are bound */
							tweenRunning:false, /* object to check if tween is running */
							sequential:{}, /* sequential scrolling object */
							langDir:$this.css("direction"), /* detect/store direction (ltr or rtl) */
							cbOffsets:null, /* object to check whether callback offsets always trigger */
							/* 
							object to check how scrolling events where last triggered 
							"internal" (default - triggered by this script), "external" (triggered by other scripts, e.g. via scrollTo method) 
							usage: object.data("mCS").trigger
							*/
							trigger:null,
							/* 
							object to check for changes in elements in order to call the update method automatically 
							*/
							poll:{size:{o:0,n:0},img:{o:0,n:0},change:{o:0,n:0}}
						});
						
						var d=$this.data(pluginPfx),o=d.opt,
							/* HTML data attributes */
							htmlDataAxis=$this.data("mcs-axis"),htmlDataSbPos=$this.data("mcs-scrollbar-position"),htmlDataTheme=$this.data("mcs-theme");
						 
						if(htmlDataAxis){o.axis=htmlDataAxis;} /* usage example: data-mcs-axis="y" */
						if(htmlDataSbPos){o.scrollbarPosition=htmlDataSbPos;} /* usage example: data-mcs-scrollbar-position="outside" */
						if(htmlDataTheme){ /* usage example: data-mcs-theme="minimal" */
							o.theme=htmlDataTheme;
							_theme(o); /* theme-specific options */
						}
						
						_pluginMarkup.call(this); /* add plugin markup */
						
						if(d && o.callbacks.onCreate && typeof o.callbacks.onCreate==="function"){o.callbacks.onCreate.call(this);} /* callbacks: onCreate */
						
						$("#mCSB_"+d.idx+"_container img:not(."+classes[2]+")").addClass(classes[2]); /* flag loaded images */
						
						methods.update.call(null,$this); /* call the update method */
					
					}
					
				});
				
			},
			/* ---------------------------------------- */
			
			
			
			/* 
			plugin update method 
			updates content and scrollbar(s) values, events and status 
			----------------------------------------
			usage: $(selector).mCustomScrollbar("update");
			*/
			
			update:function(el,cb){
				
				var selector=el || _selector.call(this); /* validate selector */
				
				return $(selector).each(function(){
					
					var $this=$(this);
					
					if($this.data(pluginPfx)){ /* check if plugin has initialized */
						
						var d=$this.data(pluginPfx),o=d.opt,
							mCSB_container=$("#mCSB_"+d.idx+"_container"),
							mCustomScrollBox=$("#mCSB_"+d.idx),
							mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")];
						
						if(!mCSB_container.length){return;}
						
						if(d.tweenRunning){_stop($this);} /* stop any running tweens while updating */
						
						if(cb && d && o.callbacks.onBeforeUpdate && typeof o.callbacks.onBeforeUpdate==="function"){o.callbacks.onBeforeUpdate.call(this);} /* callbacks: onBeforeUpdate */
						
						/* if element was disabled or destroyed, remove class(es) */
						if($this.hasClass(classes[3])){$this.removeClass(classes[3]);}
						if($this.hasClass(classes[4])){$this.removeClass(classes[4]);}
						
						/* css flexbox fix, detect/set max-height */
						mCustomScrollBox.css("max-height","none");
						if(mCustomScrollBox.height()!==$this.height()){mCustomScrollBox.css("max-height",$this.height());}
						
						_expandContentHorizontally.call(this); /* expand content horizontally */
						
						if(o.axis!=="y" && !o.advanced.autoExpandHorizontalScroll){
							mCSB_container.css("width",_contentWidth(mCSB_container));
						}
						
						d.overflowed=_overflowed.call(this); /* determine if scrolling is required */
						
						_scrollbarVisibility.call(this); /* show/hide scrollbar(s) */
						
						/* auto-adjust scrollbar dragger length analogous to content */
						if(o.autoDraggerLength){_setDraggerLength.call(this);}
						
						_scrollRatio.call(this); /* calculate and store scrollbar to content ratio */
						
						_bindEvents.call(this); /* bind scrollbar events */
						
						/* reset scrolling position and/or events */
						var to=[Math.abs(mCSB_container[0].offsetTop),Math.abs(mCSB_container[0].offsetLeft)];
						if(o.axis!=="x"){ /* y/yx axis */
							if(!d.overflowed[0]){ /* y scrolling is not required */
								_resetContentPosition.call(this); /* reset content position */
								if(o.axis==="y"){
									_unbindEvents.call(this);
								}else if(o.axis==="yx" && d.overflowed[1]){
									_scrollTo($this,to[1].toString(),{dir:"x",dur:0,overwrite:"none"});
								}
							}else if(mCSB_dragger[0].height()>mCSB_dragger[0].parent().height()){
								_resetContentPosition.call(this); /* reset content position */
							}else{ /* y scrolling is required */
								_scrollTo($this,to[0].toString(),{dir:"y",dur:0,overwrite:"none"});
								d.contentReset.y=null;
							}
						}
						if(o.axis!=="y"){ /* x/yx axis */
							if(!d.overflowed[1]){ /* x scrolling is not required */
								_resetContentPosition.call(this); /* reset content position */
								if(o.axis==="x"){
									_unbindEvents.call(this);
								}else if(o.axis==="yx" && d.overflowed[0]){
									_scrollTo($this,to[0].toString(),{dir:"y",dur:0,overwrite:"none"});
								}
							}else if(mCSB_dragger[1].width()>mCSB_dragger[1].parent().width()){
								_resetContentPosition.call(this); /* reset content position */
							}else{ /* x scrolling is required */
								_scrollTo($this,to[1].toString(),{dir:"x",dur:0,overwrite:"none"});
								d.contentReset.x=null;
							}
						}
						
						/* callbacks: onImageLoad, onSelectorChange, onUpdate */
						if(cb && d){
							if(cb===2 && o.callbacks.onImageLoad && typeof o.callbacks.onImageLoad==="function"){
								o.callbacks.onImageLoad.call(this);
							}else if(cb===3 && o.callbacks.onSelectorChange && typeof o.callbacks.onSelectorChange==="function"){
								o.callbacks.onSelectorChange.call(this);
							}else if(o.callbacks.onUpdate && typeof o.callbacks.onUpdate==="function"){
								o.callbacks.onUpdate.call(this);
							}
						}
						
						_autoUpdate.call(this); /* initialize automatic updating (for dynamic content, fluid layouts etc.) */
						
					}
					
				});
				
			},
			/* ---------------------------------------- */
			
			
			
			/* 
			plugin scrollTo method 
			triggers a scrolling event to a specific value
			----------------------------------------
			usage: $(selector).mCustomScrollbar("scrollTo",value,options);
			*/
		
			scrollTo:function(val,options){
				
				/* prevent silly things like $(selector).mCustomScrollbar("scrollTo",undefined); */
				if(typeof val=="undefined" || val==null){return;}
				
				var selector=_selector.call(this); /* validate selector */
				
				return $(selector).each(function(){
					
					var $this=$(this);
					
					if($this.data(pluginPfx)){ /* check if plugin has initialized */
					
						var d=$this.data(pluginPfx),o=d.opt,
							/* method default options */
							methodDefaults={
								trigger:"external", /* method is by default triggered externally (e.g. from other scripts) */
								scrollInertia:o.scrollInertia, /* scrolling inertia (animation duration) */
								scrollEasing:"mcsEaseInOut", /* animation easing */
								moveDragger:false, /* move dragger instead of content */
								timeout:60, /* scroll-to delay */
								callbacks:true, /* enable/disable callbacks */
								onStart:true,
								onUpdate:true,
								onComplete:true
							},
							methodOptions=$.extend(true,{},methodDefaults,options),
							to=_arr.call(this,val),dur=methodOptions.scrollInertia>0 && methodOptions.scrollInertia<17 ? 17 : methodOptions.scrollInertia;
						
						/* translate yx values to actual scroll-to positions */
						to[0]=_to.call(this,to[0],"y");
						to[1]=_to.call(this,to[1],"x");
						
						/* 
						check if scroll-to value moves the dragger instead of content. 
						Only pixel values apply on dragger (e.g. 100, "100px", "-=100" etc.) 
						*/
						if(methodOptions.moveDragger){
							to[0]*=d.scrollRatio.y;
							to[1]*=d.scrollRatio.x;
						}
						
						methodOptions.dur=_isTabHidden() ? 0 : dur; //skip animations if browser tab is hidden
						
						setTimeout(function(){ 
							/* do the scrolling */
							if(to[0]!==null && typeof to[0]!=="undefined" && o.axis!=="x" && d.overflowed[0]){ /* scroll y */
								methodOptions.dir="y";
								methodOptions.overwrite="all";
								_scrollTo($this,to[0].toString(),methodOptions);
							}
							if(to[1]!==null && typeof to[1]!=="undefined" && o.axis!=="y" && d.overflowed[1]){ /* scroll x */
								methodOptions.dir="x";
								methodOptions.overwrite="none";
								_scrollTo($this,to[1].toString(),methodOptions);
							}
						},methodOptions.timeout);
						
					}
					
				});
				
			},
			/* ---------------------------------------- */
			
			
			
			/*
			plugin stop method 
			stops scrolling animation
			----------------------------------------
			usage: $(selector).mCustomScrollbar("stop");
			*/
			stop:function(){
				
				var selector=_selector.call(this); /* validate selector */
				
				return $(selector).each(function(){
					
					var $this=$(this);
					
					if($this.data(pluginPfx)){ /* check if plugin has initialized */
										
						_stop($this);
					
					}
					
				});
				
			},
			/* ---------------------------------------- */
			
			
			
			/*
			plugin disable method 
			temporarily disables the scrollbar(s) 
			----------------------------------------
			usage: $(selector).mCustomScrollbar("disable",reset); 
			reset (boolean): resets content position to 0 
			*/
			disable:function(r){
				
				var selector=_selector.call(this); /* validate selector */
				
				return $(selector).each(function(){
					
					var $this=$(this);
					
					if($this.data(pluginPfx)){ /* check if plugin has initialized */
						
						var d=$this.data(pluginPfx);
						
						_autoUpdate.call(this,"remove"); /* remove automatic updating */
						
						_unbindEvents.call(this); /* unbind events */
						
						if(r){_resetContentPosition.call(this);} /* reset content position */
						
						_scrollbarVisibility.call(this,true); /* show/hide scrollbar(s) */
						
						$this.addClass(classes[3]); /* add disable class */
					
					}
					
				});
				
			},
			/* ---------------------------------------- */
			
			
			
			/*
			plugin destroy method 
			completely removes the scrollbar(s) and returns the element to its original state
			----------------------------------------
			usage: $(selector).mCustomScrollbar("destroy"); 
			*/
			destroy:function(){
				
				var selector=_selector.call(this); /* validate selector */
				
				return $(selector).each(function(){
					
					var $this=$(this);
					
					if($this.data(pluginPfx)){ /* check if plugin has initialized */
					
						var d=$this.data(pluginPfx),o=d.opt,
							mCustomScrollBox=$("#mCSB_"+d.idx),
							mCSB_container=$("#mCSB_"+d.idx+"_container"),
							scrollbar=$(".mCSB_"+d.idx+"_scrollbar");
					
						if(o.live){removeLiveTimers(o.liveSelector || $(selector).selector);} /* remove live timers */
						
						_autoUpdate.call(this,"remove"); /* remove automatic updating */
						
						_unbindEvents.call(this); /* unbind events */
						
						_resetContentPosition.call(this); /* reset content position */
						
						$this.removeData(pluginPfx); /* remove plugin data object */
						
						_delete(this,"mcs"); /* delete callbacks object */
						
						/* remove plugin markup */
						scrollbar.remove(); /* remove scrollbar(s) first (those can be either inside or outside plugin's inner wrapper) */
						mCSB_container.find("img."+classes[2]).removeClass(classes[2]); /* remove loaded images flag */
						mCustomScrollBox.replaceWith(mCSB_container.contents()); /* replace plugin's inner wrapper with the original content */
						/* remove plugin classes from the element and add destroy class */
						$this.removeClass(pluginNS+" _"+pluginPfx+"_"+d.idx+" "+classes[6]+" "+classes[7]+" "+classes[5]+" "+classes[3]).addClass(classes[4]);
					
					}
					
				});
				
			}
			/* ---------------------------------------- */
			
		},
	
	
	
	
		
	/* 
	----------------------------------------
	FUNCTIONS
	----------------------------------------
	*/
	
		/* validates selector (if selector is invalid or undefined uses the default one) */
		_selector=function(){
			return (typeof $(this)!=="object" || $(this).length<1) ? defaultSelector : this;
		},
		/* -------------------- */
		
		
		/* changes options according to theme */
		_theme=function(obj){
			var fixedSizeScrollbarThemes=["rounded","rounded-dark","rounded-dots","rounded-dots-dark"],
				nonExpandedScrollbarThemes=["rounded-dots","rounded-dots-dark","3d","3d-dark","3d-thick","3d-thick-dark","inset","inset-dark","inset-2","inset-2-dark","inset-3","inset-3-dark"],
				disabledScrollButtonsThemes=["minimal","minimal-dark"],
				enabledAutoHideScrollbarThemes=["minimal","minimal-dark"],
				scrollbarPositionOutsideThemes=["minimal","minimal-dark"];
			obj.autoDraggerLength=$.inArray(obj.theme,fixedSizeScrollbarThemes) > -1 ? false : obj.autoDraggerLength;
			obj.autoExpandScrollbar=$.inArray(obj.theme,nonExpandedScrollbarThemes) > -1 ? false : obj.autoExpandScrollbar;
			obj.scrollButtons.enable=$.inArray(obj.theme,disabledScrollButtonsThemes) > -1 ? false : obj.scrollButtons.enable;
			obj.autoHideScrollbar=$.inArray(obj.theme,enabledAutoHideScrollbarThemes) > -1 ? true : obj.autoHideScrollbar;
			obj.scrollbarPosition=$.inArray(obj.theme,scrollbarPositionOutsideThemes) > -1 ? "outside" : obj.scrollbarPosition;
		},
		/* -------------------- */
		
		
		/* live option timers removal */
		removeLiveTimers=function(selector){
			if(liveTimers[selector]){
				clearTimeout(liveTimers[selector]);
				_delete(liveTimers,selector);
			}
		},
		/* -------------------- */
		
		
		/* normalizes axis option to valid values: "y", "x", "yx" */
		_findAxis=function(val){
			return (val==="yx" || val==="xy" || val==="auto") ? "yx" : (val==="x" || val==="horizontal") ? "x" : "y";
		},
		/* -------------------- */
		
		
		/* normalizes scrollButtons.scrollType option to valid values: "stepless", "stepped" */
		_findScrollButtonsType=function(val){
			return (val==="stepped" || val==="pixels" || val==="step" || val==="click") ? "stepped" : "stepless";
		},
		/* -------------------- */
		
		
		/* generates plugin markup */
		_pluginMarkup=function(){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
				expandClass=o.autoExpandScrollbar ? " "+classes[1]+"_expand" : "",
				scrollbar=["<div id='mCSB_"+d.idx+"_scrollbar_vertical' class='mCSB_scrollTools mCSB_"+d.idx+"_scrollbar mCS-"+o.theme+" mCSB_scrollTools_vertical"+expandClass+"'><div class='"+classes[12]+"'><div id='mCSB_"+d.idx+"_dragger_vertical' class='mCSB_dragger' style='position:absolute;' oncontextmenu='return false;'><div class='mCSB_dragger_bar' /></div><div class='mCSB_draggerRail' /></div></div>","<div id='mCSB_"+d.idx+"_scrollbar_horizontal' class='mCSB_scrollTools mCSB_"+d.idx+"_scrollbar mCS-"+o.theme+" mCSB_scrollTools_horizontal"+expandClass+"'><div class='"+classes[12]+"'><div id='mCSB_"+d.idx+"_dragger_horizontal' class='mCSB_dragger' style='position:absolute;' oncontextmenu='return false;'><div class='mCSB_dragger_bar' /></div><div class='mCSB_draggerRail' /></div></div>"],
				wrapperClass=o.axis==="yx" ? "mCSB_vertical_horizontal" : o.axis==="x" ? "mCSB_horizontal" : "mCSB_vertical",
				scrollbars=o.axis==="yx" ? scrollbar[0]+scrollbar[1] : o.axis==="x" ? scrollbar[1] : scrollbar[0],
				contentWrapper=o.axis==="yx" ? "<div id='mCSB_"+d.idx+"_container_wrapper' class='mCSB_container_wrapper' />" : "",
				autoHideClass=o.autoHideScrollbar ? " "+classes[6] : "",
				scrollbarDirClass=(o.axis!=="x" && d.langDir==="rtl") ? " "+classes[7] : "";
			if(o.setWidth){$this.css("width",o.setWidth);} /* set element width */
			if(o.setHeight){$this.css("height",o.setHeight);} /* set element height */
			o.setLeft=(o.axis!=="y" && d.langDir==="rtl") ? "989999px" : o.setLeft; /* adjust left position for rtl direction */
			$this.addClass(pluginNS+" _"+pluginPfx+"_"+d.idx+autoHideClass+scrollbarDirClass).wrapInner("<div id='mCSB_"+d.idx+"' class='mCustomScrollBox mCS-"+o.theme+" "+wrapperClass+"'><div id='mCSB_"+d.idx+"_container' class='mCSB_container' style='position:relative; top:"+o.setTop+"; left:"+o.setLeft+";' dir="+d.langDir+" /></div>");
			var mCustomScrollBox=$("#mCSB_"+d.idx),
				mCSB_container=$("#mCSB_"+d.idx+"_container");
			if(o.axis!=="y" && !o.advanced.autoExpandHorizontalScroll){
				mCSB_container.css("width",_contentWidth(mCSB_container));
			}
			if(o.scrollbarPosition==="outside"){
				if($this.css("position")==="static"){ /* requires elements with non-static position */
					$this.css("position","relative");
				}
				$this.css("overflow","visible");
				mCustomScrollBox.addClass("mCSB_outside").after(scrollbars);
			}else{
				mCustomScrollBox.addClass("mCSB_inside").append(scrollbars);
				mCSB_container.wrap(contentWrapper);
			}
			_scrollButtons.call(this); /* add scrollbar buttons */
			/* minimum dragger length */
			var mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")];
			mCSB_dragger[0].css("min-height",mCSB_dragger[0].height());
			mCSB_dragger[1].css("min-width",mCSB_dragger[1].width());
		},
		/* -------------------- */
		
		
		/* calculates content width */
		_contentWidth=function(el){
			var val=[el[0].scrollWidth,Math.max.apply(Math,el.children().map(function(){return $(this).outerWidth(true);}).get())],w=el.parent().width();
			return val[0]>w ? val[0] : val[1]>w ? val[1] : "100%";
		},
		/* -------------------- */
		
		
		/* expands content horizontally */
		_expandContentHorizontally=function(){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
				mCSB_container=$("#mCSB_"+d.idx+"_container");
			if(o.advanced.autoExpandHorizontalScroll && o.axis!=="y"){
				/* calculate scrollWidth */
				mCSB_container.css({"width":"auto","min-width":0,"overflow-x":"scroll"});
				var w=Math.ceil(mCSB_container[0].scrollWidth);
				if(o.advanced.autoExpandHorizontalScroll===3 || (o.advanced.autoExpandHorizontalScroll!==2 && w>mCSB_container.parent().width())){
					mCSB_container.css({"width":w,"min-width":"100%","overflow-x":"inherit"});
				}else{
					/* 
					wrap content with an infinite width div and set its position to absolute and width to auto. 
					Setting width to auto before calculating the actual width is important! 
					We must let the browser set the width as browser zoom values are impossible to calculate.
					*/
					mCSB_container.css({"overflow-x":"inherit","position":"absolute"})
						.wrap("<div class='mCSB_h_wrapper' style='position:relative; left:0; width:999999px;' />")
						.css({ /* set actual width, original position and un-wrap */
							/* 
							get the exact width (with decimals) and then round-up. 
							Using jquery outerWidth() will round the width value which will mess up with inner elements that have non-integer width
							*/
							"width":(Math.ceil(mCSB_container[0].getBoundingClientRect().right+0.4)-Math.floor(mCSB_container[0].getBoundingClientRect().left)),
							"min-width":"100%",
							"position":"relative"
						}).unwrap();
				}
			}
		},
		/* -------------------- */
		
		
		/* adds scrollbar buttons */
		_scrollButtons=function(){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
				mCSB_scrollTools=$(".mCSB_"+d.idx+"_scrollbar:first"),
				tabindex=!_isNumeric(o.scrollButtons.tabindex) ? "" : "tabindex='"+o.scrollButtons.tabindex+"'",
				btnHTML=[
					"<a href='#' class='"+classes[13]+"' oncontextmenu='return false;' "+tabindex+" />",
					"<a href='#' class='"+classes[14]+"' oncontextmenu='return false;' "+tabindex+" />",
					"<a href='#' class='"+classes[15]+"' oncontextmenu='return false;' "+tabindex+" />",
					"<a href='#' class='"+classes[16]+"' oncontextmenu='return false;' "+tabindex+" />"
				],
				btn=[(o.axis==="x" ? btnHTML[2] : btnHTML[0]),(o.axis==="x" ? btnHTML[3] : btnHTML[1]),btnHTML[2],btnHTML[3]];
			if(o.scrollButtons.enable){
				mCSB_scrollTools.prepend(btn[0]).append(btn[1]).next(".mCSB_scrollTools").prepend(btn[2]).append(btn[3]);
			}
		},
		/* -------------------- */
		
		
		/* auto-adjusts scrollbar dragger length */
		_setDraggerLength=function(){
			var $this=$(this),d=$this.data(pluginPfx),
				mCustomScrollBox=$("#mCSB_"+d.idx),
				mCSB_container=$("#mCSB_"+d.idx+"_container"),
				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")],
				ratio=[mCustomScrollBox.height()/mCSB_container.outerHeight(false),mCustomScrollBox.width()/mCSB_container.outerWidth(false)],
				l=[
					parseInt(mCSB_dragger[0].css("min-height")),Math.round(ratio[0]*mCSB_dragger[0].parent().height()),
					parseInt(mCSB_dragger[1].css("min-width")),Math.round(ratio[1]*mCSB_dragger[1].parent().width())
				],
				h=oldIE && (l[1]<l[0]) ? l[0] : l[1],w=oldIE && (l[3]<l[2]) ? l[2] : l[3];
			mCSB_dragger[0].css({
				"height":h,"max-height":(mCSB_dragger[0].parent().height()-10)
			}).find(".mCSB_dragger_bar").css({"line-height":l[0]+"px"});
			mCSB_dragger[1].css({
				"width":w,"max-width":(mCSB_dragger[1].parent().width()-10)
			});
		},
		/* -------------------- */
		
		
		/* calculates scrollbar to content ratio */
		_scrollRatio=function(){
			var $this=$(this),d=$this.data(pluginPfx),
				mCustomScrollBox=$("#mCSB_"+d.idx),
				mCSB_container=$("#mCSB_"+d.idx+"_container"),
				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")],
				scrollAmount=[mCSB_container.outerHeight(false)-mCustomScrollBox.height(),mCSB_container.outerWidth(false)-mCustomScrollBox.width()],
				ratio=[
					scrollAmount[0]/(mCSB_dragger[0].parent().height()-mCSB_dragger[0].height()),
					scrollAmount[1]/(mCSB_dragger[1].parent().width()-mCSB_dragger[1].width())
				];
			d.scrollRatio={y:ratio[0],x:ratio[1]};
		},
		/* -------------------- */
		
		
		/* toggles scrolling classes */
		_onDragClasses=function(el,action,xpnd){
			var expandClass=xpnd ? classes[0]+"_expanded" : "",
				scrollbar=el.closest(".mCSB_scrollTools");
			if(action==="active"){
				el.toggleClass(classes[0]+" "+expandClass); scrollbar.toggleClass(classes[1]); 
				el[0]._draggable=el[0]._draggable ? 0 : 1;
			}else{
				if(!el[0]._draggable){
					if(action==="hide"){
						el.removeClass(classes[0]); scrollbar.removeClass(classes[1]);
					}else{
						el.addClass(classes[0]); scrollbar.addClass(classes[1]);
					}
				}
			}
		},
		/* -------------------- */
		
		
		/* checks if content overflows its container to determine if scrolling is required */
		_overflowed=function(){
			var $this=$(this),d=$this.data(pluginPfx),
				mCustomScrollBox=$("#mCSB_"+d.idx),
				mCSB_container=$("#mCSB_"+d.idx+"_container"),
				contentHeight=d.overflowed==null ? mCSB_container.height() : mCSB_container.outerHeight(false),
				contentWidth=d.overflowed==null ? mCSB_container.width() : mCSB_container.outerWidth(false),
				h=mCSB_container[0].scrollHeight,w=mCSB_container[0].scrollWidth;
			if(h>contentHeight){contentHeight=h;}
			if(w>contentWidth){contentWidth=w;}
			return [contentHeight>mCustomScrollBox.height(),contentWidth>mCustomScrollBox.width()];
		},
		/* -------------------- */
		
		
		/* resets content position to 0 */
		_resetContentPosition=function(){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
				mCustomScrollBox=$("#mCSB_"+d.idx),
				mCSB_container=$("#mCSB_"+d.idx+"_container"),
				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")];
			_stop($this); /* stop any current scrolling before resetting */
			if((o.axis!=="x" && !d.overflowed[0]) || (o.axis==="y" && d.overflowed[0])){ /* reset y */
				mCSB_dragger[0].add(mCSB_container).css("top",0);
				_scrollTo($this,"_resetY");
			}
			if((o.axis!=="y" && !d.overflowed[1]) || (o.axis==="x" && d.overflowed[1])){ /* reset x */
				var cx=dx=0;
				if(d.langDir==="rtl"){ /* adjust left position for rtl direction */
					cx=mCustomScrollBox.width()-mCSB_container.outerWidth(false);
					dx=Math.abs(cx/d.scrollRatio.x);
				}
				mCSB_container.css("left",cx);
				mCSB_dragger[1].css("left",dx);
				_scrollTo($this,"_resetX");
			}
		},
		/* -------------------- */
		
		
		/* binds scrollbar events */
		_bindEvents=function(){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt;
			if(!d.bindEvents){ /* check if events are already bound */
				_draggable.call(this);
				if(o.contentTouchScroll){_contentDraggable.call(this);}
				_selectable.call(this);
				if(o.mouseWheel.enable){ /* bind mousewheel fn when plugin is available */
					function _mwt(){
						mousewheelTimeout=setTimeout(function(){
							if(!$.event.special.mousewheel){
								_mwt();
							}else{
								clearTimeout(mousewheelTimeout);
								_mousewheel.call($this[0]);
							}
						},100);
					}
					var mousewheelTimeout;
					_mwt();
				}
				_draggerRail.call(this);
				_wrapperScroll.call(this);
				if(o.advanced.autoScrollOnFocus){_focus.call(this);}
				if(o.scrollButtons.enable){_buttons.call(this);}
				if(o.keyboard.enable){_keyboard.call(this);}
				d.bindEvents=true;
			}
		},
		/* -------------------- */
		
		
		/* unbinds scrollbar events */
		_unbindEvents=function(){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
				namespace=pluginPfx+"_"+d.idx,
				sb=".mCSB_"+d.idx+"_scrollbar",
				sel=$("#mCSB_"+d.idx+",#mCSB_"+d.idx+"_container,#mCSB_"+d.idx+"_container_wrapper,"+sb+" ."+classes[12]+",#mCSB_"+d.idx+"_dragger_vertical,#mCSB_"+d.idx+"_dragger_horizontal,"+sb+">a"),
				mCSB_container=$("#mCSB_"+d.idx+"_container");
			if(o.advanced.releaseDraggableSelectors){sel.add($(o.advanced.releaseDraggableSelectors));}
			if(o.advanced.extraDraggableSelectors){sel.add($(o.advanced.extraDraggableSelectors));}
			if(d.bindEvents){ /* check if events are bound */
				/* unbind namespaced events from document/selectors */
				$(document).add($(!_canAccessIFrame() || top.document)).unbind("."+namespace);
				sel.each(function(){
					$(this).unbind("."+namespace);
				});
				/* clear and delete timeouts/objects */
				clearTimeout($this[0]._focusTimeout); _delete($this[0],"_focusTimeout");
				clearTimeout(d.sequential.step); _delete(d.sequential,"step");
				clearTimeout(mCSB_container[0].onCompleteTimeout); _delete(mCSB_container[0],"onCompleteTimeout");
				d.bindEvents=false;
			}
		},
		/* -------------------- */
		
		
		/* toggles scrollbar visibility */
		_scrollbarVisibility=function(disabled){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
				contentWrapper=$("#mCSB_"+d.idx+"_container_wrapper"),
				content=contentWrapper.length ? contentWrapper : $("#mCSB_"+d.idx+"_container"),
				scrollbar=[$("#mCSB_"+d.idx+"_scrollbar_vertical"),$("#mCSB_"+d.idx+"_scrollbar_horizontal")],
				mCSB_dragger=[scrollbar[0].find(".mCSB_dragger"),scrollbar[1].find(".mCSB_dragger")];
			if(o.axis!=="x"){
				if(d.overflowed[0] && !disabled){
					scrollbar[0].add(mCSB_dragger[0]).add(scrollbar[0].children("a")).css("display","block");
					content.removeClass(classes[8]+" "+classes[10]);
				}else{
					if(o.alwaysShowScrollbar){
						if(o.alwaysShowScrollbar!==2){mCSB_dragger[0].css("display","none");}
						content.removeClass(classes[10]);
					}else{
						scrollbar[0].css("display","none");
						content.addClass(classes[10]);
					}
					content.addClass(classes[8]);
				}
			}
			if(o.axis!=="y"){
				if(d.overflowed[1] && !disabled){
					scrollbar[1].add(mCSB_dragger[1]).add(scrollbar[1].children("a")).css("display","block");
					content.removeClass(classes[9]+" "+classes[11]);
				}else{
					if(o.alwaysShowScrollbar){
						if(o.alwaysShowScrollbar!==2){mCSB_dragger[1].css("display","none");}
						content.removeClass(classes[11]);
					}else{
						scrollbar[1].css("display","none");
						content.addClass(classes[11]);
					}
					content.addClass(classes[9]);
				}
			}
			if(!d.overflowed[0] && !d.overflowed[1]){
				$this.addClass(classes[5]);
			}else{
				$this.removeClass(classes[5]);
			}
		},
		/* -------------------- */
		
		
		/* returns input coordinates of pointer, touch and mouse events (relative to document) */
		_coordinates=function(e){
			var t=e.type,o=e.target.ownerDocument!==document ? [$(frameElement).offset().top,$(frameElement).offset().left] : null,
				io=_canAccessIFrame() && e.target.ownerDocument!==top.document ? [$(e.view.frameElement).offset().top,$(e.view.frameElement).offset().left] : [0,0];
			switch(t){
				case "pointerdown": case "MSPointerDown": case "pointermove": case "MSPointerMove": case "pointerup": case "MSPointerUp":
					return o ? [e.originalEvent.pageY-o[0]+io[0],e.originalEvent.pageX-o[1]+io[1],false] : [e.originalEvent.pageY,e.originalEvent.pageX,false];
					break;
				case "touchstart": case "touchmove": case "touchend":
					var touch=e.originalEvent.touches[0] || e.originalEvent.changedTouches[0],
						touches=e.originalEvent.touches.length || e.originalEvent.changedTouches.length;
					return e.target.ownerDocument!==document ? [touch.screenY,touch.screenX,touches>1] : [touch.pageY,touch.pageX,touches>1];
					break;
				default:
					return o ? [e.pageY-o[0]+io[0],e.pageX-o[1]+io[1],false] : [e.pageY,e.pageX,false];
			}
		},
		/* -------------------- */
		
		
		/* 
		SCROLLBAR DRAG EVENTS
		scrolls content via scrollbar dragging 
		*/
		_draggable=function(){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
				namespace=pluginPfx+"_"+d.idx,
				draggerId=["mCSB_"+d.idx+"_dragger_vertical","mCSB_"+d.idx+"_dragger_horizontal"],
				mCSB_container=$("#mCSB_"+d.idx+"_container"),
				mCSB_dragger=$("#"+draggerId[0]+",#"+draggerId[1]),
				draggable,dragY,dragX,
				rds=o.advanced.releaseDraggableSelectors ? mCSB_dragger.add($(o.advanced.releaseDraggableSelectors)) : mCSB_dragger,
				eds=o.advanced.extraDraggableSelectors ? $(!_canAccessIFrame() || top.document).add($(o.advanced.extraDraggableSelectors)) : $(!_canAccessIFrame() || top.document);
			mCSB_dragger.bind("mousedown."+namespace+" touchstart."+namespace+" pointerdown."+namespace+" MSPointerDown."+namespace,function(e){
				e.stopImmediatePropagation();
				e.preventDefault();
				if(!_mouseBtnLeft(e)){return;} /* left mouse button only */
				touchActive=true;
				if(oldIE){document.onselectstart=function(){return false;}} /* disable text selection for IE < 9 */
				_iframe(false); /* enable scrollbar dragging over iframes by disabling their events */
				_stop($this);
				draggable=$(this);
				var offset=draggable.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left,
					h=draggable.height()+offset.top,w=draggable.width()+offset.left;
				if(y<h && y>0 && x<w && x>0){
					dragY=y; 
					dragX=x;
				}
				_onDragClasses(draggable,"active",o.autoExpandScrollbar); 
			}).bind("touchmove."+namespace,function(e){
				e.stopImmediatePropagation();
				e.preventDefault();
				var offset=draggable.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left;
				_drag(dragY,dragX,y,x);
			});
			$(document).add(eds).bind("mousemove."+namespace+" pointermove."+namespace+" MSPointerMove."+namespace,function(e){
				if(draggable){
					var offset=draggable.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left;
					if(dragY===y && dragX===x){return;} /* has it really moved? */
					_drag(dragY,dragX,y,x);
				}
			}).add(rds).bind("mouseup."+namespace+" touchend."+namespace+" pointerup."+namespace+" MSPointerUp."+namespace,function(e){
				if(draggable){
					_onDragClasses(draggable,"active",o.autoExpandScrollbar); 
					draggable=null;
				}
				touchActive=false;
				if(oldIE){document.onselectstart=null;} /* enable text selection for IE < 9 */
				_iframe(true); /* enable iframes events */
			});
			function _iframe(evt){
				var el=mCSB_container.find("iframe");
				if(!el.length){return;} /* check if content contains iframes */
				var val=!evt ? "none" : "auto";
				el.css("pointer-events",val); /* for IE11, iframe's display property should not be "block" */
			}
			function _drag(dragY,dragX,y,x){
				mCSB_container[0].idleTimer=o.scrollInertia<233 ? 250 : 0;
				if(draggable.attr("id")===draggerId[1]){
					var dir="x",to=((draggable[0].offsetLeft-dragX)+x)*d.scrollRatio.x;
				}else{
					var dir="y",to=((draggable[0].offsetTop-dragY)+y)*d.scrollRatio.y;
				}
				_scrollTo($this,to.toString(),{dir:dir,drag:true});
			}
		},
		/* -------------------- */
		
		
		/* 
		TOUCH SWIPE EVENTS
		scrolls content via touch swipe 
		Emulates the native touch-swipe scrolling with momentum found in iOS, Android and WP devices 
		*/
		_contentDraggable=function(){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
				namespace=pluginPfx+"_"+d.idx,
				mCustomScrollBox=$("#mCSB_"+d.idx),
				mCSB_container=$("#mCSB_"+d.idx+"_container"),
				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")],
				draggable,dragY,dragX,touchStartY,touchStartX,touchMoveY=[],touchMoveX=[],startTime,runningTime,endTime,distance,speed,amount,
				durA=0,durB,overwrite=o.axis==="yx" ? "none" : "all",touchIntent=[],touchDrag,docDrag,
				iframe=mCSB_container.find("iframe"),
				events=[
					"touchstart."+namespace+" pointerdown."+namespace+" MSPointerDown."+namespace, //start
					"touchmove."+namespace+" pointermove."+namespace+" MSPointerMove."+namespace, //move
					"touchend."+namespace+" pointerup."+namespace+" MSPointerUp."+namespace //end
				],
				touchAction=document.body.style.touchAction!==undefined;
			mCSB_container.bind(events[0],function(e){
				_onTouchstart(e);
			}).bind(events[1],function(e){
				_onTouchmove(e);
			});
			mCustomScrollBox.bind(events[0],function(e){
				_onTouchstart2(e);
			}).bind(events[2],function(e){
				_onTouchend(e);
			});
			if(iframe.length){
				iframe.each(function(){
					$(this).load(function(){
						/* bind events on accessible iframes */
						if(_canAccessIFrame(this)){
							$(this.contentDocument || this.contentWindow.document).bind(events[0],function(e){
								_onTouchstart(e);
								_onTouchstart2(e);
							}).bind(events[1],function(e){
								_onTouchmove(e);
							}).bind(events[2],function(e){
								_onTouchend(e);
							});
						}
					});
				});
			}
			function _onTouchstart(e){
				if(!_pointerTouch(e) || touchActive || _coordinates(e)[2]){touchable=0; return;}
				touchable=1; touchDrag=0; docDrag=0; draggable=1;
				$this.removeClass("mCS_touch_action");
				var offset=mCSB_container.offset();
				dragY=_coordinates(e)[0]-offset.top;
				dragX=_coordinates(e)[1]-offset.left;
				touchIntent=[_coordinates(e)[0],_coordinates(e)[1]];
			}
			function _onTouchmove(e){
				if(!_pointerTouch(e) || touchActive || _coordinates(e)[2]){return;}
				if(!o.documentTouchScroll){e.preventDefault();} 
				e.stopImmediatePropagation();
				if(docDrag && !touchDrag){return;}
				if(draggable){
					runningTime=_getTime();
					var offset=mCustomScrollBox.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left,
						easing="mcsLinearOut";
					touchMoveY.push(y);
					touchMoveX.push(x);
					touchIntent[2]=Math.abs(_coordinates(e)[0]-touchIntent[0]); touchIntent[3]=Math.abs(_coordinates(e)[1]-touchIntent[1]);
					if(d.overflowed[0]){
						var limit=mCSB_dragger[0].parent().height()-mCSB_dragger[0].height(),
							prevent=((dragY-y)>0 && (y-dragY)>-(limit*d.scrollRatio.y) && (touchIntent[3]*2<touchIntent[2] || o.axis==="yx"));
					}
					if(d.overflowed[1]){
						var limitX=mCSB_dragger[1].parent().width()-mCSB_dragger[1].width(),
							preventX=((dragX-x)>0 && (x-dragX)>-(limitX*d.scrollRatio.x) && (touchIntent[2]*2<touchIntent[3] || o.axis==="yx"));
					}
					if(prevent || preventX){ /* prevent native document scrolling */
						if(!touchAction){e.preventDefault();} 
						touchDrag=1;
					}else{
						docDrag=1;
						$this.addClass("mCS_touch_action");
					}
					if(touchAction){e.preventDefault();} 
					amount=o.axis==="yx" ? [(dragY-y),(dragX-x)] : o.axis==="x" ? [null,(dragX-x)] : [(dragY-y),null];
					mCSB_container[0].idleTimer=250;
					if(d.overflowed[0]){_drag(amount[0],durA,easing,"y","all",true);}
					if(d.overflowed[1]){_drag(amount[1],durA,easing,"x",overwrite,true);}
				}
			}
			function _onTouchstart2(e){
				if(!_pointerTouch(e) || touchActive || _coordinates(e)[2]){touchable=0; return;}
				touchable=1;
				e.stopImmediatePropagation();
				_stop($this);
				startTime=_getTime();
				var offset=mCustomScrollBox.offset();
				touchStartY=_coordinates(e)[0]-offset.top;
				touchStartX=_coordinates(e)[1]-offset.left;
				touchMoveY=[]; touchMoveX=[];
			}
			function _onTouchend(e){
				if(!_pointerTouch(e) || touchActive || _coordinates(e)[2]){return;}
				draggable=0;
				e.stopImmediatePropagation();
				touchDrag=0; docDrag=0;
				endTime=_getTime();
				var offset=mCustomScrollBox.offset(),y=_coordinates(e)[0]-offset.top,x=_coordinates(e)[1]-offset.left;
				if((endTime-runningTime)>30){return;}
				speed=1000/(endTime-startTime);
				var easing="mcsEaseOut",slow=speed<2.5,
					diff=slow ? [touchMoveY[touchMoveY.length-2],touchMoveX[touchMoveX.length-2]] : [0,0];
				distance=slow ? [(y-diff[0]),(x-diff[1])] : [y-touchStartY,x-touchStartX];
				var absDistance=[Math.abs(distance[0]),Math.abs(distance[1])];
				speed=slow ? [Math.abs(distance[0]/4),Math.abs(distance[1]/4)] : [speed,speed];
				var a=[
					Math.abs(mCSB_container[0].offsetTop)-(distance[0]*_m((absDistance[0]/speed[0]),speed[0])),
					Math.abs(mCSB_container[0].offsetLeft)-(distance[1]*_m((absDistance[1]/speed[1]),speed[1]))
				];
				amount=o.axis==="yx" ? [a[0],a[1]] : o.axis==="x" ? [null,a[1]] : [a[0],null];
				durB=[(absDistance[0]*4)+o.scrollInertia,(absDistance[1]*4)+o.scrollInertia];
				var md=parseInt(o.contentTouchScroll) || 0; /* absolute minimum distance required */
				amount[0]=absDistance[0]>md ? amount[0] : 0;
				amount[1]=absDistance[1]>md ? amount[1] : 0;
				if(d.overflowed[0]){_drag(amount[0],durB[0],easing,"y",overwrite,false);}
				if(d.overflowed[1]){_drag(amount[1],durB[1],easing,"x",overwrite,false);}
			}
			function _m(ds,s){
				var r=[s*1.5,s*2,s/1.5,s/2];
				if(ds>90){
					return s>4 ? r[0] : r[3];
				}else if(ds>60){
					return s>3 ? r[3] : r[2];
				}else if(ds>30){
					return s>8 ? r[1] : s>6 ? r[0] : s>4 ? s : r[2];
				}else{
					return s>8 ? s : r[3];
				}
			}
			function _drag(amount,dur,easing,dir,overwrite,drag){
				if(!amount){return;}
				_scrollTo($this,amount.toString(),{dur:dur,scrollEasing:easing,dir:dir,overwrite:overwrite,drag:drag});
			}
		},
		/* -------------------- */
		
		
		/* 
		SELECT TEXT EVENTS 
		scrolls content when text is selected 
		*/
		_selectable=function(){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,seq=d.sequential,
				namespace=pluginPfx+"_"+d.idx,
				mCSB_container=$("#mCSB_"+d.idx+"_container"),
				wrapper=mCSB_container.parent(),
				action;
			mCSB_container.bind("mousedown."+namespace,function(e){
				if(touchable){return;}
				if(!action){action=1; touchActive=true;}
			}).add(document).bind("mousemove."+namespace,function(e){
				if(!touchable && action && _sel()){
					var offset=mCSB_container.offset(),
						y=_coordinates(e)[0]-offset.top+mCSB_container[0].offsetTop,x=_coordinates(e)[1]-offset.left+mCSB_container[0].offsetLeft;
					if(y>0 && y<wrapper.height() && x>0 && x<wrapper.width()){
						if(seq.step){_seq("off",null,"stepped");}
					}else{
						if(o.axis!=="x" && d.overflowed[0]){
							if(y<0){
								_seq("on",38);
							}else if(y>wrapper.height()){
								_seq("on",40);
							}
						}
						if(o.axis!=="y" && d.overflowed[1]){
							if(x<0){
								_seq("on",37);
							}else if(x>wrapper.width()){
								_seq("on",39);
							}
						}
					}
				}
			}).bind("mouseup."+namespace+" dragend."+namespace,function(e){
				if(touchable){return;}
				if(action){action=0; _seq("off",null);}
				touchActive=false;
			});
			function _sel(){
				return 	window.getSelection ? window.getSelection().toString() : 
						document.selection && document.selection.type!="Control" ? document.selection.createRange().text : 0;
			}
			function _seq(a,c,s){
				seq.type=s && action ? "stepped" : "stepless";
				seq.scrollAmount=10;
				_sequentialScroll($this,a,c,"mcsLinearOut",s ? 60 : null);
			}
		},
		/* -------------------- */
		
		
		/* 
		MOUSE WHEEL EVENT
		scrolls content via mouse-wheel 
		via mouse-wheel plugin (https://github.com/brandonaaron/jquery-mousewheel)
		*/
		_mousewheel=function(){
			if(!$(this).data(pluginPfx)){return;} /* Check if the scrollbar is ready to use mousewheel events (issue: #185) */
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
				namespace=pluginPfx+"_"+d.idx,
				mCustomScrollBox=$("#mCSB_"+d.idx),
				mCSB_dragger=[$("#mCSB_"+d.idx+"_dragger_vertical"),$("#mCSB_"+d.idx+"_dragger_horizontal")],
				iframe=$("#mCSB_"+d.idx+"_container").find("iframe");
			if(iframe.length){
				iframe.each(function(){
					$(this).load(function(){
						/* bind events on accessible iframes */
						if(_canAccessIFrame(this)){
							$(this.contentDocument || this.contentWindow.document).bind("mousewheel."+namespace,function(e,delta){
								_onMousewheel(e,delta);
							});
						}
					});
				});
			}
			mCustomScrollBox.bind("mousewheel."+namespace,function(e,delta){
				_onMousewheel(e,delta);
			});
			function _onMousewheel(e,delta){
				_stop($this);
				if(_disableMousewheel($this,e.target)){return;} /* disables mouse-wheel when hovering specific elements */
				var deltaFactor=o.mouseWheel.deltaFactor!=="auto" ? parseInt(o.mouseWheel.deltaFactor) : (oldIE && e.deltaFactor<100) ? 100 : e.deltaFactor || 100,
					dur=o.scrollInertia;
				if(o.axis==="x" || o.mouseWheel.axis==="x"){
					var dir="x",
						px=[Math.round(deltaFactor*d.scrollRatio.x),parseInt(o.mouseWheel.scrollAmount)],
						amount=o.mouseWheel.scrollAmount!=="auto" ? px[1] : px[0]>=mCustomScrollBox.width() ? mCustomScrollBox.width()*0.9 : px[0],
						contentPos=Math.abs($("#mCSB_"+d.idx+"_container")[0].offsetLeft),
						draggerPos=mCSB_dragger[1][0].offsetLeft,
						limit=mCSB_dragger[1].parent().width()-mCSB_dragger[1].width(),
						dlt=e.deltaX || e.deltaY || delta;
				}else{
					var dir="y",
						px=[Math.round(deltaFactor*d.scrollRatio.y),parseInt(o.mouseWheel.scrollAmount)],
						amount=o.mouseWheel.scrollAmount!=="auto" ? px[1] : px[0]>=mCustomScrollBox.height() ? mCustomScrollBox.height()*0.9 : px[0],
						contentPos=Math.abs($("#mCSB_"+d.idx+"_container")[0].offsetTop),
						draggerPos=mCSB_dragger[0][0].offsetTop,
						limit=mCSB_dragger[0].parent().height()-mCSB_dragger[0].height(),
						dlt=e.deltaY || delta;
				}
				if((dir==="y" && !d.overflowed[0]) || (dir==="x" && !d.overflowed[1])){return;}
				if(o.mouseWheel.invert || e.webkitDirectionInvertedFromDevice){dlt=-dlt;}
				if(o.mouseWheel.normalizeDelta){dlt=dlt<0 ? -1 : 1;}
				if((dlt>0 && draggerPos!==0) || (dlt<0 && draggerPos!==limit) || o.mouseWheel.preventDefault){
					e.stopImmediatePropagation();
					e.preventDefault();
				}
				if(e.deltaFactor<2 && !o.mouseWheel.normalizeDelta){
					//very low deltaFactor values mean some kind of delta acceleration (e.g. osx trackpad), so adjusting scrolling accordingly
					amount=e.deltaFactor; dur=17;
				}
				_scrollTo($this,(contentPos-(dlt*amount)).toString(),{dir:dir,dur:dur});
			}
		},
		/* -------------------- */
		
		
		/* checks if iframe can be accessed */
		_canAccessIFrame=function(iframe){
			var html=null;
			if(!iframe){
				try{
					var doc=top.document;
					html=doc.body.innerHTML;
				}catch(err){/* do nothing */}
				return(html!==null);
			}else{
				try{
					var doc=iframe.contentDocument || iframe.contentWindow.document;
					html=doc.body.innerHTML;
				}catch(err){/* do nothing */}
				return(html!==null);
			}
		},
		/* -------------------- */
		
		
		/* disables mouse-wheel when hovering specific elements like select, datalist etc. */
		_disableMousewheel=function(el,target){
			var tag=target.nodeName.toLowerCase(),
				tags=el.data(pluginPfx).opt.mouseWheel.disableOver,
				/* elements that require focus */
				focusTags=["select","textarea"];
			return $.inArray(tag,tags) > -1 && !($.inArray(tag,focusTags) > -1 && !$(target).is(":focus"));
		},
		/* -------------------- */
		
		
		/* 
		DRAGGER RAIL CLICK EVENT
		scrolls content via dragger rail 
		*/
		_draggerRail=function(){
			var $this=$(this),d=$this.data(pluginPfx),
				namespace=pluginPfx+"_"+d.idx,
				mCSB_container=$("#mCSB_"+d.idx+"_container"),
				wrapper=mCSB_container.parent(),
				mCSB_draggerContainer=$(".mCSB_"+d.idx+"_scrollbar ."+classes[12]),
				clickable;
			mCSB_draggerContainer.bind("mousedown."+namespace+" touchstart."+namespace+" pointerdown."+namespace+" MSPointerDown."+namespace,function(e){
				touchActive=true;
				if(!$(e.target).hasClass("mCSB_dragger")){clickable=1;}
			}).bind("touchend."+namespace+" pointerup."+namespace+" MSPointerUp."+namespace,function(e){
				touchActive=false;
			}).bind("click."+namespace,function(e){
				if(!clickable){return;}
				clickable=0;
				if($(e.target).hasClass(classes[12]) || $(e.target).hasClass("mCSB_draggerRail")){
					_stop($this);
					var el=$(this),mCSB_dragger=el.find(".mCSB_dragger");
					if(el.parent(".mCSB_scrollTools_horizontal").length>0){
						if(!d.overflowed[1]){return;}
						var dir="x",
							clickDir=e.pageX>mCSB_dragger.offset().left ? -1 : 1,
							to=Math.abs(mCSB_container[0].offsetLeft)-(clickDir*(wrapper.width()*0.9));
					}else{
						if(!d.overflowed[0]){return;}
						var dir="y",
							clickDir=e.pageY>mCSB_dragger.offset().top ? -1 : 1,
							to=Math.abs(mCSB_container[0].offsetTop)-(clickDir*(wrapper.height()*0.9));
					}
					_scrollTo($this,to.toString(),{dir:dir,scrollEasing:"mcsEaseInOut"});
				}
			});
		},
		/* -------------------- */
		
		
		/* 
		FOCUS EVENT
		scrolls content via element focus (e.g. clicking an input, pressing TAB key etc.)
		*/
		_focus=function(){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
				namespace=pluginPfx+"_"+d.idx,
				mCSB_container=$("#mCSB_"+d.idx+"_container"),
				wrapper=mCSB_container.parent();
			mCSB_container.bind("focusin."+namespace,function(e){
				var el=$(document.activeElement),
					nested=mCSB_container.find(".mCustomScrollBox").length,
					dur=0;
				if(!el.is(o.advanced.autoScrollOnFocus)){return;}
				_stop($this);
				clearTimeout($this[0]._focusTimeout);
				$this[0]._focusTimer=nested ? (dur+17)*nested : 0;
				$this[0]._focusTimeout=setTimeout(function(){
					var	to=[_childPos(el)[0],_childPos(el)[1]],
						contentPos=[mCSB_container[0].offsetTop,mCSB_container[0].offsetLeft],
						isVisible=[
							(contentPos[0]+to[0]>=0 && contentPos[0]+to[0]<wrapper.height()-el.outerHeight(false)),
							(contentPos[1]+to[1]>=0 && contentPos[0]+to[1]<wrapper.width()-el.outerWidth(false))
						],
						overwrite=(o.axis==="yx" && !isVisible[0] && !isVisible[1]) ? "none" : "all";
					if(o.axis!=="x" && !isVisible[0]){
						_scrollTo($this,to[0].toString(),{dir:"y",scrollEasing:"mcsEaseInOut",overwrite:overwrite,dur:dur});
					}
					if(o.axis!=="y" && !isVisible[1]){
						_scrollTo($this,to[1].toString(),{dir:"x",scrollEasing:"mcsEaseInOut",overwrite:overwrite,dur:dur});
					}
				},$this[0]._focusTimer);
			});
		},
		/* -------------------- */
		
		
		/* sets content wrapper scrollTop/scrollLeft always to 0 */
		_wrapperScroll=function(){
			var $this=$(this),d=$this.data(pluginPfx),
				namespace=pluginPfx+"_"+d.idx,
				wrapper=$("#mCSB_"+d.idx+"_container").parent();
			wrapper.bind("scroll."+namespace,function(e){
				if(wrapper.scrollTop()!==0 || wrapper.scrollLeft()!==0){
					$(".mCSB_"+d.idx+"_scrollbar").css("visibility","hidden"); /* hide scrollbar(s) */
				}
			});
		},
		/* -------------------- */
		
		
		/* 
		BUTTONS EVENTS
		scrolls content via up, down, left and right buttons 
		*/
		_buttons=function(){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,seq=d.sequential,
				namespace=pluginPfx+"_"+d.idx,
				sel=".mCSB_"+d.idx+"_scrollbar",
				btn=$(sel+">a");
			btn.bind("mousedown."+namespace+" touchstart."+namespace+" pointerdown."+namespace+" MSPointerDown."+namespace+" mouseup."+namespace+" touchend."+namespace+" pointerup."+namespace+" MSPointerUp."+namespace+" mouseout."+namespace+" pointerout."+namespace+" MSPointerOut."+namespace+" click."+namespace,function(e){
				e.preventDefault();
				if(!_mouseBtnLeft(e)){return;} /* left mouse button only */
				var btnClass=$(this).attr("class");
				seq.type=o.scrollButtons.scrollType;
				switch(e.type){
					case "mousedown": case "touchstart": case "pointerdown": case "MSPointerDown":
						if(seq.type==="stepped"){return;}
						touchActive=true;
						d.tweenRunning=false;
						_seq("on",btnClass);
						break;
					case "mouseup": case "touchend": case "pointerup": case "MSPointerUp":
					case "mouseout": case "pointerout": case "MSPointerOut":
						if(seq.type==="stepped"){return;}
						touchActive=false;
						if(seq.dir){_seq("off",btnClass);}
						break;
					case "click":
						if(seq.type!=="stepped" || d.tweenRunning){return;}
						_seq("on",btnClass);
						break;
				}
				function _seq(a,c){
					seq.scrollAmount=o.scrollButtons.scrollAmount;
					_sequentialScroll($this,a,c);
				}
			});
		},
		/* -------------------- */
		
		
		/* 
		KEYBOARD EVENTS
		scrolls content via keyboard 
		Keys: up arrow, down arrow, left arrow, right arrow, PgUp, PgDn, Home, End
		*/
		_keyboard=function(){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,seq=d.sequential,
				namespace=pluginPfx+"_"+d.idx,
				mCustomScrollBox=$("#mCSB_"+d.idx),
				mCSB_container=$("#mCSB_"+d.idx+"_container"),
				wrapper=mCSB_container.parent(),
				editables="input,textarea,select,datalist,keygen,[contenteditable='true']",
				iframe=mCSB_container.find("iframe"),
				events=["blur."+namespace+" keydown."+namespace+" keyup."+namespace];
			if(iframe.length){
				iframe.each(function(){
					$(this).load(function(){
						/* bind events on accessible iframes */
						if(_canAccessIFrame(this)){
							$(this.contentDocument || this.contentWindow.document).bind(events[0],function(e){
								_onKeyboard(e);
							});
						}
					});
				});
			}
			mCustomScrollBox.attr("tabindex","0").bind(events[0],function(e){
				_onKeyboard(e);
			});
			function _onKeyboard(e){
				switch(e.type){
					case "blur":
						if(d.tweenRunning && seq.dir){_seq("off",null);}
						break;
					case "keydown": case "keyup":
						var code=e.keyCode ? e.keyCode : e.which,action="on";
						if((o.axis!=="x" && (code===38 || code===40)) || (o.axis!=="y" && (code===37 || code===39))){
							/* up (38), down (40), left (37), right (39) arrows */
							if(((code===38 || code===40) && !d.overflowed[0]) || ((code===37 || code===39) && !d.overflowed[1])){return;}
							if(e.type==="keyup"){action="off";}
							if(!$(document.activeElement).is(editables)){
								e.preventDefault();
								e.stopImmediatePropagation();
								_seq(action,code);
							}
						}else if(code===33 || code===34){
							/* PgUp (33), PgDn (34) */
							if(d.overflowed[0] || d.overflowed[1]){
								e.preventDefault();
								e.stopImmediatePropagation();
							}
							if(e.type==="keyup"){
								_stop($this);
								var keyboardDir=code===34 ? -1 : 1;
								if(o.axis==="x" || (o.axis==="yx" && d.overflowed[1] && !d.overflowed[0])){
									var dir="x",to=Math.abs(mCSB_container[0].offsetLeft)-(keyboardDir*(wrapper.width()*0.9));
								}else{
									var dir="y",to=Math.abs(mCSB_container[0].offsetTop)-(keyboardDir*(wrapper.height()*0.9));
								}
								_scrollTo($this,to.toString(),{dir:dir,scrollEasing:"mcsEaseInOut"});
							}
						}else if(code===35 || code===36){
							/* End (35), Home (36) */
							if(!$(document.activeElement).is(editables)){
								if(d.overflowed[0] || d.overflowed[1]){
									e.preventDefault();
									e.stopImmediatePropagation();
								}
								if(e.type==="keyup"){
									if(o.axis==="x" || (o.axis==="yx" && d.overflowed[1] && !d.overflowed[0])){
										var dir="x",to=code===35 ? Math.abs(wrapper.width()-mCSB_container.outerWidth(false)) : 0;
									}else{
										var dir="y",to=code===35 ? Math.abs(wrapper.height()-mCSB_container.outerHeight(false)) : 0;
									}
									_scrollTo($this,to.toString(),{dir:dir,scrollEasing:"mcsEaseInOut"});
								}
							}
						}
						break;
				}
				function _seq(a,c){
					seq.type=o.keyboard.scrollType;
					seq.scrollAmount=o.keyboard.scrollAmount;
					if(seq.type==="stepped" && d.tweenRunning){return;}
					_sequentialScroll($this,a,c);
				}
			}
		},
		/* -------------------- */
		
		
		/* scrolls content sequentially (used when scrolling via buttons, keyboard arrows etc.) */
		_sequentialScroll=function(el,action,trigger,e,s){
			var d=el.data(pluginPfx),o=d.opt,seq=d.sequential,
				mCSB_container=$("#mCSB_"+d.idx+"_container"),
				once=seq.type==="stepped" ? true : false,
				steplessSpeed=o.scrollInertia < 26 ? 26 : o.scrollInertia, /* 26/1.5=17 */
				steppedSpeed=o.scrollInertia < 1 ? 17 : o.scrollInertia;
			switch(action){
				case "on":
					seq.dir=[
						(trigger===classes[16] || trigger===classes[15] || trigger===39 || trigger===37 ? "x" : "y"),
						(trigger===classes[13] || trigger===classes[15] || trigger===38 || trigger===37 ? -1 : 1)
					];
					_stop(el);
					if(_isNumeric(trigger) && seq.type==="stepped"){return;}
					_on(once);
					break;
				case "off":
					_off();
					if(once || (d.tweenRunning && seq.dir)){
						_on(true);
					}
					break;
			}
			
			/* starts sequence */
			function _on(once){
				if(o.snapAmount){seq.scrollAmount=!(o.snapAmount instanceof Array) ? o.snapAmount : seq.dir[0]==="x" ? o.snapAmount[1] : o.snapAmount[0];} /* scrolling snapping */
				var c=seq.type!=="stepped", /* continuous scrolling */
					t=s ? s : !once ? 1000/60 : c ? steplessSpeed/1.5 : steppedSpeed, /* timer */
					m=!once ? 2.5 : c ? 7.5 : 40, /* multiplier */
					contentPos=[Math.abs(mCSB_container[0].offsetTop),Math.abs(mCSB_container[0].offsetLeft)],
					ratio=[d.scrollRatio.y>10 ? 10 : d.scrollRatio.y,d.scrollRatio.x>10 ? 10 : d.scrollRatio.x],
					amount=seq.dir[0]==="x" ? contentPos[1]+(seq.dir[1]*(ratio[1]*m)) : contentPos[0]+(seq.dir[1]*(ratio[0]*m)),
					px=seq.dir[0]==="x" ? contentPos[1]+(seq.dir[1]*parseInt(seq.scrollAmount)) : contentPos[0]+(seq.dir[1]*parseInt(seq.scrollAmount)),
					to=seq.scrollAmount!=="auto" ? px : amount,
					easing=e ? e : !once ? "mcsLinear" : c ? "mcsLinearOut" : "mcsEaseInOut",
					onComplete=!once ? false : true;
				if(once && t<17){
					to=seq.dir[0]==="x" ? contentPos[1] : contentPos[0];
				}
				_scrollTo(el,to.toString(),{dir:seq.dir[0],scrollEasing:easing,dur:t,onComplete:onComplete});
				if(once){
					seq.dir=false;
					return;
				}
				clearTimeout(seq.step);
				seq.step=setTimeout(function(){
					_on();
				},t);
			}
			/* stops sequence */
			function _off(){
				clearTimeout(seq.step);
				_delete(seq,"step");
				_stop(el);
			}
		},
		/* -------------------- */
		
		
		/* returns a yx array from value */
		_arr=function(val){
			var o=$(this).data(pluginPfx).opt,vals=[];
			if(typeof val==="function"){val=val();} /* check if the value is a single anonymous function */
			/* check if value is object or array, its length and create an array with yx values */
			if(!(val instanceof Array)){ /* object value (e.g. {y:"100",x:"100"}, 100 etc.) */
				vals[0]=val.y ? val.y : val.x || o.axis==="x" ? null : val;
				vals[1]=val.x ? val.x : val.y || o.axis==="y" ? null : val;
			}else{ /* array value (e.g. [100,100]) */
				vals=val.length>1 ? [val[0],val[1]] : o.axis==="x" ? [null,val[0]] : [val[0],null];
			}
			/* check if array values are anonymous functions */
			if(typeof vals[0]==="function"){vals[0]=vals[0]();}
			if(typeof vals[1]==="function"){vals[1]=vals[1]();}
			return vals;
		},
		/* -------------------- */
		
		
		/* translates values (e.g. "top", 100, "100px", "#id") to actual scroll-to positions */
		_to=function(val,dir){
			if(val==null || typeof val=="undefined"){return;}
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
				mCSB_container=$("#mCSB_"+d.idx+"_container"),
				wrapper=mCSB_container.parent(),
				t=typeof val;
			if(!dir){dir=o.axis==="x" ? "x" : "y";}
			var contentLength=dir==="x" ? mCSB_container.outerWidth(false) : mCSB_container.outerHeight(false),
				contentPos=dir==="x" ? mCSB_container[0].offsetLeft : mCSB_container[0].offsetTop,
				cssProp=dir==="x" ? "left" : "top";
			switch(t){
				case "function": /* this currently is not used. Consider removing it */
					return val();
					break;
				case "object": /* js/jquery object */
					var obj=val.jquery ? val : $(val);
					if(!obj.length){return;}
					return dir==="x" ? _childPos(obj)[1] : _childPos(obj)[0];
					break;
				case "string": case "number":
					if(_isNumeric(val)){ /* numeric value */
						return Math.abs(val);
					}else if(val.indexOf("%")!==-1){ /* percentage value */
						return Math.abs(contentLength*parseInt(val)/100);
					}else if(val.indexOf("-=")!==-1){ /* decrease value */
						return Math.abs(contentPos-parseInt(val.split("-=")[1]));
					}else if(val.indexOf("+=")!==-1){ /* inrease value */
						var p=(contentPos+parseInt(val.split("+=")[1]));
						return p>=0 ? 0 : Math.abs(p);
					}else if(val.indexOf("px")!==-1 && _isNumeric(val.split("px")[0])){ /* pixels string value (e.g. "100px") */
						return Math.abs(val.split("px")[0]);
					}else{
						if(val==="top" || val==="left"){ /* special strings */
							return 0;
						}else if(val==="bottom"){
							return Math.abs(wrapper.height()-mCSB_container.outerHeight(false));
						}else if(val==="right"){
							return Math.abs(wrapper.width()-mCSB_container.outerWidth(false));
						}else if(val==="first" || val==="last"){
							var obj=mCSB_container.find(":"+val);
							return dir==="x" ? _childPos(obj)[1] : _childPos(obj)[0];
						}else{
							if($(val).length){ /* jquery selector */
								return dir==="x" ? _childPos($(val))[1] : _childPos($(val))[0];
							}else{ /* other values (e.g. "100em") */
								mCSB_container.css(cssProp,val);
								methods.update.call(null,$this[0]);
								return;
							}
						}
					}
					break;
			}
		},
		/* -------------------- */
		
		
		/* calls the update method automatically */
		_autoUpdate=function(rem){
			var $this=$(this),d=$this.data(pluginPfx),o=d.opt,
				mCSB_container=$("#mCSB_"+d.idx+"_container");
			if(rem){
				/* 
				removes autoUpdate timer 
				usage: _autoUpdate.call(this,"remove");
				*/
				clearTimeout(mCSB_container[0].autoUpdate);
				_delete(mCSB_container[0],"autoUpdate");
				return;
			}
			upd();
			function upd(){
				clearTimeout(mCSB_container[0].autoUpdate);
				if($this.parents("html").length===0){
					/* check element in dom tree */
					$this=null;
					return;
				}
				mCSB_container[0].autoUpdate=setTimeout(function(){
					/* update on specific selector(s) length and size change */
					if(o.advanced.updateOnSelectorChange){
						d.poll.change.n=sizesSum();
						if(d.poll.change.n!==d.poll.change.o){
							d.poll.change.o=d.poll.change.n;
							doUpd(3);
							return;
						}
					}
					/* update on main element and scrollbar size changes */
					if(o.advanced.updateOnContentResize){
						d.poll.size.n=$this[0].scrollHeight+$this[0].scrollWidth+mCSB_container[0].offsetHeight+$this[0].offsetHeight+$this[0].offsetWidth;
						if(d.poll.size.n!==d.poll.size.o){
							d.poll.size.o=d.poll.size.n;
							doUpd(1);
							return;
						}
					}
					/* update on image load */
					if(o.advanced.updateOnImageLoad){
						if(!(o.advanced.updateOnImageLoad==="auto" && o.axis==="y")){ //by default, it doesn't run on vertical content
							d.poll.img.n=mCSB_container.find("img").length;
							if(d.poll.img.n!==d.poll.img.o){
								d.poll.img.o=d.poll.img.n;
								mCSB_container.find("img").each(function(){
									imgLoader(this);
								});
								return;
							}
						}
					}
					if(o.advanced.updateOnSelectorChange || o.advanced.updateOnContentResize || o.advanced.updateOnImageLoad){upd();}
				},o.advanced.autoUpdateTimeout);
			}
			/* a tiny image loader */
			function imgLoader(el){
				if($(el).hasClass(classes[2])){doUpd(); return;}
				var img=new Image();
				function createDelegate(contextObject,delegateMethod){
					return function(){return delegateMethod.apply(contextObject,arguments);}
				}
				function imgOnLoad(){
					this.onload=null;
					$(el).addClass(classes[2]);
					doUpd(2);
				}
				img.onload=createDelegate(img,imgOnLoad);
				img.src=el.src;
			}
			/* returns the total height and width sum of all elements matching the selector */
			function sizesSum(){
				if(o.advanced.updateOnSelectorChange===true){o.advanced.updateOnSelectorChange="*";}
				var total=0,sel=mCSB_container.find(o.advanced.updateOnSelectorChange);
				if(o.advanced.updateOnSelectorChange && sel.length>0){sel.each(function(){total+=this.offsetHeight+this.offsetWidth;});}
				return total;
			}
			/* calls the update method */
			function doUpd(cb){
				clearTimeout(mCSB_container[0].autoUpdate);
				methods.update.call(null,$this[0],cb);
			}
		},
		/* -------------------- */
		
		
		/* snaps scrolling to a multiple of a pixels number */
		_snapAmount=function(to,amount,offset){
			return (Math.round(to/amount)*amount-offset); 
		},
		/* -------------------- */
		
		
		/* stops content and scrollbar animations */
		_stop=function(el){
			var d=el.data(pluginPfx),
				sel=$("#mCSB_"+d.idx+"_container,#mCSB_"+d.idx+"_container_wrapper,#mCSB_"+d.idx+"_dragger_vertical,#mCSB_"+d.idx+"_dragger_horizontal");
			sel.each(function(){
				_stopTween.call(this);
			});
		},
		/* -------------------- */
		
		
		/* 
		ANIMATES CONTENT 
		This is where the actual scrolling happens
		*/
		_scrollTo=function(el,to,options){
			var d=el.data(pluginPfx),o=d.opt,
				defaults={
					trigger:"internal",
					dir:"y",
					scrollEasing:"mcsEaseOut",
					drag:false,
					dur:o.scrollInertia,
					overwrite:"all",
					callbacks:true,
					onStart:true,
					onUpdate:true,
					onComplete:true
				},
				options=$.extend(defaults,options),
				dur=[options.dur,(options.drag ? 0 : options.dur)],
				mCustomScrollBox=$("#mCSB_"+d.idx),
				mCSB_container=$("#mCSB_"+d.idx+"_container"),
				wrapper=mCSB_container.parent(),
				totalScrollOffsets=o.callbacks.onTotalScrollOffset ? _arr.call(el,o.callbacks.onTotalScrollOffset) : [0,0],
				totalScrollBackOffsets=o.callbacks.onTotalScrollBackOffset ? _arr.call(el,o.callbacks.onTotalScrollBackOffset) : [0,0];
			d.trigger=options.trigger;
			if(wrapper.scrollTop()!==0 || wrapper.scrollLeft()!==0){ /* always reset scrollTop/Left */
				$(".mCSB_"+d.idx+"_scrollbar").css("visibility","visible");
				wrapper.scrollTop(0).scrollLeft(0);
			}
			if(to==="_resetY" && !d.contentReset.y){
				/* callbacks: onOverflowYNone */
				if(_cb("onOverflowYNone")){o.callbacks.onOverflowYNone.call(el[0]);}
				d.contentReset.y=1;
			}
			if(to==="_resetX" && !d.contentReset.x){
				/* callbacks: onOverflowXNone */
				if(_cb("onOverflowXNone")){o.callbacks.onOverflowXNone.call(el[0]);}
				d.contentReset.x=1;
			}
			if(to==="_resetY" || to==="_resetX"){return;}
			if((d.contentReset.y || !el[0].mcs) && d.overflowed[0]){
				/* callbacks: onOverflowY */
				if(_cb("onOverflowY")){o.callbacks.onOverflowY.call(el[0]);}
				d.contentReset.x=null;
			}
			if((d.contentReset.x || !el[0].mcs) && d.overflowed[1]){
				/* callbacks: onOverflowX */
				if(_cb("onOverflowX")){o.callbacks.onOverflowX.call(el[0]);}
				d.contentReset.x=null;
			}
			if(o.snapAmount){ /* scrolling snapping */
				var snapAmount=!(o.snapAmount instanceof Array) ? o.snapAmount : options.dir==="x" ? o.snapAmount[1] : o.snapAmount[0];
				to=_snapAmount(to,snapAmount,o.snapOffset);
			}
			switch(options.dir){
				case "x":
					var mCSB_dragger=$("#mCSB_"+d.idx+"_dragger_horizontal"),
						property="left",
						contentPos=mCSB_container[0].offsetLeft,
						limit=[
							mCustomScrollBox.width()-mCSB_container.outerWidth(false),
							mCSB_dragger.parent().width()-mCSB_dragger.width()
						],
						scrollTo=[to,to===0 ? 0 : (to/d.scrollRatio.x)],
						tso=totalScrollOffsets[1],
						tsbo=totalScrollBackOffsets[1],
						totalScrollOffset=tso>0 ? tso/d.scrollRatio.x : 0,
						totalScrollBackOffset=tsbo>0 ? tsbo/d.scrollRatio.x : 0;
					break;
				case "y":
					var mCSB_dragger=$("#mCSB_"+d.idx+"_dragger_vertical"),
						property="top",
						contentPos=mCSB_container[0].offsetTop,
						limit=[
							mCustomScrollBox.height()-mCSB_container.outerHeight(false),
							mCSB_dragger.parent().height()-mCSB_dragger.height()
						],
						scrollTo=[to,to===0 ? 0 : (to/d.scrollRatio.y)],
						tso=totalScrollOffsets[0],
						tsbo=totalScrollBackOffsets[0],
						totalScrollOffset=tso>0 ? tso/d.scrollRatio.y : 0,
						totalScrollBackOffset=tsbo>0 ? tsbo/d.scrollRatio.y : 0;
					break;
			}
			if(scrollTo[1]<0 || (scrollTo[0]===0 && scrollTo[1]===0)){
				scrollTo=[0,0];
			}else if(scrollTo[1]>=limit[1]){
				scrollTo=[limit[0],limit[1]];
			}else{
				scrollTo[0]=-scrollTo[0];
			}
			if(!el[0].mcs){
				_mcs();  /* init mcs object (once) to make it available before callbacks */
				if(_cb("onInit")){o.callbacks.onInit.call(el[0]);} /* callbacks: onInit */
			}
			clearTimeout(mCSB_container[0].onCompleteTimeout);
			_tweenTo(mCSB_dragger[0],property,Math.round(scrollTo[1]),dur[1],options.scrollEasing);
			if(!d.tweenRunning && ((contentPos===0 && scrollTo[0]>=0) || (contentPos===limit[0] && scrollTo[0]<=limit[0]))){return;}
			_tweenTo(mCSB_container[0],property,Math.round(scrollTo[0]),dur[0],options.scrollEasing,options.overwrite,{
				onStart:function(){
					if(options.callbacks && options.onStart && !d.tweenRunning){
						/* callbacks: onScrollStart */
						if(_cb("onScrollStart")){_mcs(); o.callbacks.onScrollStart.call(el[0]);}
						d.tweenRunning=true;
						_onDragClasses(mCSB_dragger);
						d.cbOffsets=_cbOffsets();
					}
				},onUpdate:function(){
					if(options.callbacks && options.onUpdate){
						/* callbacks: whileScrolling */
						if(_cb("whileScrolling")){_mcs(); o.callbacks.whileScrolling.call(el[0]);}
					}
				},onComplete:function(){
					if(options.callbacks && options.onComplete){
						if(o.axis==="yx"){clearTimeout(mCSB_container[0].onCompleteTimeout);}
						var t=mCSB_container[0].idleTimer || 0;
						mCSB_container[0].onCompleteTimeout=setTimeout(function(){
							/* callbacks: onScroll, onTotalScroll, onTotalScrollBack */
							if(_cb("onScroll")){_mcs(); o.callbacks.onScroll.call(el[0]);}
							if(_cb("onTotalScroll") && scrollTo[1]>=limit[1]-totalScrollOffset && d.cbOffsets[0]){_mcs(); o.callbacks.onTotalScroll.call(el[0]);}
							if(_cb("onTotalScrollBack") && scrollTo[1]<=totalScrollBackOffset && d.cbOffsets[1]){_mcs(); o.callbacks.onTotalScrollBack.call(el[0]);}
							d.tweenRunning=false;
							mCSB_container[0].idleTimer=0;
							_onDragClasses(mCSB_dragger,"hide");
						},t);
					}
				}
			});
			/* checks if callback function exists */
			function _cb(cb){
				return d && o.callbacks[cb] && typeof o.callbacks[cb]==="function";
			}
			/* checks whether callback offsets always trigger */
			function _cbOffsets(){
				return [o.callbacks.alwaysTriggerOffsets || contentPos>=limit[0]+tso,o.callbacks.alwaysTriggerOffsets || contentPos<=-tsbo];
			}
			/* 
			populates object with useful values for the user 
			values: 
				content: this.mcs.content
				content top position: this.mcs.top 
				content left position: this.mcs.left 
				dragger top position: this.mcs.draggerTop 
				dragger left position: this.mcs.draggerLeft 
				scrolling y percentage: this.mcs.topPct 
				scrolling x percentage: this.mcs.leftPct 
				scrolling direction: this.mcs.direction
			*/
			function _mcs(){
				var cp=[mCSB_container[0].offsetTop,mCSB_container[0].offsetLeft], /* content position */
					dp=[mCSB_dragger[0].offsetTop,mCSB_dragger[0].offsetLeft], /* dragger position */
					cl=[mCSB_container.outerHeight(false),mCSB_container.outerWidth(false)], /* content length */
					pl=[mCustomScrollBox.height(),mCustomScrollBox.width()]; /* content parent length */
				el[0].mcs={
					content:mCSB_container, /* original content wrapper as jquery object */
					top:cp[0],left:cp[1],draggerTop:dp[0],draggerLeft:dp[1],
					topPct:Math.round((100*Math.abs(cp[0]))/(Math.abs(cl[0])-pl[0])),leftPct:Math.round((100*Math.abs(cp[1]))/(Math.abs(cl[1])-pl[1])),
					direction:options.dir
				};
				/* 
				this refers to the original element containing the scrollbar(s)
				usage: this.mcs.top, this.mcs.leftPct etc. 
				*/
			}
		},
		/* -------------------- */
		
		
		/* 
		CUSTOM JAVASCRIPT ANIMATION TWEEN 
		Lighter and faster than jquery animate() and css transitions 
		Animates top/left properties and includes easings 
		*/
		_tweenTo=function(el,prop,to,duration,easing,overwrite,callbacks){
			if(!el._mTween){el._mTween={top:{},left:{}};}
			var callbacks=callbacks || {},
				onStart=callbacks.onStart || function(){},onUpdate=callbacks.onUpdate || function(){},onComplete=callbacks.onComplete || function(){},
				startTime=_getTime(),_delay,progress=0,from=el.offsetTop,elStyle=el.style,_request,tobj=el._mTween[prop];
			if(prop==="left"){from=el.offsetLeft;}
			var diff=to-from;
			tobj.stop=0;
			if(overwrite!=="none"){_cancelTween();}
			_startTween();
			function _step(){
				if(tobj.stop){return;}
				if(!progress){onStart.call();}
				progress=_getTime()-startTime;
				_tween();
				if(progress>=tobj.time){
					tobj.time=(progress>tobj.time) ? progress+_delay-(progress-tobj.time) : progress+_delay-1;
					if(tobj.time<progress+1){tobj.time=progress+1;}
				}
				if(tobj.time<duration){tobj.id=_request(_step);}else{onComplete.call();}
			}
			function _tween(){
				if(duration>0){
					tobj.currVal=_ease(tobj.time,from,diff,duration,easing);
					elStyle[prop]=Math.round(tobj.currVal)+"px";
				}else{
					elStyle[prop]=to+"px";
				}
				onUpdate.call();
			}
			function _startTween(){
				_delay=1000/60;
				tobj.time=progress+_delay;
				_request=(!window.requestAnimationFrame) ? function(f){_tween(); return setTimeout(f,0.01);} : window.requestAnimationFrame;
				tobj.id=_request(_step);
			}
			function _cancelTween(){
				if(tobj.id==null){return;}
				if(!window.requestAnimationFrame){clearTimeout(tobj.id);
				}else{window.cancelAnimationFrame(tobj.id);}
				tobj.id=null;
			}
			function _ease(t,b,c,d,type){
				switch(type){
					case "linear": case "mcsLinear":
						return c*t/d + b;
						break;
					case "mcsLinearOut":
						t/=d; t--; return c * Math.sqrt(1 - t*t) + b;
						break;
					case "easeInOutSmooth":
						t/=d/2;
						if(t<1) return c/2*t*t + b;
						t--;
						return -c/2 * (t*(t-2) - 1) + b;
						break;
					case "easeInOutStrong":
						t/=d/2;
						if(t<1) return c/2 * Math.pow( 2, 10 * (t - 1) ) + b;
						t--;
						return c/2 * ( -Math.pow( 2, -10 * t) + 2 ) + b;
						break;
					case "easeInOut": case "mcsEaseInOut":
						t/=d/2;
						if(t<1) return c/2*t*t*t + b;
						t-=2;
						return c/2*(t*t*t + 2) + b;
						break;
					case "easeOutSmooth":
						t/=d; t--;
						return -c * (t*t*t*t - 1) + b;
						break;
					case "easeOutStrong":
						return c * ( -Math.pow( 2, -10 * t/d ) + 1 ) + b;
						break;
					case "easeOut": case "mcsEaseOut": default:
						var ts=(t/=d)*t,tc=ts*t;
						return b+c*(0.499999999999997*tc*ts + -2.5*ts*ts + 5.5*tc + -6.5*ts + 4*t);
				}
			}
		},
		/* -------------------- */
		
		
		/* returns current time */
		_getTime=function(){
			if(window.performance && window.performance.now){
				return window.performance.now();
			}else{
				if(window.performance && window.performance.webkitNow){
					return window.performance.webkitNow();
				}else{
					if(Date.now){return Date.now();}else{return new Date().getTime();}
				}
			}
		},
		/* -------------------- */
		
		
		/* stops a tween */
		_stopTween=function(){
			var el=this;
			if(!el._mTween){el._mTween={top:{},left:{}};}
			var props=["top","left"];
			for(var i=0; i<props.length; i++){
				var prop=props[i];
				if(el._mTween[prop].id){
					if(!window.requestAnimationFrame){clearTimeout(el._mTween[prop].id);
					}else{window.cancelAnimationFrame(el._mTween[prop].id);}
					el._mTween[prop].id=null;
					el._mTween[prop].stop=1;
				}
			}
		},
		/* -------------------- */
		
		
		/* deletes a property (avoiding the exception thrown by IE) */
		_delete=function(c,m){
			try{delete c[m];}catch(e){c[m]=null;}
		},
		/* -------------------- */
		
		
		/* detects left mouse button */
		_mouseBtnLeft=function(e){
			return !(e.which && e.which!==1);
		},
		/* -------------------- */
		
		
		/* detects if pointer type event is touch */
		_pointerTouch=function(e){
			var t=e.originalEvent.pointerType;
			return !(t && t!=="touch" && t!==2);
		},
		/* -------------------- */
		
		
		/* checks if value is numeric */
		_isNumeric=function(val){
			return !isNaN(parseFloat(val)) && isFinite(val);
		},
		/* -------------------- */
		
		
		/* returns element position according to content */
		_childPos=function(el){
			var p=el.parents(".mCSB_container");
			return [el.offset().top-p.offset().top,el.offset().left-p.offset().left];
		},
		/* -------------------- */
		
		
		/* checks if browser tab is hidden/inactive via Page Visibility API */
		_isTabHidden=function(){
			var prop=_getHiddenProp();
			if(!prop) return false;
			return document[prop];
			function _getHiddenProp(){
				var pfx=["webkit","moz","ms","o"];
				if("hidden" in document) return "hidden"; //natively supported
				for(var i=0; i<pfx.length; i++){ //prefixed
				    if((pfx[i]+"Hidden") in document) 
				        return pfx[i]+"Hidden";
				}
				return null; //not supported
			}
		};
		/* -------------------- */
		
	
	
	
	
	/* 
	----------------------------------------
	PLUGIN SETUP 
	----------------------------------------
	*/
	
	/* plugin constructor functions */
	$.fn[pluginNS]=function(method){ /* usage: $(selector).mCustomScrollbar(); */
		if(methods[method]){
			return methods[method].apply(this,Array.prototype.slice.call(arguments,1));
		}else if(typeof method==="object" || !method){
			return methods.init.apply(this,arguments);
		}else{
			$.error("Method "+method+" does not exist");
		}
	};
	$[pluginNS]=function(method){ /* usage: $.mCustomScrollbar(); */
		if(methods[method]){
			return methods[method].apply(this,Array.prototype.slice.call(arguments,1));
		}else if(typeof method==="object" || !method){
			return methods.init.apply(this,arguments);
		}else{
			$.error("Method "+method+" does not exist");
		}
	};
	
	/* 
	allow setting plugin default options. 
	usage: $.mCustomScrollbar.defaults.scrollInertia=500; 
	to apply any changed default options on default selectors (below), use inside document ready fn 
	e.g.: $(document).ready(function(){ $.mCustomScrollbar.defaults.scrollInertia=500; });
	*/
	$[pluginNS].defaults=defaults;
	
	/* 
	add window object (window.mCustomScrollbar) 
	usage: if(window.mCustomScrollbar){console.log("custom scrollbar plugin loaded");}
	*/
	window[pluginNS]=true;
	
	$(window).load(function(){
		
		$(defaultSelector)[pluginNS](); /* add scrollbars automatically on default selector */
		
		/* extend jQuery expressions */
		$.extend($.expr[":"],{
			/* checks if element is within scrollable viewport */
			mcsInView:$.expr[":"].mcsInView || function(el){
				var $el=$(el),content=$el.parents(".mCSB_container"),wrapper,cPos;
				if(!content.length){return;}
				wrapper=content.parent();
				cPos=[content[0].offsetTop,content[0].offsetLeft];
				return 	cPos[0]+_childPos($el)[0]>=0 && cPos[0]+_childPos($el)[0]<wrapper.height()-$el.outerHeight(false) && 
						cPos[1]+_childPos($el)[1]>=0 && cPos[1]+_childPos($el)[1]<wrapper.width()-$el.outerWidth(false);
			},
			/* checks if element is overflowed having visible scrollbar(s) */
			mcsOverflow:$.expr[":"].mcsOverflow || function(el){
				var d=$(el).data(pluginPfx);
				if(!d){return;}
				return d.overflowed[0] || d.overflowed[1];
			}
		});
	
	});

}))}));;
/*
 * jQuery Iframe Transport Plugin 1.8.3
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2011, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

/* global define, require, window, document */

(function (factory) {
    'use strict';
    if (typeof define === 'function' && define.amd) {
        // Register as an anonymous AMD module:
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS:
        factory(require('jquery'));
    } else {
        // Browser globals:
        factory(window.jQuery);
    }
}(function ($) {
    'use strict';

    // Helper variable to create unique names for the transport iframes:
    var counter = 0;

    // The iframe transport accepts four additional options:
    // options.fileInput: a jQuery collection of file input fields
    // options.paramName: the parameter name for the file form data,
    //  overrides the name property of the file input field(s),
    //  can be a string or an array of strings.
    // options.formData: an array of objects with name and value properties,
    //  equivalent to the return data of .serializeArray(), e.g.:
    //  [{name: 'a', value: 1}, {name: 'b', value: 2}]
    // options.initialIframeSrc: the URL of the initial iframe src,
    //  by default set to "javascript:false;"
    $.ajaxTransport('iframe', function (options) {
        if (options.async) {
            // javascript:false as initial iframe src
            // prevents warning popups on HTTPS in IE6:
            /*jshint scripturl: true */
            var initialIframeSrc = options.initialIframeSrc || 'javascript:false;',
            /*jshint scripturl: false */
                form,
                iframe,
                addParamChar;
            return {
                send: function (_, completeCallback) {
                    form = $('<form style="display:none;"></form>');
                    form.attr('accept-charset', options.formAcceptCharset);
                    addParamChar = /\?/.test(options.url) ? '&' : '?';
                    // XDomainRequest only supports GET and POST:
                    if (options.type === 'DELETE') {
                        options.url = options.url + addParamChar + '_method=DELETE';
                        options.type = 'POST';
                    } else if (options.type === 'PUT') {
                        options.url = options.url + addParamChar + '_method=PUT';
                        options.type = 'POST';
                    } else if (options.type === 'PATCH') {
                        options.url = options.url + addParamChar + '_method=PATCH';
                        options.type = 'POST';
                    }
                    // IE versions below IE8 cannot set the name property of
                    // elements that have already been added to the DOM,
                    // so we set the name along with the iframe HTML markup:
                    counter += 1;
                    iframe = $(
                        '<iframe src="' + initialIframeSrc +
                            '" name="iframe-transport-' + counter + '"></iframe>'
                    ).bind('load', function () {
                        var fileInputClones,
                            paramNames = $.isArray(options.paramName) ?
                                    options.paramName : [options.paramName];
                        iframe
                            .unbind('load')
                            .bind('load', function () {
                                var response;
                                // Wrap in a try/catch block to catch exceptions thrown
                                // when trying to access cross-domain iframe contents:
                                try {
                                    response = iframe.contents();
                                    // Google Chrome and Firefox do not throw an
                                    // exception when calling iframe.contents() on
                                    // cross-domain requests, so we unify the response:
                                    if (!response.length || !response[0].firstChild) {
                                        throw new Error();
                                    }
                                } catch (e) {
                                    response = undefined;
                                }
                                // The complete callback returns the
                                // iframe content document as response object:
                                completeCallback(
                                    200,
                                    'success',
                                    {'iframe': response}
                                );
                                // Fix for IE endless progress bar activity bug
                                // (happens on form submits to iframe targets):
                                $('<iframe src="' + initialIframeSrc + '"></iframe>')
                                    .appendTo(form);
                                window.setTimeout(function () {
                                    // Removing the form in a setTimeout call
                                    // allows Chrome's developer tools to display
                                    // the response result
                                    form.remove();
                                }, 0);
                            });
                        form
                            .prop('target', iframe.prop('name'))
                            .prop('action', options.url)
                            .prop('method', options.type);
                        if (options.formData) {
                            $.each(options.formData, function (index, field) {
                                $('<input type="hidden"/>')
                                    .prop('name', field.name)
                                    .val(field.value)
                                    .appendTo(form);
                            });
                        }
                        if (options.fileInput && options.fileInput.length &&
                                options.type === 'POST') {
                            fileInputClones = options.fileInput.clone();
                            // Insert a clone for each file input field:
                            options.fileInput.after(function (index) {
                                return fileInputClones[index];
                            });
                            if (options.paramName) {
                                options.fileInput.each(function (index) {
                                    $(this).prop(
                                        'name',
                                        paramNames[index] || options.paramName
                                    );
                                });
                            }
                            // Appending the file input fields to the hidden form
                            // removes them from their original location:
                            form
                                .append(options.fileInput)
                                .prop('enctype', 'multipart/form-data')
                                // enctype must be set as encoding for IE:
                                .prop('encoding', 'multipart/form-data');
                            // Remove the HTML5 form attribute from the input(s):
                            options.fileInput.removeAttr('form');
                        }
                        form.submit();
                        // Insert the file input fields at their original location
                        // by replacing the clones with the originals:
                        if (fileInputClones && fileInputClones.length) {
                            options.fileInput.each(function (index, input) {
                                var clone = $(fileInputClones[index]);
                                // Restore the original name and form properties:
                                $(input)
                                    .prop('name', clone.prop('name'))
                                    .attr('form', clone.attr('form'));
                                clone.replaceWith(input);
                            });
                        }
                    });
                    form.append(iframe).appendTo(document.body);
                },
                abort: function () {
                    if (iframe) {
                        // javascript:false as iframe src aborts the request
                        // and prevents warning popups on HTTPS in IE6.
                        // concat is used to avoid the "Script URL" JSLint error:
                        iframe
                            .unbind('load')
                            .prop('src', initialIframeSrc);
                    }
                    if (form) {
                        form.remove();
                    }
                }
            };
        }
    });

    // The iframe transport returns the iframe content document as response.
    // The following adds converters from iframe to text, json, html, xml
    // and script.
    // Please note that the Content-Type for JSON responses has to be text/plain
    // or text/html, if the browser doesn't include application/json in the
    // Accept header, else IE will show a download dialog.
    // The Content-Type for XML responses on the other hand has to be always
    // application/xml or text/xml, so IE properly parses the XML response.
    // See also
    // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation
    $.ajaxSetup({
        converters: {
            'iframe text': function (iframe) {
                return iframe && $(iframe[0].body).text();
            },
            'iframe json': function (iframe) {
                return iframe && $.parseJSON($(iframe[0].body).text());
            },
            'iframe html': function (iframe) {
                return iframe && $(iframe[0].body).html();
            },
            'iframe xml': function (iframe) {
                var xmlDoc = iframe && iframe[0];
                return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc :
                        $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) ||
                            $(xmlDoc.body).html());
            },
            'iframe script': function (iframe) {
                return iframe && $.globalEval($(iframe[0].body).text());
            }
        }
    });

}));
;
/*
 * jQuery File Upload Plugin 5.42.2
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

/* jshint nomen:false */
/* global define, require, window, document, location, Blob, FormData */

(function (factory) {
    'use strict';
    if (typeof define === 'function' && define.amd) {
        // Register as an anonymous AMD module:
        define([
            'jquery',
            'jquery.ui.widget'
        ], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS:
        factory(
            require('jquery'),
            require('./vendor/jquery.ui.widget')
        );
    } else {
        // Browser globals:
        factory(window.jQuery);
    }
}(function ($) {
    'use strict';

    // Detect file input support, based on
    // http://viljamis.com/blog/2012/file-upload-support-on-mobile/
    $.support.fileInput = !(new RegExp(
        // Handle devices which give false positives for the feature detection:
        '(Android (1\\.[0156]|2\\.[01]))' +
            '|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' +
            '|(w(eb)?OSBrowser)|(webOS)' +
            '|(Kindle/(1\\.0|2\\.[05]|3\\.0))'
    ).test(window.navigator.userAgent) ||
        // Feature detection for all other devices:
        $('<input type="file">').prop('disabled'));

    // The FileReader API is not actually used, but works as feature detection,
    // as some Safari versions (5?) support XHR file uploads via the FormData API,
    // but not non-multipart XHR file uploads.
    // window.XMLHttpRequestUpload is not available on IE10, so we check for
    // window.ProgressEvent instead to detect XHR2 file upload capability:
    $.support.xhrFileUpload = !!(window.ProgressEvent && window.FileReader);
    $.support.xhrFormDataFileUpload = !!window.FormData;

    // Detect support for Blob slicing (required for chunked uploads):
    $.support.blobSlice = window.Blob && (Blob.prototype.slice ||
        Blob.prototype.webkitSlice || Blob.prototype.mozSlice);

    // Helper function to create drag handlers for dragover/dragenter/dragleave:
    function getDragHandler(type) {
        var isDragOver = type === 'dragover';
        return function (e) {
            e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
            var dataTransfer = e.dataTransfer;
            if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1 &&
                    this._trigger(
                        type,
                        $.Event(type, {delegatedEvent: e})
                    ) !== false) {
                e.preventDefault();
                if (isDragOver) {
                    dataTransfer.dropEffect = 'copy';
                }
            }
        };
    }

    // The fileupload widget listens for change events on file input fields defined
    // via fileInput setting and paste or drop events of the given dropZone.
    // In addition to the default jQuery Widget methods, the fileupload widget
    // exposes the "add" and "send" methods, to add or directly send files using
    // the fileupload API.
    // By default, files added via file input selection, paste, drag & drop or
    // "add" method are uploaded immediately, but it is possible to override
    // the "add" callback option to queue file uploads.
    $.widget('blueimp.fileupload', {

        options: {
            // The drop target element(s), by the default the complete document.
            // Set to null to disable drag & drop support:
            dropZone: $(document),
            // The paste target element(s), by the default undefined.
            // Set to a DOM node or jQuery object to enable file pasting:
            pasteZone: undefined,
            // The file input field(s), that are listened to for change events.
            // If undefined, it is set to the file input fields inside
            // of the widget element on plugin initialization.
            // Set to null to disable the change listener.
            fileInput: undefined,
            // By default, the file input field is replaced with a clone after
            // each input field change event. This is required for iframe transport
            // queues and allows change events to be fired for the same file
            // selection, but can be disabled by setting the following option to false:
            replaceFileInput: true,
            // The parameter name for the file form data (the request argument name).
            // If undefined or empty, the name property of the file input field is
            // used, or "files[]" if the file input name property is also empty,
            // can be a string or an array of strings:
            paramName: undefined,
            // By default, each file of a selection is uploaded using an individual
            // request for XHR type uploads. Set to false to upload file
            // selections in one request each:
            singleFileUploads: true,
            // To limit the number of files uploaded with one XHR request,
            // set the following option to an integer greater than 0:
            limitMultiFileUploads: undefined,
            // The following option limits the number of files uploaded with one
            // XHR request to keep the request size under or equal to the defined
            // limit in bytes:
            limitMultiFileUploadSize: undefined,
            // Multipart file uploads add a number of bytes to each uploaded file,
            // therefore the following option adds an overhead for each file used
            // in the limitMultiFileUploadSize configuration:
            limitMultiFileUploadSizeOverhead: 512,
            // Set the following option to true to issue all file upload requests
            // in a sequential order:
            sequentialUploads: false,
            // To limit the number of concurrent uploads,
            // set the following option to an integer greater than 0:
            limitConcurrentUploads: undefined,
            // Set the following option to true to force iframe transport uploads:
            forceIframeTransport: false,
            // Set the following option to the location of a redirect url on the
            // origin server, for cross-domain iframe transport uploads:
            redirect: undefined,
            // The parameter name for the redirect url, sent as part of the form
            // data and set to 'redirect' if this option is empty:
            redirectParamName: undefined,
            // Set the following option to the location of a postMessage window,
            // to enable postMessage transport uploads:
            postMessage: undefined,
            // By default, XHR file uploads are sent as multipart/form-data.
            // The iframe transport is always using multipart/form-data.
            // Set to false to enable non-multipart XHR uploads:
            multipart: true,
            // To upload large files in smaller chunks, set the following option
            // to a preferred maximum chunk size. If set to 0, null or undefined,
            // or the browser does not support the required Blob API, files will
            // be uploaded as a whole.
            maxChunkSize: undefined,
            // When a non-multipart upload or a chunked multipart upload has been
            // aborted, this option can be used to resume the upload by setting
            // it to the size of the already uploaded bytes. This option is most
            // useful when modifying the options object inside of the "add" or
            // "send" callbacks, as the options are cloned for each file upload.
            uploadedBytes: undefined,
            // By default, failed (abort or error) file uploads are removed from the
            // global progress calculation. Set the following option to false to
            // prevent recalculating the global progress data:
            recalculateProgress: true,
            // Interval in milliseconds to calculate and trigger progress events:
            progressInterval: 100,
            // Interval in milliseconds to calculate progress bitrate:
            bitrateInterval: 500,
            // By default, uploads are started automatically when adding files:
            autoUpload: true,

            // Error and info messages:
            messages: {
                uploadedBytes: 'Uploaded bytes exceed file size'
            },

            // Translation function, gets the message key to be translated
            // and an object with context specific data as arguments:
            i18n: function (message, context) {
                message = this.messages[message] || message.toString();
                if (context) {
                    $.each(context, function (key, value) {
                        message = message.replace('{' + key + '}', value);
                    });
                }
                return message;
            },

            // Additional form data to be sent along with the file uploads can be set
            // using this option, which accepts an array of objects with name and
            // value properties, a function returning such an array, a FormData
            // object (for XHR file uploads), or a simple object.
            // The form of the first fileInput is given as parameter to the function:
            formData: function (form) {
                return form.serializeArray();
            },

            // The add callback is invoked as soon as files are added to the fileupload
            // widget (via file input selection, drag & drop, paste or add API call).
            // If the singleFileUploads option is enabled, this callback will be
            // called once for each file in the selection for XHR file uploads, else
            // once for each file selection.
            //
            // The upload starts when the submit method is invoked on the data parameter.
            // The data object contains a files property holding the added files
            // and allows you to override plugin options as well as define ajax settings.
            //
            // Listeners for this callback can also be bound the following way:
            // .bind('fileuploadadd', func);
            //
            // data.submit() returns a Promise object and allows to attach additional
            // handlers using jQuery's Deferred callbacks:
            // data.submit().done(func).fail(func).always(func);
            add: function (e, data) {
                if (e.isDefaultPrevented()) {
                    return false;
                }
                if (data.autoUpload || (data.autoUpload !== false &&
                        $(this).fileupload('option', 'autoUpload'))) {
                    data.process().done(function () {
                        data.submit();
                    });
                }
            },

            // Other callbacks:

            // Callback for the submit event of each file upload:
            // submit: function (e, data) {}, // .bind('fileuploadsubmit', func);

            // Callback for the start of each file upload request:
            // send: function (e, data) {}, // .bind('fileuploadsend', func);

            // Callback for successful uploads:
            // done: function (e, data) {}, // .bind('fileuploaddone', func);

            // Callback for failed (abort or error) uploads:
            // fail: function (e, data) {}, // .bind('fileuploadfail', func);

            // Callback for completed (success, abort or error) requests:
            // always: function (e, data) {}, // .bind('fileuploadalways', func);

            // Callback for upload progress events:
            // progress: function (e, data) {}, // .bind('fileuploadprogress', func);

            // Callback for global upload progress events:
            // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);

            // Callback for uploads start, equivalent to the global ajaxStart event:
            // start: function (e) {}, // .bind('fileuploadstart', func);

            // Callback for uploads stop, equivalent to the global ajaxStop event:
            // stop: function (e) {}, // .bind('fileuploadstop', func);

            // Callback for change events of the fileInput(s):
            // change: function (e, data) {}, // .bind('fileuploadchange', func);

            // Callback for paste events to the pasteZone(s):
            // paste: function (e, data) {}, // .bind('fileuploadpaste', func);

            // Callback for drop events of the dropZone(s):
            // drop: function (e, data) {}, // .bind('fileuploaddrop', func);

            // Callback for dragover events of the dropZone(s):
            // dragover: function (e) {}, // .bind('fileuploaddragover', func);

            // Callback for the start of each chunk upload request:
            // chunksend: function (e, data) {}, // .bind('fileuploadchunksend', func);

            // Callback for successful chunk uploads:
            // chunkdone: function (e, data) {}, // .bind('fileuploadchunkdone', func);

            // Callback for failed (abort or error) chunk uploads:
            // chunkfail: function (e, data) {}, // .bind('fileuploadchunkfail', func);

            // Callback for completed (success, abort or error) chunk upload requests:
            // chunkalways: function (e, data) {}, // .bind('fileuploadchunkalways', func);

            // The plugin options are used as settings object for the ajax calls.
            // The following are jQuery ajax settings required for the file uploads:
            processData: false,
            contentType: false,
            cache: false
        },

        // A list of options that require reinitializing event listeners and/or
        // special initialization code:
        _specialOptions: [
            'fileInput',
            'dropZone',
            'pasteZone',
            'multipart',
            'forceIframeTransport'
        ],

        _blobSlice: $.support.blobSlice && function () {
            var slice = this.slice || this.webkitSlice || this.mozSlice;
            return slice.apply(this, arguments);
        },

        _BitrateTimer: function () {
            this.timestamp = ((Date.now) ? Date.now() : (new Date()).getTime());
            this.loaded = 0;
            this.bitrate = 0;
            this.getBitrate = function (now, loaded, interval) {
                var timeDiff = now - this.timestamp;
                if (!this.bitrate || !interval || timeDiff > interval) {
                    this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
                    this.loaded = loaded;
                    this.timestamp = now;
                }
                return this.bitrate;
            };
        },

        _isXHRUpload: function (options) {
            return !options.forceIframeTransport &&
                ((!options.multipart && $.support.xhrFileUpload) ||
                $.support.xhrFormDataFileUpload);
        },

        _getFormData: function (options) {
            var formData;
            if ($.type(options.formData) === 'function') {
                return options.formData(options.form);
            }
            if ($.isArray(options.formData)) {
                return options.formData;
            }
            if ($.type(options.formData) === 'object') {
                formData = [];
                $.each(options.formData, function (name, value) {
                    formData.push({name: name, value: value});
                });
                return formData;
            }
            return [];
        },

        _getTotal: function (files) {
            var total = 0;
            $.each(files, function (index, file) {
                total += file.size || 1;
            });
            return total;
        },

        _initProgressObject: function (obj) {
            var progress = {
                loaded: 0,
                total: 0,
                bitrate: 0
            };
            if (obj._progress) {
                $.extend(obj._progress, progress);
            } else {
                obj._progress = progress;
            }
        },

        _initResponseObject: function (obj) {
            var prop;
            if (obj._response) {
                for (prop in obj._response) {
                    if (obj._response.hasOwnProperty(prop)) {
                        delete obj._response[prop];
                    }
                }
            } else {
                obj._response = {};
            }
        },

        _onProgress: function (e, data) {
            if (e.lengthComputable) {
                var now = ((Date.now) ? Date.now() : (new Date()).getTime()),
                    loaded;
                if (data._time && data.progressInterval &&
                        (now - data._time < data.progressInterval) &&
                        e.loaded !== e.total) {
                    return;
                }
                data._time = now;
                loaded = Math.floor(
                    e.loaded / e.total * (data.chunkSize || data._progress.total)
                ) + (data.uploadedBytes || 0);
                // Add the difference from the previously loaded state
                // to the global loaded counter:
                this._progress.loaded += (loaded - data._progress.loaded);
                this._progress.bitrate = this._bitrateTimer.getBitrate(
                    now,
                    this._progress.loaded,
                    data.bitrateInterval
                );
                data._progress.loaded = data.loaded = loaded;
                data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate(
                    now,
                    loaded,
                    data.bitrateInterval
                );
                // Trigger a custom progress event with a total data property set
                // to the file size(s) of the current upload and a loaded data
                // property calculated accordingly:
                this._trigger(
                    'progress',
                    $.Event('progress', {delegatedEvent: e}),
                    data
                );
                // Trigger a global progress event for all current file uploads,
                // including ajax calls queued for sequential file uploads:
                this._trigger(
                    'progressall',
                    $.Event('progressall', {delegatedEvent: e}),
                    this._progress
                );
            }
        },

        _initProgressListener: function (options) {
            var that = this,
                xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
            // Accesss to the native XHR object is required to add event listeners
            // for the upload progress event:
            if (xhr.upload) {
                $(xhr.upload).bind('progress', function (e) {
                    var oe = e.originalEvent;
                    // Make sure the progress event properties get copied over:
                    e.lengthComputable = oe.lengthComputable;
                    e.loaded = oe.loaded;
                    e.total = oe.total;
                    that._onProgress(e, options);
                });
                options.xhr = function () {
                    return xhr;
                };
            }
        },

        _isInstanceOf: function (type, obj) {
            // Cross-frame instanceof check
            return Object.prototype.toString.call(obj) === '[object ' + type + ']';
        },

        _initXHRData: function (options) {
            var that = this,
                formData,
                file = options.files[0],
                // Ignore non-multipart setting if not supported:
                multipart = options.multipart || !$.support.xhrFileUpload,
                paramName = $.type(options.paramName) === 'array' ?
                    options.paramName[0] : options.paramName;
            options.headers = $.extend({}, options.headers);
            if (options.contentRange) {
                options.headers['Content-Range'] = options.contentRange;
            }
            if (!multipart || options.blob || !this._isInstanceOf('File', file)) {
                options.headers['Content-Disposition'] = 'attachment; filename="' +
                    encodeURI(file.name) + '"';
            }
            if (!multipart) {
                options.contentType = file.type || 'application/octet-stream';
                options.data = options.blob || file;
            } else if ($.support.xhrFormDataFileUpload) {
                if (options.postMessage) {
                    // window.postMessage does not allow sending FormData
                    // objects, so we just add the File/Blob objects to
                    // the formData array and let the postMessage window
                    // create the FormData object out of this array:
                    formData = this._getFormData(options);
                    if (options.blob) {
                        formData.push({
                            name: paramName,
                            value: options.blob
                        });
                    } else {
                        $.each(options.files, function (index, file) {
                            formData.push({
                                name: ($.type(options.paramName) === 'array' &&
                                    options.paramName[index]) || paramName,
                                value: file
                            });
                        });
                    }
                } else {
                    if (that._isInstanceOf('FormData', options.formData)) {
                        formData = options.formData;
                    } else {
                        formData = new FormData();
                        $.each(this._getFormData(options), function (index, field) {
                            formData.append(field.name, field.value);
                        });
                    }
                    if (options.blob) {
                        formData.append(paramName, options.blob, file.name);
                    } else {
                        $.each(options.files, function (index, file) {
                            // This check allows the tests to run with
                            // dummy objects:
                            if (that._isInstanceOf('File', file) ||
                                    that._isInstanceOf('Blob', file)) {
                                formData.append(
                                    ($.type(options.paramName) === 'array' &&
                                        options.paramName[index]) || paramName,
                                    file,
                                    file.uploadName || file.name
                                );
                            }
                        });
                    }
                }
                options.data = formData;
            }
            // Blob reference is not needed anymore, free memory:
            options.blob = null;
        },

        _initIframeSettings: function (options) {
            var targetHost = $('<a></a>').prop('href', options.url).prop('host');
            // Setting the dataType to iframe enables the iframe transport:
            options.dataType = 'iframe ' + (options.dataType || '');
            // The iframe transport accepts a serialized array as form data:
            options.formData = this._getFormData(options);
            // Add redirect url to form data on cross-domain uploads:
            if (options.redirect && targetHost && targetHost !== location.host) {
                options.formData.push({
                    name: options.redirectParamName || 'redirect',
                    value: options.redirect
                });
            }
        },

        _initDataSettings: function (options) {
            if (this._isXHRUpload(options)) {
                if (!this._chunkedUpload(options, true)) {
                    if (!options.data) {
                        this._initXHRData(options);
                    }
                    this._initProgressListener(options);
                }
                if (options.postMessage) {
                    // Setting the dataType to postmessage enables the
                    // postMessage transport:
                    options.dataType = 'postmessage ' + (options.dataType || '');
                }
            } else {
                this._initIframeSettings(options);
            }
        },

        _getParamName: function (options) {
            var fileInput = $(options.fileInput),
                paramName = options.paramName;
            if (!paramName) {
                paramName = [];
                fileInput.each(function () {
                    var input = $(this),
                        name = input.prop('name') || 'files[]',
                        i = (input.prop('files') || [1]).length;
                    while (i) {
                        paramName.push(name);
                        i -= 1;
                    }
                });
                if (!paramName.length) {
                    paramName = [fileInput.prop('name') || 'files[]'];
                }
            } else if (!$.isArray(paramName)) {
                paramName = [paramName];
            }
            return paramName;
        },

        _initFormSettings: function (options) {
            // Retrieve missing options from the input field and the
            // associated form, if available:
            if (!options.form || !options.form.length) {
                options.form = $(options.fileInput.prop('form'));
                // If the given file input doesn't have an associated form,
                // use the default widget file input's form:
                if (!options.form.length) {
                    options.form = $(this.options.fileInput.prop('form'));
                }
            }
            options.paramName = this._getParamName(options);
            if (!options.url) {
                options.url = options.form.prop('action') || location.href;
            }
            // The HTTP request method must be "POST" or "PUT":
            options.type = (options.type ||
                ($.type(options.form.prop('method')) === 'string' &&
                    options.form.prop('method')) || ''
                ).toUpperCase();
            if (options.type !== 'POST' && options.type !== 'PUT' &&
                    options.type !== 'PATCH') {
                options.type = 'POST';
            }
            if (!options.formAcceptCharset) {
                options.formAcceptCharset = options.form.attr('accept-charset');
            }
        },

        _getAJAXSettings: function (data) {
            var options = $.extend({}, this.options, data);
            this._initFormSettings(options);
            this._initDataSettings(options);
            return options;
        },

        // jQuery 1.6 doesn't provide .state(),
        // while jQuery 1.8+ removed .isRejected() and .isResolved():
        _getDeferredState: function (deferred) {
            if (deferred.state) {
                return deferred.state();
            }
            if (deferred.isResolved()) {
                return 'resolved';
            }
            if (deferred.isRejected()) {
                return 'rejected';
            }
            return 'pending';
        },

        // Maps jqXHR callbacks to the equivalent
        // methods of the given Promise object:
        _enhancePromise: function (promise) {
            promise.success = promise.done;
            promise.error = promise.fail;
            promise.complete = promise.always;
            return promise;
        },

        // Creates and returns a Promise object enhanced with
        // the jqXHR methods abort, success, error and complete:
        _getXHRPromise: function (resolveOrReject, context, args) {
            var dfd = $.Deferred(),
                promise = dfd.promise();
            context = context || this.options.context || promise;
            if (resolveOrReject === true) {
                dfd.resolveWith(context, args);
            } else if (resolveOrReject === false) {
                dfd.rejectWith(context, args);
            }
            promise.abort = dfd.promise;
            return this._enhancePromise(promise);
        },

        // Adds convenience methods to the data callback argument:
        _addConvenienceMethods: function (e, data) {
            var that = this,
                getPromise = function (args) {
                    return $.Deferred().resolveWith(that, args).promise();
                };
            data.process = function (resolveFunc, rejectFunc) {
                if (resolveFunc || rejectFunc) {
                    data._processQueue = this._processQueue =
                        (this._processQueue || getPromise([this])).pipe(
                            function () {
                                if (data.errorThrown) {
                                    return $.Deferred()
                                        .rejectWith(that, [data]).promise();
                                }
                                return getPromise(arguments);
                            }
                        ).pipe(resolveFunc, rejectFunc);
                }
                return this._processQueue || getPromise([this]);
            };
            data.submit = function () {
                if (this.state() !== 'pending') {
                    data.jqXHR = this.jqXHR =
                        (that._trigger(
                            'submit',
                            $.Event('submit', {delegatedEvent: e}),
                            this
                        ) !== false) && that._onSend(e, this);
                }
                return this.jqXHR || that._getXHRPromise();
            };
            data.abort = function () {
                if (this.jqXHR) {
                    return this.jqXHR.abort();
                }
                this.errorThrown = 'abort';
                that._trigger('fail', null, this);
                return that._getXHRPromise(false);
            };
            data.state = function () {
                if (this.jqXHR) {
                    return that._getDeferredState(this.jqXHR);
                }
                if (this._processQueue) {
                    return that._getDeferredState(this._processQueue);
                }
            };
            data.processing = function () {
                return !this.jqXHR && this._processQueue && that
                    ._getDeferredState(this._processQueue) === 'pending';
            };
            data.progress = function () {
                return this._progress;
            };
            data.response = function () {
                return this._response;
            };
        },

        // Parses the Range header from the server response
        // and returns the uploaded bytes:
        _getUploadedBytes: function (jqXHR) {
            var range = jqXHR.getResponseHeader('Range'),
                parts = range && range.split('-'),
                upperBytesPos = parts && parts.length > 1 &&
                    parseInt(parts[1], 10);
            return upperBytesPos && upperBytesPos + 1;
        },

        // Uploads a file in multiple, sequential requests
        // by splitting the file up in multiple blob chunks.
        // If the second parameter is true, only tests if the file
        // should be uploaded in chunks, but does not invoke any
        // upload requests:
        _chunkedUpload: function (options, testOnly) {
            options.uploadedBytes = options.uploadedBytes || 0;
            var that = this,
                file = options.files[0],
                fs = file.size,
                ub = options.uploadedBytes,
                mcs = options.maxChunkSize || fs,
                slice = this._blobSlice,
                dfd = $.Deferred(),
                promise = dfd.promise(),
                jqXHR,
                upload;
            if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
                    options.data) {
                return false;
            }
            if (testOnly) {
                return true;
            }
            if (ub >= fs) {
                file.error = options.i18n('uploadedBytes');
                return this._getXHRPromise(
                    false,
                    options.context,
                    [null, 'error', file.error]
                );
            }
            // The chunk upload method:
            upload = function () {
                // Clone the options object for each chunk upload:
                var o = $.extend({}, options),
                    currentLoaded = o._progress.loaded;
                o.blob = slice.call(
                    file,
                    ub,
                    ub + mcs,
                    file.type
                );
                // Store the current chunk size, as the blob itself
                // will be dereferenced after data processing:
                o.chunkSize = o.blob.size;
                // Expose the chunk bytes position range:
                o.contentRange = 'bytes ' + ub + '-' +
                    (ub + o.chunkSize - 1) + '/' + fs;
                // Process the upload data (the blob and potential form data):
                that._initXHRData(o);
                // Add progress listeners for this chunk upload:
                that._initProgressListener(o);
                jqXHR = ((that._trigger('chunksend', null, o) !== false && $.ajax(o)) ||
                        that._getXHRPromise(false, o.context))
                    .done(function (result, textStatus, jqXHR) {
                        ub = that._getUploadedBytes(jqXHR) ||
                            (ub + o.chunkSize);
                        // Create a progress event if no final progress event
                        // with loaded equaling total has been triggered
                        // for this chunk:
                        if (currentLoaded + o.chunkSize - o._progress.loaded) {
                            that._onProgress($.Event('progress', {
                                lengthComputable: true,
                                loaded: ub - o.uploadedBytes,
                                total: ub - o.uploadedBytes
                            }), o);
                        }
                        options.uploadedBytes = o.uploadedBytes = ub;
                        o.result = result;
                        o.textStatus = textStatus;
                        o.jqXHR = jqXHR;
                        that._trigger('chunkdone', null, o);
                        that._trigger('chunkalways', null, o);
                        if (ub < fs) {
                            // File upload not yet complete,
                            // continue with the next chunk:
                            upload();
                        } else {
                            dfd.resolveWith(
                                o.context,
                                [result, textStatus, jqXHR]
                            );
                        }
                    })
                    .fail(function (jqXHR, textStatus, errorThrown) {
                        o.jqXHR = jqXHR;
                        o.textStatus = textStatus;
                        o.errorThrown = errorThrown;
                        that._trigger('chunkfail', null, o);
                        that._trigger('chunkalways', null, o);
                        dfd.rejectWith(
                            o.context,
                            [jqXHR, textStatus, errorThrown]
                        );
                    });
            };
            this._enhancePromise(promise);
            promise.abort = function () {
                return jqXHR.abort();
            };
            upload();
            return promise;
        },

        _beforeSend: function (e, data) {
            if (this._active === 0) {
                // the start callback is triggered when an upload starts
                // and no other uploads are currently running,
                // equivalent to the global ajaxStart event:
                this._trigger('start');
                // Set timer for global bitrate progress calculation:
                this._bitrateTimer = new this._BitrateTimer();
                // Reset the global progress values:
                this._progress.loaded = this._progress.total = 0;
                this._progress.bitrate = 0;
            }
            // Make sure the container objects for the .response() and
            // .progress() methods on the data object are available
            // and reset to their initial state:
            this._initResponseObject(data);
            this._initProgressObject(data);
            data._progress.loaded = data.loaded = data.uploadedBytes || 0;
            data._progress.total = data.total = this._getTotal(data.files) || 1;
            data._progress.bitrate = data.bitrate = 0;
            this._active += 1;
            // Initialize the global progress values:
            this._progress.loaded += data.loaded;
            this._progress.total += data.total;
        },

        _onDone: function (result, textStatus, jqXHR, options) {
            var total = options._progress.total,
                response = options._response;
            if (options._progress.loaded < total) {
                // Create a progress event if no final progress event
                // with loaded equaling total has been triggered:
                this._onProgress($.Event('progress', {
                    lengthComputable: true,
                    loaded: total,
                    total: total
                }), options);
            }
            response.result = options.result = result;
            response.textStatus = options.textStatus = textStatus;
            response.jqXHR = options.jqXHR = jqXHR;
            this._trigger('done', null, options);
        },

        _onFail: function (jqXHR, textStatus, errorThrown, options) {
            var response = options._response;
            if (options.recalculateProgress) {
                // Remove the failed (error or abort) file upload from
                // the global progress calculation:
                this._progress.loaded -= options._progress.loaded;
                this._progress.total -= options._progress.total;
            }
            response.jqXHR = options.jqXHR = jqXHR;
            response.textStatus = options.textStatus = textStatus;
            response.errorThrown = options.errorThrown = errorThrown;
            this._trigger('fail', null, options);
        },

        _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
            // jqXHRorResult, textStatus and jqXHRorError are added to the
            // options object via done and fail callbacks
            this._trigger('always', null, options);
        },

        _onSend: function (e, data) {
            if (!data.submit) {
                this._addConvenienceMethods(e, data);
            }
            var that = this,
                jqXHR,
                aborted,
                slot,
                pipe,
                options = that._getAJAXSettings(data),
                send = function () {
                    that._sending += 1;
                    // Set timer for bitrate progress calculation:
                    options._bitrateTimer = new that._BitrateTimer();
                    jqXHR = jqXHR || (
                        ((aborted || that._trigger(
                            'send',
                            $.Event('send', {delegatedEvent: e}),
                            options
                        ) === false) &&
                        that._getXHRPromise(false, options.context, aborted)) ||
                        that._chunkedUpload(options) || $.ajax(options)
                    ).done(function (result, textStatus, jqXHR) {
                        that._onDone(result, textStatus, jqXHR, options);
                    }).fail(function (jqXHR, textStatus, errorThrown) {
                        that._onFail(jqXHR, textStatus, errorThrown, options);
                    }).always(function (jqXHRorResult, textStatus, jqXHRorError) {
                        that._onAlways(
                            jqXHRorResult,
                            textStatus,
                            jqXHRorError,
                            options
                        );
                        that._sending -= 1;
                        that._active -= 1;
                        if (options.limitConcurrentUploads &&
                                options.limitConcurrentUploads > that._sending) {
                            // Start the next queued upload,
                            // that has not been aborted:
                            var nextSlot = that._slots.shift();
                            while (nextSlot) {
                                if (that._getDeferredState(nextSlot) === 'pending') {
                                    nextSlot.resolve();
                                    break;
                                }
                                nextSlot = that._slots.shift();
                            }
                        }
                        if (that._active === 0) {
                            // The stop callback is triggered when all uploads have
                            // been completed, equivalent to the global ajaxStop event:
                            that._trigger('stop');
                        }
                    });
                    return jqXHR;
                };
            this._beforeSend(e, options);
            if (this.options.sequentialUploads ||
                    (this.options.limitConcurrentUploads &&
                    this.options.limitConcurrentUploads <= this._sending)) {
                if (this.options.limitConcurrentUploads > 1) {
                    slot = $.Deferred();
                    this._slots.push(slot);
                    pipe = slot.pipe(send);
                } else {
                    this._sequence = this._sequence.pipe(send, send);
                    pipe = this._sequence;
                }
                // Return the piped Promise object, enhanced with an abort method,
                // which is delegated to the jqXHR object of the current upload,
                // and jqXHR callbacks mapped to the equivalent Promise methods:
                pipe.abort = function () {
                    aborted = [undefined, 'abort', 'abort'];
                    if (!jqXHR) {
                        if (slot) {
                            slot.rejectWith(options.context, aborted);
                        }
                        return send();
                    }
                    return jqXHR.abort();
                };
                return this._enhancePromise(pipe);
            }
            return send();
        },

        _onAdd: function (e, data) {
            var that = this,
                result = true,
                options = $.extend({}, this.options, data),
                files = data.files,
                filesLength = files.length,
                limit = options.limitMultiFileUploads,
                limitSize = options.limitMultiFileUploadSize,
                overhead = options.limitMultiFileUploadSizeOverhead,
                batchSize = 0,
                paramName = this._getParamName(options),
                paramNameSet,
                paramNameSlice,
                fileSet,
                i,
                j = 0;
            if (limitSize && (!filesLength || files[0].size === undefined)) {
                limitSize = undefined;
            }
            if (!(options.singleFileUploads || limit || limitSize) ||
                    !this._isXHRUpload(options)) {
                fileSet = [files];
                paramNameSet = [paramName];
            } else if (!(options.singleFileUploads || limitSize) && limit) {
                fileSet = [];
                paramNameSet = [];
                for (i = 0; i < filesLength; i += limit) {
                    fileSet.push(files.slice(i, i + limit));
                    paramNameSlice = paramName.slice(i, i + limit);
                    if (!paramNameSlice.length) {
                        paramNameSlice = paramName;
                    }
                    paramNameSet.push(paramNameSlice);
                }
            } else if (!options.singleFileUploads && limitSize) {
                fileSet = [];
                paramNameSet = [];
                for (i = 0; i < filesLength; i = i + 1) {
                    batchSize += files[i].size + overhead;
                    if (i + 1 === filesLength ||
                            ((batchSize + files[i + 1].size + overhead) > limitSize) ||
                            (limit && i + 1 - j >= limit)) {
                        fileSet.push(files.slice(j, i + 1));
                        paramNameSlice = paramName.slice(j, i + 1);
                        if (!paramNameSlice.length) {
                            paramNameSlice = paramName;
                        }
                        paramNameSet.push(paramNameSlice);
                        j = i + 1;
                        batchSize = 0;
                    }
                }
            } else {
                paramNameSet = paramName;
            }
            data.originalFiles = files;
            $.each(fileSet || files, function (index, element) {
                var newData = $.extend({}, data);
                newData.files = fileSet ? element : [element];
                newData.paramName = paramNameSet[index];
                that._initResponseObject(newData);
                that._initProgressObject(newData);
                that._addConvenienceMethods(e, newData);
                result = that._trigger(
                    'add',
                    $.Event('add', {delegatedEvent: e}),
                    newData
                );
                return result;
            });
            return result;
        },

        _replaceFileInput: function (data) {
            var input = data.fileInput,
                inputClone = input.clone(true);
            // Add a reference for the new cloned file input to the data argument:
            data.fileInputClone = inputClone;
            $('<form></form>').append(inputClone)[0].reset();
            // Detaching allows to insert the fileInput on another form
            // without loosing the file input value:
            input.after(inputClone).detach();
            // Avoid memory leaks with the detached file input:
            $.cleanData(input.unbind('remove'));
            // Replace the original file input element in the fileInput
            // elements set with the clone, which has been copied including
            // event handlers:
            this.options.fileInput = this.options.fileInput.map(function (i, el) {
                if (el === input[0]) {
                    return inputClone[0];
                }
                return el;
            });
            // If the widget has been initialized on the file input itself,
            // override this.element with the file input clone:
            if (input[0] === this.element[0]) {
                this.element = inputClone;
            }
        },

        _handleFileTreeEntry: function (entry, path) {
            var that = this,
                dfd = $.Deferred(),
                errorHandler = function (e) {
                    if (e && !e.entry) {
                        e.entry = entry;
                    }
                    // Since $.when returns immediately if one
                    // Deferred is rejected, we use resolve instead.
                    // This allows valid files and invalid items
                    // to be returned together in one set:
                    dfd.resolve([e]);
                },
                successHandler = function (entries) {
                    that._handleFileTreeEntries(
                        entries,
                        path + entry.name + '/'
                    ).done(function (files) {
                        dfd.resolve(files);
                    }).fail(errorHandler);
                },
                readEntries = function () {
                    dirReader.readEntries(function (results) {
                        if (!results.length) {
                            successHandler(entries);
                        } else {
                            entries = entries.concat(results);
                            readEntries();
                        }
                    }, errorHandler);
                },
                dirReader, entries = [];
            path = path || '';
            if (entry.isFile) {
                if (entry._file) {
                    // Workaround for Chrome bug #149735
                    entry._file.relativePath = path;
                    dfd.resolve(entry._file);
                } else {
                    entry.file(function (file) {
                        file.relativePath = path;
                        dfd.resolve(file);
                    }, errorHandler);
                }
            } else if (entry.isDirectory) {
                dirReader = entry.createReader();
                readEntries();
            } else {
                // Return an empy list for file system items
                // other than files or directories:
                dfd.resolve([]);
            }
            return dfd.promise();
        },

        _handleFileTreeEntries: function (entries, path) {
            var that = this;
            return $.when.apply(
                $,
                $.map(entries, function (entry) {
                    return that._handleFileTreeEntry(entry, path);
                })
            ).pipe(function () {
                return Array.prototype.concat.apply(
                    [],
                    arguments
                );
            });
        },

        _getDroppedFiles: function (dataTransfer) {
            dataTransfer = dataTransfer || {};
            var items = dataTransfer.items;
            if (items && items.length && (items[0].webkitGetAsEntry ||
                    items[0].getAsEntry)) {
                return this._handleFileTreeEntries(
                    $.map(items, function (item) {
                        var entry;
                        if (item.webkitGetAsEntry) {
                            entry = item.webkitGetAsEntry();
                            if (entry) {
                                // Workaround for Chrome bug #149735:
                                entry._file = item.getAsFile();
                            }
                            return entry;
                        }
                        return item.getAsEntry();
                    })
                );
            }
            return $.Deferred().resolve(
                $.makeArray(dataTransfer.files)
            ).promise();
        },

        _getSingleFileInputFiles: function (fileInput) {
            fileInput = $(fileInput);
            var entries = fileInput.prop('webkitEntries') ||
                    fileInput.prop('entries'),
                files,
                value;
            if (entries && entries.length) {
                return this._handleFileTreeEntries(entries);
            }
            files = $.makeArray(fileInput.prop('files'));
            if (!files.length) {
                value = fileInput.prop('value');
                if (!value) {
                    return $.Deferred().resolve([]).promise();
                }
                // If the files property is not available, the browser does not
                // support the File API and we add a pseudo File object with
                // the input value as name with path information removed:
                files = [{name: value.replace(/^.*\\/, '')}];
            } else if (files[0].name === undefined && files[0].fileName) {
                // File normalization for Safari 4 and Firefox 3:
                $.each(files, function (index, file) {
                    file.name = file.fileName;
                    file.size = file.fileSize;
                });
            }
            return $.Deferred().resolve(files).promise();
        },

        _getFileInputFiles: function (fileInput) {
            if (!(fileInput instanceof $) || fileInput.length === 1) {
                return this._getSingleFileInputFiles(fileInput);
            }
            return $.when.apply(
                $,
                $.map(fileInput, this._getSingleFileInputFiles)
            ).pipe(function () {
                return Array.prototype.concat.apply(
                    [],
                    arguments
                );
            });
        },

        _onChange: function (e) {
            var that = this,
                data = {
                    fileInput: $(e.target),
                    form: $(e.target.form)
                };
            this._getFileInputFiles(data.fileInput).always(function (files) {
                data.files = files;
                if (that.options.replaceFileInput) {
                    that._replaceFileInput(data);
                }
                if (that._trigger(
                        'change',
                        $.Event('change', {delegatedEvent: e}),
                        data
                    ) !== false) {
                    that._onAdd(e, data);
                }
            });
        },

        _onPaste: function (e) {
            var items = e.originalEvent && e.originalEvent.clipboardData &&
                    e.originalEvent.clipboardData.items,
                data = {files: []};
            if (items && items.length) {
                $.each(items, function (index, item) {
                    var file = item.getAsFile && item.getAsFile();
                    if (file) {
                        data.files.push(file);
                    }
                });
                if (this._trigger(
                        'paste',
                        $.Event('paste', {delegatedEvent: e}),
                        data
                    ) !== false) {
                    this._onAdd(e, data);
                }
            }
        },

        _onDrop: function (e) {
            e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
            var that = this,
                dataTransfer = e.dataTransfer,
                data = {};
            if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
                e.preventDefault();
                this._getDroppedFiles(dataTransfer).always(function (files) {
                    data.files = files;
                    if (that._trigger(
                            'drop',
                            $.Event('drop', {delegatedEvent: e}),
                            data
                        ) !== false) {
                        that._onAdd(e, data);
                    }
                });
            }
        },

        _onDragOver: getDragHandler('dragover'),

        _onDragEnter: getDragHandler('dragenter'),

        _onDragLeave: getDragHandler('dragleave'),

        _initEventHandlers: function () {
            if (this._isXHRUpload(this.options)) {
                this._on(this.options.dropZone, {
                    dragover: this._onDragOver,
                    drop: this._onDrop,
                    // event.preventDefault() on dragenter is required for IE10+:
                    dragenter: this._onDragEnter,
                    // dragleave is not required, but added for completeness:
                    dragleave: this._onDragLeave
                });
                this._on(this.options.pasteZone, {
                    paste: this._onPaste
                });
            }
            if ($.support.fileInput) {
                this._on(this.options.fileInput, {
                    change: this._onChange
                });
            }
        },

        _destroyEventHandlers: function () {
            this._off(this.options.dropZone, 'dragenter dragleave dragover drop');
            this._off(this.options.pasteZone, 'paste');
            this._off(this.options.fileInput, 'change');
        },

        _setOption: function (key, value) {
            var reinit = $.inArray(key, this._specialOptions) !== -1;
            if (reinit) {
                this._destroyEventHandlers();
            }
            this._super(key, value);
            if (reinit) {
                this._initSpecialOptions();
                this._initEventHandlers();
            }
        },

        _initSpecialOptions: function () {
            var options = this.options;
            if (options.fileInput === undefined) {
                options.fileInput = this.element.is('input[type="file"]') ?
                        this.element : this.element.find('input[type="file"]');
            } else if (!(options.fileInput instanceof $)) {
                options.fileInput = $(options.fileInput);
            }
            if (!(options.dropZone instanceof $)) {
                options.dropZone = $(options.dropZone);
            }
            if (!(options.pasteZone instanceof $)) {
                options.pasteZone = $(options.pasteZone);
            }
        },

        _getRegExp: function (str) {
            var parts = str.split('/'),
                modifiers = parts.pop();
            parts.shift();
            return new RegExp(parts.join('/'), modifiers);
        },

        _isRegExpOption: function (key, value) {
            return key !== 'url' && $.type(value) === 'string' &&
                /^\/.*\/[igm]{0,3}$/.test(value);
        },

        _initDataAttributes: function () {
            var that = this,
                options = this.options,
                clone = $(this.element[0].cloneNode(false)),
                data = clone.data();
            // Avoid memory leaks:
            clone.remove();
            // Initialize options set via HTML5 data-attributes:
            $.each(
                data,
                function (key, value) {
                    var dataAttributeName = 'data-' +
                        // Convert camelCase to hyphen-ated key:
                        key.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
                    if (clone.attr(dataAttributeName)) {
                        if (that._isRegExpOption(key, value)) {
                            value = that._getRegExp(value);
                        }
                        options[key] = value;
                    }
                }
            );
        },

        _create: function () {
            this._initDataAttributes();
            this._initSpecialOptions();
            this._slots = [];
            this._sequence = this._getXHRPromise(true);
            this._sending = this._active = 0;
            this._initProgressObject(this);
            this._initEventHandlers();
        },

        // This method is exposed to the widget API and allows to query
        // the number of active uploads:
        active: function () {
            return this._active;
        },

        // This method is exposed to the widget API and allows to query
        // the widget upload progress.
        // It returns an object with loaded, total and bitrate properties
        // for the running uploads:
        progress: function () {
            return this._progress;
        },

        // This method is exposed to the widget API and allows adding files
        // using the fileupload API. The data parameter accepts an object which
        // must have a files property and can contain additional options:
        // .fileupload('add', {files: filesList});
        add: function (data) {
            var that = this;
            if (!data || this.options.disabled) {
                return;
            }
            if (data.fileInput && !data.files) {
                this._getFileInputFiles(data.fileInput).always(function (files) {
                    data.files = files;
                    that._onAdd(null, data);
                });
            } else {
                data.files = $.makeArray(data.files);
                this._onAdd(null, data);
            }
        },

        // This method is exposed to the widget API and allows sending files
        // using the fileupload API. The data parameter accepts an object which
        // must have a files or fileInput property and can contain additional options:
        // .fileupload('send', {files: filesList});
        // The method returns a Promise object for the file upload call.
        send: function (data) {
            if (data && !this.options.disabled) {
                if (data.fileInput && !data.files) {
                    var that = this,
                        dfd = $.Deferred(),
                        promise = dfd.promise(),
                        jqXHR,
                        aborted;
                    promise.abort = function () {
                        aborted = true;
                        if (jqXHR) {
                            return jqXHR.abort();
                        }
                        dfd.reject(null, 'abort', 'abort');
                        return promise;
                    };
                    this._getFileInputFiles(data.fileInput).always(
                        function (files) {
                            if (aborted) {
                                return;
                            }
                            if (!files.length) {
                                dfd.reject();
                                return;
                            }
                            data.files = files;
                            jqXHR = that._onSend(null, data);
                            jqXHR.then(
                                function (result, textStatus, jqXHR) {
                                    dfd.resolve(result, textStatus, jqXHR);
                                },
                                function (jqXHR, textStatus, errorThrown) {
                                    dfd.reject(jqXHR, textStatus, errorThrown);
                                }
                            );
                        }
                    );
                    return this._enhancePromise(promise);
                }
                data.files = $.makeArray(data.files);
                if (data.files.length) {
                    return this._onSend(null, data);
                }
            }
            return this._getXHRPromise(false, data && data.context);
        }

    });

}));
;
/*
 * jQuery File Upload Processing Plugin 1.3.1
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2012, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

/* jshint nomen:false */
/* global define, require, window */

(function (factory) {
    'use strict';
    if (typeof define === 'function' && define.amd) {
        // Register as an anonymous AMD module:
        define([
            'jquery',
            './jquery.fileupload'
        ], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS:
        factory(require('jquery'));
    } else {
        // Browser globals:
        factory(
            window.jQuery
        );
    }
}(function ($) {
    'use strict';

    var originalAdd = $.blueimp.fileupload.prototype.options.add;

    // The File Upload Processing plugin extends the fileupload widget
    // with file processing functionality:
    $.widget('blueimp.fileupload', $.blueimp.fileupload, {

        options: {
            // The list of processing actions:
            processQueue: [
                /*
                {
                    action: 'log',
                    type: 'debug'
                }
                */
            ],
            add: function (e, data) {
                var $this = $(this);
                data.process(function () {
                    return $this.fileupload('process', data);
                });
                originalAdd.call(this, e, data);
            }
        },

        processActions: {
            /*
            log: function (data, options) {
                console[options.type](
                    'Processing "' + data.files[data.index].name + '"'
                );
            }
            */
        },

        _processFile: function (data, originalData) {
            var that = this,
                dfd = $.Deferred().resolveWith(that, [data]),
                chain = dfd.promise();
            this._trigger('process', null, data);
            $.each(data.processQueue, function (i, settings) {
                var func = function (data) {
                    if (originalData.errorThrown) {
                        return $.Deferred()
                                .rejectWith(that, [originalData]).promise();
                    }
                    return that.processActions[settings.action].call(
                        that,
                        data,
                        settings
                    );
                };
                chain = chain.pipe(func, settings.always && func);
            });
            chain
                .done(function () {
                    that._trigger('processdone', null, data);
                    that._trigger('processalways', null, data);
                })
                .fail(function () {
                    that._trigger('processfail', null, data);
                    that._trigger('processalways', null, data);
                });
            return chain;
        },

        // Replaces the settings of each processQueue item that
        // are strings starting with an "@", using the remaining
        // substring as key for the option map,
        // e.g. "@autoUpload" is replaced with options.autoUpload:
        _transformProcessQueue: function (options) {
            var processQueue = [];
            $.each(options.processQueue, function () {
                var settings = {},
                    action = this.action,
                    prefix = this.prefix === true ? action : this.prefix;
                $.each(this, function (key, value) {
                    if ($.type(value) === 'string' &&
                            value.charAt(0) === '@') {
                        settings[key] = options[
                            value.slice(1) || (prefix ? prefix +
                                key.charAt(0).toUpperCase() + key.slice(1) : key)
                        ];
                    } else {
                        settings[key] = value;
                    }

                });
                processQueue.push(settings);
            });
            options.processQueue = processQueue;
        },

        // Returns the number of files currently in the processsing queue:
        processing: function () {
            return this._processing;
        },

        // Processes the files given as files property of the data parameter,
        // returns a Promise object that allows to bind callbacks:
        process: function (data) {
            var that = this,
                options = $.extend({}, this.options, data);
            if (options.processQueue && options.processQueue.length) {
                this._transformProcessQueue(options);
                if (this._processing === 0) {
                    this._trigger('processstart');
                }
                $.each(data.files, function (index) {
                    var opts = index ? $.extend({}, options) : options,
                        func = function () {
                            if (data.errorThrown) {
                                return $.Deferred()
                                        .rejectWith(that, [data]).promise();
                            }
                            return that._processFile(opts, data);
                        };
                    opts.index = index;
                    that._processing += 1;
                    that._processingQueue = that._processingQueue.pipe(func, func)
                        .always(function () {
                            that._processing -= 1;
                            if (that._processing === 0) {
                                that._trigger('processstop');
                            }
                        });
                });
            }
            return this._processingQueue;
        },

        _create: function () {
            this._super();
            this._processing = 0;
            this._processingQueue = $.Deferred().resolveWith(this)
                .promise();
        }

    });

}));
;
/*
 * jQuery File Upload User Interface Plugin 9.6.1
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

/* jshint nomen:false */
/* global define, require, window */

(function(factory) {
    'use strict';
    if (typeof define === 'function' && define.amd) {
        // Register as an anonymous AMD module:
        define([
            'jquery',
            'tmpl',
            './jquery.fileupload-image',
            './jquery.fileupload-audio',
            './jquery.fileupload-video',
            './jquery.fileupload-validate'
        ], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS:
        factory(
            require('jquery'),
            require('tmpl')
        );
    } else {
        // Browser globals:
        factory(
            window.jQuery,
            window.tmpl
        );
    }
}(function($, tmpl) {
    'use strict';

    $.blueimp.fileupload.prototype._specialOptions.push(
        'filesContainer',
        'uploadTemplateId',
        'downloadTemplateId'
    );

    // The UI version extends the file upload widget
    // and adds complete user interface interaction:
    $.widget('blueimp.fileupload', $.blueimp.fileupload, {

        options: {
            // By default, files added to the widget are uploaded as soon
            // as the user clicks on the start buttons. To enable automatic
            // uploads, set the following option to true:
            autoUpload: false,
            // The ID of the upload template:
            uploadTemplateId: 'template-upload',
            // The ID of the download template:
            downloadTemplateId: 'template-download',
            // The container for the list of files. If undefined, it is set to
            // an element with class "files" inside of the widget element:
            filesContainer: undefined,
            // By default, files are appended to the files container.
            // Set the following option to true, to prepend files instead:
            prependFiles: false,
            // The expected data type of the upload response, sets the dataType
            // option of the $.ajax upload requests:
            dataType: 'json',

            // Error and info messages:
            messages: {
                unknownError: 'Unknown error'
            },

            // Function returning the current number of files,
            // used by the maxNumberOfFiles validation:
            getNumberOfFiles: function() {
                return this.filesContainer.children()
                    .not('.processing').length;
            },

            // Callback to retrieve the list of files from the server response:
            getFilesFromResponse: function(data) {
                if (data.result && $.isArray(data.result.files)) {
                    return data.result.files;
                }
                return [];
            },

            // The add callback is invoked as soon as files are added to the fileupload
            // widget (via file input selection, drag & drop or add API call).
            // See the basic file upload widget for more information:
            add: function(e, data) {
                if (e.isDefaultPrevented()) {
                    return false;
                }
                var $this = $(this),
                    that = $this.data('blueimp-fileupload') ||
                    $this.data('fileupload'),
                    options = that.options;

                data.context = that._renderUpload(data.files)
                    .data('data', data)
                    .addClass('processing');

                options.filesContainer[
                    options.prependFiles ? 'prepend' : 'append'
                ](data.context);
                that._forceReflow(data.context);
                that._transition(data.context);
                data.process(function() {
                    return $this.fileupload('process', data);
                }).always(function() {
                    data.context.each(function(index) {
                        $(this).find('.size').text(
                            that._formatFileSize(data.files[index].size)
                        );
                    }).removeClass('processing');
                    that._renderPreviews(data);
                }).done(function() {
                    data.context.find('.start').prop('disabled', false);
                    if ((that._trigger('added', e, data) !== false) &&
                        (options.autoUpload || data.autoUpload) &&
                        data.autoUpload !== false) {
                        data.submit();
                    }
                }).fail(function() {
                    if (data.files.error) {
                        data.context.each(function(index) {
                            var error = data.files[index].error;
                            if (error) {
                                $(this).find('.error').text(error);
                            }
                        });
                    }
                });


            },
            // Callback for the start of each file upload request:
            send: function(e, data) {
                if (e.isDefaultPrevented()) {
                    return false;
                }
                var that = $(this).data('blueimp-fileupload') ||
                    $(this).data('fileupload');
                if (data.context && data.dataType &&
                    data.dataType.substr(0, 6) === 'iframe') {
                    // Iframe Transport does not support progress events.
                    // In lack of an indeterminate progress bar, we set
                    // the progress to 100%, showing the full animated bar:
                    data.context
                        .find('.progress').addClass(!$.support.transition && 'progress-animated')
                        .attr('aria-valuenow', 100)
                        .children().first().css(
                            'width',
                            '100%'
                        );
                }
                return that._trigger('sent', e, data);
            },
            // Callback for successful uploads:
            done: function(e, data) {
                if (e.isDefaultPrevented()) {
                    return false;
                }
                var that = $(this).data('blueimp-fileupload') ||
                    $(this).data('fileupload'),
                    getFilesFromResponse = data.getFilesFromResponse ||
                    that.options.getFilesFromResponse,
                    files = getFilesFromResponse(data),
                    template,
                    deferred;
                if (data.context) {
                    data.context.each(function(index) {
                        var file = files[index] || {
                            error: 'Empty file upload result'
                        };
                        deferred = that._addFinishedDeferreds();
                        that._transition($(this)).done(
                            function() {
                                var node = $(this);
                                template = that._renderDownload([file])
                                    .replaceAll(node);
                                that._forceReflow(template);
                                that._transition(template).done(
                                    function() {
                                        data.context = $(this);
                                        that._trigger('completed', e, data);
                                        that._trigger('finished', e, data);
                                        deferred.resolve();
                                    }
                                );
                            }
                        );
                    });
                } else {
                    template = that._renderDownload(files)[
                        that.options.prependFiles ? 'prependTo' : 'appendTo'
                    ](that.options.filesContainer);
                    that._forceReflow(template);
                    deferred = that._addFinishedDeferreds();
                    that._transition(template).done(
                        function() {
                            data.context = $(this);
                            that._trigger('completed', e, data);
                            that._trigger('finished', e, data);
                            deferred.resolve();
                        }
                    );
                }
            },
            // Callback for failed (abort or error) uploads:
            fail: function(e, data) {
                if (e.isDefaultPrevented()) {
                    return false;
                }
                var that = $(this).data('blueimp-fileupload') ||
                    $(this).data('fileupload'),
                    template,
                    deferred;
                if (data.context) {
                    data.context.each(function(index) {
                        if (data.errorThrown !== 'abort') {
                            var file = data.files[index];
                            file.error = file.error || data.errorThrown ||
                                data.i18n('unknownError');
                            deferred = that._addFinishedDeferreds();
                            that._transition($(this)).done(
                                function() {
                                    var node = $(this);
                                    template = that._renderDownload([file])
                                        .replaceAll(node);
                                    that._forceReflow(template);
                                    that._transition(template).done(
                                        function() {
                                            data.context = $(this);
                                            that._trigger('failed', e, data);
                                            that._trigger('finished', e, data);
                                            deferred.resolve();
                                        }
                                    );
                                }
                            );
                        } else {
                            deferred = that._addFinishedDeferreds();
                            that._transition($(this)).done(
                                function() {
                                    $(this).remove();
                                    that._trigger('failed', e, data);
                                    that._trigger('finished', e, data);
                                    deferred.resolve();
                                }
                            );
                        }
                    });
                } else if (data.errorThrown !== 'abort') {
                    data.context = that._renderUpload(data.files)[
                            that.options.prependFiles ? 'prependTo' : 'appendTo'
                        ](that.options.filesContainer)
                        .data('data', data);
                    that._forceReflow(data.context);
                    deferred = that._addFinishedDeferreds();
                    that._transition(data.context).done(
                        function() {
                            data.context = $(this);
                            that._trigger('failed', e, data);
                            that._trigger('finished', e, data);
                            deferred.resolve();
                        }
                    );
                } else {
                    that._trigger('failed', e, data);
                    that._trigger('finished', e, data);
                    that._addFinishedDeferreds().resolve();
                }
            },
            // Callback for upload progress events:
            progress: function(e, data) {
                if (e.isDefaultPrevented()) {
                    return false;
                }
                var progress = Math.floor(data.loaded / data.total * 100);
                if (data.context) {
                    data.context.each(function() {
                        $(this).find('.progress')
                            .attr('aria-valuenow', progress)
                            .children().first().css(
                                'width',
                                progress + '%'
                            );
                    });
                }
            },
            // Callback for global upload progress events:
            progressall: function(e, data) {
                if (e.isDefaultPrevented()) {
                    return false;
                }
                var $this = $(this),
                    progress = Math.floor(data.loaded / data.total * 100),
                    globalProgressNode = $this.find('.fileupload-progress'),
                    extendedProgressNode = globalProgressNode
                    .find('.progress-extended');
                if (extendedProgressNode.length) {
                    extendedProgressNode.html(
                        ($this.data('blueimp-fileupload') || $this.data('fileupload'))
                        ._renderExtendedProgress(data)
                    );
                }
                globalProgressNode
                    .find('.progress')
                    .attr('aria-valuenow', progress)
                    .children().first().css(
                        'width',
                        progress + '%'
                    );
            },
            // Callback for uploads start, equivalent to the global ajaxStart event:
            start: function(e) {
                if (e.isDefaultPrevented()) {
                    return false;
                }
                var that = $(this).data('blueimp-fileupload') ||
                    $(this).data('fileupload');
                that._resetFinishedDeferreds();
                that._transition($(this).find('.fileupload-progress')).done(
                    function() {
                        that._trigger('started', e);
                    }
                );
            },
            // Callback for uploads stop, equivalent to the global ajaxStop event:
            stop: function(e) {
                if (e.isDefaultPrevented()) {
                    return false;
                }
                var that = $(this).data('blueimp-fileupload') ||
                    $(this).data('fileupload'),
                    deferred = that._addFinishedDeferreds();
                $.when.apply($, that._getFinishedDeferreds())
                    .done(function() {
                        that._trigger('stopped', e);
                    });
                that._transition($(this).find('.fileupload-progress')).done(
                    function() {
                        $(this).find('.progress')
                            .attr('aria-valuenow', '0')
                            .children().first().css('width', '0%');
                        $(this).find('.progress-extended').html('&nbsp;');
                        deferred.resolve();
                    }
                );
            },
            processstart: function(e) {
                if (e.isDefaultPrevented()) {
                    return false;
                }
                $(this).addClass('fileupload-processing');
            },
            processstop: function(e) {
                if (e.isDefaultPrevented()) {
                    return false;
                }
                $(this).removeClass('fileupload-processing');
            },
            // Callback for file deletion:
            destroy: function(e, data) {
                if (e.isDefaultPrevented()) {
                    return false;
                }
                var that = $(this).data('blueimp-fileupload') ||
                    $(this).data('fileupload'),
                    removeNode = function() {
                        that._transition(data.context).done(
                            function() {
                                $(this).remove();
                                that._trigger('destroyed', e, data);
                            }
                        );
                    };
                if (data.url) {
                    data.dataType = data.dataType || that.options.dataType;
                    $.ajax(data).done(removeNode).fail(function() {
                        that._trigger('destroyfailed', e, data);
                    });
                } else {
                    removeNode();
                }
            }
        },

        _resetFinishedDeferreds: function() {
            this._finishedUploads = [];
        },

        _addFinishedDeferreds: function(deferred) {
            if (!deferred) {
                deferred = $.Deferred();
            }
            this._finishedUploads.push(deferred);
            return deferred;
        },

        _getFinishedDeferreds: function() {
            return this._finishedUploads;
        },

        // Link handler, that allows to download files
        // by drag & drop of the links to the desktop:
        _enableDragToDesktop: function() {
            var link = $(this),
                url = link.prop('href'),
                name = link.prop('download'),
                type = 'application/octet-stream';
            link.bind('dragstart', function(e) {
                try {
                    e.originalEvent.dataTransfer.setData(
                        'DownloadURL', [type, name, url].join(':')
                    );
                } catch (ignore) {}
            });
        },

        _formatFileSize: function(bytes) {
            if (typeof bytes !== 'number') {
                return '';
            }
            if (bytes >= 1000000000) {
                return (bytes / 1000000000).toFixed(2) + ' GB';
            }
            if (bytes >= 1000000) {
                return (bytes / 1000000).toFixed(2) + ' MB';
            }
            return (bytes / 1000).toFixed(2) + ' KB';
        },

        _formatBitrate: function(bits) {
            if (typeof bits !== 'number') {
                return '';
            }
            if (bits >= 1000000000) {
                return (bits / 1000000000).toFixed(2) + ' Gbit/s';
            }
            if (bits >= 1000000) {
                return (bits / 1000000).toFixed(2) + ' Mbit/s';
            }
            if (bits >= 1000) {
                return (bits / 1000).toFixed(2) + ' kbit/s';
            }
            return bits.toFixed(2) + ' bit/s';
        },

        _formatTime: function(seconds) {
            var date = new Date(seconds * 1000),
                days = Math.floor(seconds / 86400);
            days = days ? days + 'd ' : '';
            return days +
                ('0' + date.getUTCHours()).slice(-2) + ':' +
                ('0' + date.getUTCMinutes()).slice(-2) + ':' +
                ('0' + date.getUTCSeconds()).slice(-2);
        },

        _formatPercentage: function(floatValue) {
            return (floatValue * 100).toFixed(2) + ' %';
        },

        _renderExtendedProgress: function(data) {
            return this._formatBitrate(data.bitrate) + ' | ' +
                this._formatTime(
                    (data.total - data.loaded) * 8 / data.bitrate
                ) + ' | ' +
                this._formatPercentage(
                    data.loaded / data.total
                ) + ' | ' +
                this._formatFileSize(data.loaded) + ' / ' +
                this._formatFileSize(data.total);
        },

        _renderTemplate: function(func, files) {
            if (!func) {
                return $();
            }
            var result = func({
                files: files,
                formatFileSize: this._formatFileSize,
                options: this.options
            });
            if (result instanceof $) {
                return result;
            }
            return $(this.options.templatesContainer).html(result).children();
        },

        _renderPreviews: function(data) {
            data.context.find('.preview').each(function(index, elm) {
                $(elm).append(data.files[index].preview);
            });
        },

        _renderUpload: function(files) {
            return this._renderTemplate(
                this.options.uploadTemplate,
                files
            );
        },

        _renderDownload: function(files) {
            return this._renderTemplate(
                this.options.downloadTemplate,
                files
            ).find('a[download]').each(this._enableDragToDesktop).end();
        },

        _startHandler: function(e) {
            e.preventDefault();
            var button = $(e.currentTarget),
                template = button.closest('.template-upload'),
                data = template.data('data');
            button.prop('disabled', true);
            if (data && data.submit) {
                data.submit();
            }
        },

        _cancelHandler: function(e) {
            e.preventDefault();
            var template = $(e.currentTarget)
                .closest('.template-upload,.template-download'),
                data = template.data('data') || {};
            data.context = data.context || template;
            if (data.abort) {
                data.abort();
            } else {
                data.errorThrown = 'abort';
                this._trigger('fail', e, data);
            }
        },

        _deleteHandler: function(e) {
            e.preventDefault();
            var button = $(e.currentTarget);
            this._trigger('destroy', e, $.extend({
                context: button.closest('.template-download'),
                type: 'DELETE'
            }, button.data()));
        },

        _forceReflow: function(node) {
            return $.support.transition && node.length &&
                node[0].offsetWidth;
        },

        _transition: function(node) {
            var dfd = $.Deferred();
            if ($.support.transition && node.hasClass('fade') && node.is(':visible')) {
                node.bind(
                    $.support.transition.end,
                    function(e) {
                        // Make sure we don't respond to other transitions events
                        // in the container element, e.g. from button elements:
                        if (e.target === node[0]) {
                            node.unbind($.support.transition.end);
                            dfd.resolveWith(node);
                        }
                    }
                ).toggleClass('in');
            } else {
                node.toggleClass('in');
                dfd.resolveWith(node);
            }
            return dfd;
        },

        _initButtonBarEventHandlers: function() {
            var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
                filesList = this.options.filesContainer;
            this._on(fileUploadButtonBar.find('.start'), {
                click: function(e) {
                    e.preventDefault();
                    filesList.find('.start').click();
                }
            });
            this._on(fileUploadButtonBar.find('.cancel'), {
                click: function(e) {
                    e.preventDefault();
                    filesList.find('.cancel').click();
                }
            });
            this._on(fileUploadButtonBar.find('.delete'), {
                click: function(e) {
                    e.preventDefault();
                    filesList.find('.toggle:checked')
                        .closest('.template-download')
                        .find('.delete').click();
                    fileUploadButtonBar.find('.toggle')
                        .prop('checked', false);
                }
            });
            this._on(fileUploadButtonBar.find('.toggle'), {
                change: function(e) {
                    filesList.find('.toggle').prop(
                        'checked',
                        $(e.currentTarget).is(':checked')
                    );
                }
            });
        },

        _destroyButtonBarEventHandlers: function() {
            this._off(
                this.element.find('.fileupload-buttonbar')
                .find('.start, .cancel, .delete'),
                'click'
            );
            this._off(
                this.element.find('.fileupload-buttonbar .toggle'),
                'change.'
            );
        },

        _initEventHandlers: function() {
            this._super();
            this._on(this.options.filesContainer, {
                'click .start': this._startHandler,
                'click .cancel': this._cancelHandler,
                'click .delete': this._deleteHandler
            });
            this._initButtonBarEventHandlers();
        },

        _destroyEventHandlers: function() {
            this._destroyButtonBarEventHandlers();
            this._off(this.options.filesContainer, 'click');
            this._super();
        },

        _enableFileInputButton: function() {
            this.element.find('.fileinput-button input')
                .prop('disabled', false)
                .parent().removeClass('disabled');
        },

        _disableFileInputButton: function() {
            this.element.find('.fileinput-button input')
                .prop('disabled', true)
                .parent().addClass('disabled');
        },

        _initTemplates: function() {
            var options = this.options;
            options.templatesContainer = this.document[0].createElement(
                options.filesContainer.prop('nodeName')
            );
            if (tmpl) {
                if (options.uploadTemplateId) {
                    options.uploadTemplate = tmpl(options.uploadTemplateId);
                }
                if (options.downloadTemplateId) {
                    options.downloadTemplate = tmpl(options.downloadTemplateId);
                }
            }
        },

        _initFilesContainer: function() {
            var options = this.options;
            if (options.filesContainer === undefined) {
                options.filesContainer = this.element.find('.files');
            } else if (!(options.filesContainer instanceof $)) {
                options.filesContainer = $(options.filesContainer);
            }
        },

        _initSpecialOptions: function() {
            this._super();
            this._initFilesContainer();
            this._initTemplates();
        },

        _create: function() {
            this._super();
            this._resetFinishedDeferreds();
            if (!$.support.fileInput) {
                this._disableFileInputButton();
            }
        },

        enable: function() {
            var wasDisabled = false;
            if (this.options.disabled) {
                wasDisabled = true;
            }
            this._super();
            if (wasDisabled) {
                this.element.find('input, button').prop('disabled', false);
                this._enableFileInputButton();
            }
        },

        disable: function() {
            if (!this.options.disabled) {
                this.element.find('input, button').prop('disabled', true);
                this._disableFileInputButton();
            }
            this._super();
        }

    });

}));
;
/*
 * jQuery File Upload Validation Plugin 1.1.3
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2013, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

/* global define, require, window */

(function (factory) {
    'use strict';
    if (typeof define === 'function' && define.amd) {
        // Register as an anonymous AMD module:
        define([
            'jquery',
            './jquery.fileupload-process'
        ], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS:
        factory(require('jquery'));
    } else {
        // Browser globals:
        factory(
            window.jQuery
        );
    }
}(function ($) {
    'use strict';

    // Append to the default processQueue:
    $.blueimp.fileupload.prototype.options.processQueue.push(
        {
            action: 'validate',
            // Always trigger this action,
            // even if the previous action was rejected:
            always: true,
            // Options taken from the global options map:
            acceptFileTypes: '@',
            maxFileSize: '@',
            minFileSize: '@',
            maxNumberOfFiles: '@',
            disabled: '@disableValidation'
        }
    );

    // The File Upload Validation plugin extends the fileupload widget
    // with file validation functionality:
    $.widget('blueimp.fileupload', $.blueimp.fileupload, {

        options: {
            /*
            // The regular expression for allowed file types, matches
            // against either file type or file name:
            acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
            // The maximum allowed file size in bytes:
            maxFileSize: 10000000, // 10 MB
            // The minimum allowed file size in bytes:
            minFileSize: undefined, // No minimal file size
            // The limit of files to be uploaded:
            maxNumberOfFiles: 10,
            */

            // Function returning the current number of files,
            // has to be overriden for maxNumberOfFiles validation:
            getNumberOfFiles: $.noop,

            // Error and info messages:
            messages: {
                maxNumberOfFiles: 'Maximum number of files exceeded',
                acceptFileTypes: 'File type not allowed',
                maxFileSize: 'File is too large',
                minFileSize: 'File is too small'
            }
        },

        processActions: {

            validate: function (data, options) {
                if (options.disabled) {
                    return data;
                }
                var dfd = $.Deferred(),
                    settings = this.options,
                    file = data.files[data.index],
                    fileSize;
                if (options.minFileSize || options.maxFileSize) {
                    fileSize = file.size;
                }
                if ($.type(options.maxNumberOfFiles) === 'number' &&
                        (settings.getNumberOfFiles() || 0) + data.files.length >
                            options.maxNumberOfFiles) {
                    file.error = settings.i18n('maxNumberOfFiles');
                } else if (options.acceptFileTypes &&
                        !(options.acceptFileTypes.test(file.type) ||
                        options.acceptFileTypes.test(file.name))) {
                    file.error = settings.i18n('acceptFileTypes');
                } else if (fileSize > options.maxFileSize) {
                    file.error = settings.i18n('maxFileSize');
                } else if ($.type(fileSize) === 'number' &&
                        fileSize < options.minFileSize) {
                    file.error = settings.i18n('minFileSize');
                } else {
                    delete file.error;
                }
                if (file.error || data.files.error) {
                    data.files.error = true;
                    dfd.rejectWith(this, [data]);
                } else {
                    dfd.resolveWith(this, [data]);
                }
                return dfd.promise();
            }

        }

    });

}));
;
/*! jQuery Validation Plugin - v1.14.0 - 6/30/2015
 * http://jqueryvalidation.org/
 * Copyright (c) 2015 Jörn Zaefferer; Licensed MIT */
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){a.extend(a.fn,{validate:function(b){if(!this.length)return void(b&&b.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing."));var c=a.data(this[0],"validator");return c?c:(this.attr("novalidate","novalidate"),c=new a.validator(b,this[0]),a.data(this[0],"validator",c),c.settings.onsubmit&&(this.on("click.validate",":submit",function(b){c.settings.submitHandler&&(c.submitButton=b.target),a(this).hasClass("cancel")&&(c.cancelSubmit=!0),void 0!==a(this).attr("formnovalidate")&&(c.cancelSubmit=!0)}),this.on("submit.validate",function(b){function d(){var d,e;return c.settings.submitHandler?(c.submitButton&&(d=a("<input type='hidden'/>").attr("name",c.submitButton.name).val(a(c.submitButton).val()).appendTo(c.currentForm)),e=c.settings.submitHandler.call(c,c.currentForm,b),c.submitButton&&d.remove(),void 0!==e?e:!1):!0}return c.settings.debug&&b.preventDefault(),c.cancelSubmit?(c.cancelSubmit=!1,d()):c.form()?c.pendingRequest?(c.formSubmitted=!0,!1):d():(c.focusInvalid(),!1)})),c)},valid:function(){var b,c,d;return a(this[0]).is("form")?b=this.validate().form():(d=[],b=!0,c=a(this[0].form).validate(),this.each(function(){b=c.element(this)&&b,d=d.concat(c.errorList)}),c.errorList=d),b},rules:function(b,c){var d,e,f,g,h,i,j=this[0];if(b)switch(d=a.data(j.form,"validator").settings,e=d.rules,f=a.validator.staticRules(j),b){case"add":a.extend(f,a.validator.normalizeRule(c)),delete f.messages,e[j.name]=f,c.messages&&(d.messages[j.name]=a.extend(d.messages[j.name],c.messages));break;case"remove":return c?(i={},a.each(c.split(/\s/),function(b,c){i[c]=f[c],delete f[c],"required"===c&&a(j).removeAttr("aria-required")}),i):(delete e[j.name],f)}return g=a.validator.normalizeRules(a.extend({},a.validator.classRules(j),a.validator.attributeRules(j),a.validator.dataRules(j),a.validator.staticRules(j)),j),g.required&&(h=g.required,delete g.required,g=a.extend({required:h},g),a(j).attr("aria-required","true")),g.remote&&(h=g.remote,delete g.remote,g=a.extend(g,{remote:h})),g}}),a.extend(a.expr[":"],{blank:function(b){return!a.trim(""+a(b).val())},filled:function(b){return!!a.trim(""+a(b).val())},unchecked:function(b){return!a(b).prop("checked")}}),a.validator=function(b,c){this.settings=a.extend(!0,{},a.validator.defaults,b),this.currentForm=c,this.init()},a.validator.format=function(b,c){return 1===arguments.length?function(){var c=a.makeArray(arguments);return c.unshift(b),a.validator.format.apply(this,c)}:(arguments.length>2&&c.constructor!==Array&&(c=a.makeArray(arguments).slice(1)),c.constructor!==Array&&(c=[c]),a.each(c,function(a,c){b=b.replace(new RegExp("\\{"+a+"\\}","g"),function(){return c})}),b)},a.extend(a.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",validClass:"valid",errorElement:"label",focusCleanup:!1,focusInvalid:!0,errorContainer:a([]),errorLabelContainer:a([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(a){this.lastActive=a,this.settings.focusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,a,this.settings.errorClass,this.settings.validClass),this.hideThese(this.errorsFor(a)))},onfocusout:function(a){this.checkable(a)||!(a.name in this.submitted)&&this.optional(a)||this.element(a)},onkeyup:function(b,c){var d=[16,17,18,20,35,36,37,38,39,40,45,144,225];9===c.which&&""===this.elementValue(b)||-1!==a.inArray(c.keyCode,d)||(b.name in this.submitted||b===this.lastElement)&&this.element(b)},onclick:function(a){a.name in this.submitted?this.element(a):a.parentNode.name in this.submitted&&this.element(a.parentNode)},highlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).addClass(c).removeClass(d):a(b).addClass(c).removeClass(d)},unhighlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).removeClass(c).addClass(d):a(b).removeClass(c).addClass(d)}},setDefaults:function(b){a.extend(a.validator.defaults,b)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date ( ISO ).",number:"Please enter a valid number.",digits:"Please enter only digits.",creditcard:"Please enter a valid credit card number.",equalTo:"Please enter the same value again.",maxlength:a.validator.format("Please enter no more than {0} characters."),minlength:a.validator.format("Please enter at least {0} characters."),rangelength:a.validator.format("Please enter a value between {0} and {1} characters long."),range:a.validator.format("Please enter a value between {0} and {1}."),max:a.validator.format("Please enter a value less than or equal to {0}."),min:a.validator.format("Please enter a value greater than or equal to {0}.")},autoCreateRanges:!1,prototype:{init:function(){function b(b){var c=a.data(this.form,"validator"),d="on"+b.type.replace(/^validate/,""),e=c.settings;e[d]&&!a(this).is(e.ignore)&&e[d].call(c,this,b)}this.labelContainer=a(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||a(this.currentForm),this.containers=a(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var c,d=this.groups={};a.each(this.settings.groups,function(b,c){"string"==typeof c&&(c=c.split(/\s/)),a.each(c,function(a,c){d[c]=b})}),c=this.settings.rules,a.each(c,function(b,d){c[b]=a.validator.normalizeRule(d)}),a(this.currentForm).on("focusin.validate focusout.validate keyup.validate",":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], [type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], [type='radio'], [type='checkbox']",b).on("click.validate","select, option, [type='radio'], [type='checkbox']",b),this.settings.invalidHandler&&a(this.currentForm).on("invalid-form.validate",this.settings.invalidHandler),a(this.currentForm).find("[required], [data-rule-required], .required").attr("aria-required","true")},form:function(){return this.checkForm(),a.extend(this.submitted,this.errorMap),this.invalid=a.extend({},this.errorMap),this.valid()||a(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var a=0,b=this.currentElements=this.elements();b[a];a++)this.check(b[a]);return this.valid()},element:function(b){var c=this.clean(b),d=this.validationTargetFor(c),e=!0;return this.lastElement=d,void 0===d?delete this.invalid[c.name]:(this.prepareElement(d),this.currentElements=a(d),e=this.check(d)!==!1,e?delete this.invalid[d.name]:this.invalid[d.name]=!0),a(b).attr("aria-invalid",!e),this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),e},showErrors:function(b){if(b){a.extend(this.errorMap,b),this.errorList=[];for(var c in b)this.errorList.push({message:b[c],element:this.findByName(c)[0]});this.successList=a.grep(this.successList,function(a){return!(a.name in b)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){a.fn.resetForm&&a(this.currentForm).resetForm(),this.submitted={},this.lastElement=null,this.prepareForm(),this.hideErrors();var b,c=this.elements().removeData("previousValue").removeAttr("aria-invalid");if(this.settings.unhighlight)for(b=0;c[b];b++)this.settings.unhighlight.call(this,c[b],this.settings.errorClass,"");else c.removeClass(this.settings.errorClass)},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(a){var b,c=0;for(b in a)c++;return c},hideErrors:function(){this.hideThese(this.toHide)},hideThese:function(a){a.not(this.containers).text(""),this.addWrapper(a).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{a(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(b){}},findLastActive:function(){var b=this.lastActive;return b&&1===a.grep(this.errorList,function(a){return a.element.name===b.name}).length&&b},elements:function(){var b=this,c={};return a(this.currentForm).find("input, select, textarea").not(":submit, :reset, :image, :disabled").not(this.settings.ignore).filter(function(){return!this.name&&b.settings.debug&&window.console&&console.error("%o has no name assigned",this),this.name in c||!b.objectLength(a(this).rules())?!1:(c[this.name]=!0,!0)})},clean:function(b){return a(b)[0]},errors:function(){var b=this.settings.errorClass.split(" ").join(".");return a(this.settings.errorElement+"."+b,this.errorContext)},reset:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=a([]),this.toHide=a([]),this.currentElements=a([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(a){this.reset(),this.toHide=this.errorsFor(a)},elementValue:function(b){var c,d=a(b),e=b.type;return"radio"===e||"checkbox"===e?this.findByName(b.name).filter(":checked").val():"number"===e&&"undefined"!=typeof b.validity?b.validity.badInput?!1:d.val():(c=d.val(),"string"==typeof c?c.replace(/\r/g,""):c)},check:function(b){b=this.validationTargetFor(this.clean(b));var c,d,e,f=a(b).rules(),g=a.map(f,function(a,b){return b}).length,h=!1,i=this.elementValue(b);for(d in f){e={method:d,parameters:f[d]};try{if(c=a.validator.methods[d].call(this,i,b,e.parameters),"dependency-mismatch"===c&&1===g){h=!0;continue}if(h=!1,"pending"===c)return void(this.toHide=this.toHide.not(this.errorsFor(b)));if(!c)return this.formatAndAdd(b,e),!1}catch(j){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+b.id+", check the '"+e.method+"' method.",j),j instanceof TypeError&&(j.message+=".  Exception occurred when checking element "+b.id+", check the '"+e.method+"' method."),j}}if(!h)return this.objectLength(f)&&this.successList.push(b),!0},customDataMessage:function(b,c){return a(b).data("msg"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase())||a(b).data("msg")},customMessage:function(a,b){var c=this.settings.messages[a];return c&&(c.constructor===String?c:c[b])},findDefined:function(){for(var a=0;a<arguments.length;a++)if(void 0!==arguments[a])return arguments[a];return void 0},defaultMessage:function(b,c){return this.findDefined(this.customMessage(b.name,c),this.customDataMessage(b,c),!this.settings.ignoreTitle&&b.title||void 0,a.validator.messages[c],"<strong>Warning: No message defined for "+b.name+"</strong>")},formatAndAdd:function(b,c){var d=this.defaultMessage(b,c.method),e=/\$?\{(\d+)\}/g;"function"==typeof d?d=d.call(this,c.parameters,b):e.test(d)&&(d=a.validator.format(d.replace(e,"{$1}"),c.parameters)),this.errorList.push({message:d,element:b,method:c.method}),this.errorMap[b.name]=d,this.submitted[b.name]=d},addWrapper:function(a){return this.settings.wrapper&&(a=a.add(a.parent(this.settings.wrapper))),a},defaultShowErrors:function(){var a,b,c;for(a=0;this.errorList[a];a++)c=this.errorList[a],this.settings.highlight&&this.settings.highlight.call(this,c.element,this.settings.errorClass,this.settings.validClass),this.showLabel(c.element,c.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(a=0;this.successList[a];a++)this.showLabel(this.successList[a]);if(this.settings.unhighlight)for(a=0,b=this.validElements();b[a];a++)this.settings.unhighlight.call(this,b[a],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return a(this.errorList).map(function(){return this.element})},showLabel:function(b,c){var d,e,f,g=this.errorsFor(b),h=this.idOrName(b),i=a(b).attr("aria-describedby");g.length?(g.removeClass(this.settings.validClass).addClass(this.settings.errorClass),g.html(c)):(g=a("<"+this.settings.errorElement+">").attr("id",h+"-error").addClass(this.settings.errorClass).html(c||""),d=g,this.settings.wrapper&&(d=g.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(d):this.settings.errorPlacement?this.settings.errorPlacement(d,a(b)):d.insertAfter(b),g.is("label")?g.attr("for",h):0===g.parents("label[for='"+h+"']").length&&(f=g.attr("id").replace(/(:|\.|\[|\]|\$)/g,"\\$1"),i?i.match(new RegExp("\\b"+f+"\\b"))||(i+=" "+f):i=f,a(b).attr("aria-describedby",i),e=this.groups[b.name],e&&a.each(this.groups,function(b,c){c===e&&a("[name='"+b+"']",this.currentForm).attr("aria-describedby",g.attr("id"))}))),!c&&this.settings.success&&(g.text(""),"string"==typeof this.settings.success?g.addClass(this.settings.success):this.settings.success(g,b)),this.toShow=this.toShow.add(g)},errorsFor:function(b){var c=this.idOrName(b),d=a(b).attr("aria-describedby"),e="label[for='"+c+"'], label[for='"+c+"'] *";return d&&(e=e+", #"+d.replace(/\s+/g,", #")),this.errors().filter(e)},idOrName:function(a){return this.groups[a.name]||(this.checkable(a)?a.name:a.id||a.name)},validationTargetFor:function(b){return this.checkable(b)&&(b=this.findByName(b.name)),a(b).not(this.settings.ignore)[0]},checkable:function(a){return/radio|checkbox/i.test(a.type)},findByName:function(b){return a(this.currentForm).find("[name='"+b+"']")},getLength:function(b,c){switch(c.nodeName.toLowerCase()){case"select":return a("option:selected",c).length;case"input":if(this.checkable(c))return this.findByName(c.name).filter(":checked").length}return b.length},depend:function(a,b){return this.dependTypes[typeof a]?this.dependTypes[typeof a](a,b):!0},dependTypes:{"boolean":function(a){return a},string:function(b,c){return!!a(b,c.form).length},"function":function(a,b){return a(b)}},optional:function(b){var c=this.elementValue(b);return!a.validator.methods.required.call(this,c,b)&&"dependency-mismatch"},startRequest:function(a){this.pending[a.name]||(this.pendingRequest++,this.pending[a.name]=!0)},stopRequest:function(b,c){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[b.name],c&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(a(this.currentForm).submit(),this.formSubmitted=!1):!c&&0===this.pendingRequest&&this.formSubmitted&&(a(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(b){return a.data(b,"previousValue")||a.data(b,"previousValue",{old:null,valid:!0,message:this.defaultMessage(b,"remote")})},destroy:function(){this.resetForm(),a(this.currentForm).off(".validate").removeData("validator")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(b,c){b.constructor===String?this.classRuleSettings[b]=c:a.extend(this.classRuleSettings,b)},classRules:function(b){var c={},d=a(b).attr("class");return d&&a.each(d.split(" "),function(){this in a.validator.classRuleSettings&&a.extend(c,a.validator.classRuleSettings[this])}),c},normalizeAttributeRule:function(a,b,c,d){/min|max/.test(c)&&(null===b||/number|range|text/.test(b))&&(d=Number(d),isNaN(d)&&(d=void 0)),d||0===d?a[c]=d:b===c&&"range"!==b&&(a[c]=!0)},attributeRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)"required"===c?(d=b.getAttribute(c),""===d&&(d=!0),d=!!d):d=f.attr(c),this.normalizeAttributeRule(e,g,c,d);return e.maxlength&&/-1|2147483647|524288/.test(e.maxlength)&&delete e.maxlength,e},dataRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)d=f.data("rule"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase()),this.normalizeAttributeRule(e,g,c,d);return e},staticRules:function(b){var c={},d=a.data(b.form,"validator");return d.settings.rules&&(c=a.validator.normalizeRule(d.settings.rules[b.name])||{}),c},normalizeRules:function(b,c){return a.each(b,function(d,e){if(e===!1)return void delete b[d];if(e.param||e.depends){var f=!0;switch(typeof e.depends){case"string":f=!!a(e.depends,c.form).length;break;case"function":f=e.depends.call(c,c)}f?b[d]=void 0!==e.param?e.param:!0:delete b[d]}}),a.each(b,function(d,e){b[d]=a.isFunction(e)?e(c):e}),a.each(["minlength","maxlength"],function(){b[this]&&(b[this]=Number(b[this]))}),a.each(["rangelength","range"],function(){var c;b[this]&&(a.isArray(b[this])?b[this]=[Number(b[this][0]),Number(b[this][1])]:"string"==typeof b[this]&&(c=b[this].replace(/[\[\]]/g,"").split(/[\s,]+/),b[this]=[Number(c[0]),Number(c[1])]))}),a.validator.autoCreateRanges&&(null!=b.min&&null!=b.max&&(b.range=[b.min,b.max],delete b.min,delete b.max),null!=b.minlength&&null!=b.maxlength&&(b.rangelength=[b.minlength,b.maxlength],delete b.minlength,delete b.maxlength)),b},normalizeRule:function(b){if("string"==typeof b){var c={};a.each(b.split(/\s/),function(){c[this]=!0}),b=c}return b},addMethod:function(b,c,d){a.validator.methods[b]=c,a.validator.messages[b]=void 0!==d?d:a.validator.messages[b],c.length<3&&a.validator.addClassRules(b,a.validator.normalizeRule(b))},methods:{required:function(b,c,d){if(!this.depend(d,c))return"dependency-mismatch";if("select"===c.nodeName.toLowerCase()){var e=a(c).val();return e&&e.length>0}return this.checkable(c)?this.getLength(b,c)>0:b.length>0},email:function(a,b){return this.optional(b)||/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(a)},url:function(a,b){return this.optional(b)||/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(a)},date:function(a,b){return this.optional(b)||!/Invalid|NaN/.test(new Date(a).toString())},dateISO:function(a,b){return this.optional(b)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(a)},number:function(a,b){return this.optional(b)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(a)},digits:function(a,b){return this.optional(b)||/^\d+$/.test(a)},creditcard:function(a,b){if(this.optional(b))return"dependency-mismatch";if(/[^0-9 \-]+/.test(a))return!1;var c,d,e=0,f=0,g=!1;if(a=a.replace(/\D/g,""),a.length<13||a.length>19)return!1;for(c=a.length-1;c>=0;c--)d=a.charAt(c),f=parseInt(d,10),g&&(f*=2)>9&&(f-=9),e+=f,g=!g;return e%10===0},minlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d},maxlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||d>=e},rangelength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d[0]&&e<=d[1]},min:function(a,b,c){return this.optional(b)||a>=c},max:function(a,b,c){return this.optional(b)||c>=a},range:function(a,b,c){return this.optional(b)||a>=c[0]&&a<=c[1]},equalTo:function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.off(".validate-equalTo").on("blur.validate-equalTo",function(){a(c).valid()}),b===e.val()},remote:function(b,c,d){if(this.optional(c))return"dependency-mismatch";var e,f,g=this.previousValue(c);return this.settings.messages[c.name]||(this.settings.messages[c.name]={}),g.originalMessage=this.settings.messages[c.name].remote,this.settings.messages[c.name].remote=g.message,d="string"==typeof d&&{url:d}||d,g.old===b?g.valid:(g.old=b,e=this,this.startRequest(c),f={},f[c.name]=b,a.ajax(a.extend(!0,{mode:"abort",port:"validate"+c.name,dataType:"json",data:f,context:e.currentForm,success:function(d){var f,h,i,j=d===!0||"true"===d;e.settings.messages[c.name].remote=g.originalMessage,j?(i=e.formSubmitted,e.prepareElement(c),e.formSubmitted=i,e.successList.push(c),delete e.invalid[c.name],e.showErrors()):(f={},h=d||e.defaultMessage(c,"remote"),f[c.name]=g.message=a.isFunction(h)?h(b):h,e.invalid[c.name]=!0,e.showErrors(f)),g.valid=j,e.stopRequest(c,j)}},d)),"pending")}}});var b,c={};a.ajaxPrefilter?a.ajaxPrefilter(function(a,b,d){var e=a.port;"abort"===a.mode&&(c[e]&&c[e].abort(),c[e]=d)}):(b=a.ajax,a.ajax=function(d){var e=("mode"in d?d:a.ajaxSettings).mode,f=("port"in d?d:a.ajaxSettings).port;return"abort"===e?(c[f]&&c[f].abort(),c[f]=b.apply(this,arguments),c[f]):b.apply(this,arguments)})});;
/* NUGET: BEGIN LICENSE TEXT
 *
 * Microsoft grants you the right to use these script files for the sole
 * purpose of either: (i) interacting through your browser with the Microsoft
 * website or online service, subject to the applicable licensing or use
 * terms; or (ii) using the files as included with a Microsoft product subject
 * to that product's license terms. Microsoft reserves all other rights to the
 * files not expressly granted by Microsoft, whether by implication, estoppel
 * or otherwise. Insofar as a script file is dual licensed under GPL,
 * Microsoft neither took the code under GPL nor distributes it thereunder but
 * under the terms set out in this paragraph. All notices and licenses
 * below are for informational purposes only.
 *
 * NUGET: END LICENSE TEXT */
/*
** Unobtrusive validation support library for jQuery and jQuery Validate
** Copyright (C) Microsoft Corporation. All rights reserved.
*/
(function(a){var d=a.validator,b,e="unobtrusiveValidation";function c(a,b,c){a.rules[b]=c;if(a.message)a.messages[b]=a.message}function j(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function f(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function h(a){return a.substr(0,a.lastIndexOf(".")+1)}function g(a,b){if(a.indexOf("*.")===0)a=a.replace("*.",b);return a}function m(c,e){var b=a(this).find("[data-valmsg-for='"+f(e[0].name)+"']"),d=b.attr("data-valmsg-replace"),g=d?a.parseJSON(d)!==false:null;b.removeClass("field-validation-valid").addClass("field-validation-error");c.data("unobtrusiveContainer",b);if(g){b.empty();c.removeClass("input-validation-error").appendTo(b)}else c.hide()}function l(e,d){var c=a(this).find("[data-valmsg-summary=true]"),b=c.find("ul");if(b&&b.length&&d.errorList.length){b.empty();c.addClass("validation-summary-errors").removeClass("validation-summary-valid");a.each(d.errorList,function(){a("<li />").html(this.message).appendTo(b)})}}function k(d){var b=d.data("unobtrusiveContainer"),c=b.attr("data-valmsg-replace"),e=c?a.parseJSON(c):null;if(b){b.addClass("field-validation-valid").removeClass("field-validation-error");d.removeData("unobtrusiveContainer");e&&b.empty()}}function n(){var b=a(this),c="__jquery_unobtrusive_validation_form_reset";if(b.data(c))return;b.data(c,true);try{b.data("validator").resetForm()}finally{b.removeData(c)}b.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors");b.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}function i(b){var c=a(b),f=c.data(e),i=a.proxy(n,b),g=d.unobtrusive.options||{},h=function(e,d){var c=g[e];c&&a.isFunction(c)&&c.apply(b,d)};if(!f){f={options:{errorClass:g.errorClass||"input-validation-error",errorElement:g.errorElement||"span",errorPlacement:function(){m.apply(b,arguments);h("errorPlacement",arguments)},invalidHandler:function(){l.apply(b,arguments);h("invalidHandler",arguments)},messages:{},rules:{},success:function(){k.apply(b,arguments);h("success",arguments)}},attachValidation:function(){c.off("reset."+e,i).on("reset."+e,i).validate(this.options)},validate:function(){c.validate();return c.valid()}};c.data(e,f)}return f}d.unobtrusive={adapters:[],parseElement:function(b,h){var d=a(b),f=d.parents("form")[0],c,e,g;if(!f)return;c=i(f);c.options.rules[b.name]=e={};c.options.messages[b.name]=g={};a.each(this.adapters,function(){var c="data-val-"+this.name,i=d.attr(c),h={};if(i!==undefined){c+="-";a.each(this.params,function(){h[this]=d.attr(c+this)});this.adapt({element:b,form:f,message:i,params:h,rules:e,messages:g})}});a.extend(e,{__dummy__:true});!h&&c.attachValidation()},parse:function(c){var b=a(c),e=b.parents().addBack().filter("form").add(b.find("form")).has("[data-val=true]");b.find("[data-val=true]").each(function(){d.unobtrusive.parseElement(this,true)});e.each(function(){var a=i(this);a&&a.attachValidation()})}};b=d.unobtrusive.adapters;b.add=function(c,a,b){if(!b){b=a;a=[]}this.push({name:c,params:a,adapt:b});return this};b.addBool=function(a,b){return this.add(a,function(d){c(d,b||a,true)})};b.addMinMax=function(e,g,f,a,d,b){return this.add(e,[d||"min",b||"max"],function(b){var e=b.params.min,d=b.params.max;if(e&&d)c(b,a,[e,d]);else if(e)c(b,g,e);else d&&c(b,f,d)})};b.addSingleVal=function(a,b,d){return this.add(a,[b||"val"],function(e){c(e,d||a,e.params[b])})};d.addMethod("__dummy__",function(){return true});d.addMethod("regex",function(b,c,d){var a;if(this.optional(c))return true;a=(new RegExp(d)).exec(b);return a&&a.index===0&&a[0].length===b.length});d.addMethod("nonalphamin",function(c,d,b){var a;if(b){a=c.match(/\W/g);a=a&&a.length>=b}return a});if(d.methods.extension){b.addSingleVal("accept","mimtype");b.addSingleVal("extension","extension")}else b.addSingleVal("extension","extension","accept");b.addSingleVal("regex","pattern");b.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");b.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range");b.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength");b.add("equalto",["other"],function(b){var i=h(b.element.name),j=b.params.other,d=g(j,i),e=a(b.form).find(":input").filter("[name='"+f(d)+"']")[0];c(b,"equalTo",e)});b.add("required",function(a){(a.element.tagName.toUpperCase()!=="INPUT"||a.element.type.toUpperCase()!=="CHECKBOX")&&c(a,"required",true)});b.add("remote",["url","type","additionalfields"],function(b){var d={url:b.params.url,type:b.params.type||"GET",data:{}},e=h(b.element.name);a.each(j(b.params.additionalfields||b.element.name),function(i,h){var c=g(h,e);d.data[c]=function(){var d=a(b.form).find(":input").filter("[name='"+f(c)+"']");return d.is(":checkbox")?d.filter(":checked").val()||d.filter(":hidden").val()||"":d.is(":radio")?d.filter(":checked").val()||"":d.val()}});c(b,"remote",d)});b.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&c(a,"minlength",a.params.min);a.params.nonalphamin&&c(a,"nonalphamin",a.params.nonalphamin);a.params.regex&&c(a,"regex",a.params.regex)});a(function(){d.unobtrusive.parse(document)})})(jQuery);;
/* NUGET: BEGIN LICENSE TEXT
 *
 * Microsoft grants you the right to use these script files for the sole
 * purpose of either: (i) interacting through your browser with the Microsoft
 * website or online service, subject to the applicable licensing or use
 * terms; or (ii) using the files as included with a Microsoft product subject
 * to that product's license terms. Microsoft reserves all other rights to the
 * files not expressly granted by Microsoft, whether by implication, estoppel
 * or otherwise. Insofar as a script file is dual licensed under GPL,
 * Microsoft neither took the code under GPL nor distributes it thereunder but
 * under the terms set out in this paragraph. All notices and licenses
 * below are for informational purposes only.
 *
 * NUGET: END LICENSE TEXT */
/*
** Unobtrusive Ajax support library for jQuery
** Copyright (C) Microsoft Corporation. All rights reserved.
*/
(function(a){var b="unobtrusiveAjaxClick",d="unobtrusiveAjaxClickTarget",h="unobtrusiveValidation";function c(d,b){var a=window,c=(d||"").split(".");while(a&&c.length)a=a[c.shift()];if(typeof a==="function")return a;b.push(d);return Function.constructor.apply(null,b)}function e(a){return a==="GET"||a==="POST"}function g(b,a){!e(a)&&b.setRequestHeader("X-HTTP-Method-Override",a)}function i(c,b,e){var d;if(e.indexOf("application/x-javascript")!==-1)return;d=(c.getAttribute("data-ajax-mode")||"").toUpperCase();a(c.getAttribute("data-ajax-update")).each(function(f,c){var e;switch(d){case"BEFORE":e=c.firstChild;a("<div />").html(b).contents().each(function(){c.insertBefore(this,e)});break;case"AFTER":a("<div />").html(b).contents().each(function(){c.appendChild(this)});break;case"REPLACE-WITH":a(c).replaceWith(b);break;default:a(c).html(b)}})}function f(b,d){var j,k,f,h;j=b.getAttribute("data-ajax-confirm");if(j&&!window.confirm(j))return;k=a(b.getAttribute("data-ajax-loading"));h=parseInt(b.getAttribute("data-ajax-loading-duration"),10)||0;a.extend(d,{type:b.getAttribute("data-ajax-method")||undefined,url:b.getAttribute("data-ajax-url")||undefined,cache:!!b.getAttribute("data-ajax-cache"),beforeSend:function(d){var a;g(d,f);a=c(b.getAttribute("data-ajax-begin"),["xhr"]).apply(b,arguments);a!==false&&k.show(h);return a},complete:function(){k.hide(h);c(b.getAttribute("data-ajax-complete"),["xhr","status"]).apply(b,arguments)},success:function(a,e,d){i(b,a,d.getResponseHeader("Content-Type")||"text/html");c(b.getAttribute("data-ajax-success"),["data","status","xhr"]).apply(b,arguments)},error:function(){c(b.getAttribute("data-ajax-failure"),["xhr","status","error"]).apply(b,arguments)}});d.data.push({name:"X-Requested-With",value:"XMLHttpRequest"});f=d.type.toUpperCase();if(!e(f)){d.type="POST";d.data.push({name:"X-HTTP-Method-Override",value:f})}a.ajax(d)}function j(c){var b=a(c).data(h);return!b||!b.validate||b.validate()}a(document).on("click","a[data-ajax=true]",function(a){a.preventDefault();f(this,{url:this.href,type:"GET",data:[]})});a(document).on("click","form[data-ajax=true] input[type=image]",function(c){var g=c.target.name,e=a(c.target),f=a(e.parents("form")[0]),d=e.offset();f.data(b,[{name:g+".x",value:Math.round(c.pageX-d.left)},{name:g+".y",value:Math.round(c.pageY-d.top)}]);setTimeout(function(){f.removeData(b)},0)});a(document).on("click","form[data-ajax=true] :submit",function(e){var g=e.currentTarget.name,f=a(e.target),c=a(f.parents("form")[0]);c.data(b,g?[{name:g,value:e.currentTarget.value}]:[]);c.data(d,f);setTimeout(function(){c.removeData(b);c.removeData(d)},0)});a(document).on("submit","form[data-ajax=true]",function(h){var e=a(this).data(b)||[],c=a(this).data(d),g=c&&c.hasClass("cancel");h.preventDefault();if(!g&&!j(this))return;f(this,{url:this.action,type:this.method||"GET",data:e.concat(a(this).serializeArray())})})})(jQuery);;
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('7(A 3c.3q!=="9"){3c.3q=9(e){9 t(){}t.5S=e;p 5R t}}(9(e,t,n){h r={1N:9(t,n){h r=c;r.$k=e(n);r.6=e.4M({},e.37.2B.6,r.$k.v(),t);r.2A=t;r.4L()},4L:9(){9 r(e){h n,r="";7(A t.6.33==="9"){t.6.33.R(c,[e])}l{1A(n 38 e.d){7(e.d.5M(n)){r+=e.d[n].1K}}t.$k.2y(r)}t.3t()}h t=c,n;7(A t.6.2H==="9"){t.6.2H.R(c,[t.$k])}7(A t.6.2O==="2Y"){n=t.6.2O;e.5K(n,r)}l{t.3t()}},3t:9(){h e=c;e.$k.v("d-4I",e.$k.2x("2w")).v("d-4F",e.$k.2x("H"));e.$k.z({2u:0});e.2t=e.6.q;e.4E();e.5v=0;e.1X=14;e.23()},23:9(){h e=c;7(e.$k.25().N===0){p b}e.1M();e.4C();e.$S=e.$k.25();e.E=e.$S.N;e.4B();e.$G=e.$k.17(".d-1K");e.$K=e.$k.17(".d-1p");e.3u="U";e.13=0;e.26=[0];e.m=0;e.4A();e.4z()},4z:9(){h e=c;e.2V();e.2W();e.4t();e.30();e.4r();e.4q();e.2p();e.4o();7(e.6.2o!==b){e.4n(e.6.2o)}7(e.6.O===j){e.6.O=4Q}e.19();e.$k.17(".d-1p").z("4i","4h");7(!e.$k.2m(":3n")){e.3o()}l{e.$k.z("2u",1)}e.5O=b;e.2l();7(A e.6.3s==="9"){e.6.3s.R(c,[e.$k])}},2l:9(){h e=c;7(e.6.1Z===j){e.1Z()}7(e.6.1B===j){e.1B()}e.4g();7(A e.6.3w==="9"){e.6.3w.R(c,[e.$k])}},3x:9(){h e=c;7(A e.6.3B==="9"){e.6.3B.R(c,[e.$k])}e.3o();e.2V();e.2W();e.4f();e.30();e.2l();7(A e.6.3D==="9"){e.6.3D.R(c,[e.$k])}},3F:9(){h e=c;t.1c(9(){e.3x()},0)},3o:9(){h e=c;7(e.$k.2m(":3n")===b){e.$k.z({2u:0});t.18(e.1C);t.18(e.1X)}l{p b}e.1X=t.4d(9(){7(e.$k.2m(":3n")){e.3F();e.$k.4b({2u:1},2M);t.18(e.1X)}},5x)},4B:9(){h e=c;e.$S.5n(\'<L H="d-1p">\').4a(\'<L H="d-1K"></L>\');e.$k.17(".d-1p").4a(\'<L H="d-1p-49">\');e.1H=e.$k.17(".d-1p-49");e.$k.z("4i","4h")},1M:9(){h e=c,t=e.$k.1I(e.6.1M),n=e.$k.1I(e.6.2i);7(!t){e.$k.I(e.6.1M)}7(!n){e.$k.I(e.6.2i)}},2V:9(){h t=c,n,r;7(t.6.2Z===b){p b}7(t.6.48===j){t.6.q=t.2t=1;t.6.1h=b;t.6.1s=b;t.6.1O=b;t.6.22=b;t.6.1Q=b;t.6.1R=b;p b}n=e(t.6.47).1f();7(n>(t.6.1s[0]||t.2t)){t.6.q=t.2t}7(t.6.1h!==b){t.6.1h.5g(9(e,t){p e[0]-t[0]});1A(r=0;r<t.6.1h.N;r+=1){7(t.6.1h[r][0]<=n){t.6.q=t.6.1h[r][1]}}}l{7(n<=t.6.1s[0]&&t.6.1s!==b){t.6.q=t.6.1s[1]}7(n<=t.6.1O[0]&&t.6.1O!==b){t.6.q=t.6.1O[1]}7(n<=t.6.22[0]&&t.6.22!==b){t.6.q=t.6.22[1]}7(n<=t.6.1Q[0]&&t.6.1Q!==b){t.6.q=t.6.1Q[1]}7(n<=t.6.1R[0]&&t.6.1R!==b){t.6.q=t.6.1R[1]}}7(t.6.q>t.E&&t.6.46===j){t.6.q=t.E}},4r:9(){h n=c,r,i;7(n.6.2Z!==j){p b}i=e(t).1f();n.3d=9(){7(e(t).1f()!==i){7(n.6.O!==b){t.18(n.1C)}t.5d(r);r=t.1c(9(){i=e(t).1f();n.3x()},n.6.45)}};e(t).44(n.3d)},4f:9(){h e=c;e.2g(e.m);7(e.6.O!==b){e.3j()}},43:9(){h t=c,n=0,r=t.E-t.6.q;t.$G.2f(9(i){h s=e(c);s.z({1f:t.M}).v("d-1K",3p(i));7(i%t.6.q===0||i===r){7(!(i>r)){n+=1}}s.v("d-24",n)})},42:9(){h e=c,t=e.$G.N*e.M;e.$K.z({1f:t*2,T:0});e.43()},2W:9(){h e=c;e.40();e.42();e.3Z();e.3v()},40:9(){h e=c;e.M=1F.4O(e.$k.1f()/e.6.q)},3v:9(){h e=c,t=(e.E*e.M-e.6.q*e.M)*-1;7(e.6.q>e.E){e.D=0;t=0;e.3z=0}l{e.D=e.E-e.6.q;e.3z=t}p t},3Y:9(){p 0},3Z:9(){h t=c,n=0,r=0,i,s,o;t.J=[0];t.3E=[];1A(i=0;i<t.E;i+=1){r+=t.M;t.J.2D(-r);7(t.6.12===j){s=e(t.$G[i]);o=s.v("d-24");7(o!==n){t.3E[n]=t.J[i];n=o}}}},4t:9(){h t=c;7(t.6.2a===j||t.6.1v===j){t.B=e(\'<L H="d-5A"/>\').5m("5l",!t.F.15).5c(t.$k)}7(t.6.1v===j){t.3T()}7(t.6.2a===j){t.3S()}},3S:9(){h t=c,n=e(\'<L H="d-4U"/>\');t.B.1o(n);t.1u=e("<L/>",{"H":"d-1n",2y:t.6.2U[0]||""});t.1q=e("<L/>",{"H":"d-U",2y:t.6.2U[1]||""});n.1o(t.1u).1o(t.1q);n.w("2X.B 21.B",\'L[H^="d"]\',9(e){e.1l()});n.w("2n.B 28.B",\'L[H^="d"]\',9(n){n.1l();7(e(c).1I("d-U")){t.U()}l{t.1n()}})},3T:9(){h t=c;t.1k=e(\'<L H="d-1v"/>\');t.B.1o(t.1k);t.1k.w("2n.B 28.B",".d-1j",9(n){n.1l();7(3p(e(c).v("d-1j"))!==t.m){t.1g(3p(e(c).v("d-1j")),j)}})},3P:9(){h t=c,n,r,i,s,o,u;7(t.6.1v===b){p b}t.1k.2y("");n=0;r=t.E-t.E%t.6.q;1A(s=0;s<t.E;s+=1){7(s%t.6.q===0){n+=1;7(r===s){i=t.E-t.6.q}o=e("<L/>",{"H":"d-1j"});u=e("<3N></3N>",{4R:t.6.39===j?n:"","H":t.6.39===j?"d-59":""});o.1o(u);o.v("d-1j",r===s?i:s);o.v("d-24",n);t.1k.1o(o)}}t.35()},35:9(){h t=c;7(t.6.1v===b){p b}t.1k.17(".d-1j").2f(9(){7(e(c).v("d-24")===e(t.$G[t.m]).v("d-24")){t.1k.17(".d-1j").Z("2d");e(c).I("2d")}})},3e:9(){h e=c;7(e.6.2a===b){p b}7(e.6.2e===b){7(e.m===0&&e.D===0){e.1u.I("1b");e.1q.I("1b")}l 7(e.m===0&&e.D!==0){e.1u.I("1b");e.1q.Z("1b")}l 7(e.m===e.D){e.1u.Z("1b");e.1q.I("1b")}l 7(e.m!==0&&e.m!==e.D){e.1u.Z("1b");e.1q.Z("1b")}}},30:9(){h e=c;e.3P();e.3e();7(e.B){7(e.6.q>=e.E){e.B.3K()}l{e.B.3J()}}},55:9(){h e=c;7(e.B){e.B.3k()}},U:9(e){h t=c;7(t.1E){p b}t.m+=t.6.12===j?t.6.q:1;7(t.m>t.D+(t.6.12===j?t.6.q-1:0)){7(t.6.2e===j){t.m=0;e="2k"}l{t.m=t.D;p b}}t.1g(t.m,e)},1n:9(e){h t=c;7(t.1E){p b}7(t.6.12===j&&t.m>0&&t.m<t.6.q){t.m=0}l{t.m-=t.6.12===j?t.6.q:1}7(t.m<0){7(t.6.2e===j){t.m=t.D;e="2k"}l{t.m=0;p b}}t.1g(t.m,e)},1g:9(e,n,r){h i=c,s;7(i.1E){p b}7(A i.6.1Y==="9"){i.6.1Y.R(c,[i.$k])}7(e>=i.D){e=i.D}l 7(e<=0){e=0}i.m=i.d.m=e;7(i.6.2o!==b&&r!=="4e"&&i.6.q===1&&i.F.1x===j){i.1t(0);7(i.F.1x===j){i.1L(i.J[e])}l{i.1r(i.J[e],1)}i.2r();i.4l();p b}s=i.J[e];7(i.F.1x===j){i.1T=b;7(n===j){i.1t("1w");t.1c(9(){i.1T=j},i.6.1w)}l 7(n==="2k"){i.1t(i.6.2v);t.1c(9(){i.1T=j},i.6.2v)}l{i.1t("1m");t.1c(9(){i.1T=j},i.6.1m)}i.1L(s)}l{7(n===j){i.1r(s,i.6.1w)}l 7(n==="2k"){i.1r(s,i.6.2v)}l{i.1r(s,i.6.1m)}}i.2r()},2g:9(e){h t=c;7(A t.6.1Y==="9"){t.6.1Y.R(c,[t.$k])}7(e>=t.D||e===-1){e=t.D}l 7(e<=0){e=0}t.1t(0);7(t.F.1x===j){t.1L(t.J[e])}l{t.1r(t.J[e],1)}t.m=t.d.m=e;t.2r()},2r:9(){h e=c;e.26.2D(e.m);e.13=e.d.13=e.26[e.26.N-2];e.26.5f(0);7(e.13!==e.m){e.35();e.3e();e.2l();7(e.6.O!==b){e.3j()}}7(A e.6.3y==="9"&&e.13!==e.m){e.6.3y.R(c,[e.$k])}},X:9(){h e=c;e.3A="X";t.18(e.1C)},3j:9(){h e=c;7(e.3A!=="X"){e.19()}},19:9(){h e=c;e.3A="19";7(e.6.O===b){p b}t.18(e.1C);e.1C=t.4d(9(){e.U(j)},e.6.O)},1t:9(e){h t=c;7(e==="1m"){t.$K.z(t.2z(t.6.1m))}l 7(e==="1w"){t.$K.z(t.2z(t.6.1w))}l 7(A e!=="2Y"){t.$K.z(t.2z(e))}},2z:9(e){p{"-1G-1a":"2C "+e+"1z 2s","-1W-1a":"2C "+e+"1z 2s","-o-1a":"2C "+e+"1z 2s",1a:"2C "+e+"1z 2s"}},3H:9(){p{"-1G-1a":"","-1W-1a":"","-o-1a":"",1a:""}},3I:9(e){p{"-1G-P":"1i("+e+"V, C, C)","-1W-P":"1i("+e+"V, C, C)","-o-P":"1i("+e+"V, C, C)","-1z-P":"1i("+e+"V, C, C)",P:"1i("+e+"V, C,C)"}},1L:9(e){h t=c;t.$K.z(t.3I(e))},3L:9(e){h t=c;t.$K.z({T:e})},1r:9(e,t){h n=c;n.29=b;n.$K.X(j,j).4b({T:e},{54:t||n.6.1m,3M:9(){n.29=j}})},4E:9(){h e=c,r="1i(C, C, C)",i=n.56("L"),s,o,u,a;i.2w.3O="  -1W-P:"+r+"; -1z-P:"+r+"; -o-P:"+r+"; -1G-P:"+r+"; P:"+r;s=/1i\\(C, C, C\\)/g;o=i.2w.3O.5i(s);u=o!==14&&o.N===1;a="5z"38 t||t.5Q.4P;e.F={1x:u,15:a}},4q:9(){h e=c;7(e.6.27!==b||e.6.1U!==b){e.3Q();e.3R()}},4C:9(){h e=c,t=["s","e","x"];e.16={};7(e.6.27===j&&e.6.1U===j){t=["2X.d 21.d","2N.d 3U.d","2n.d 3V.d 28.d"]}l 7(e.6.27===b&&e.6.1U===j){t=["2X.d","2N.d","2n.d 3V.d"]}l 7(e.6.27===j&&e.6.1U===b){t=["21.d","3U.d","28.d"]}e.16.3W=t[0];e.16.2K=t[1];e.16.2J=t[2]},3R:9(){h t=c;t.$k.w("5y.d",9(e){e.1l()});t.$k.w("21.3X",9(t){p e(t.1d).2m("5C, 5E, 5F, 5N")})},3Q:9(){9 s(e){7(e.2b!==W){p{x:e.2b[0].2c,y:e.2b[0].41}}7(e.2b===W){7(e.2c!==W){p{x:e.2c,y:e.41}}7(e.2c===W){p{x:e.52,y:e.53}}}}9 o(t){7(t==="w"){e(n).w(r.16.2K,a);e(n).w(r.16.2J,f)}l 7(t==="Q"){e(n).Q(r.16.2K);e(n).Q(r.16.2J)}}9 u(n){h u=n.3h||n||t.3g,a;7(u.5a===3){p b}7(r.E<=r.6.q){p}7(r.29===b&&!r.6.3f){p b}7(r.1T===b&&!r.6.3f){p b}7(r.6.O!==b){t.18(r.1C)}7(r.F.15!==j&&!r.$K.1I("3b")){r.$K.I("3b")}r.11=0;r.Y=0;e(c).z(r.3H());a=e(c).2h();i.2S=a.T;i.2R=s(u).x-a.T;i.2P=s(u).y-a.5o;o("w");i.2j=b;i.2L=u.1d||u.4c}9 a(o){h u=o.3h||o||t.3g,a,f;r.11=s(u).x-i.2R;r.2I=s(u).y-i.2P;r.Y=r.11-i.2S;7(A r.6.2E==="9"&&i.3C!==j&&r.Y!==0){i.3C=j;r.6.2E.R(r,[r.$k])}7((r.Y>8||r.Y<-8)&&r.F.15===j){7(u.1l!==W){u.1l()}l{u.5L=b}i.2j=j}7((r.2I>10||r.2I<-10)&&i.2j===b){e(n).Q("2N.d")}a=9(){p r.Y/5};f=9(){p r.3z+r.Y/5};r.11=1F.3v(1F.3Y(r.11,a()),f());7(r.F.1x===j){r.1L(r.11)}l{r.3L(r.11)}}9 f(n){h s=n.3h||n||t.3g,u,a,f;s.1d=s.1d||s.4c;i.3C=b;7(r.F.15!==j){r.$K.Z("3b")}7(r.Y<0){r.1y=r.d.1y="T"}l{r.1y=r.d.1y="3i"}7(r.Y!==0){u=r.4j();r.1g(u,b,"4e");7(i.2L===s.1d&&r.F.15!==j){e(s.1d).w("3a.4k",9(t){t.4S();t.4T();t.1l();e(t.1d).Q("3a.4k")});a=e.4N(s.1d,"4V").3a;f=a.4W();a.4X(0,0,f)}}o("Q")}h r=c,i={2R:0,2P:0,4Y:0,2S:0,2h:14,4Z:14,50:14,2j:14,51:14,2L:14};r.29=j;r.$k.w(r.16.3W,".d-1p",u)},4j:9(){h e=c,t=e.4m();7(t>e.D){e.m=e.D;t=e.D}l 7(e.11>=0){t=0;e.m=0}p t},4m:9(){h t=c,n=t.6.12===j?t.3E:t.J,r=t.11,i=14;e.2f(n,9(s,o){7(r-t.M/20>n[s+1]&&r-t.M/20<o&&t.34()==="T"){i=o;7(t.6.12===j){t.m=e.4p(i,t.J)}l{t.m=s}}l 7(r+t.M/20<o&&r+t.M/20>(n[s+1]||n[s]-t.M)&&t.34()==="3i"){7(t.6.12===j){i=n[s+1]||n[n.N-1];t.m=e.4p(i,t.J)}l{i=n[s+1];t.m=s+1}}});p t.m},34:9(){h e=c,t;7(e.Y<0){t="3i";e.3u="U"}l{t="T";e.3u="1n"}p t},4A:9(){h e=c;e.$k.w("d.U",9(){e.U()});e.$k.w("d.1n",9(){e.1n()});e.$k.w("d.19",9(t,n){e.6.O=n;e.19();e.32="19"});e.$k.w("d.X",9(){e.X();e.32="X"});e.$k.w("d.1g",9(t,n){e.1g(n)});e.$k.w("d.2g",9(t,n){e.2g(n)})},2p:9(){h e=c;7(e.6.2p===j&&e.F.15!==j&&e.6.O!==b){e.$k.w("57",9(){e.X()});e.$k.w("58",9(){7(e.32!=="X"){e.19()}})}},1Z:9(){h t=c,n,r,i,s,o;7(t.6.1Z===b){p b}1A(n=0;n<t.E;n+=1){r=e(t.$G[n]);7(r.v("d-1e")==="1e"){4s}i=r.v("d-1K");s=r.17(".5b");7(A s.v("1J")!=="2Y"){r.v("d-1e","1e");4s}7(r.v("d-1e")===W){s.3K();r.I("4u").v("d-1e","5e")}7(t.6.4v===j){o=i>=t.m}l{o=j}7(o&&i<t.m+t.6.q&&s.N){t.4w(r,s)}}},4w:9(e,n){9 o(){e.v("d-1e","1e").Z("4u");n.5h("v-1J");7(r.6.4x==="4y"){n.5j(5k)}l{n.3J()}7(A r.6.2T==="9"){r.6.2T.R(c,[r.$k])}}9 u(){i+=1;7(r.2Q(n.3l(0))||s===j){o()}l 7(i<=2q){t.1c(u,2q)}l{o()}}h r=c,i=0,s;7(n.5p("5q")==="5r"){n.z("5s-5t","5u("+n.v("1J")+")");s=j}l{n[0].1J=n.v("1J")}u()},1B:9(){9 s(){h r=e(n.$G[n.m]).2G();n.1H.z("2G",r+"V");7(!n.1H.1I("1B")){t.1c(9(){n.1H.I("1B")},0)}}9 o(){i+=1;7(n.2Q(r.3l(0))){s()}l 7(i<=2q){t.1c(o,2q)}l{n.1H.z("2G","")}}h n=c,r=e(n.$G[n.m]).17("5w"),i;7(r.3l(0)!==W){i=0;o()}l{s()}},2Q:9(e){h t;7(!e.3M){p b}t=A e.4D;7(t!=="W"&&e.4D===0){p b}p j},4g:9(){h t=c,n;7(t.6.2F===j){t.$G.Z("2d")}t.1D=[];1A(n=t.m;n<t.m+t.6.q;n+=1){t.1D.2D(n);7(t.6.2F===j){e(t.$G[n]).I("2d")}}t.d.1D=t.1D},4n:9(e){h t=c;t.4G="d-"+e+"-5B";t.4H="d-"+e+"-38"},4l:9(){9 a(e){p{2h:"5D",T:e+"V"}}h e=c,t=e.4G,n=e.4H,r=e.$G.1S(e.m),i=e.$G.1S(e.13),s=1F.4J(e.J[e.m])+e.J[e.13],o=1F.4J(e.J[e.m])+e.M/2,u="5G 5H 5I 5J";e.1E=j;e.$K.I("d-1P").z({"-1G-P-1P":o+"V","-1W-4K-1P":o+"V","4K-1P":o+"V"});i.z(a(s,10)).I(t).w(u,9(){e.3m=j;i.Q(u);e.31(i,t)});r.I(n).w(u,9(){e.36=j;r.Q(u);e.31(r,n)})},31:9(e,t){h n=c;e.z({2h:"",T:""}).Z(t);7(n.3m&&n.36){n.$K.Z("d-1P");n.3m=b;n.36=b;n.1E=b}},4o:9(){h e=c;e.d={2A:e.2A,5P:e.$k,S:e.$S,G:e.$G,m:e.m,13:e.13,1D:e.1D,15:e.F.15,F:e.F,1y:e.1y}},3G:9(){h r=c;r.$k.Q(".d d 21.3X");e(n).Q(".d d");e(t).Q("44",r.3d)},1V:9(){h e=c;7(e.$k.25().N!==0){e.$K.3r();e.$S.3r().3r();7(e.B){e.B.3k()}}e.3G();e.$k.2x("2w",e.$k.v("d-4I")||"").2x("H",e.$k.v("d-4F"))},5T:9(){h e=c;e.X();t.18(e.1X);e.1V();e.$k.5U()},5V:9(t){h n=c,r=e.4M({},n.2A,t);n.1V();n.1N(r,n.$k)},5W:9(e,t){h n=c,r;7(!e){p b}7(n.$k.25().N===0){n.$k.1o(e);n.23();p b}n.1V();7(t===W||t===-1){r=-1}l{r=t}7(r>=n.$S.N||r===-1){n.$S.1S(-1).5X(e)}l{n.$S.1S(r).5Y(e)}n.23()},5Z:9(e){h t=c,n;7(t.$k.25().N===0){p b}7(e===W||e===-1){n=-1}l{n=e}t.1V();t.$S.1S(n).3k();t.23()}};e.37.2B=9(t){p c.2f(9(){7(e(c).v("d-1N")===j){p b}e(c).v("d-1N",j);h n=3c.3q(r);n.1N(t,c);e.v(c,"2B",n)})};e.37.2B.6={q:5,1h:b,1s:[60,4],1O:[61,3],22:[62,2],1Q:b,1R:[63,1],48:b,46:b,1m:2M,1w:64,2v:65,O:b,2p:b,2a:b,2U:["1n","U"],2e:j,12:b,1v:j,39:b,2Z:j,45:2M,47:t,1M:"d-66",2i:"d-2i",1Z:b,4v:j,4x:"4y",1B:b,2O:b,33:b,3f:j,27:j,1U:j,2F:b,2o:b,3B:b,3D:b,2H:b,3s:b,1Y:b,3y:b,3w:b,2E:b,2T:b}})(67,68,69)',62,382,'||||||options|if||function||false|this|owl||||var||true|elem|else|currentItem|||return|items|||||data|on|||css|typeof|owlControls|0px|maximumItem|itemsAmount|browser|owlItems|class|addClass|positionsInArray|owlWrapper|div|itemWidth|length|autoPlay|transform|off|apply|userItems|left|next|px|undefined|stop|newRelativeX|removeClass||newPosX|scrollPerPage|prevItem|null|isTouch|ev_types|find|clearInterval|play|transition|disabled|setTimeout|target|loaded|width|goTo|itemsCustom|translate3d|page|paginationWrapper|preventDefault|slideSpeed|prev|append|wrapper|buttonNext|css2slide|itemsDesktop|swapSpeed|buttonPrev|pagination|paginationSpeed|support3d|dragDirection|ms|for|autoHeight|autoPlayInterval|visibleItems|isTransition|Math|webkit|wrapperOuter|hasClass|src|item|transition3d|baseClass|init|itemsDesktopSmall|origin|itemsTabletSmall|itemsMobile|eq|isCss3Finish|touchDrag|unWrap|moz|checkVisible|beforeMove|lazyLoad||mousedown|itemsTablet|setVars|roundPages|children|prevArr|mouseDrag|mouseup|isCssFinish|navigation|touches|pageX|active|rewindNav|each|jumpTo|position|theme|sliding|rewind|eachMoveUpdate|is|touchend|transitionStyle|stopOnHover|100|afterGo|ease|orignalItems|opacity|rewindSpeed|style|attr|html|addCssSpeed|userOptions|owlCarousel|all|push|startDragging|addClassActive|height|beforeInit|newPosY|end|move|targetElement|200|touchmove|jsonPath|offsetY|completeImg|offsetX|relativePos|afterLazyLoad|navigationText|updateItems|calculateAll|touchstart|string|responsive|updateControls|clearTransStyle|hoverStatus|jsonSuccess|moveDirection|checkPagination|endCurrent|fn|in|paginationNumbers|click|grabbing|Object|resizer|checkNavigation|dragBeforeAnimFinish|event|originalEvent|right|checkAp|remove|get|endPrev|visible|watchVisibility|Number|create|unwrap|afterInit|logIn|playDirection|max|afterAction|updateVars|afterMove|maximumPixels|apStatus|beforeUpdate|dragging|afterUpdate|pagesInArray|reload|clearEvents|removeTransition|doTranslate|show|hide|css2move|complete|span|cssText|updatePagination|gestures|disabledEvents|buildButtons|buildPagination|mousemove|touchcancel|start|disableTextSelect|min|loops|calculateWidth|pageY|appendWrapperSizes|appendItemsSizes|resize|responsiveRefreshRate|itemsScaleUp|responsiveBaseWidth|singleItem|outer|wrap|animate|srcElement|setInterval|drag|updatePosition|onVisibleItems|block|display|getNewPosition|disable|singleItemTransition|closestItem|transitionTypes|owlStatus|inArray|moveEvents|response|continue|buildControls|loading|lazyFollow|lazyPreload|lazyEffect|fade|onStartup|customEvents|wrapItems|eventTypes|naturalWidth|checkBrowser|originalClasses|outClass|inClass|originalStyles|abs|perspective|loadContent|extend|_data|round|msMaxTouchPoints|5e3|text|stopImmediatePropagation|stopPropagation|buttons|events|pop|splice|baseElWidth|minSwipe|maxSwipe|dargging|clientX|clientY|duration|destroyControls|createElement|mouseover|mouseout|numbers|which|lazyOwl|appendTo|clearTimeout|checked|shift|sort|removeAttr|match|fadeIn|400|clickable|toggleClass|wrapAll|top|prop|tagName|DIV|background|image|url|wrapperWidth|img|500|dragstart|ontouchstart|controls|out|input|relative|textarea|select|webkitAnimationEnd|oAnimationEnd|MSAnimationEnd|animationend|getJSON|returnValue|hasOwnProperty|option|onstartup|baseElement|navigator|new|prototype|destroy|removeData|reinit|addItem|after|before|removeItem|1199|979|768|479|800|1e3|carousel|jQuery|window|document'.split('|'),0,{}));
/*
Copyright © 2013 Adobe Systems Incorporated.
Licensed under the Apache License, Version 2.0 (the “License”);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/**
 * See <a href="http://jquery.com">http://jquery.com</a>.
 * @name jquery
 * @class
 * See the jQuery Library  (<a href="http://jquery.com">http://jquery.com</a>) for full details.  This just
 * documents the function and classes that are added to jQuery by this plug-in.
 */

/**
 * See <a href="http://jquery.com">http://jquery.com</a>
 * @name fn
 * @class
 * See the jQuery Library  (<a href="http://jquery.com">http://jquery.com</a>) for full details.  This just
 * documents the function and classes that are added to jQuery by this plug-in.
 * @memberOf jquery
 */

/**
 * @fileOverview accessibleMegaMenu plugin
 *
 *<p>Licensed under the Apache License, Version 2.0 (the “License”)
 *<br />Copyright © 2013 Adobe Systems Incorporated.
 *<br />Project page <a href="https://github.com/adobe-accessibility/Accessible-Mega-Menu">https://github.com/adobe-accessibility/Accessible-Mega-Menu</a>
 * @version 0.1
 * @author Michael Jordan
 * @requires jquery
 */

/*jslint browser: true, devel: true, plusplus: true, nomen: true */
/*global jQuery */
(function ($, window, document) {
    "use strict";
    var pluginName = "accessibleMegaMenu",
        defaults = {
            uuidPrefix: "accessible-megamenu", // unique ID's are required to indicate aria-owns, aria-controls and aria-labelledby
            menuClass: "accessible-megamenu", // default css class used to define the megamenu styling
            topNavItemClass: "accessible-megamenu-top-nav-item", // default css class for a top-level navigation item in the megamenu
            panelClass: "accessible-megamenu-panel", // default css class for a megamenu panel
            panelGroupClass: "accessible-megamenu-panel-group", // default css class for a group of items within a megamenu panel
            hoverClass: "hover", // default css class for the hover state
            focusClass: "focus", // default css class for the focus state
            openClass: "open" // default css class for the open state
        },
        Keyboard = {
            BACKSPACE: 8,
            COMMA: 188,
            DELETE: 46,
            DOWN: 40,
            END: 35,
            ENTER: 13,
            ESCAPE: 27,
            HOME: 36,
            LEFT: 37,
            PAGE_DOWN: 34,
            PAGE_UP: 33,
            PERIOD: 190,
            RIGHT: 39,
            SPACE: 32,
            TAB: 9,
            UP: 38,
            keyMap: {
                48: "0",
                49: "1",
                50: "2",
                51: "3",
                52: "4",
                53: "5",
                54: "6",
                55: "7",
                56: "8",
                57: "9",
                59: ";",
                65: "a",
                66: "b",
                67: "c",
                68: "d",
                69: "e",
                70: "f",
                71: "g",
                72: "h",
                73: "i",
                74: "j",
                75: "k",
                76: "l",
                77: "m",
                78: "n",
                79: "o",
                80: "p",
                81: "q",
                82: "r",
                83: "s",
                84: "t",
                85: "u",
                86: "v",
                87: "w",
                88: "x",
                89: "y",
                90: "z",
                96: "0",
                97: "1",
                98: "2",
                99: "3",
                100: "4",
                101: "5",
                102: "6",
                103: "7",
                104: "8",
                105: "9",
                190: "."
            }
        };
    /**
     * @desc Creates a new accessible mega menu instance.
     * @param {jquery} element
     * @param {object} [options] Mega Menu options
     * @param {string} [options.uuidPrefix=accessible-megamenu] - Prefix for generated unique id attributes, which are required to indicate aria-owns, aria-controls and aria-labelledby
     * @param {string} [options.menuClass=accessible-megamenu] - CSS class used to define the megamenu styling
     * @param {string} [options.topNavItemClass=accessible-megamenu-top-nav-item] - CSS class for a top-level navigation item in the megamenu
     * @param {string} [options.panelClass=accessible-megamenu-panel] - CSS class for a megamenu panel
     * @param {string} [options.panelGroupClass=accessible-megamenu-panel-group] - CSS class for a group of items within a megamenu panel
     * @param {string} [options.hoverClass=hover] - CSS class for the hover state
     * @param {string} [options.focusClass=focus] - CSS class for the focus state
     * @param {string} [options.openClass=open] - CSS class for the open state
     * @constructor
     */
    function AccessibleMegaMenu(element, options) {
        this.element = element;

        // merge optional settings and defaults into settings
        this.settings = $.extend({}, defaults, options);

        this._defaults = defaults;
        this._name = pluginName;

        this.mouseTimeoutID = null;
        this.focusTimeoutID = null;
        this.mouseFocused = false;
        this.justFocused = false;

        this.init();
    }

    AccessibleMegaMenu.prototype = (function () {

        /* private attributes and methods ------------------------ */
        var uuid = 0,
            keydownTimeoutDuration = 1000,
            keydownSearchString = "",
            isTouch = typeof window.hasOwnProperty === "function" && !!window.hasOwnProperty("ontouchstart"),
            _getPlugin,
            _addUniqueId,
            _togglePanel,
            _clickHandler,
            _clickOutsideHandler,
            _DOMAttrModifiedHandler,
            _focusInHandler,
            _focusOutHandler,
            _keyDownHandler,
            _mouseDownHandler,
            _mouseOverHandler,
            _mouseOutHandler,
            _toggleExpandedEventHandlers;

        /**
         * @name jQuery.fn.accessibleMegaMenu~_getPlugin
         * @desc Returns the parent accessibleMegaMenu instance for a given element
         * @param {jQuery} element
         * @memberof jQuery.fn.accessibleMegaMenu
         * @inner
         * @private
         */
        _getPlugin = function (element) {
            return $(element).closest(':data(plugin_' + pluginName + ')').data("plugin_" + pluginName);
        };

        /**
         * @name jQuery.fn.accessibleMegaMenu~_addUniqueId
         * @desc Adds a unique id and element.
         * The id string starts with the
         * string defined in settings.uuidPrefix.
         * @param {jQuery} element
         * @memberof jQuery.fn.accessibleMegaMenu
         * @inner
         * @private
         */
        _addUniqueId = function (element) {
            element = $(element);
            var settings = this.settings;
            if (!element.attr("id")) {
                element.attr("id", settings.uuidPrefix + "-" + new Date().getTime() + "-" + (++uuid));
            }
        };

        /**
         * @name jQuery.fn.accessibleMegaMenu~_togglePanel
         * @desc Toggle the display of mega menu panels in response to an event.
         * The optional boolean value 'hide' forces all panels to hide.
         * @param {event} event
         * @param {Boolean} [hide] Hide all mega menu panels when true
         * @memberof jQuery.fn.accessibleMegaMenu
         * @inner
         * @private
         */
        _togglePanel = function (event, hide) {
            var target = $(event.target),
                that = this,
                settings = this.settings,
                menu = this.menu,
                topli = target.closest('.' + settings.topNavItemClass),
                panel = target.hasClass(settings.panelClass) ? target : target.closest('.' + settings.panelClass),
                newfocus;

            _toggleExpandedEventHandlers.call(this, true);

            if (hide) {
                topli = menu.find('.' + settings.topNavItemClass + ' .' + settings.openClass + ':first').closest('.' + settings.topNavItemClass);
                if (!(topli.is(event.relatedTarget) || topli.has(event.relatedTarget).length > 0)) {
                    if ((event.type === 'mouseout' || event.type === 'focusout') && topli.has(document.activeElement).length > 0) {
                        return;
                    }
                    topli.find('[aria-expanded]')
                        .attr('aria-expanded', 'false')
                        .removeClass(settings.openClass)
                        .filter('.' + settings.panelClass)
                        .attr('aria-hidden', 'true');
                    if ((event.type === 'keydown' && event.keyCode === Keyboard.ESCAPE) || event.type === 'DOMAttrModified') {
                        newfocus = topli.find(':tabbable:first');
                        setTimeout(function () {
                            menu.find('[aria-expanded].' + that.settings.panelClass).off('DOMAttrModified.accessible-megamenu');
                            newfocus.focus();
                            that.justFocused = false;
                        }, 99);
                    }
                } else if (topli.length === 0) {
                    menu.find('[aria-expanded=true]')
                        .attr('aria-expanded', 'false')
                        .removeClass(settings.openClass)
                        .filter('.' + settings.panelClass)
                        .attr('aria-hidden', 'true');
                }
            } else {
                clearTimeout(that.focusTimeoutID);
                topli.siblings()
                    .find('[aria-expanded]')
                    .attr('aria-expanded', 'false')
                    .removeClass(settings.openClass)
                    .filter('.' + settings.panelClass)
                    .attr('aria-hidden', 'true');
                topli.find('[aria-expanded]')
                    .attr('aria-expanded', 'true')
                    .addClass(settings.openClass)
                    .filter('.' + settings.panelClass)
                    .attr('aria-hidden', 'false');
                if (event.type === 'mouseover' && target.is(':tabbable') && topli.length === 1 && panel.length === 0 && menu.has(document.activeElement).length > 0) {
                    target.focus();
                    that.justFocused = false;
                }

                _toggleExpandedEventHandlers.call(that);
            }
        };

        /**
         * @name jQuery.fn.accessibleMegaMenu~_clickHandler
         * @desc Handle click event on mega menu item
         * @param {event} Event object
         * @memberof jQuery.fn.accessibleMegaMenu
         * @inner
         * @private
         */
        _clickHandler = function (event) {
            var target = $(event.currentTarget),
                topli = target.closest('.' + this.settings.topNavItemClass),
                panel = target.closest('.' + this.settings.panelClass);
            if (topli.length === 1
                    && panel.length === 0
                    && topli.find('.' + this.settings.panelClass).length === 1) {
                if (!target.hasClass(this.settings.openClass)) {
                    event.preventDefault();
                    event.stopPropagation();
                    _togglePanel.call(this, event);
                    this.justFocused = false;
                } else {
                    if (this.justFocused) {
                        event.preventDefault();
                        event.stopPropagation();
                        this.justFocused = false;
                    } else if (isTouch) {
                        event.preventDefault();
                        event.stopPropagation();
                        _togglePanel.call(this, event, target.hasClass(this.settings.openClass));
                    }
                }
            }
        };

        /**
         * @name jQuery.fn.accessibleMegaMenu~_clickOutsideHandler
         * @desc Handle click event outside of a the megamenu
         * @param {event} Event object
         * @memberof jQuery.fn.accessibleMegaMenu
         * @inner
         * @private
         */
        _clickOutsideHandler = function (event) {
            if ($(event.target).closest(this.menu).length === 0) {
                event.preventDefault();
                event.stopPropagation();
                _togglePanel.call(this, event, true);
            }
        };

        /**
         * @name jQuery.fn.accessibleMegaMenu~_DOMAttrModifiedHandler
         * @desc Handle DOMAttrModified event on panel to respond to Windows 8 Narrator ExpandCollapse pattern
         * @param {event} Event object
         * @memberof jQuery.fn.accessibleMegaMenu
         * @inner
         * @private
         */
        _DOMAttrModifiedHandler = function (event) {
            if (event.originalEvent.attrName === 'aria-expanded'
                    && event.originalEvent.newValue === 'false'
                    && $(event.target).hasClass(this.settings.openClass)) {
                event.preventDefault();
                event.stopPropagation();
                _togglePanel.call(this, event, true);
            }
        };

        /**
         * @name jQuery.fn.accessibleMegaMenu~_focusInHandler
         * @desc Handle focusin event on mega menu item.
         * @param {event} Event object
         * @memberof jQuery.fn.accessibleMegaMenu
         * @inner
         * @private
         */
        _focusInHandler = function (event) {
            clearTimeout(this.focusTimeoutID);
            var target = $(event.target),
                panel = target.closest('.' + this.settings.panelClass);
            target
                .addClass(this.settings.focusClass)
                .on('click.accessible-megamenu', $.proxy(_clickHandler, this));
            this.justFocused = !this.mouseFocused;
            this.mouseFocused = false;
            if (this.panels.not(panel).filter('.' + this.settings.openClass).length) {
                _togglePanel.call(this, event);
            }
        };

        /**
         * @name jQuery.fn.accessibleMegaMenu~_focusOutHandler
         * @desc Handle focusout event on mega menu item.
         * @param {event} Event object
         * @memberof jQuery.fn.accessibleMegaMenu
         * @inner
         * @private
         */
        _focusOutHandler = function (event) {
            this.justFocused = false;
            var that = this,
                target = $(event.target),
                topli = target.closest('.' + this.settings.topNavItemClass),
                keepOpen = false;
            target
                .removeClass(this.settings.focusClass)
                .off('click.accessible-megamenu');

            if (window.cvox) {
                // If ChromeVox is running...
                that.focusTimeoutID = setTimeout(function () {
                    window.cvox.Api.getCurrentNode(function (node) {
                        if (topli.has(node).length) {
                            // and the current node being voiced is in
                            // the mega menu, clearTimeout,
                            // so the panel stays open.
                            clearTimeout(that.focusTimeoutID);
                        } else {
                            that.focusTimeoutID = setTimeout(function (scope, event, hide) {
                                _togglePanel.call(scope, event, hide);
                            }, 275, that, event, true);
                        }
                    });
                }, 25);
            } else {
                that.focusTimeoutID = setTimeout(function () {
                    _togglePanel.call(that, event, true);
                }, 300);
            }
        };

        /**
         * @name jQuery.fn.accessibleMegaMenu~_keyDownHandler
         * @desc Handle keydown event on mega menu.
         * @param {event} Event object
         * @memberof jQuery.fn.accessibleMegaMenu
         * @inner
         * @private
         */
        _keyDownHandler = function (event) {
            var that = (this.constructor === AccessibleMegaMenu) ? this : _getPlugin(this), // determine the AccessibleMegaMenu plugin instance
                settings = that.settings,
                target = $($(this).is('.' + settings.hoverClass + ':tabbable') ? this : event.target), // if the element is hovered the target is this, otherwise, its the focused element
                menu = that.menu,
                topnavitems = that.topnavitems,
                topli = target.closest('.' + settings.topNavItemClass),
                tabbables = menu.find(':tabbable'),
                panel = target.hasClass(settings.panelClass) ? target : target.closest('.' + settings.panelClass),
                panelGroups = panel.find('.' + settings.panelGroupClass),
                currentPanelGroup = target.closest('.' + settings.panelGroupClass),
                next,
                keycode = event.keyCode || event.which,
                start,
                i,
                o,
                label,
                found = false,
                newString = Keyboard.keyMap[event.keyCode] || '',
                regex,
                isTopNavItem = (topli.length === 1 && panel.length === 0);

            if (target.is("input:focus, select:focus, textarea:focus, button:focus")) {
                // if the event target is a form element we should handle keydown normally
                return;
            }

            if (target.is('.' + settings.hoverClass + ':tabbable')) {
                $('html').off('keydown.accessible-megamenu');
            }

            switch (keycode) {
            case Keyboard.ESCAPE:
                _togglePanel.call(that, event, true);
                break;
            case Keyboard.DOWN:
                event.preventDefault();
                if (isTopNavItem) {
                    _togglePanel.call(that, event);
                    found = (topli.find('.' + settings.panelClass + ' :tabbable:first').focus().length === 1);
                } else {
                    found = (tabbables.filter(':gt(' + tabbables.index(target) + '):first').focus().length === 1);
                }

                if (!found && window.opera && opera.toString() === "[object Opera]" && (event.ctrlKey || event.metaKey)) {
                    tabbables = $(':tabbable');
                    i = tabbables.index(target);
                    found = ($(':tabbable:gt(' + $(':tabbable').index(target) + '):first').focus().length === 1);
                }
                break;
            case Keyboard.UP:
                event.preventDefault();
                if (isTopNavItem && target.hasClass(settings.openClass)) {
                    _togglePanel.call(that, event, true);
                    next = topnavitems.filter(':lt(' + topnavitems.index(topli) + '):last');
                    if (next.children('.' + settings.panelClass).length) {
                        found = (next.children()
                            .attr('aria-expanded', 'true')
                            .addClass(settings.openClass)
                            .filter('.' + settings.panelClass)
                            .attr('aria-hidden', 'false')
                            .find(':tabbable:last')
                            .focus() === 1);
                    }
                } else if (!isTopNavItem) {
                    found = (tabbables.filter(':lt(' + tabbables.index(target) + '):last').focus().length === 1);
                }

                if (!found && window.opera && opera.toString() === "[object Opera]" && (event.ctrlKey || event.metaKey)) {
                    tabbables = $(':tabbable');
                    i = tabbables.index(target);
                    found = ($(':tabbable:lt(' + $(':tabbable').index(target) + '):first').focus().length === 1);
                }
                break;
            case Keyboard.RIGHT:
                event.preventDefault();
                if (isTopNavItem) {
                    found = (topnavitems.filter(':gt(' + topnavitems.index(topli) + '):first').find(':tabbable:first').focus().length === 1);
                } else {
                    if (panelGroups.length && currentPanelGroup.length) {
                        // if the current panel contains panel groups, and we are able to focus the first tabbable element of the next panel group
                        found = (panelGroups.filter(':gt(' + panelGroups.index(currentPanelGroup) + '):first').find(':tabbable:first').focus().length === 1);
                    }

                    if (!found) {
                        found = (topli.find(':tabbable:first').focus().length === 1);
                    }
                }
                break;
            case Keyboard.LEFT:
                event.preventDefault();
                if (isTopNavItem) {
                    found = (topnavitems.filter(':lt(' + topnavitems.index(topli) + '):last').find(':tabbable:first').focus().length === 1);
                } else {
                    if (panelGroups.length && currentPanelGroup.length) {
                        // if the current panel contains panel groups, and we are able to focus the first tabbable element of the previous panel group
                        found = (panelGroups.filter(':lt(' + panelGroups.index(currentPanelGroup) + '):last').find(':tabbable:first').focus().length === 1);
                    }

                    if (!found) {
                        found = (topli.find(':tabbable:first').focus().length === 1);
                    }
                }
                break;
            case Keyboard.TAB:
                i = tabbables.index(target);
                if (event.shiftKey && isTopNavItem && target.hasClass(settings.openClass)) {
                    _togglePanel.call(that, event, true);
                    next = topnavitems.filter(':lt(' + topnavitems.index(topli) + '):last');
                    if (next.children('.' + settings.panelClass).length) {
                        found = next.children()
                            .attr('aria-expanded', 'true')
                            .addClass(settings.openClass)
                            .filter('.' + settings.panelClass)
                            .attr('aria-hidden', 'false')
                            .find(':tabbable:last')
                            .focus();
                    }
                } else if (event.shiftKey && i > 0) {
                    found = (tabbables.filter(':lt(' + i + '):last').focus().length === 1);
                } else if (!event.shiftKey && i < tabbables.length - 1) {
                    found = (tabbables.filter(':gt(' + i + '):first').focus().length === 1);
                } else if (window.opera && opera.toString() === "[object Opera]") {
                    tabbables = $(':tabbable');
                    i = tabbables.index(target);
                    if (event.shiftKey) {
                        found = ($(':tabbable:lt(' + $(':tabbable').index(target) + '):last').focus().length === 1);
                    } else {
                        found = ($(':tabbable:gt(' + $(':tabbable').index(target) + '):first').focus().length === 1);
                    }
                }

                if (found) {
                    event.preventDefault();
                }
                break;
            case Keyboard.SPACE:
                if (isTopNavItem) {
                    event.preventDefault();
                    _clickHandler.call(that, event);
                } else {
                    return true;
                }
                break;
            case Keyboard.ENTER:
                return true;
                break;
            default:
                // alphanumeric filter
                clearTimeout(this.keydownTimeoutID);

                keydownSearchString += newString !== keydownSearchString ? newString : '';

                if (keydownSearchString.length === 0) {
                    return;
                }

                this.keydownTimeoutID = setTimeout(function () {
                    keydownSearchString = '';
                }, keydownTimeoutDuration);

                if (isTopNavItem && !target.hasClass(settings.openClass)) {
                    tabbables = tabbables.filter(':not(.' + settings.panelClass + ' :tabbable)');
                } else {
                    tabbables = topli.find(':tabbable');
                }

                if (event.shiftKey) {
                    tabbables = $(tabbables.get()
                        .reverse());
                }

                for (i = 0; i < tabbables.length; i++) {
                    o = tabbables.eq(i);
                    if (o.is(target)) {
                        start = (keydownSearchString.length === 1) ? i + 1 : i;
                        break;
                    }
                }

                regex = new RegExp('^' + keydownSearchString.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'), 'i');

                for (i = start; i < tabbables.length; i++) {
                    o = tabbables.eq(i);
                    label = $.trim(o.text());
                    if (regex.test(label)) {
                        found = true;
                        o.focus();
                        break;
                    }
                }
                if (!found) {
                    for (i = 0; i < start; i++) {
                        o = tabbables.eq(i);
                        label = $.trim(o.text());
                        if (regex.test(label)) {
                            o.focus();
                            break;
                        }
                    }
                }
                break;
            }
            that.justFocused = false;
        };

        /**
         * @name jQuery.fn.accessibleMegaMenu~_mouseDownHandler
         * @desc Handle mousedown event on mega menu.
         * @param {event} Event object
         * @memberof accessibleMegaMenu
         * @inner
         * @private
         */
        _mouseDownHandler = function (event) {
            if ($(event.target).is(this.settings.panelClass) || $(event.target).closest(":focusable").length) {
                this.mouseFocused = true;
            }
            this.mouseTimeoutID = setTimeout(function () {
                clearTimeout(this.focusTimeoutID);
            }, 1);
        };

        /**
         * @name jQuery.fn.accessibleMegaMenu~_mouseOverHandler
         * @desc Handle mouseover event on mega menu.
         * @param {event} Event object
         * @memberof jQuery.fn.accessibleMegaMenu
         * @inner
         * @private
         */
        _mouseOverHandler = function (event) {
            clearTimeout(this.mouseTimeoutID);
            $(event.target)
                .addClass(this.settings.hoverClass);
            _togglePanel.call(this, event);
            if ($(event.target).is(':tabbable')) {
                $('html').on('keydown.accessible-megamenu', $.proxy(_keyDownHandler, event.target));
            }
        };

        /**
         * @name jQuery.fn.accessibleMegaMenu~_mouseOutHandler
         * @desc Handle mouseout event on mega menu.
         * @param {event} Event object
         * @memberof jQuery.fn.accessibleMegaMenu
         * @inner
         * @private
         */
        _mouseOutHandler = function (event) {
            var that = this;
            $(event.target)
                .removeClass(that.settings.hoverClass);

            that.mouseTimeoutID = setTimeout(function () {
                _togglePanel.call(that, event, true);
            }, 250);
            if ($(event.target).is(':tabbable')) {
                $('html').off('keydown.accessible-megamenu');
            }
        };

        _toggleExpandedEventHandlers = function (hide) {
            var menu = this.menu;
            if (hide) {
                $('html').off('mouseup.outside-accessible-megamenu, touchend.outside-accessible-megamenu, mspointerup.outside-accessible-megamenu,  pointerup.outside-accessible-megamenu');

                menu.find('[aria-expanded].' + this.settings.panelClass).off('DOMAttrModified.accessible-megamenu');
            } else {
                $('html').on('mouseup.outside-accessible-megamenu, touchend.outside-accessible-megamenu, mspointerup.outside-accessible-megamenu,  pointerup.outside-accessible-megamenu', $.proxy(_clickOutsideHandler, this));

                /* Narrator in Windows 8 automatically toggles the aria-expanded property on double tap or click.
                   To respond to the change to collapse the panel, we must add a listener for a DOMAttrModified event. */
                menu.find('[aria-expanded=true].' + this.settings.panelClass).on('DOMAttrModified.accessible-megamenu', $.proxy(_DOMAttrModifiedHandler, this));
            }
        };

        /* public attributes and methods ------------------------- */
        return {
            constructor: AccessibleMegaMenu,

            /**
             * @lends jQuery.fn.accessibleMegaMenu
             * @desc Initializes an instance of the accessibleMegaMenu plugins
             * @memberof jQuery.fn.accessibleMegaMenu
             * @instance
             */
            init: function () {
                var settings = this.settings,
                    nav = $(this.element),
                    menu = nav.children().first(),
                    topnavitems = menu.children();
                this.start(settings, nav, menu, topnavitems);
            },

            start: function(settings, nav, menu, topnavitems) {
                var that = this;
                this.settings = settings;
                this.menu = menu;
                this.topnavitems = topnavitems;

                nav.attr("role", "navigation");
                menu.addClass(settings.menuClass);
                topnavitems.each(function (i, topnavitem) {
                    var topnavitemlink, topnavitempanel;
                    topnavitem = $(topnavitem);
                    topnavitem.addClass(settings.topNavItemClass);
                    topnavitemlink = topnavitem.find(":tabbable:first");
                    topnavitempanel = topnavitem.children(":not(:tabbable):last");
                    _addUniqueId.call(that, topnavitemlink);
                    if (topnavitempanel.length) {
                        _addUniqueId.call(that, topnavitempanel);
                        topnavitemlink.attr({
                            "aria-haspopup": true,
                            "aria-controls": topnavitempanel.attr("id"),
                            "aria-expanded": false
                        });

                        topnavitempanel.attr({
                            "role": "group",
                            "aria-expanded": false,
                            "aria-hidden": true
                        })
                            .addClass(settings.panelClass)
                            .not("[aria-labelledby]")
                            .attr("aria-labelledby", topnavitemlink.attr("id"));
                    }
                });

                this.panels = menu.find("." + settings.panelClass);

                menu.on("focusin.accessible-megamenu", ":focusable, ." + settings.panelClass, $.proxy(_focusInHandler, this))
                    .on("focusout.accessible-megamenu", ":focusable, ." + settings.panelClass, $.proxy(_focusOutHandler, this))
                    .on("keydown.accessible-megamenu", $.proxy(_keyDownHandler, this))
                    .on("mouseover.accessible-megamenu", $.proxy(_mouseOverHandler, this))
                    .on("mouseout.accessible-megamenu", $.proxy(_mouseOutHandler, this))
                    .on("mousedown.accessible-megamenu", $.proxy(_mouseDownHandler, this));

                if (isTouch) {
                    menu.on("touchstart.accessible-megamenu",  $.proxy(_clickHandler, this));
                }

                menu.find("hr").attr("role", "separator");

                if ($(document.activeElement).closest(menu).length) {
                  $(document.activeElement).trigger("focusin.accessible-megamenu");
                }
            },

            /**
             * @desc Get default values
             * @example $(selector).accessibleMegaMenu("getDefaults");
             * @return {object}
             * @memberof jQuery.fn.accessibleMegaMenu
             * @instance
             */
            getDefaults: function () {
                return this._defaults;
            },

            /**
             * @desc Get any option set to plugin using its name (as string)
             * @example $(selector).accessibleMegaMenu("getOption", some_option);
             * @param {string} opt
             * @return {string}
             * @memberof jQuery.fn.accessibleMegaMenu
             * @instance
             */
            getOption: function (opt) {
                return this.settings[opt];
            },

            /**
             * @desc Get all options
             * @example $(selector).accessibleMegaMenu("getAllOptions");
             * @return {object}
             * @memberof jQuery.fn.accessibleMegaMenu
             * @instance
             */
            getAllOptions: function () {
                return this.settings;
            },

            /**
             * @desc Set option
             * @example $(selector).accessibleMegaMenu("setOption", "option_name",  "option_value",  reinitialize);
             * @param {string} opt - Option name
             * @param {string} val - Option value
             * @param {boolean} [reinitialize] - boolean to re-initialize the menu.
             * @memberof jQuery.fn.accessibleMegaMenu
             * @instance
             */
            setOption: function (opt, value, reinitialize) {
                this.settings[opt] = value;
                if (reinitialize) {
                    this.init();
                }
            }
        };
    }());

    /* lightweight plugin wrapper around the constructor,
       to prevent against multiple instantiations */

    /**
     * @class accessibleMegaMenu
     * @memberOf jQuery.fn
     * @classdesc Implements an accessible mega menu as a jQuery plugin.
     * <p>The mega-menu It is modeled after the mega menu on {@link http://adobe.com|adobe.com} but has been simplified for use by others. A brief description of the interaction design choices can be found in a blog post at {@link http://blogs.adobe.com/accessibility/2013/05/adobe-com.html|Mega menu accessibility on adobe.com}.</p>
     * <h3>Keyboard Accessibility</h3>
     * <p>The accessible mega menu supports keyboard interaction modeled after the behavior described in the {@link http://www.w3.org/TR/wai-aria-practices/#menu|WAI-ARIA Menu or Menu bar (widget) design pattern}, however we also try to respect users' general expectations for the behavior of links in a global navigation. To this end, the accessible mega menu implementation permits tab focus on each of the six top-level menu items. When one of the menu items has focus, pressing the Enter key, Spacebar or Down arrow will open the submenu panel, and pressing the Left or Right arrow key will shift focus to the adjacent menu item. Links within the submenu panels are included in the tab order when the panel is open. They can also be navigated with the arrow keys or by typing the first character in the link name, which speeds up keyboard navigation considerably. Pressing the Escape key closes the submenu and restores focus to the parent menu item.</p>
     * <h3>Screen Reader Accessibility</h3>
     * <p>The accessible mega menu models its use of WAI-ARIA Roles, States, and Properties after those described in the {@link http://www.w3.org/TR/wai-aria-practices/#menu|WAI-ARIA Menu or Menu bar (widget) design pattern} with some notable exceptions, so that it behaves better with screen reader user expectations for global navigation. We don't use <code class="prettyprint prettyprinted" style=""><span class="pln">role</span><span class="pun">=</span><span class="str">"menu"</span></code> for the menu container and <code class="prettyprint prettyprinted" style=""><span class="pln">role</span><span class="pun">=</span><span class="str">"menuitem"</span></code> for each of the links therein, because if we do, assistive technology will no longer interpret the links as links, but instead, as menu items, and the links in our global navigation will no longer show up when a screen reader user executes a shortcut command to bring up a list of links in the page.</p>
     * @example <h4>HTML</h4><hr/>
&lt;nav&gt;
    &lt;ul class=&quot;nav-menu&quot;&gt;
        &lt;li class=&quot;nav-item&quot;&gt;
            &lt;a href=&quot;?movie&quot;&gt;Movies&lt;/a&gt;
            &lt;div class=&quot;sub-nav&quot;&gt;
                &lt;ul class=&quot;sub-nav-group&quot;&gt;
                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=0&quot;&gt;Action &amp;amp; Adventure&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=2&quot;&gt;Children &amp;amp; Family&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
                &lt;/ul&gt;
                &lt;ul class=&quot;sub-nav-group&quot;&gt;
                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=7&quot;&gt;Dramas&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=9&quot;&gt;Foreign&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
                &lt;/ul&gt;
                &lt;ul class=&quot;sub-nav-group&quot;&gt;
                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=14&quot;&gt;Musicals&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&lt;a href=&quot;?movie&amp;genre=15&quot;&gt;Romance&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
                &lt;/ul&gt;
            &lt;/div&gt;
        &lt;/li&gt;
        &lt;li class=&quot;nav-item&quot;&gt;
            &lt;a href=&quot;?tv&quot;&gt;TV Shows&lt;/a&gt;
            &lt;div class=&quot;sub-nav&quot;&gt;
                &lt;ul class=&quot;sub-nav-group&quot;&gt;
                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=20&quot;&gt;Classic TV&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=21&quot;&gt;Crime TV&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
                &lt;/ul&gt;
                &lt;ul class=&quot;sub-nav-group&quot;&gt;
                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=27&quot;&gt;Reality TV&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=30&quot;&gt;TV Action&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
                &lt;/ul&gt;
                &lt;ul class=&quot;sub-nav-group&quot;&gt;
                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=33&quot;&gt;TV Dramas&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&lt;a href=&quot;?tv&amp;genre=34&quot;&gt;TV Horror&lt;/a&gt;&lt;/li&gt;
                    &lt;li&gt;&amp;#8230;&lt;/li&gt;
                &lt;/ul&gt;
            &lt;/div&gt;
        &lt;/li&gt;
    &lt;/ul&gt;
&lt;/nav&gt;
     * @example <h4>CSS</h4><hr/>
&#47;* Rudimentary mega menu CSS for demonstration *&#47;
&#47;* mega menu list *&#47;
.nav-menu {
    display: block;
    position: relative;
    list-style: none;
    margin: 0;
    padding: 0;
    z-index: 15;
}
&#47;* a top level navigation item in the mega menu *&#47;
.nav-item {
    list-style: none;
    display: inline-block;
    padding: 0;
    margin: 0;
}
&#47;* first descendant link within a top level navigation item *&#47;
.nav-item &gt; a {
    position: relative;
    display: inline-block;
    padding: 0.5em 1em;
    margin: 0 0 -1px 0;
    border: 1px solid transparent;
}
&#47;* focus/open states of first descendant link within a top level
   navigation item *&#47;
.nav-item &gt; a:focus,
.nav-item &gt; a.open {
    border: 1px solid #dedede;
}
&#47;* open state of first descendant link within a top level
   navigation item *&#47;
.nav-item &gt; a.open {
    background-color: #fff;
    border-bottom: none;
    z-index: 1;
}
&#47;* sub-navigation panel *&#47;
.sub-nav {
    position: absolute;
    display: none;
    top: 2.2em;
    margin-top: -1px;
    padding: 0.5em 1em;
    border: 1px solid #dedede;
    background-color: #fff;
}
&#47;* sub-navigation panel open state *&#47;
.sub-nav.open {
    display: block;
}
&#47;* list of items within sub-navigation panel *&#47;
.sub-nav ul {
    display: inline-block;
    vertical-align: top;
    margin: 0 1em 0 0;
    padding: 0;
}
&#47;* list item within sub-navigation panel *&#47;
.sub-nav li {
    display: block;
    list-style-type: none;
    margin: 0;
    padding: 0;
}
     * @example <h4>JavaScript</h4><hr/>
&lt;!-- include jquery --&gt;
&lt;script src=&quot;http://code.jquery.com/jquery-1.10.1.min.js&quot;&gt;&lt;/script&gt;
&lt;!-- include the jquery-accessibleMegaMenu plugin script --&gt;
&lt;script src=&quot;js/jquery-accessibleMegaMenu.js&quot;&gt;&lt;/script&gt;
&lt;!-- initialize a selector as an accessibleMegaMenu --&gt;
&lt;script&gt;
    $(&quot;nav:first&quot;).accessibleMegaMenu({
        &#47;* prefix for generated unique id attributes, which are required to indicate aria-owns, aria-controls and aria-labelledby *&#47;
        uuidPrefix: &quot;accessible-megamenu&quot;,
        &#47;* css class used to define the megamenu styling *&#47;
        menuClass: &quot;nav-menu&quot;,
        &#47;* css class for a top-level navigation item in the megamenu *&#47;
        topNavItemClass: &quot;nav-item&quot;,
        &#47;* css class for a megamenu panel *&#47;
        panelClass: &quot;sub-nav&quot;,
        &#47;* css class for a group of items within a megamenu panel *&#47;
        panelGroupClass: &quot;sub-nav-group&quot;,
        &#47;* css class for the hover state *&#47;
        hoverClass: &quot;hover&quot;,
        &#47;* css class for the focus state *&#47;
        focusClass: &quot;focus&quot;,
        &#47;* css class for the open state *&#47;
        openClass: &quot;open&quot;
    });
&lt;/script&gt;
     * @param {object} [options] Mega Menu options
     * @param {string} [options.uuidPrefix=accessible-megamenu] - Prefix for generated unique id attributes, which are required to indicate aria-owns, aria-controls and aria-labelledby
     * @param {string} [options.menuClass=accessible-megamenu] - CSS class used to define the megamenu styling
     * @param {string} [options.topNavItemClass=accessible-megamenu-top-nav-item] - CSS class for a top-level navigation item in the megamenu
     * @param {string} [options.panelClass=accessible-megamenu-panel] - CSS class for a megamenu panel
     * @param {string} [options.panelGroupClass=accessible-megamenu-panel-group] - CSS class for a group of items within a megamenu panel
     * @param {string} [options.hoverClass=hover] - CSS class for the hover state
     * @param {string} [options.focusClass=focus] - CSS class for the focus state
     * @param {string} [options.openClass=open] - CSS class for the open state
     */
    $.fn[pluginName] = function (options) {
        return this.each(function () {
            if (!$.data(this, "plugin_" + pluginName)) {
                $.data(this, "plugin_" + pluginName, new $.fn[pluginName].AccessibleMegaMenu(this, options));
            }
        });
    };

    $.fn[pluginName].AccessibleMegaMenu = AccessibleMegaMenu;

    /* :focusable and :tabbable selectors from
       https://raw.github.com/jquery/jquery-ui/master/ui/jquery.ui.core.js */

    /**
     * @private
     */
    function visible(element) {
        return $.expr.filters.visible(element) && !$(element).parents().addBack().filter(function () {
            return $.css(this, "visibility") === "hidden";
        }).length;
    }

    /**
     * @private
     */
    function focusable(element, isTabIndexNotNaN) {
        var map, mapName, img,
            nodeName = element.nodeName.toLowerCase();
        if ("area" === nodeName) {
            map = element.parentNode;
            mapName = map.name;
            if (!element.href || !mapName || map.nodeName.toLowerCase() !== "map") {
                return false;
            }
            img = $("img[usemap=#" + mapName + "]")[0];
            return !!img && visible(img);
        }
        return (/input|select|textarea|button|object/.test(nodeName) ? !element.disabled :
                "a" === nodeName ?
                        element.href || isTabIndexNotNaN :
                        isTabIndexNotNaN) &&
                            // the element and all of its ancestors must be visible
                            visible(element);
    }

    $.extend($.expr[":"], {
        data: $.expr.createPseudo ? $.expr.createPseudo(function (dataName) {
            return function (elem) {
                return !!$.data(elem, dataName);
            };
        }) : // support: jQuery <1.8
                function (elem, i, match) {
                    return !!$.data(elem, match[3]);
                },

        focusable: function (element) {
            return focusable(element, !isNaN($.attr(element, "tabindex")));
        },

        tabbable: function (element) {
            var tabIndex = $.attr(element, "tabindex"),
                isTabIndexNaN = isNaN(tabIndex);
            return (isTabIndexNaN || tabIndex >= 0) && focusable(element, !isTabIndexNaN);
        }
    });
}(jQuery, window, document));;
/**
 * @description Extends the jQuery Widget factory to get options from data-attributes. Supports nested objects by using double dashes.
 * @example
 *
 *    <div data-one="true" data-selectors--list-item=".js-list-item">
 *
 *    will map to:
 *    
 *      options: {
 *          one: true,
 *          selectors: {
 *              listItem: '.js-list-item'
 *          }
 *      }
 * @author Didier Ghys
 */

(function ($, undefined) {
		
    'use strict';
	
	/**
	 * Borrowed from Select2
	 * https://github.com/select2/select2/blob/master/src/js/select2/utils.js
	 */
	function _convertData(data) {
        
        for (var originalKey in data) {
            var keys = originalKey.split('-');

            var dataLevel = data;

            if (keys.length === 1) {
                continue;
            }

            for (var k = 0; k < keys.length; k++) {
                var key = keys[k];

                // Lowercase the first letter
                // By default, dash-separated becomes camelCase
                key = key.substring(0, 1).toLowerCase() + key.substring(1);

                if (!(key in dataLevel)) {
                    dataLevel[key] = {};
                }

                if (k == keys.length - 1) {
                    dataLevel[key] = data[originalKey];
                }

                dataLevel = dataLevel[key];
            }

            delete data[originalKey];
        }

        return data;
    }
	
	var rcapitals = /[A-Z]/g,
		replaceFunction = function( c ) {
			return "-" + c.toLowerCase();
		};

	$.extend( $.Widget.prototype, {
		
		/**
         * @description Leverages options set via data-* attributes.
         */
		_getCreateOptions: function () {

            var elem = this.element,
				options = {},
				dataset = {};

            // Prefer the element's `dataset` attribute if it exists
            // jQuery 1.x does not correctly handle data attributes with multiple dashes
            if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && elem[0].dataset) {
                dataset = $.extend(true, {}, elem[0].dataset, elem.data());
            } else {
                dataset = elem.data();
            }

            var data = $.extend(true, {}, dataset);

            data = _convertData(data);

            for (var key in data) {
                
                // exclude widget instance itself
                if (key.replace(rcapitals, replaceFunction) === this.widgetFullName) {
                    continue;
                }

                if ($.isPlainObject(this.options[key])) {
                    $.extend(this.options[key], data[key]);
                } else {
                    this.options[key] = data[key];
                }
            }

            return options;
        }

	});

})(window.jQuery);
;
/**
 * Plugin to allow the upload of files via iFrame submission (mainly for IE8)
 * configuration object in `defaults` is very dependent on jquery.cx.fileUpload.js
 * in fact a lot of this plugin is so shouldn't be used without it.
 */

(function($) {

	'use strict';

	$.fn.iframeFileUpload = function(defaults) {

		var options = $.extend({
		}, defaults );

		/**
		 * JSON Response from the server
		 */
		var serverResponse = function(data, formElem) {
			_renderFile(data, formElem);
			_removeiFrame(formElem);

			options.filesChanged($('.fileupload__item--success', formElem).length);
		};

		/**
		 * Render the file in the UI
		 *
		 * @param data
		 * @private
		 */
		var _renderFile = function(data, formElem) {

			var self = this;
			var overwriteElem = arguments[2];
			var fileItem;
			var deleteBtn;
			var cancelBtn;

			/*
			 * Get the html for the result.
			 *
			 * This is what blue imp does...
			 */
			data = $.extend({}, data, {options: options});

			fileItem = $(options.downloadTemplate(data));

			/*
			 * Attach handler to cancel (which isn't delete)
			 */
			cancelBtn = fileItem.find('.cancel');
			cancelBtn.on('click', function(e) {
				e.preventDefault();
				fileItem.fadeOut(300, function() {
					fileItem.remove();
				});
			});

			/*
			 * Attach handler to the delete button
			 */
			_addDeleteHander(fileItem, formElem, data);

			/*
			 * Render the results
			 */
			if ( overwriteElem ) {
				overwriteElem.replaceWith(fileItem);
			}
			else {
				$(options.filesContainer, formElem).append(fileItem);
			}
		};

		/**
		 * Delete a file.
		 *
		 * @param config
		 * @private
		 */
		var _deleteFile = function(config) {

			$.ajax({
				url: config.deleteBtn.data('url'),
				type: config.deleteBtn.data('type'),
				success: function (data, textStatus, jqXHR) {
					config.fileItemElem.fadeOut(300, function() {
						config.fileItemElem.remove();
						options.filesChanged($('.fileupload__item--success', config.formElem).length);
					});
				},
				error: function(jqXHR, textStatus, errorThrown) {
					// we will only have a config.data error if the

					if ( $.isEmptyObject(config.data)  ) {
						// the file item was in the page on load, so we don't
						// have all the data required. Do a generic error.
						config.fileItemElem.append('<div class="fileupload__item__error zeta">' + options.messages.unknownError + '</div>');
					}
					else {
						// render a generic error to the file item
						config.data.files[0].error = 'unknownError';
						_renderFile(config.data, config.formElem, config.fileItemElem);
					}
				}
			});
		};

		/**
		 * Add the delete event to the file item.
		 *
		 * @param fileItem
		 * @param formElem
		 * @param data
		 * @private
		 */
		var _addDeleteHander = function(fileItem, formElem, data) {

			/*
			 * Attach handler to the delete button
			 */
			var deleteBtn = fileItem.find('.delete');
			deleteBtn.on('click', function(e) {
				e.preventDefault();
				_deleteFile({
					'deleteBtn': $(this),
					'fileItemElem': fileItem,
					'data': data,
					'formElem': formElem
				});
			});
		};


		/**
		 * Need to attach an iframe to the form and then modify the
		 * form attributes.
		 *
		 * @param formElem
		 * @private
		 */
		var _modifyForm = function(formElem) {

			var uid = formElem.data('uid');

			/*
			 * Add a hidden iframe to the form
			 */
			var iframeElem = $('<iframe />', {
				'id': 'ifTarget-' + uid,
				'name': 'ifTarget-' + uid,
				'class': 'visuallyhidden'
			}).on('load', function() {

				/*
				 * Clean up the response from the server which is in the contents
				 * of the iframe.  The response content-type has to be 'text/plain'
				 * otherwise IE wil try and download the JSON response.
				 */
				var response = $(this).contents().find('body').text();
				var json;

				if ( response ) {
					json = JSON.parse(response);
					serverResponse(json, formElem);
				}
			});

			/*
			 * Store the existing attributes so that we can revert the form
			 */
			formElem.data('action', formElem.attr('action'));

			/*
			 * Change the form attributes. Target it is important, the
			 * form will submit to the iframe defined by this attribute.
			 */
			formElem.attr({
				'action': options.url,
				'target': 'ifTarget-' + uid
			});

			formElem.append(iframeElem);


		};

		/**
		 * Need to clean up after uploading a file so that the original form
		 * isn't messed up.
		 */
		var _removeiFrame = function(formElem) {
			var uid = formElem.data('uid');
 			var iframe = $('#ifTarget-' + uid);

			/*
			 * Remove the iframe
			 */
			iframe.remove();

			/*
			 * Revert the form
			 */
			formElem.removeAttr('target');
			formElem.attr('action', formElem.data('action'));


		};

		/*
		 * Apply the plugin to each matching element...
		 */
		return this.each(function() {

			var formElem = $(this);
			var uid;

			/*
			 * We need to attach delete events on file uploads that
			 * may already exist on the page
			 */
			$('.fileupload__item', formElem).each(function() {
				_addDeleteHander($(this), formElem, {});
			});

			/*
			 * Add a unique id to the form
			 */
			formElem.uniqueId();
			uid = formElem.attr('id');
			formElem.data('uid', uid);

			/*
			 * When the file upload changes, invoke the form submit.
			 */
			$('input[type="file"]', formElem).on('change', function(e) {

				_modifyForm(formElem);

				formElem.submit();
			});

		});
	};

})(jQuery);;
/**
 * @fileOverview A jQuery UI Widget for mobile menu functionality -
 * @author Joel Mitchell
 * @name $.cx.mobile_menu
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function ($) {
    'use strict';

    $.widget('cx.mobile_menu', {
        options: {
            'buttonSelector': '#nav-open-btn',
            'htmlClass': 'js-nav',
            'readyClass': 'js-ready',
            'delay': 330,
            'mobileSecondaryNavSelector': '.mobile-secondary-nav',
            'mobileSecondaryNavActiveClass': 'mobile-secondary-nav--active',
            'mobileSecondaryNavLinkSelector': '.mobile-secondary-nav-link',
            'mobileSecondaryNavBackSelector': '.mobile-secondary-nav__back .js-menu-mobile-back, .mobile-secondary-nav__back .btn',
            'mobileSwipeToCloseAreaSelector': '#off-canvas-menu-wrapper',
            'mobileClickToCloseAreaSelector': '#off-canvas-menu-wrapper',
            'overlayTemplate' : '<div class="mobile-menuopen-overlay closed"></div>'
        },

        // Add an underscore to the event prefix to make event names easier to read
        widgetEventPrefix: 'menu_',

        /**
         * Constructor
         */
        _create: function () {
            var self = this;
            this._html = $('html');
            this._wrapper = $('.masthead__mobile__inner');
            this._button = $(this.options.buttonSelector);
            this._navSelector = $(this.options.mobileSecondaryNavSelector);
            $('#off-canvas-menu-wrapper').prepend(self.options.overlayTemplate);

            this._on((function () {
                var events = {};
                events['click ' + self.options.buttonSelector] = self._toggleMenu;
                events['click ' + self.options.mobileSecondaryNavBackSelector] = self._toggleSecondaryMenu;
                events['click ' + self.options.mobileSecondaryNavLinkSelector] = self._animateSecondaryMenu;
                
                 //when user clicks or swipes on main contents ( and skin ) when mobile menu is open, menu should close again
                events['click ' +  '.mobile-menuopen-overlay'] = self._toggleMenu;
                events['swipeleft ' + '.mobile-menuopen-overlay'] = self._toggleMenu;
                return events;
            })());
        
             
            
            // Listen for the window reorientation event
            this._on(this.window, {
                'orientationchange': this._forceRepaint
            });
            
            /*this._on(this.window, {
                'swipeleft': this._forceRepaint
            });*/
            
           $(self.options.mobileClickToCloseAreaSelector).on( 'swipeleft','.mobile-menuopen-overlay',  function(e){
               if( $('#off-canvas-menu-inner').hasClass('menuopen') ){
                  //console.log('closemenu');
                  self._toggleMenu(e);
                  }
           } );
            

            this._html.addClass(self.options.readyClass);

            // move mobile sub navs (only 1 exists currently, for complain)
            this._navSelector.appendTo(self._wrapper);
            this._setMinHeightBody();

            this._forceRepaint();
            
            

        },

        /**
         * toggle the menu
         */
        _toggleMenu: function (e) {
            e.preventDefault();
              e.stopPropagation();
            this._delay(function () {
                this._forceRepaint();
            }, 250);
            this._html.toggleClass(this.options.htmlClass);
            $(this.options.mobileClickToCloseAreaSelector +  ' .mobile-menuopen-overlay').toggleClass('closed');
            $('#off-canvas-menu-inner').toggleClass('menuopen');
        },

        _toggleSecondaryMenu: function (event) {
            event.preventDefault();
            $(event.target)
                .closest(this.options.mobileSecondaryNavSelector)
                .toggleClass(this.options.mobileSecondaryNavActiveClass);

        },


        closeSecondaryMenu: function (event) {
            event.preventDefault();
            $(event.target)
                .closest(this.options.mobileSecondaryNavSelector)
                .removeClass(this.options.mobileSecondaryNavActiveClass);
        },

        openSecondaryMenu: function (event) {


            event.preventDefault();
            $(event.target)
                .closest(this.options.mobileSecondaryNavSelector)
                .addClass(this.options.mobileSecondaryNavActiveClass);
        },

        _animateSecondaryMenu: function (event) {
            var self = this,
                options = this.options,
                link = $(event.target);

            event.preventDefault();
            $('.' + options.mobileSecondaryNavActiveClass).toggleClass(options.mobileSecondaryNavActiveClass);

            $(link.attr('href')).addClass(self.options.mobileSecondaryNavActiveClass);
        },

        /**
         * open the menu
         */
        _openMenu: function () {
            this._html.addClass(this.options.htmlClass);
        },

        _forceRepaint: function () {
            //console.log('force repaint');
            this._html[0].style.display = 'none';
            //this._html[0].offsetHeight; // no need to store this anywhere, the reference is enough
            this._html[0].style.display = '';
        },
        
        /**
         * check height of main is high enough for the menu to show
         */
        _setMinHeightBody: function () {
            if($('.masthead__mobile__inner').height() > $('body #main').height()){
                $('body #main').css('min-height', $('.masthead__mobile__inner').height() );
                //console.log('im ok now');            
            }
            
        },
        
        /**
         * close the menu
         */
        _closeMenu: function () {
            this._html.removeClass(this.options.htmlClass);
        },

        /**
         * destroy
         */
        _destroy: function () {
            this._html
                .removeClass(this.options.htmlClass)
                .removeClass(this.options.readyClass);
        }
    });

})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to conditionally allow access to more form elements
 * need to simplify this and maybe make the initialisation call via an event
 * @author Joel Mitchell
 * @name $.cx.select_reveal
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function($) {
	'use strict';

	$.widget('cx.select_reveal', {
		options: {
			'inputClass': 'form__field--boolean',
			'groupClass': '.form__group',
			'revealElement': '.is-hidden-form'
		},

		// Add an underscore to the event prefix to make event names easier to read
		widgetEventPrefix: 'reveal_',

		/**
		 * Constructor
		 */
		_create: function() {
			var self = this;

			self._formGroup = self.element.find(this.options.groupClass);
			self._revealedElement = $(this.options.revealElement).eq(0);

			self._on((function() {
				var events = {};
				events['click .' + self.options.inputClass] = self._test;
				return events;
			})());

			self._test();

		},

		_test: function() {
			var self = this;
			var totals = [];

			self._formGroup.each(function(index, item) {
				var $inputs = $(item).find('.' + self.options.inputClass);
				var pass = false;

				$.each($inputs, function(i, v) {
					if ($(v).is(':checked')) {
						pass = true;
					}
				});
				totals.push(pass);
			});

			// @joel bit poor - make better with more time
			if ($.inArray(false, totals) !== -1) {
				self._revealedElement.addClass('visuallyhidden');
			} else {
				self._revealedElement.removeClass('visuallyhidden');
			}


		},

		/**
		 * destroy
		 */
		_destroy: function() {

		}
	});

})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to trigger collapsible form items and reset boolean items
 * @author Joel Mitchell
 * @name $.cx.conditional_form_item
 * @dependencies: jQuery, jQuery UI widget factory
 */
(function($) {
    'use strict';

    $.widget('cx.conditional_form_item', {

        options: {
            'trigger': 'js-conditional-trigger',
            'selecttrigger': 'js-conditional-trigger--select',
            'content': 'is-collapsible-conditional',
            'contentSelect': 'is-collapsible-select',
            'subClassTrigger': 'js-conditional-sub-trigger',
            'subClassContent': 'is-sub-collapsible-conditional',
            'open': 'is-open-conditional',
            'input': 'form__field--boolean',
            'checkbox': 'form__field--checkbox'
        },

        // make event names easier to read
        widgetEventPrefix: 'conditional_forms_',

        /**
         * Constructor
         */
        _create: function() {
            var self = this;

            // bind click events
            self._on((function() {
                var events = {};
                events['click .' + self.options.trigger] = self._booleanClick;
                events['change .' + self.options.selecttrigger] = self._toggleBlocks;
                events['click .' + self.options.subClassTrigger] = self._booleanSubClick;
                return events;
            })());

            // create collection of first level triggers - taken from class name (was a bit wonky doing it without)
            self._collapsibleTrigger = self.element.find('.' + self.options.trigger);
            self._collapsibleTriggerSelect = self.element.find('.' + self.options.selecttrigger);

            if (self._collapsibleTriggerSelect.length > 0) {
                // toggle collection for select
                self._selectToggleCollection = self.element.find('.' + self.options.contentSelect);
                self._collapsibleTriggerSelect.each(function() {
                    $(this).eq(0).trigger('change');
                });
            }

            // initialise triggers
            self._initBoolean(self._collapsibleTrigger);

        },

        _initBoolean: function(collection) {
            var self = this;
            collection.each(function(ind, item) {
                var $item = $(item);
                var $content = $item.siblings('.' + self.options.content);
                self._initSecondary($content, ind);
                self._checker($item, $content);
            });

        },

        _initSecondary: function(content) {
            var self = this;
            var $el = content.find('.' + self.options.input);

            $el.addClass(self.options.subClassTrigger)
                .each(function(ind, item) {
                    var $item = $(item);
                    var $content = $item.siblings('.' + self.options.subClassContent);
                    self._checker($item, $content);
                });

        },

        _toggleBlocks: function(event) {
            var self = this;
            var target = $(event.target);
            var val;
            var index;

            /*
             * By default the value of the input field indicates which block to show.
             * However, we cannot rely on using the value of select boxes.  Therefore,
             * allow developers to specify a trigger data-attribute value.
             */
			 
			/*
			 * find the selected option and then get the value
			 * of the data attribute specified by `data-select-value`.
			 */
			 
			var dataAttr = target.find(':selected').data('select-value');
            if (dataAttr) {
                val = dataAttr;
            } else {
                //dont' use target.val() because in case there is no value the text will be passed instead
                val = $(target).find("option:selected").attr('value');
            }

            //find element to exlude
            var elementsToExclude = [];
            if (val != null) {
                var idsToExclude = val.split(' ');
                elementsToExclude = $.grep(self._selectToggleCollection, function(item) {
                    var found = false;
                    $(idsToExclude).each(function(index) {
                        found = $(item).attr('id') === this;
                        if (found) {
                            return false;
                        }
                    });
                    return found;
                });
            }

            //get ids from select-value option attribute
            var selectEl = target.parent();
            var optsEl = selectEl.find('option');
            var ids = [];
            $(optsEl).each(function() {
                var selectValueStr;
                if ($(this).data('select-value') != null) {
                    selectValueStr = $(this).data('select-value');
                } else {
                    selectValueStr = $(this).attr('value');
                }

                if (selectValueStr != null) {
                    var selectValueIds = selectValueStr.split(' ');
                    $(selectValueIds).each(function() {
                        if (this != null) {
                            ids.push(this);
                        }
                    });
                }
            })

            //filter ids to be unique
            var uniqueIds = $.grep(ids, function(el, index) {
                return index === $.inArray(el, ids);
            });

			//generate a collection of DOM elements
            var elCollection = [];
            $(uniqueIds).each(function() {
                elCollection.push($('#' + this));
            });

            self.closeAllPanels(elCollection, elementsToExclude);
        },

        _booleanClick: function(event) {
            var self = this;
            var $el = $(event.currentTarget);
            var index = self._collapsibleTrigger.index($el);
            if ($el.is('input:radio')) {
                self.closeAll(self._collapsibleTrigger, index);
                self.open(self._collapsibleTrigger, index);
            } else {
                self._toggleBoolean(self._collapsibleTrigger, index, true);
            }

        },

        /**
         * toggle for checkbox
         */
        _toggleBoolean: function(element, index, level) {
            var self = this;
            var $content;
            if (level) {
                var $el = $(element[index]);
                $content = $el.siblings('.' + self.options.content);
                self._checker($el, $content);
            } else {
                $content = element.siblings('.' + self.options.subClassContent);
                self._checker(element, $content);
            }

        },

        closeAll: function(collection, exclude, secondLevel) {
            var self = this;
            $.each(collection, function(index) {
                if (parseInt(exclude, 10) !== parseInt(index, 10)) {
                    self.close(collection, index, secondLevel);
                }
            });
        },

        closeAllPanels: function(collection, exclude, duration) {
            var self = this;
            var rate = duration || 0;

            //close all items
            $(collection).each(function() {
                self.closeContent($(this), rate);
            });

            //open only items 
            $(exclude).each(function() {
                self.openContent($(this), rate);
            });

            /*
            $.each(collection, function(index, item) {
            	//TODO each function on exlude elements (to parse as # because exlude paramter is just a string)
            		$(exclude).each(function(){
	          				if (parseInt(this, 10) !== parseInt(index, 10)) {
	                    self.closeContent($(item), rate);
	                } else {
	                    self.openContent($(item), rate, true);
	                    return false;
	                }
            		});
            });*/
        },

        /**
         * Close the block
         */
        close: function(collection, index, secondLevel) {
            var self = this;
            var $el = collection.eq(index);
            var $content = secondLevel === true ?
                $el.siblings('.' + self.options.subClassContent) :
                $el.siblings('.' + self.options.content);

            if ($content.hasClass(self.options.open)) {
                self._closeAndReset($content);
            }
        },

        /**
         * Open the block
         */
        open: function(collection, index, secondLevel) {
            var self = this;
            var $el = collection.eq(index);
            var $content = secondLevel === true ?
                $el.siblings('.' + self.options.subClassContent) :
                $el.siblings('.' + self.options.content);

            self.openContent($content);
        },

        /**
         * Reset the contents of the collapsed panels
         */
        resetBooleans: function(collection) {
            var $booleans = collection.find('input:radio, input:checkbox');
            var $content = $booleans.siblings('.' + this.options.subClassContent);
            $booleans.prop('checked', false);
            collection.find('input:text, input:password').val('');
            this.closeContent($content);
        },

        /**
         * Tests for checked
         */
        _checker: function(element, content) {

            if (element.is(':checked')) {
                this.openContent(content);
            } else {
                this._closeAndReset(content);
            }
        },

        _closeAndReset: function(content) {
            this.closeContent(content);
            // this.resetBooleans(content);
        },

        _booleanSubClick: function(event) {
            var self = this;
            var $el = $(event.currentTarget);
            // var content = $el.siblings('.' + self.options.subClassContent);

            if ($el.is('input:radio')) {
                var collection = $el.closest('.form__group').find('.' + self.options.input);
                var index = collection.index($el);
                self.closeAll(collection, index, true);
                self.open(collection, index, true);
            } else {
                self._toggleBoolean($el /*, index*/ );
            }
        },

        /*
         * Standard open / close stuff - replace with collapsible?
         */
        openContent: function(content, rate, focus) {
            content
                .addClass(this.options.open)
                .stop()
                .slideDown(rate, function() {
                    content.css({
                        'height': 'auto'
                    });

                });

        },

        closeContent: function(content, rate) {
            content
                .removeClass(this.options.open)
                .stop()
                .slideUp(rate);
        },


        /**
         * Destroy
         */
        _destroy: function() {

        }
    });

})(jQuery);;
/**
 * @fileOverview A jQuery UI Widget to handle slideshows
 * @author Andy Mantell
 * @name $.cx.skiplink
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function($) {
    'use strict';

    $.widget('cx.skiplink', {

        options: {},

        // Add an underscore to the event prefix to make event names easier to read
        widgetEventPrefix: 'skiplink_',

        /**
         * Constructor
         */
        _create: function() {
            var self = this;

            // Find the target of the skiplink
            self._target = $(self.element.attr('href'));

            // Bind a click event on the skiplink
            // use keydown rather than click
            self._on({
                'keydown': function(e) {
                    if (e.keyCode === 13) {
                        self._skip();
                    }
                }
            });

            self._on(self._target, {
                'click': self._removeTabindex,
                'blur': self._removeTabindex
            });

        },

        /**
         * Skip to the element defined
         */
        _skip: function() {
            var self = this;

            // Allow the target to be receive focus via js
            self._target.attr('tabindex', -1);

            // Focus on the target
            self._target.focus();

        },

        _removeTabindex: function() {

            // Allow the tabindex to be removed on blur
            this._target.removeAttr('tabindex');

        },


        /**
         * Destroy
         */
        _destroy: function() {
            var self = this;
        }
    });

})(jQuery);
;
(function($) {
    "use strict";
    $.widget("cx.scrollToElement", {
        options: {
            "duration": 450,
            "delay": 50,
            "easing": "swing",
            "top": "top"
        },

        // the constructor
        _create: function() {

            this._on(this.element, {
                "click": function(event) {
                    event.preventDefault();
                    this.scrollTop(event);
                }
            });
        },

        scrollTop: function(event) {
            var self = this,
                options = self.options,
                $element = $(event.currentTarget).prop("href"),
                hash = $element.split("#")[1];

            var yPos = hash !== options.top ? $("#" + hash).offset().top : 0;

            //   include both html and body for some browsers
            $("html,body")
                .delay(options.delay)
                .animate({
                        scrollTop: yPos
                    },
                    options.duration,
                    options.easing)
                .promise() //     make a promise to prevent duplication of the animation
                .done(function() {

                    //   if there is a callback needed - stipulate it in the startup script
                    if (options.callback !== "" && typeof options.callback === "function") {
                        options.callback.call(this);
                    }
                });

            self._trigger("scrollTop");

        },

        _destroy: function() {


        }
    });

})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to provide show more/ fewer functionality within a table
 * @author Joel Mitchell
 * @name $.cx.show_more_table
 * @dependencies: jQuery, jQuery UI widget factory (Mustache?)
 */
// @todo: subclass the collapsing behaviour to share across all the collapsibles


(function ($) {
    'use strict';
    $.widget('cx.show_more_table', {
        options: {
            'show': 3,
            'viewMore': 'Voir plus',
            'viewFewer': 'Voir moins',
            'isMobile': false,
            'subheadingClass': '.compare-table__subheading',
            'triggerClass': 'show-more__trigger',
            'hiddenRowClass': 'compare-table__row--hidden',
            'wrapperClass': 'compare-table__inner-wrap',
            'wrapper': '<div class="{{wrap}}"></div>',
            'templates': {
                // output the row template that houses the show more / less trigger link
                'row': '<tr class="hide-on-desktop"><td class="compare-table__heading--centred" colspan="5"><span class="compare-table__subheading"><a href="#" class="{{trigger}}">{{label}}</a></span></td></tr><tr><th><span class="compare-table__subheading"><a href="#" class="{{trigger}}">{{label}}</a></span></th>{{#rows}}<td class="compare-table__row--hidden {{#.}}{{.}}{{/.}}"></td>{{/rows}}</tr>',
            },

            // Classes
            'activeClass': 'is-showmore--active',
            'openClass': 'is-showmore--open',
            'closedClass': 'is-showmore--closed',
            'scrollClass': 'is-showmore--scroll', // Should the collapsible scroll when animating?

            // Misc options
            'duration': 250 // Duration of slide animations

        },

        widgetEventPrefix: 'show_more_table_',


        // the constructor
        _create: function () {
            var self = this;
            var label = this.element.data('show-table-label').split(',');

            this.options.viewMore = label[0];
            this.options.viewFewer = label[1];

            // stash our collapsed things for easy reference
            this._collapsedItems = [];

            // Grab elements
            // if mobile handle differently...
            // grab the items to show from the data- attributes
            this._show = this.element.data('show-items') === 0 ? 0 : this.element.data('show-items') || this.options.show;

            // slightly hacky way of adjusting the numbers
            // so sorry - this thing is pretty ugly in places : (
            if (this.options.isMobile) {
                this._items = this.element.children();
                this._show = (this._show + 1) * 2;
            } else {
                this._items = this.element.children(':not(".hide-on-desktop")');
                this._show = (this._show + 1);
            }

            //
            if (this._items.length <= this._show) {
                return;
            }

            // create an array of the last item's table classes and cell numbers to pass to the Mustache template
            var rows = $.map(this._items.last().children('td'), function (value, index) {
                return $(value).attr('class') || '';
            });

            // create an inner wrapper for table cells to enable it to collapse
            var wrapper = Mustache.render(this.options.wrapper, {
                'wrap': this.options.wrapperClass
            });

            // loop through items from the offset, stash the collapsed items for reuse
            this._items
                .slice(this._show)
                .each(function (index, item) {
                    var $item = $(item);
                    self._collapsedItems.push($item);
                    $item
                        .children()
                        .wrapInner(wrapper);

                });


            // make the view more row - copy across the styles from the previous row's cells
            this._row = $(Mustache.render(this.options.templates.row, {
                'colspan': this.element.data('show-table-colspan') || 5,
                'label': this.options.viewMore,
                'trigger': this.options.triggerClass,
                'rows': rows,
                'populated': rows
            }));

            // add the row at the end
            this.element.append(this._row);

            // find and populate the trigger link
            // store it for later use
            this._triggerLink = this._row.find('.' + this.options.triggerClass);


            this._triggerLink.text(this.options.viewMore);

            // loop over collection but offset loop from items current index
            this._on(this._triggerLink, {
                'click': self._toggleCells,
                'keydown': self._keyboardHandler
            });

            this.close(this._collapsedItems, 0);
        },

        //  bind space bar and enter keys
        _keyboardHandler: function (e) {
            var keyCode = e.keyCode;

            if (keyCode === 32 || keyCode === 13) {
                e.preventDefault();
                this._toggleCells(e);
            }

        },

        // much to refactor to make a base class for all the collapsible items
        _toggleCells: function (event) {

            event.preventDefault();
            if (this._open) {
                this.close(this._collapsedItems);

            } else {
                this.open(this._collapsedItems);
            }

            this._open = !this._open;

        },


        open: function (items) {
            var self = this;
            var itemLength = items.length;

            $.each(items, function (index, item) {
                $(item).removeClass(self.options.hiddenRowClass)
                    .find('.' + self.options.wrapperClass)
                    .stop(true, true)
                    .slideDown({
                        'duration': self.options.duration
                    });
            });

            this._viewFewer();
        },

        close: function (items, duration) {
            var self = this;
            var itemLength = items.length;

            typeof duration !== 'undefined' ? duration : this.options.duration;

            $.each(items, function (index, item) {
                $(item).addClass(self.options.hiddenRowClass)
                    .find('.' + self.options.wrapperClass)
                    .stop(true, true)
                    .slideUp({
                        'duration': duration
                    });
            });

            this._viewMore();
        },


        // update triggerlink text
        _viewMore: function () {
            this.element
                .removeClass(this.options.openClass);

            this._triggerLink
                .text(this.options.viewMore)
                .removeClass(this.options.openClass);
        },

        // update triggerlink text
        _viewFewer: function () {
            this.element.addClass(this.options.openClass);

            this._triggerLink
                .text(this.options.viewFewer)
                .addClass(this.options.openClass);
        },

        /**
        * Helper function to scroll to a dom target.
        * This function attempts to scroll the window to a moving target.
        * applied on mobile only - and not always : )
        */
        _scrollTo: function ($target) {
            var self = this;

            if (!this.options.isMobile) {
                return;
            }

            if ($target.length > 0) {
                var $window = $(self.window);

                // Work out where we started scrolling from
                var scrollStart = $window.scrollTop();

                // Animate a dummy object so that we can make use of jQuerys animation progress function to scroll the window
                $({ dummy: 0 })
                    .animate({ dummy: 1 }, {
                        easing: 'linear',
                        duration: self.options.duration,
                        progress: function (animation, progress, remainingMs) {

                            // Where's the top of our target at this point?
                            var targetTop = $target.offset().top;

                            // Work out how far away from the target scroll position we are
                            var scrollDelta = targetTop - scrollStart;

                            // Move the window to this scroll position in sync with the slide animation's progress
                            $window.scrollTop(scrollStart + (scrollDelta * progress));
                        }
                    });

            }
        },


        _destroy: function () {
            var self = this;
            $.each(this._collapsedItems, function (index, item) {
                $(item)
                    .show()
                    .find('.' + self.options.wrapperClass)
                    .children()
                    .unwrap(); // ugh - have to unwrap things - ugh
            });

            if (this._row) {
                this._row.remove();
            }

        }
    });

})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to render a set of collapsible sections
 * @author Andy Mantell
 * @name $.cx.collapsible
 * @dependencies: jQuery, jQuery UI widget factory, jQuery tinypubsub
 */

/**
 * Tabs
 */
(function ($) {

    'use strict';

    var DESKTOP_ONLY_MODE = 1;
    var MOBILE_ONLY_MODE = 2;

    $.widget('cx.collapsible', {

        options: {
            active: 0,
            duration: 300, // Duration of slide animations
            selectors: {
                panel: '.collapsible',
                heading: '.collapsible__heading:eq(0)',
                content: '.collapsible__content:eq(0)',
                trigger: '.collapsible__trigger:eq(0)',
            },
            templates: {
                headingWrap: '<a href="#" class="collapsible__trigger"></a>',
                icon: '<i class="icon-font icon-chevron-right"></i>',
            },
            classes: {
                mobile: 'js-collapse--mobile',
                desktop: 'js-collapse--desktop',
                icon: 'chevron-link--before',
                active: 'is-collapsible--active',
                accordion: 'is-collapsible--accordion', // Can multiple panels be open at any one time?
                noScroll: 'collapsible--noscroll', // Should the collapsible scroll when animating?
                animatingClosed: 'is-collapsible--animating-closed',
                animatingOpen: 'is-collapsible--animating-open',
            },
            events: {
                clickHeading: 'click .collapsible__trigger',
                keydownHeading: 'keydown .collapsible__trigger',
                focusElement: 'focus',
            }
        },

        widgetEventPrefix: 'collapsible_',

        /**
         * Standard jQuery UI create function
         */
        _create: function () {

            var self = this,
                elem = self.element,
                classes = self.options.classes;

            // try to detect in which responsive situation we should enable or not this plugin:
            // - only --mobile class   -> mobile only
            // - only --desktop class  -> desktop only
            // - any other case        -> all the time

            if (elem.hasClass(classes.mobile) && !elem.hasClass(classes.desktop)) {

                self._activationMode = MOBILE_ONLY_MODE;

            } else if (elem.hasClass(classes.desktop) && !elem.hasClass(classes.mobile)) {

                self._activationMode = DESKTOP_ONLY_MODE;

            }
            
            // handle switch between desktop and mobile using media queries

            this._onMatchingTabletMinHandler = function (matches) {


                switch (self._activationMode) {

                    case DESKTOP_ONLY_MODE:
                        self[matches ? '_addCollapsibleBehavior' : '_removeCollapsibleBehavior'].call(self);
                        break;

                    case MOBILE_ONLY_MODE:
                        self[matches ? '_removeCollapsibleBehavior' : '_addCollapsibleBehavior'].call(self);
                        break;

                    default:
                        if ( !self._initialized ) {
                            self._addCollapsibleBehavior();
                        }
                        break;

                }
                
            };

            EC.Mq.on(EC.Mq.tabletMin(), this._onMatchingTabletMinHandler, true);

        },

        /**
         * 
         */
        _addCollapsibleBehavior: function () {

            var self = this,
                opts = self.options,
                selectors = opts.selectors,
                classes = opts.classes,
                tmpls = opts.templates,
                eventDefinitions = opts.events;

            // wrap the headings in an anchor for native tabindexing - insert icon

            var $panels = self._panels = self.element.find(selectors.panel);
            $panels.find( selectors.heading )
                .addClass( classes.icon )
                .prepend( tmpls.icon )
                .wrap(tmpls.headingWrap);

            var $headings = self._headings = self._panels.find(selectors.trigger);
            var $contents = self._contents = self._panels.find(selectors.content);

            // collapse all by default, except any that initially have the active class

            var tot = $panels.length;
            for ( var i = 0; i < tot; i++ ) {
                var $item = $($panels[i]);
                if (!$item.hasClass(classes.active)) {
                    $item.find(selectors.content).hide();
                }
            }

            // allow keyboard focusing

            $headings.attr('tabindex', 0);
            $contents.attr('tabindex', -1);

            // if has the accordion class then set to true for later use

            self._isAccordion = self.element.hasClass(classes.accordion);

            // bind events

            var events = {};
            events[eventDefinitions.clickHeading] = self._togglePanel;
            events[eventDefinitions.keydownHeading] = self._keyboardHandler;
            events[eventDefinitions.focusElement] = function() {
                $headings.eq(0).focus();
            };

            self._on(events);

            // and we're done

            self.element.addClass('is-collapsible').attr('tabindex', -1);
            self._initialized = true;

        },

        /**
         * 
         */
        _removeCollapsibleBehavior: function( panel ) {
            
            var self = this,
                opts = self.options,
                selectors = opts.selectors,
                classes = opts.classes;

            if ( !self._initialized ) {
                return;
            }

            self.element.removeClass('is-collapsible');
			
			// show all hidden content
            self._contents.show();
            
			// remove added tabindexes
            self._headings.add(self._contents).add(self.element).removeAttr('tabindex');

			// remove icon class and unwrap from anchors
            self._panels
				.find(selectors.heading)
				.removeClass(classes.icon)
				.unwrap();
					
			// remove icons
            self._panels
				.find('i')
                .remove();
        },

        _keyboardHandler: function (event) {

            var self = this, $headings = self._headings;

            if (event.altKey || event.ctrlKey) {
                return;
            }

            var keyCode = event.keyCode,
                length = $headings.length,
                currentIndex = $headings.index(event.currentTarget),
                toFocus = false;

            switch (keyCode) {
                case 39: //keyCode.RIGHT:
                case 40: //keyCode.DOWN:

                    toFocus = $headings[(currentIndex + 1) % length];
                    break;
                case 37: //:keyCode.LEFT:
                case 38: //keyCode.UP:

                    toFocus = $headings[(currentIndex - 1 + length) % length];
                    break;
                case 32: //keyCode.SPACE:
                case 13: //keyCode.ENTER:
                    toFocus = $headings[currentIndex];
                    self._togglePanel(event);
                    event.preventDefault();
                    break;
                case 36: //keyCode.HOME:
                    toFocus = $headings[0];
                    break;
                case 35: //keyCode.END:
                    toFocus = $headings[length - 1];
                    break;
                    // case 9: //keyCode.TAB:
                    //     // toFocus = self.element;
                    //     break;
            }

            if (!toFocus) {
                return;
            }

            $(event.currentTarget).attr('tabIndex', -1);
            $(toFocus)
                .attr('tabIndex', 0)
                .focus();

            event.preventDefault();
        },

        /**
         * Toggle a panel
         */
        _togglePanel: function (e) {

            e.preventDefault();

            var self = this,
                opts = self.options,
                selectors = opts.selectors,
                classes = opts.classes;

            var $panel = $(e.currentTarget).closest(selectors.panel),
                $heading = $panel.find(selectors.trigger),
                $content = $panel.find(selectors.content),
                index = $panel.index(self._panels),
                complete = function () {}; // A function to run after the slide animation is complete

            // If the widget is in single panel only mode, collapse all the other panels
            if (self._isAccordion) {
                self._contents.not($content).stop(true, true).slideUp(opts.duration);
                self._panels.not(':eq(' + index + ')').removeClass(classes.active);
            }

            // If we are about to open the selected panel, rather than close it
            if ($content.is(':hidden')) {

                // Toggle the active class straight away
                $panel.addClass(classes.active);
                $panel.addClass(classes.animatingOpen);

                // Trigger an event
                self._trigger('collapsible_before_open', e, {
                    'panel': $panel
                });
            } else {

                $panel.addClass(classes.animatingClosed);
                $panel.removeClass(classes.active);

                // Toggle the active class in the animation complete function
                // complete = function() {
                //     $panel.removeClass(classes.active);
                // };

                //  before close event as requested [https://cxpartners.codebasehq.com/projects/euroconsumer/tickets/577]
                self._trigger('collapsible_before_close', e, {
                    'panel': $panel
                });
            }

            // Set keyboard focus on the heading
            $heading.focus();

            // Toggle the selected panel
            $content
                .stop(true, true)
                .slideToggle({
                    'duration': opts.duration,
                    'complete': function() {
                        complete();

                        $panel.removeClass(classes.animatingOpen);
                        $panel.removeClass(classes.animatingClosed);

                        if ($content.is(':visible')) {
                            self._trigger('collapsible_after_open', e, {
                                'panel': $panel
                            });

                            //  Set keyboard focus on the panel content
                            // $content.focus();
                        }
                    }
                });

        },


        /**
         * Tear everything down again.
         */
        _destroy: function() {

            if ( this._initialized ) {
                this._removeCollapsibleBehavior();
            }

            if ( this._onMatchingTabletMinHandler ) {
                EC.Mq.off(EC.Mq.tabletMin, this.__onMatchingTabletMinHandler);
            }

        }

    });



})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to handle the compare item behaviour on desktop and tablet
 * @author Joel Mitchell
 * @name $.cx.compare_desktop
 * @dependencies: jQuery, jQuery UI widget factory, Mustache
 */

(function ($, undefined) {
    'use strict';

    $.widget('cx.compare_desktop', {
        options: {
            'moreThan': 1,
            'max': 5,
            'compareItem': 'listing__item__compare',
            'compareBarItem': 'compare-bar__item',
            'hasCompareBarClass': 'has-compare-bar',
            'removingCompareItem': 'compare-bar__item--removing',
            'removeableClass': 'compare-bar__item--removeable',
            'checkedClass': 'listing__item__compare--checked',
            'disabledCheckClass': 'listing__item__compare--disabled',
            'waitingClass': 'listing__item__compare--waiting',
            'removeAllClass': 'compare-bar__remove-link',
            'disabledClass': 'is-disabled',
            'hiddenClass': 'hidden',
            'errorClass': 'error--server',
            'closeClass': 'compare-remove',
            'isCollapsed': 'compare-bar--is-collapsed',
            'isOpen': 'is-showmore--open',
            'collapseClass': 'compare--collapse-toggle',
            'compareActiveClass': 'is-active',
            'compareButton': 'js-btn--compare',
            'compareButtonCount': 'listing__btn-bar__count',
            'compareBarButtonClass': 'compare-bar__button__action',
            'compareBarButtonCountClass': 'compare-bar__button__count',
            'compareBarTextCountClass': 'compare-bar__saved-products-count',
            'duration': 300,
            'messages': {
                'singleItem': 'Please add another item to compare'
            },
            'templates': {
                'error': '<div class="error {{error_class}}">{{fail_message}}</div>',
                'compareItemEmpty': '<li class="compare-bar__item compare-bar__item--removeable"><div class="compare-bar__image__container"></div><h3 class="zeta"></h3></li>',
                'compareItemFull': '{{#data}}<li class="compare-bar__item has-image  compare-bar__item--removeable" data-compare-id="{{id}}"  data-compare-img="{{img}}"  data-compare-title="{{title}}"><div class="compare-bar__image__container"><a tabindex="0" class="compare-remove icon-x"><span class="visuallyhidden">Remove item</span></a><img src="{{{img}}}" class="compare-bar__image__item" alt="" /></div><h3 class="zeta"><a href="{{{href}}}">{{title}}</a></h3></li>{{/data}}'
            }
        },

        'widgetEventPrefix': 'compare_desktop_',

        /**
         * Standard jQuery UI create function
         */
        _create: function () {
            var self = this;

            self._html = $('#off-canvas-menu-wrapper');

            // gathers the page rendered variables for the items already on other pages
            // this array is the one to check for length, details etc
            self._checkList = [];

            // does a data object exist from a previously re-sized page? Can this be inherited from the mobile version of the page?
            if (self.element.data('compare_data') || self.element.data('compare_data') !== undefined) {
                self._checkList = self.element.data('compare_data');
            }

            // bind trigger link click
            self._on((function () {
                var events = {};
                events['click .' + self.options.compareItem] = self._toggleCheckedButtons;
                events['click .' + self.options.closeClass] = self._removeItem;
                events['click .' + self.options.collapseClass] = self._collapse;
                events['keydown .' + self.options.closeClass] = function (e) {
                    if (e.keyCode === 32 || e.keyCode === 13) {
                        self._removeItem(e);
                    }
                };
                events['click .' + self.options.removeAllClass] = self._removeAllItem;


                return events;
            })());

            // grab DOM stuff
            self._checkBoxes = self.element.find('.' + self.options.compareItem);
            self._listItems = self.element.find(self.options.listItem);
            self._compareBar = self.element.find('.compare-bar');
            self._compareBarContent = self.element.find('.compare-bar__content');
            self._compareList = self._compareBar.find('.compare-bar__list');
            self._compareButton = self._compareBar.find('.' + self.options.compareBarButtonClass);
            self._compareButtonCount = self._compareBar.find('.' + self.options.compareBarButtonCountClass);
            self._compareTextCount = self._compareBar.find('.' + self.options.compareBarTextCountClass);
            self._collapseButton = self._compareBar.find('.' + self.options.collapseClass);

            // timeout for resize event
            self._timeoutId = null;
            self._wrapperWidth = 0;

            // binding keyboard events to the checkboxes
            self._on('.' + self.options.compareItem, {
                'keydown': function (e) {
                    var key = e.keyCode || e.which;

                    if (key === 32 || key === 13) {
                        self._toggleCheckedButtons(e);
                    }
                }
            });

            // if (self._checkList.length > 0) {
            self._renderItems(self._checkList);
            self.element.addClass(self.options.compareActiveClass);
            self._revealCompareBar(self._checkList.length);

            self.countCheckButtons();
            // }

            //  window resize handler binding
            self._on(self.window, {
                'resize': self._resizeHandler,
                'orientationchange': self._resizeHandler
            });

            $(document.body).on('compareitems-updated', function (e, results) {
                self._checkList = results;
                self._renderItems(self._checkList);
                self.element.addClass(self.options.compareActiveClass);
                self._revealCompareBar(self._checkList.length);
                self._updateCompareCheckboxes(results);
                self.countCheckButtons();
            });
            },


        _updateCompareCheckboxes: function(results) {
            var compareBoxes = this._findCheckboxes();
            var compareIds = results.map(function (r) { return r.id; });
            for (var i = 0; i < compareBoxes.length; i++) {
                var serviceId = compareBoxes[i].getAttribute("data-compare-id");
                if ($.inArray(serviceId, compareIds) > -1) {
                    $(compareBoxes[i]).addClass("listing__item__compare--checked");
                }
            }
            },

        /**
         * updates the compare list array of objects -
         * removes by id and returns items not having that id (removes multiple instances if any)
         */
        _removeFromCollection: function (removeItem) {
            this._checkList = $.grep(this._checkList, function (value) {
                return value.id.toString() !== removeItem.id.toString();
            });
        },

        /**
         * Basic resize throttler to resize
         */
        _resizeHandler: function () {

            var self = this;
            var width = self._compareBar.width();

            if (width < 850 || width > 960) {
                return;
            }

            if (self._wrapperWidth === width) {
                return;
            }

            self._wrapperWidth = width;

            clearTimeout(self._timeoutId);
            self._timeoutId = setTimeout(function () {

                // @joel - reinstate peepage for testing

                // var childWidth = self._compareList.children().eq(0).outerWidth(true);
                // console.log(width%childWidth);
                // if(width%childWidth ){

                // }
                // var totalWidth;


                // self._compareList.children().width();

            }, 150);
        },

        /**
         * remove the checked state when clicking the remove button of a compare bar item
         */
        _remotelyUncheckCheckboxes: function (removeItem) {
            var self = this;
            var checkbox = $.grep(self._findCheckboxes(), function (value) {
                var id = $(value).data('compare-id');
                return id.toString() === removeItem.id.toString();
            });

            if (checkbox) {
                $(checkbox).removeClass(self.options.checkedClass);
            }

        },

        // use the self._checkList array to render the items using Mustache templates
        // if fewer than 5 add in blank items
        _renderItems: function (items) {
            var self = this;
            var len = items.length;
            var diff = self.options.max - len;
            var html = Mustache.render(self.options.templates.compareItemFull, {
                'data': items
            });

            // add in blanks -
            for (var i = 0; i < diff; i++) {
                html += self.options.templates.compareItemEmpty;
            }

            self._compareList.empty().html(html);
            self._tooFewWarning(self._returnRemoveable(), true);
        },

        /**
         * bound toggle event - raise an add or remove event and runs a series of checks for count and state
         */
        _toggleCheckedButtons: function (event) {
            event.preventDefault();
            var self = this;
            var target = $(event.currentTarget);
            var $item;
            var previousItem;

            //  horrible hack to allow ie8 to pass an event reference
            //  kicks out a No member present error otherwise
            var eventCopy = {
                'type': event.type
            };

            // if waiting or disabled, abort - prevent multiple silly clicks while waiting
            if (target.hasClass(self.options.disabledCheckClass) || target.hasClass(self.options.waitingClass)) {
                return false;
            }

            // generate data object to pass to AJAX
            var data = {
                'id': target.data('compare-id'),
                'img': target.data('compare-img'),
                'title': target.data('compare-title'),
                'href': target.data('compare-href')
            };

            // there could be an error message next to the checkox - remove it
            self._removeErrorMessage(target);

            //	waiting...
            target.addClass(self.options.waitingClass);

            //	raise different events depending on whether the 'checkbox' is active or not
            //	complete function from mockjax (in integration) sends back the object to either remove from or push to the self._checkList array
            // 	then check the length of the array
            if (target.hasClass(self.options.checkedClass)) {

                // abstracted as it is used by checkboxes and the remove buttons on the compare bar
                self._removeEvent(eventCopy, target, data);

            } else {

                // find the next empty slot in the compare bar and bag it for adding the item to
                // sorts issue of failed attempt leaving an empty slot
                $.each(self._returnRemoveable(), function (index, item) {
                    var $el = $(item);
                    if (!$el.hasClass('has-image') && !$el.hasClass(self.options.waitingClass)) {
                        $item = $el;
                        $el.addClass(self.options.waitingClass);
                        previousItem = index;
                        return false;
                    }
                });

                if ($item === undefined) {
                    return;
                }

                // push it, don't splice it, want it at the end of the array
                // do it first thing to update the array count and allow the checkbuttons to be disabled
                self._checkList.push(data);

                //  we do the button count now to
                self.countCheckButtons();

                // we have assumed a successful server response to allow for the disabling of the checkboaxes
                // but we need to disable the compare button in case
                if (self._checkList.length <= 2) {
                    this._disableCompareButtons(1);
                }

                // depending on how many things there are in the checklist, animate the compare bar left or right to highlight
                // the item that is being removed or added
                self._animateCompareBarHorizontal(self._checkList.length, previousItem);

                self._trigger('add', null, {
                    'id': data,
                    'complete': function (ui) {

                        // if fail append message and reset classes and numbers
                        if (ui.status === 'failure') {

                            $item.removeClass(self.options.removingCompareItem).removeClass(self.options.waitingClass);

                            //	add in error message
                            //  remove waitin and checked class
                            target.after(Mustache.render(self.options.templates.error, {
                                'fail_message': ui.fail_message,
                                'error_class': self.options.errorClass
                            })).removeClass(self.options.waitingClass).removeClass(self.options.checkedClass);

                            // if returns as failed, remove the item from the checklist and recount the items
                            // this will free up the checkboxes
                            self._removeFromCollection(ui.data.id);
                            self.countCheckButtons();
                            self._deleteItem($item);

                            return;
                        }


                        // success - make checked and remove blocking
                        target.addClass(self.options.checkedClass).removeClass(self.options.disabledCheckClass).removeClass(self.options.waitingClass);

                        // pass reference of the holding item so it can be swapped out with the html
                        // returned from the 'server'
                        self._addSingleItem($item, ui, target);

                        self._tooFewWarning(self._returnRemoveable(), true);
                    }
                });
            }

        },

        // create a chunk of html and swap the blank item out with it
        _addSingleItem: function (item, data, target) {
            var self = this;
            var html = Mustache.render(self.options.templates.compareItemFull, {
                data: data.data.id
            });

            if (item) {
                item.replaceWith(html);
            }

            target.removeClass(self.options.waitingClass).addClass(self.options.checkedClass);

            //  count items now that the thing has been selected / checked
            self.countCheckButtons();

        },

        /**
         * when we add an item, if there are greater than 3 items have it scroll left prior to adding so we see it happening
         */
        _animateCompareBarHorizontal: function (count, index) {
            this._compareBarContent.animate({
                'scrollLeft': (count > 3 && index > 2) ? 400 : 0
            }, this.options.duration);
        },

        /**
         * if there is an error item after the checkbox, remove it
         */
        _removeErrorMessage: function (target) {
            target.next('.' + this.options.errorClass).remove();
        },

        /**
         * extracted removal event for use with checkboxes and close buttons
         * this could do with being smaller and less smelly
         */
        _removeEvent: function (event, target, data) {
            var self = this;
            var $item;
            var previousItem;

            // loop through checklist to check to match up compare bar ids with checkboxes
            // store item and index for later use
            /*$.each(self._checkList, function (index, value) {
                if (value.id.toString() === data.id.toString()) {
                    $item = self._compareList.find('[data-compare-id="' + value.id.toString() + '"]');
                    $item.addClass(self.options.removingCompareItem);
                    previousItem = index;
                    return false;
                }
            });*/

            // refactored: no need to break out an $.each for one element
            // refactored: no need to validate against checklist since we won't be able to map an item otherwise
            $item = self._compareList.find('[data-compare-id="' + data.id + '"]');
            $item.addClass(self.options.removingCompareItem);
            previousItem = $item.index();

            // if the item wasn't triggered for delete in another event
            if (!$item.hasClass(self.options.waitingClass)) {
                // stick a waiting class on it to block checkbox until the server returns something
                target.addClass(self.options.waitingClass).removeClass(self.options.checkedClass);

                // remove error messages
                self._removeErrorMessage(target);

                // raise a remove event to the server
                self._trigger('remove', null, {
                    'id': data,
                    'complete': function (ui) {

                        // troubles - error handling. reset things
                        if (ui.status === 'failure') {

                            $item.removeClass(self.options.removingCompareItem).removeClass(self.options.waitingClass);

                            // add error message and reinstate de-selected check class, remove waiting class
                            target.after(Mustache.render(self.options.templates.error, {
                                'fail_message': ui.fail_message,
                                'error_class': self.options.errorClass
                            })).addClass(self.options.checkedClass).removeClass(self.options.waitingClass);

                            // self._tooFewWarning(self._returnRemoveable());

                            // if we were using the keyboard and the target was the compare list remove buttons - return focus to the button
                            if (event.type === 'keydown') {
                                $item.find('.' + self.options.closeClass).focus();
                            }

                            return;
                        }

                        self._animateCompareBarHorizontal(self._checkList.length, previousItem);

                        // remove waiting class from checkbox and compare bar item
                        $item.removeClass(self.options.waitingClass);
                        target.removeClass(self.options.waitingClass);


                        // if using the remove buttons on items from the compare bar, uncheck the corresponding checkboxes if they exist on the page
                        self._remotelyUncheckCheckboxes(data);

                        self._deleteItem($item, previousItem, event);

                        // remove this item from the checklist collection
                        self._removeFromCollection(ui.data.id);

                        self.countCheckButtons();
                    }
                });
            }
        },

        //  abstracted delete item
        _deleteItem: function ($item, previousItem, event) {
            var self = this;

            // hide item to animate, remove on end, then hackily remove it using a set timeout as te delay doesn't work.
            $item.hide(self.options.duration, function () {
                var removeable = self._returnRemoveable();

                if (removeable.length < 6) {
                    self._compareList.append(Mustache.render(self.options.templates.compareItemEmpty));
                    self._tooFewWarning(removeable, false);
                }

                if (previousItem !== undefined) {

                    // if navigation is via keyboard - return focus to previous compare item on removal
                    if (event.type === 'keydown') {
                        var length = removeable.filter('.has-image').length;


                        //	if there is just one left, when we remove it return the focus to the previous focusable object
                        //	add tabindex to allow it to receive it, then jump out
                        if (length === 1) {
                            self.element.find('.listing__footer').prop('tabIndex', -1).focus();

                            return;
                        }

                        // if there are items before removing object set focus on them
                        // if not, set focus to item after
                        if (previousItem !== -1 && previousItem > 0) {
                            self._compareList.children().eq(previousItem - 1).find('.' + self.options.closeClass).focus();
                        } else {
                            self._compareList.children().eq(previousItem + 1).find('.' + self.options.closeClass).focus();
                        }
                    }
                }

                setTimeout(function () {

                    $item.remove();
                    self.countCheckButtons();

                }, self.options.duration);

            });
        },

        // if there is only one item in the compare bar, show a prompt to explain why the compare button is disabled
        _tooFewWarning: function (removeable, add) {
            var itemsWithImages = removeable.filter('.has-image').length;
            var num = add ? 1 : 2;
            if (itemsWithImages === num) {
                removeable.eq(num).find('.zeta').text(this._compareBar.data("add-another-item-text"));
            }
        },

        // return this - need to keep using this can't just do once so extract it
        _returnRemoveable: function () {
            return this._compareList.children();
        },

        /**
         * counts the checklist array items and acts accordingly
         */
        countCheckButtons: function () {
            var count = this._checkList.length;

            this._disableCompareButtons(count);
            this._disableCheckboxes(count);
            this._revealCompareBar(count);

            this._updateCount(this._compareButtonCount, count);
            this._updateCount(this._compareTextCount, count);

            // add checklist array to a data object for maintaining state
            this.element.data('compare_data', this._checkList);
            this.element.find('.' + this.options.compareButtonCount).text('(' + count + ')');
            var compareBtn = this.element.find('.' + this.options.compareButton);
            if (count > this.options.moreThan) {
                compareBtn.removeClass(this.options.disabledClass).prop('tabIndex', 0);
            } else {
                compareBtn.addClass(this.options.disabledClass).prop('tabIndex', -1);
            }
        },

        _updateCount: function (element, count) {
            element.text(count);
        },

        /**
         * if count is 0 then remove the 'is-active' class - otherwise add it
         * add / remove an html class to give the body some bottom padding to accommodate the bar
         */
        _revealCompareBar: function (count) {
            var isVisible = count !== 0;

            this._compareBar.toggleClass(this.options.hiddenClass, !isVisible);
            this._compareBar.toggleClass(this.options.compareActiveClass, isVisible);
            this._html.toggleClass(this.options.hasCompareBarClass, isVisible);
            this._setToggleButtonText();
        },

        _collapse: function (event) {
            var isCollapsed = this._compareBar.hasClass(this.options.isCollapsed);

            event.preventDefault();

            this._compareBar.toggleClass(this.options.isCollapsed, !isCollapsed);
            this._setToggleButtonText();
        },

        _setToggleButtonText: function () {
            var isCollapsed = this._compareBar.hasClass(this.options.isCollapsed),
                dataTxt = isCollapsed ? 'view-more-text' : 'view-less-text';

            this._collapseButton.toggleClass(this.options.isOpen, !isCollapsed).text(this._compareBar.data(dataTxt));
        },

        // remove compare bar item - calls a grep function to remove the specific item from the bar
        // and if in the page, uncheck the corresponding checkbox
        _removeItem: function (event) {
            event.preventDefault();
            var target = $(event.currentTarget).closest('.' + this.options.compareBarItem);
            var data = {
                'id': target.data('compare-id'),
                'img': target.data('compare-img'),
                'title': target.data('compare-title'),
                'href': target.data('compare-href'),
            };
            //  horrible hack to allow ie8 to pass an event reference
            //  kicks out a No member present error otherwise
            var eventCopy = {
                'type': event.type
            };

            this._removeEvent(eventCopy, target, data);

            this._compareList.find('.' + this.options.errorClass).remove();

        },

        // remove all the items - submit the data to the server and on response delete everything and close up the compare bar
        _removeAllItem: function (e) {
            e.preventDefault();

            var self = this;
            var target = $(e.currentTarget).closest('.' + self.options.compareBarItem);
            var items = self._returnRemoveable().filter('.has-image');

            // add waiting/removal classes
            self._findCheckboxes().add(items).addClass(self.options.waitingClass);
            items.addClass(self.options.removingCompareItem);

            // remove error messages
            self._removeErrorMessage(target);

            // raise a remove event to the server
            self._trigger('remove_all', null, {
                'complete': function (ui) {
                    if (ui.status === 'failure') {

                        self._findCheckboxes().removeClass(self.options.waitingClass);

                        // add error message and reinstate deselected check class, remove waiting class
                        self._compareBarContent.append(Mustache.render(self.options.templates.error, { 'fail_message': ui.fail_message, 'error_class': self.options.errorClass }));

                        items.removeClass(self.options.waitingClass).removeClass(self.options.removingCompareItem);

                        return;
                    }

                    self._findCheckboxes().removeClass(self.options.waitingClass).removeClass(self.options.checkedClass);
                    items.removeClass(self.options.waitingClass).removeClass(self.options.removingCompareItem);

                    // empty it out! Clean it up!
                    self._checkList = [];

                    // return bar to original position
                    self._animateCompareBarHorizontal(0);

                    self._renderItems(self._checkList);
                    self.countCheckButtons();
                    self._removeErrorMessage(self.element.find('.' + self.options.compareItem));
                }
            });
        },

        /**
         * disable / enable checkboxes depending on count
         */
        _disableCheckboxes: function (count) {
            if (count >= this.options.max) {
                this._findCheckboxes().not('.' + this.options.checkedClass).addClass(this.options.disabledCheckClass).prop('tabIndex', -1);
            } else {
                this._findCheckboxes().removeClass(this.options.disabledCheckClass).prop('tabIndex', 0);
            }
        },

        // disable / enable buttons depending on count
        _disableCompareButtons: function (count) {
            var isDisabled = count <= this.options.moreThan,
                tabIndex = isDisabled ? -1 : 0;

            this._compareButton.prop({ disabled: isDisabled, tabIndex: tabIndex }).toggleClass(this.options.disabledClass, isDisabled);
        },

        /**
         * Break it down again.
         */
        _destroy: function () {
            var self = this;
            //  window resize handler binding removal
            self._off(self.window, 'resize orientationchange');
            self._html.removeClass(self.options.hasCompareBarClass);
            self._findCheckboxes().next('.' + this.options.errorClass).remove();
        },

        /**
         *  Find all checkboxes on the page. This method is used 
         *  because checkboxes are dynamically placed on page (by Mustache)
         */
        _findCheckboxes: function () {
            return this.element.find('.' + this.options.compareItem);
        }

    });

})(jQuery);;
/**
 * @fileOverview A jQuery UI Widget to duplicate areas of forms and assign a
 * @author Alex Jegtnes
 * @name $.cx.duplicateFormSection
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function($) {
    'use strict';

    $.widget('cx.duplicateFormSection', {

        options: {
          'cloneActionSelector': '.js-cx-duplicate-trigger',
          'cloneElementDataAttribute': 'cx-duplicate-name',
          'cloneElementSelector': '[data-cx-duplicate-name]'
        },

        /**
         * Constructor
         */
        _create: function() {
          var self = this;
          self._duplicateCount = 0; // keep track of how many duplicated elems exist

          self._template = $(self.element[0]).clone(true).off(); // stores duplicate for reuse

          $(self.options.cloneActionSelector).on('click', function(e) {
            e.preventDefault();
            self._cloneElement();
          });
        },

        _cloneElement: function() {
          var self = this;
          self._duplicateCount++;

          // Before modifying the DOM, remove uniform as it's a DOM rabblerouser
          $.uniform.restore('select');

          var newSubForm = self._template.insertBefore(self.options.cloneActionSelector);

          var inputs = newSubForm.find(
            'input' + self.options.cloneElementSelector +
            ',select' + self.options.cloneElementSelector +
            ',textarea' + self.options.cloneElementSelector
          );

          var labels = newSubForm.find('label' + self.options.cloneElementSelector);

          labels.each(function() {
            self._updateClonedLabels(this);
          });

          inputs.each(function() {
            self._updateClonedInputs(this);
          });

          newSubForm.addClass('js-duplicated-form-element');

          // Reinstate uniform styling after everything else is said and done
          $('select').uniform();

          // When cloning this through tabbing, you lose your focus order
          // and end up past the cloned element. Restore:
          $(inputs).first().focus();
        },

        /**
        * Updates a label's appropriate attribute. Use on a cloned element
        */
        _updateClonedLabels: function(element) {
          var self = this;
          var elemName = $(element).data(self.options.cloneElementDataAttribute);
          self._setIncrementalAttr($(element), 'for', self._duplicateCount, elemName);
        },

        /**
        * Updates an input field's appropriate attributes. Use on cloned elements
        */
        _updateClonedInputs: function(element) {
          var self = this;
          var elemName = $(element).data(self.options.cloneElementDataAttribute);
          self._setIncrementalAttr($(element), 'name', self._duplicateCount, elemName);
          self._setIncrementalAttr($(element), 'id', self._duplicateCount, elemName);
        },

        /**
        * Create a composite attribute name and apply it to an element.
        * This function helps creating unique IDs, names, and for-attributes,
        * assisting in sumbitting and retrieving data from forms with a
        * potentially unlimited number of "rows".
        */
        _setIncrementalAttr: function(element, attribute, number, elemName) {
          $(element).attr(attribute, '[' + number + '].' + elemName);
        },

        /**
         * Destroy
         */
        _destroy: function() {
            var self = this;
            $('.js-duplicated-form-element').remove();
        }
    });
})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to trigger hover on submenu focus and add a delay for hovering
 * @author Joel Mitchell
 * @name $.cx.menu_hover
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function($) {
  'use strict';

  $.widget('cx.menu_hover', {

    options: {
      'subnav': 'primary-nav__item--has-subnav',
      'primaryNavItem': '.primary-nav__item',
      'primaryNavLink': '.primary-nav__link',
      'focusClass': 'has-focus',
      'delay': 300
    },

    // make event names easier to read
    widgetEventPrefix: 'menu_hover_',

    /**
     * Constructor
     */
    _create: function() {
      var self = this;
      var $body = $('body');
      var $currentTarget = null;
      self._timeoutId = null;

      self._navItems = self.element.children("." + self.options.subnav);
      self._navLinks = self._navItems.children(self.options.primaryNavLink);

      self._dropdowns = {};

      self._navItems.each(function(index, item) {
        var $item = $(item);
        if ($item.hasClass(self.options.subnav)) {
          self._dropdowns[index] = $item;
          $item.addClass('has-dropdown');
        }
      });


      // Bind events
      self._on(self._navItems, {
        'click': self._menuLinkClick,
        'focus a': self._menuOn,
        'blur a': self._menuOff,
        'mouseover': self._menuOn,
        'mouseout': self._menuOff
      });


      self._on($body, {
        'touchend': function(e) {
          // slightly brutal way of adding a close to the body
          if (!$(e.target).closest('.' + self.options.subnav).hasClass(self.options.focusClass)) {
            self.closeAll();
          }
        }
      });

      self._on({
        'menu_hover_current': function(e, data) {
          $currentTarget = data.target;
        }
      });

    },

    /**
     * lets first menu item click act as a hover -
     */
    _menuLinkClick: function(e) {
      var self = this;
      var $target = $(e.currentTarget);
      var index = self._navItems.index($target);
      var $menuItem = self._navItems.eq(index);

      // If the menu item isn't open prevent the click and open the menu
      if (!($menuItem.is('.' + self.options.focusClass))) {
        e.preventDefault();
        self.open(index, e);
      }
    },


    /**
     * on
     */
    _menuOn: function(e) {

      var self = this;
      var $target = $(e.currentTarget).closest('.' + self.options.subnav);
      var index = self._navItems.index($target);

      if (typeof e !== 'undefined' && e.type === 'mouseover') {
        clearTimeout(self._timeoutId);
        self._timeoutId = setTimeout(function() {
            self.open(index, e);
          },
          self.options.delay);
      } else {
        self.open(index, e);
      }

    },

    /**
     * off
     */
    _menuOff: function(e) {

      var self = this;
      var $target = $(e.currentTarget).closest('.' + self.options.subnav);
      var index = self._navItems.index($target);

      self.close(index, e);

    },

    /**
     * open it
     */
    open: function(index, e) {
      var self = this;
      var $el = self._navItems.eq(index);

      if (index === -1) {
        self.closeAll(index);
        return;
      } else {
        self.closeAll(index);
      }

      $el.addClass(self.options.focusClass);

    },

    /**
     * close it
     */
    close: function(index, e) {
      var self = this;
      var $el = self._navItems.eq(index);

      // Grab the associated menu item and toggle the open/closed classes
      if (typeof e !== 'undefined' && e.type === 'mouseout') {
        clearTimeout(self._timeoutId);
        self._timeoutId = setTimeout(function() {
            $el.removeClass(self.options.focusClass);
          },
          self.options.delay);
      } else {
        $el.removeClass(self.options.focusClass);
      }

    },

    closeAll: function(exclude) {
      var self = this;

      // alert(exclude, event);
      $.each(self._navItems, function(index) {
        if (parseInt(exclude, 10) !== parseInt(index, 10)) {
          self.close(index);
        }
      });
    },


    /**
     * Destroy
     */
    _destroy: function() {
      var self = this;
    }
  });

})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to handle highlighting results in a table
 * @author Alex Jegtnes
 * @name $.cx.highlight-results
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function($) {
    "use strict";

    $.widget("cx.highlightresults", {

        options: {
          "highlightClass": "is-highlighted",
          "checkClass": "js-highlight-results"
        },

        // Add an underscore to the event prefix to make event names easier to read
        widgetEventPrefix: "highlightresults_",

        /**
         * Constructor
         */
        _create: function() {
          var self = this;
          self._checkboxes = self.element.find("." + self.options.checkClass);
          self._isToggled = self._checkboxes.first().prop("checked");

          // Syncronising the checked state on create basically means that all
          // the checkboxes will inherit the checked value of the first checkbox
          // on the page. This shouldn't be necessary but hey, just in case
          self._checkAll(self._isToggled);

          self._on(self._checkboxes, {
            "change": function(event) {
              self._isToggled = $(event.target).prop("checked");
              self._checkAll(self._isToggled);
            },

            // if enter, check box
            "keydown": function(event) {
              if (event.keyCode === 13) {
                // reverse the current value with ! —necessary as change event
                //  returns the value after change, where this doesn't
                self._isToggled = !$(event.target).prop("checked");
                this._checkAll(self._isToggled);
              }
            }
          });
        },

        // Set the check state of all the checkboxes targeted by plugin
        _checkAll: function(checked) {
          var self = this;
          self._checkboxes.prop("checked", checked);
          if (checked) {
            self.element.addClass(self.options.highlightClass);
          } else {
            self.element.removeClass(self.options.highlightClass);
          }
        },

        _destroy: function() {
          var self = this;
        }
    });
})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to handle responsive images
 * @author Andy Mantell
 * @name $.cx.responsive_image
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function ($) {

    'use strict';

    $.widget('cx.responsive_image', {

        options: {
            callback: function () { }
        },

        // Add an underscore to the event prefix to make event names easier to read
        widgetEventPrefix: 'responsive_image_',

        /**
         * Constructor
         */
        _create: function () {

            var self = this,
                el = self.element,
                cb = self.options.callback;

            // construct the image tag
            var img = self._img = $('<img />', {
                alt: el.data('alt') || '',
                title: el.data('title'),
                'class': el.attr('class') // Inherit all classes from the span
            });

            // append the image

            el.before(img);

            // bind a load event on the image

            self._on(img, {
                'load': function () {
                    img.css('opacity', 1).show();
                    if (typeof (cb) === 'function') {
                        cb();
                    }
                }
            });

            // change image source based on media query matching

            this._onMatchingTabletMinHandler = function (matches) {

                var src,
                    mode = matches ? 'desktop' : 'mobile',
                    dataSrc = el.data('src'),
                    dataModeSrc = el.data(mode + '-src');

                // determine the image source
                if (typeof (dataSrc) !== 'undefined') {

                    // if 'data-src', always use this wether this is mobile or desktop
                    src = dataSrc;

                } else if (typeof (dataModeSrc) !== 'undefined' && dataModeSrc.length > 0) {

                    // if 'data-<mode>-src', always it
                    src = dataModeSrc;

                } else {

                    // no suitable src, hide the image
                    img.hide();

                }

                // if the image source has changed
                if (src !== img.attr('src')) {
                    img.css('opacity', 0).attr('src', '');
                    img.attr('src', src);
                }

            };

            EC.Mq.on(EC.Mq.tabletMin(), this._onMatchingTabletMinHandler, true);
        },


        /**
         * Destroy
         */
        _destroy: function () {
            var self = this;
            self._img.remove();
        }
    });

})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to provide modal window behaviour and extend it to
 * @author Joel Mitchell
 * @name $.cx.modal
 * @dependencies: jQuery, jQuery UI widget factory, magnifique popup
 */

(function ($) {
    'use strict';

    $.widget('cx.modal', {

        options: {
            'type': 'inline',
            'noscroll': 'collapsible--noscroll',
            'popupClass': 'popup--small',
            'closeClass': 'popup-close',
            'checkList': '.js-collapsible, .js-collapsible-mobile, .js-collapsible-desktop',
            'button': '<button class="mfp-close icon-x"><span class="visuallyhidden">Close overlay</span></button>',
            'secondaryModalContainer': '<div class="mfp-wrap mfp-auto-cursor popup--super"><div class="popup mfp-container popup--super__content"><div class="mfp-content"></div></div></div>',
            'secondaryUnderlay': '<div class="mfp-bg popup--super__underlay"></div>',
            'containerClass': 'popup--super__overlay',
            'init_form': function (e, ui) {
                // raise this event in case
                $.publish('domupdated.popup-form', ui.content);
            }
        },

        // make event names easier to read
        widgetEventPrefix: 'modal_',

        /**
		 * Constructor
		 */
        _create: function () {

            var self = this;
            var _type = self.element.data('popup-type') || self.options.type;
            var _modal = self.element.data('popup-modal') || false;
            var _mainClass = _modal === true ? 'popup popup--modal' : 'popup popup--removeable';
            var _focus = self.element.data('popup-focus-field') || '';
            self._initialised = false;

            self._on({
                'modal_init_super_popup': function (e, ui) {
                    self._initialiseSuperPopup(e, ui);
                }
            });

            var popFinder = function (magnific, obj) {
                if ($(magnific.content).hasClass(self.options.popupClass)) {
                    $(magnific.contentContainer).addClass(self.options.popupClass);
                }

                obj.find('.js-popup')
					.unbind()
					.on('click', function (event) {
					    event.preventDefault();
					    self._trigger('init_super_popup', 'click', {
					        'magnific': magnific,
					        'trigger': event.currentTarget
					    });
					})
					.end()
					.find(self.options.checkList)
					.addClass(self.options.noscroll);
            };

            // pass options and launch Magnific popup
            self.element.magnificPopup({
                mainClass: _mainClass,
                // focus: _focus,
                type: _type,
                showCloseBtn: true,
                closeBtnInside: true,
                modal: _modal,
                closeMarkup: self.options.button,
                callbacks: {
                    open: function () {
                        var magnific = $.magnificPopup.instance;
                        magnific.container.on('click', '.popup-close', function (e) {
                            e.preventDefault();
                            magnific.close();
                        });

                        magnific.container.on('click', '#modal-confirmation-close', function (evt) {
                            $('.mfp-container').removeClass('dirty');
                            $.magnificPopup.proto.close.call(this);
                        });

                        magnific.container.on('click', '#modal-confirmation-cancel', function (evt) {
                            $('#modal-confirmation').hide();
                        });

                        magnific.close = function () {
                            if ($('.mfp-container.dirty').length) {
                                $('#modal-confirmation').show();
                                $('.popup').scrollTop(0);
                                return;
                            }
                            $.magnificPopup.proto.close.call(this);
                        };

                        self._trigger('init_form', 'update', {
                            'content': this.content
                        });
                        // search content for popup links in case of a secondary popup link
                        // need to do a bit of cheekiness to unbind and rebind the event to prevent weirdness
                        popFinder(magnific, magnific.container);

                        //	remove classes - no horizontal forms in the modal
                        self._stackFormLayout(magnific.container);

                    },
                    beforeClose: function () {
                        var magnific = $.magnificPopup.instance;
                        magnific.container
							.off('click')
							.find(self.options.checkList)
							.removeClass(self.options.noscroll);

                        $.publish('popup_close');
                    },
                    change: function () {

                    },
                    // grab content from remote page - search page for an id fragment if available
                    parseAjax: function (mfpResponse) {
                        var magnific = $.magnificPopup.instance;
                        var $el = magnific.ev.prop('href');
                        var fragment = $el.split('#')[1];

                        if (fragment !== '') {
                            mfpResponse.data = $(mfpResponse.data).find('#' + fragment);
							if (mfpResponse.data.hasClass('mfp-hide'))
								mfpResponse.data.removeClass('mfp-hide');
                        }

                        popFinder(magnific, mfpResponse.data);

                    },
                    // if this is a form or contains a form trigger an init_form event to bind the form javascripts to it.
                    ajaxContentAdded: function () {
                        self._trigger('init_form', 'update', {
                            'content': this.content
                        });
                    }
                }
            });

        },

        /*
		 *   force a stacked layout by stripping the form--horizontal class
		 */
        _stackFormLayout: function (element) {
            element
				.find('.form')
				.removeClass('form--horizontal');
        },

        /*
		 *   launch a mocal on top of the existing modal - reproducing functionality form magnific popup
		 */
        _initialiseSuperPopup: function (e, ui) {
            var self = this;
            var target = $(ui.trigger).prop('href');
            var href = target.split('#')[1];
            var body = $('body');
            var content = $('#' + href);
            var html = content.html();
            var modalNamespace = '.mfp';

            // possibly not needed but didnt want to risk duplication of ids pulled in from an inline form - replaced on closing
            content.empty();

            //	if not initialising, do so
            if (self._initialised === false) {
                self._initialised = true;
                self._container = $(self.options.secondaryModalContainer);
                self._container
					.attr('tabindex', -1)
					.css({
					    'overflowY': 'auto',
					    'overflowX': 'hidden'
					})
					.prependTo(body)
					.hide();

                self._underlay = $(self.options.secondaryUnderlay);

                // temporarily remove the namespaced functions from magnific that have been fighting me
                self.document.off('focusin' + modalNamespace, ui.magnific._onFocusIn);
                self.document.off('keyup' + modalNamespace);

                //  add underlay and make it appear
                self._underlay
					.hide()
					.prependTo(body)
					.fadeIn(0);

                // reinstate the mfp functions and close the super popup
                self._on(self._container, {
                    'click .mfp-close': function () {
                        self._removeSuperPopup(ui, content, html);
                    }
                });

                // remove uppermost popup
                self._on(self._underlay, {
                    'click': function () {
                        self._removeSuperPopup(ui, content, html);
                    }
                });

                /*
				 *	fade in popup body, find content pane and apply methods to it
				 */
                self._container.fadeIn('fast', function () {
                    var contenter = self._container.find('.mfp-content');

                    contenter
						.attr('tabindex', -1)
						.html(html)
						.prepend(self.options.button)
						.focus()
						.removeAttr('tabindex');

                    self._stackFormLayout(contenter);

                    //	add namespaced keyup to remove overlay or test whether focus has escaped the window
                    self.document.on('keyup.secondary', function (e) {
                        if (e.keyCode === 27) {
                            self._removeSuperPopup(ui, content, html);
                        } else if (e.keyCode === 9) {
                            self._onFocusIn(e, contenter);
                        }
                    });

                    //	raise event to run form initialisation scripts - for forms within modals
                    self._trigger('init_form', 'update', {
                        'content': contenter
                    });

                    //	if clicking outside secondary popup, remove it
                    self._on('.popup--super__content', {
                        'click': function (e) {
                            var target = $(e.target);

                            if (target.parents(contenter).size() === 3) {
                                self._removeSuperPopup(ui, content, html);
                            }
                        }
                    });

                });
            }
        },

        /*
		 *	test to see if focus has escaped the popup - if so return it to the container
		 */
        _onFocusIn: function (e, contenter) {
            var t = contenter.find(e.target);
            if (t.length === 0) {
                contenter
					.attr('tabindex', -1)
					.focus()
					.removeAttr('tabindex');
                return false;
            }
        },

        /*
		 *	cleanup operation to remove secondary popup - rebind magnific popup functions
		 */
        _removeSuperPopup: function (ui, content, html) {
            var self = this;

            content.html(html);
            self.document.off('keyup.secondary');
            self._initialised = false;
            self._underlay.remove();
            self._container.remove();
            self.document.on('focusin.mfp', ui.magnific._onFocusIn);

            self.document.on('keyup.mfp', function (e) {
                if (e.keyCode === 27) {
                    ui.magnific.close();
                }
            });

            // risky? ressets popup email in open forms
            $.publish('popup_close');
        },

        /**
		 * Destroy
		 */
        _destroy: function () {
            var self = this;
        }
    });

})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to conditionally add a class/classes to compact form layouts
 * @author Joel Mitchell
 * @name $.cx.compact
 * @dependencies: jQuery, jQuery UI widget factory
 */

/**
 * Tabs
 */
(function($) {
    'use strict';

    $.widget('cx.compact', {
        options: {
            'addClasses': 'is-compact', // @joel space separated list of classes to add here
            'removeClasses': 'form--horizontal', // @joel - add a space separated list of classes to remove here ie 'form--horizontal another-class a-third-class another-class'
            'throttle': 100 // change the update rate
        },

        widgetEventPrefix: 'compact_',

        /**
         * Standard jQuery UI create function
         */
        _create: function() {
            var self = this;
            self._testWidth = self.element.data('width');

            if (typeof self._testWidth === 'undefined' || self._testWidth === '') {
                return;
            }

            // timeout for resize event
            self._timeoutId = null;
            self._wrapperWidth = 0;
            self._isApplied = false;


            self._setUpClasses();

            //  window resize handler binding
            self._on(self.window, {
                'resize': self._resizeHandler,
                'orientationchange': self._resizeHandler
            });

            self._resizeHandler();
        },

        /*
         *   generate an array of classes to remove and remember for re-application
         */
        _setUpClasses: function() {
            var self = this;
            var classList = self.options.removeClasses;

            // check if element has any of the classes to remove: if so, build an array to add and remove
            self._classList = $.map(classList.split(' '), function(className) {
                return self.element.hasClass(className) ? className : null;
            });

        },

        /*
         *   add the new classes, remove the whitelisted ones
         */
        addNewClasses: function() {
            var $this = this.element;
            $this.addClass(this.options.addClasses);
            $.each(this._classList, function(index, value) {
                $this.removeClass(value);
            });
        },

        /*
         *   remove the new classes, restore  the whitelisted ones
         */
        _restoreOldClasses: function() {
            var $this = this.element;
            $this.removeClass(this.options.addClasses);
            $.each(this._classList, function(index, value) {
                $this.addClass(value);
            });

        },

        /*
         *   pretty standard
         */
        _resizeHandler: function() {

            var self = this;


            clearTimeout(self._timeoutId);
            self._timeoutId = setTimeout(function() {
                var width = self.element.width();

                // Joel - modded this to work inside the popup immediately
                // shout if it blows up

                // if (self._wrapperWidth === width) {
                //     return;
                // }

                self._wrapperWidth = width;
                // @Joel - looks long winded but doesnt work with an && chucked in there
                // also prevents a weird choking issue
                if (self._wrapperWidth < self._testWidth) {
                    if (!self._isApplied) {
                        self.addNewClasses();
                    }
                    self._isApplied = true;
                } else {
                    if (self._isApplied) {
                        self._restoreOldClasses();
                    }
                    self._isApplied = false;
                }

            }, self.options.throttle);
        },


        /**
         * Tear everything down again.
         */
        _destroy: function() {
            this._restoreOldClasses();
            this._off(this.window, 'resize orientationchange');

        }

    });

})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to show/hide
 * @author Joel Mitchell
 * @name $.cx.show_more
 * @dependencies: jQuery, jQuery UI widget factory
 */

/**
 * Tabs
 */
(function ($) {
    'use strict';

    $.widget('cx.show_more', {
        options: {
            'show': 3,
            'viewMore': 'Voir plus',
            'viewFewer': 'Voir moins',
            'triggerClass': 'show-more__trigger',
            'offset': 80,
            'template': '<div class="show-more"><a href="#" class="show-more__trigger"></a></div>',
            'liTemplate': '<li class="trigger"></li>',
            // Classes
            'activeClass': 'is-showmore--active',
            'openClass': 'is-showmore--open',
            'closedClass': 'is-showmore--closed',
            'scrollClass': 'is-showmore--scroll', // Should the collapsible scroll when animating?
            // Misc options
            'duration': 300 // Duration of slide animations
        },

        widgetEventPrefix: 'show_more_',

        /**
         * Standard jQuery UI create function
         */
        _create: function () {

            var self = this;

            // array of collapsed elements
            var _collapsedItems = [];

            // Use data labels if provided
            var label = this.element.data('show-more-label');
            if (label) {
                label = label.split(',');
                this.options.viewMore = label[0];
                this.options.viewFewer = label[1];
            }

            // Wrap text nodes in a paragraph
            // Due to SiteCore RTE fields: text is not necessarily in a child node => self.element.children() will fail to pick up those nodes
            self._wrapTextNodes();

            // Grab elements
            self._items = self.element.children();
            self._show = self.element.data('show-items') === 0 ? 0 : self.element.data('show-items') || self.options.show;

            if (self._items.length <= self._show) {
                return;
            }

            self._open = false;
            self._triggerLink = $(self.options.template);

            /*
             *  is trigger link before or after the collapsed block?
             */
            if (self.element.data('show-position') === 'before') {
                self.element.prepend(self._triggerLink);
            } else {
                self.element.append(self._triggerLink);
            }

            // scroll closed?
            if (self.element.data('show-scroll')) {
                self.element.addClass(self.options.scrollClass);
            }

            self._return = self.element.data('show-return') === false ? false : true;

            if (self._items.eq(0).is('li')) {
                self._triggerLink = self._triggerLink.wrap(self.options.liTemplate);
            }

            //  add text in there - not hard coded
            self._triggerText = self._triggerLink.find('.' + self.options.triggerClass);
            self._triggerText.text(self.options.viewMore);


            // loop over collection but offset loop from items current index
            self._items
                .slice(self._show)
                .each(function (index, item) {
                    var $item = $(item);
                    _collapsedItems.push($item);
                    $item.hide();
                });

            /*
             *  bind trigger link click
             */
            self._on((function () {
                var events = {};
                events['click .' + self.options.triggerClass] = function (e) {
                    e.preventDefault();
                    self._togglePanel(e, _collapsedItems);
                };
                return events;
            })());

            /*
             *  And keyboard handler
             */
            self._on(self._triggerLink, {
                'keydown': function (e) {
                    self._keyboardHandler(e, _collapsedItems);
                }
            });

            // odd method of removing the trigger link
            self._on({
                'show_more_open_complete': function (e, ui) {
                    if (!ui.self._return) {
                        ui.self._removeTrigger();
                    }
                },
                'show_more_close_complete': function () {
                    self._scrollTo(self.element);
                }
            });

            self.element.addClass(self.options.activeClass);

        },

        _removeTrigger: function () {
            this._off(this._triggerLink, 'click');
            this._triggerLink
                .fadeOut()
                .remove();

        },

        _keyboardHandler: function (e, _collapsedItems) {
            var self = this;
            var keyCode = e.keyCode;

            if (keyCode === 32 || keyCode === 13) {
                e.preventDefault();
                self._togglePanel(e, _collapsedItems);
            }

        },

        /**
         * Toggle a panel
         */
        _togglePanel: function (e, items) {

            var self = this;
            e.preventDefault();

            if (self._open) {
                self.close(items);
            } else {
                self.open(items);
            }

            self._open = !self._open;

        },

        open: function (items) {
            var self = this;
            var itemLength = items.length;

            $.each(items, function (index, item) {
                item
                    .stop(true, true)
                    .slideDown({
                        'duration': self.options.duration,
                        'complete': function () {
                            if (index === itemLength - 1) {
                                self._trigger('open_complete', null, {
                                    'self': self
                                });
                            }
                        }
                    });
            });

            self._viewFewer();
        },

        close: function (items) {
            var self = this;
            var itemLength = items.length;

            $.each(items, function (index, item) {
                item
                    .stop(true, true)
                    .slideUp({
                        'duration': self.options.duration,
                        'complete': function () {
                            if (index === itemLength - 1) {
                                self._trigger('close_complete', null, {
                                    'self': self
                                });
                            }
                        }
                    });
            });

            self._viewMore();
        },

        _viewMore: function () {
            var self = this;
            self.element
                .removeClass(self.options.openClass);

            self._triggerText
                .text(self.options.viewMore)
                .removeClass(self.options.openClass);
        },

        _viewFewer: function () {
            var self = this;
            self.element
                .addClass(self.options.openClass);

            self._triggerText.text(self.options.viewFewer)
                .addClass(self.options.openClass);
        },

        /**
         * Returns True if "mobile-max" media query matches.
         * @returns {Boolean}
         */
        _isMobile: function() {
            return EC.Mq.matches(EC.Mq.mobileMax());
        },


        /**
         * Helper function to scroll to a dom target.
         * This function attempts to scroll the window to a moving target.
         */
        _scrollTo: function ($target) {
            var self = this;

            if (self.element.hasClass(self.options.noScrollClass) || self._isMobile() === false) {
                return;
            }

            if ($target.length > 0) {
                // Work out where we started scrolling from
                var scrollStart = $(self.window).scrollTop();
                var $window = $(self.window);

                // Animate a dummy object so that we can make use of jQuerys animation progress function to scroll the window
                $({
                    dummy: 0
                })
                    .animate({
                        dummy: 1
                    }, {
                        easing: 'linear',
                        duration: self.options.duration,
                        progress: function (animation, progress) {

                            // Where's the top of our target at this point?
                            var targetTop = $target.offset().top - self.options.offset;

                            // Work out how far away from the target scroll position we are
                            var scrollDelta = targetTop - scrollStart;

                            // Move the window to this scroll position in sync with the slide animation's progress
                            $window.scrollTop(scrollStart + (scrollDelta * progress));
                        }
                    });

            }
        },

        /**
         * Helper function to check nodeTypes and the length
         * Wraps NON-EMPTY (using $.trim()) textNodes in a paragraph
         */
        _wrapTextNodes: function () {
            var self = this;

            self.element.contents().filter(function () {
                return this.nodeType === 3 && $.trim(this.nodeValue).length;
            }).wrap('<p />');
        },

        _setOption: function (key, value) {
            var self = this;
            self._super(key, value);

        },

        /**
         * Break it down again.
         */
        _destroy: function () {
            var self = this;
            self._removeTrigger();
            self._items.show();
            self.element
                .removeClass(self.options.scrollClass)
                .removeClass(self.options.activeClass)
                .removeClass(self.options.openClass)
                .removeClass(self.options.closedClass);
        }

    });

})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to provide very simple UI blocking / ajax loading spinner
 * @author Joel Mitchell
 * @name $.cx.ui_block
 * @dependencies: jQuery, jQuery UI widget factory, Mustache, pub/sub
 */

(function($, Mustache) {
	'use strict';

	$.widget('cx.ui_block', {
		options: {
			'mode': 'desktop',
			'domObject': 'body',
			'activeClass': 'uiblock--has-spinner',
			'staticClass': 'uiblock--is-static',
			'containerClass': 'uiblock',
			'innerClass': 'uiblock__inner',
			'iconClass': 'uiblock__inner--waiting',
			'blockingClass': 'uiblock--muted',
			'blockingMessage': 'Loading things please wait....',
			'underlayTemplate': '<div class="{{block}}"><div class="{{inner}}"><span class="{{icon}}"></span><span class="message">{{message}}</span></div></div>',
			'fadeDuration': 200
		},

		widgetEventPrefix: 'uiblock_',
		/**
		 * Constructor
		 */
		_create: function() {

			this._open = false;

			// underlay template
			this._underlay = $(Mustache.render(this.options.underlayTemplate, {
				'block': this.options.containerClass,
				'inner': this.options.innerClass,
				'icon': this.options.iconClass,
				'message': this.options.blockingMessage
			}));

		},

		//  added as an init as we want the first run to create it then subsequent firing
		//  to reuse the scaffold
		_init: function() {

			//  return if already open
			if (this._open) {
				return;
			}

			// if desktop open it on the specified DOM object
			// if mobile then do a full page block
			if (this.options.mode === 'desktop') {
				this._element = this.element;
			} else {
				this._element = $(this.options.domObject);
			}

			// add active class and append blocking layer
			this._element
				.addClass(this.options.activeClass)
				.append(this._underlay);

			this.openBlocker();

		},

		/*
		 *	open the blocker - public function
		 */
		openBlocker: function() {

			// hook these
			var dims = {
				'winHeight': this.window.height(),
				'height': this._element.height()
			};

			this._open = true;

			// weird browser thing that stops a transition class being added unless a rapid timeout is applied
			setTimeout($.proxy(function() {
				this._element.find('.' + this.options.containerClass).addClass(this.options.blockingClass);
			}, this), 0);


			// check if the element is taller than the window - if so place the icon and message more carefully
			if (dims.height > dims.winHeight) {
				this._underlay.addClass(this.options.staticClass);
				this._positionIcon(dims);
			}

			// trigger an event to return
			// if this is coming from an ajax event this auto closes
			// @todo: remove this - the close should be called via another mechanism
			var self = this;
			this._trigger(
				'open', null, {
					'complete': function(ui) {
						if (ui.status === 'success') {
							self.closeBlocker();
						}
					}
				});

		},

		_positionIcon: function(dims) {
			// var scrollTop =  this._element.scrollTop();
			// this._underlay.find('.'+this.options.innerClass).css({
			// 	'top': (dims.winHeight/2)+ scrollTop
			// });

		},

		/*
		 *	close the blocker - public function
		 */
		closeBlocker: function() {

			//  clean things up
			this._element.find('.' + this.options.containerClass).removeClass(this.options.blockingClass);

			// again remove things after a timeout
			setTimeout($.proxy(function() {
					this._underlay
						.removeClass(this.options.staticClass)
						.remove();
					this._element.removeClass(this.options.activeClass);
					this._open = false;
				}, this),
				this.options.fadeDuration);

		},


		/**
		 * Destroy function - clean up
		 */
		_destroy: function() {
			this._element.find('.' + this.options.containerClass).removeClass(this.options.blockingClass);
			this._underlay.remove();
			this._element.removeClass(this.options.activeClass);
			this._open = false;
		}
	});

})(jQuery, Mustache);
;
/**
 * @fileOverview A jQuery UI Widget to reveal a panel
 * @author Joel Mitchell
 * @name $.cx.reveal_panel
 * @dependencies: jQuery, jQuery UI widget factory, Mustache (slight dependency on Modernizr for feature detection)
 */

(function($, Mustache) {
	'use strict';

	$.widget('cx.reveal_panel', {
		options: {
			'activeClass': 'is-reveal--active',
			'openClass': 'is-reveal--open',
			'jsHideClass': 'js-hide', // standard class to hide an item when js is present
			'closedClass': 'is-reveal--closed',
			'isMobile': false,
			'labels': 'show more, show less',
			'linkTemplate': '<a href="{{item}}" class="{{js}} {{extra_classes}}">{{label}}</a>',
			'duration': 250
		},

		widgetEventPrefix: 'reveal_item_',
		/**
		 * Constructor
		 */
		_create: function() {
			var item = this.element.data('reveal-item');
			this.item_alt = this.element.data('reveal-item-secondary');

			// if no item  abort
			if (item === '' || item === undefined) {
				return;
			}

			this._target = $(item);

			// if no target found to open, abort
			// @optimize - does this need to be a two stage test?
			if (this._target.length === 0) {
				return;
			}

			this._open = false;

			// hide the target and remove the js hide class - do in this order to prevent a flash of content

			this._target
				.hide()
				.removeClass(this.options.jsHideClass);

			this._return = this.element.data('reveal-open') || false;
			var label = this.element.data('reveal-labels').split(',') || this.options.labels.split(',');


			// link template - stored for reuse
			var self = this; // closure
			this._link = $(Mustache.render(this.options.linkTemplate, {
				'item': item,
				'extra_classes': this.element.data('reveal-classes') || null,
				'label': label[0]
			}));

			this.element.hide().after(this._link);

			/*
			 *  bind trigger link click
			 */
			this._on(this._link, {
				'click': function(e) {
					this._togglePanel(e, this._target, label);
				},
				'keydown': function(e) {
					this._keyboardHandler(e, this._target, label);
				}
			});

			// odd method of removing the trigger link
			this._on({
				'reveal_item_open_complete': function(e, ui) {
					if (ui.self._return) {
						ui.self._removeTrigger();

						// removes secondary item if no return link and secondary item specified. bit smelly
						if (ui.self.item_alt !== '' && ui.self.item_alt !== undefined) {
							$(ui.self.item_alt).hide();
						}
					}
				}
			});

			this._link.addClass(this.options.activeClass);

		},

		/**
		 *	if the data-reveal-return attribute is set to false - then remove the link and it's click event
		 */
		_removeTrigger: function() {

			this._off(this._link, 'click');
			this._link.hide(this.options.duration);

		},

		/**
		 *	bind keyboard handling
		 */
		_keyboardHandler: function(e, _target, label) {
			var keyCode = e.keyCode;

			if (keyCode === 32 || keyCode === 13) {
				e.preventDefault();
				this._togglePanel(e, _target, label);
			}

		},

		/**
		 * Toggle a panel
		 */
		_togglePanel: function(e, _target, label) {
			var self = this;
			e.preventDefault();

			if (self._open) {
				self.close(_target, label);
			} else {
				self.open(_target, label);
			}
			self._open = !self._open;
		},

		open: function(_target, label) {
			var self = this;

			_target
				.stop(true, true)
				.slideDown({
					'duration': self.options.duration,
					'complete': function() {
						self._trigger('open_complete', null, {
							'self': self
						});
					}
				});

			self._viewFewer(label);
		},

		close: function(_target, label) {
			var self = this;
			_target
				.stop(true, true)
				.slideUp({
					'duration': self.options.duration,
					'complete': function() {
						self._trigger('close_complete', null, {
							'self': self
						});
					}
				});

			self._viewMore(label);
		},

		_viewMore: function(label) {
			this._target.removeClass(this.options.openClass);
			this._link
				.text(label[0])
				.removeClass(this.options.openClass);
		},

		_viewFewer: function(label) {
			this._target.addClass(this.options.openClass);
			this._link
				.text(label[1])
				.addClass(this.options.openClass);
		},

		/**
		 * Destroy
		 */
		_destroy: function() {
			this._link.remove();
			this.element.show();
			this._target
				.addClass(this.options.jsHideClass)
				.show();
		}
	});

})(jQuery, Mustache);
;
/**
 * @fileOverview A jQuery UI Widget to reveal an input field
 * @author Joel Mitchell
 * @name $.cx.edit_field
 * @dependencies: jQuery, jQuery UI widget factory Mustache
 */

(function($, Mustache) {
    'use strict';

    $.widget('cx.edit_field', {
        options: {
            'activeClass': 'is-editable--active',
            'openClass': 'is-editable',
            'saveMessage': 'updating address',
            'saveLink': '<button class="btn btn--secondary action-link btn--small" >{{label}}</button>',
            'labels': 'Edit,cancel'
        },

        widgetEventPrefix: 'edit_field_',
        /**
         * Constructor
         */
        _create: function() {
            var item = this.element.data('edit-item');

            // if no item  abort
            if (item === '' || item === undefined) {
                return;
            }

            this._target = $(item);

            // if no target found to open, abort
            // @optimize - does this need to be a two stage test?
            if (this._target.length === 0) {
                return;
            }

            this._open = false;
            this._text = this._target.text();

            var label = this.element.data('edit-labels').split(',') || this.options.labels.split(',');
            this._saveLabel = this.element.data('save-label') || 'save';
            /*
             *  bind trigger link click
             */
            this._on({
                'click': function(e) {
                    this._editField(e, this._target, label);
                },
                'keydown': function(e) {
                    this._keyboardHandler(e, this._target, label);
                }
            });

            this._target.addClass(this.options.activeClass);

        },


        /**
         *  bind keyboard handling
         */
        _keyboardHandler: function(e, _target, label) {
            var keyCode = e.keyCode;

            if (keyCode === 32 || keyCode === 13) {
                e.preventDefault();
                this._editField(e, _target, label);
            }

        },

        /**
         * Toggle the field
         */
        _editField: function(e, _target, label) {
            e.preventDefault();

            if (this._open) {
                this.close(_target, label);
                this._cancel(_target, label);
            } else {
                this.open(_target, label);
                this._addSaveButton(_target, label);
            }

        },

        /**
         *  change the field to editable and swap link out
         */
        open: function(_target, label) {
            this._target
                .prop('contenteditable', true)
                .addClass(this.options.openClass)
                .focus();

            this._select(this._target[0]);

            this.element
                .addClass(this.options.activeClass)
                .text(label[1]);

            this._open = true;

        },

        /**
         *  change the field back
         */
        close: function(_target, label) {

            this._target
                .prop('contenteditable', false)
                .removeClass(this.options.openClass);

            this.element.text(label[0]);
            this._open = false;

            this._trigger('close', null, {
                'self': this
            });
        },


        /**
         *  change the field back
         */
        _save: function(label) {
            var self = this;

            // saving to edit-fields.js in integration
            this._trigger('save', null, {
                'text': this._target.text(),
                'complete': function(response) {
                    if (response.status === 'success') {
                        self._text = response.text;

                        // update field with server response text
                        self._target.text(self._text);
                        self._removeSaveButton();
                        self.close(this._target, label);
                        self.element.focus();
                    }
                }
            });

        },

        /**
         *  when we open the field and make it editable, add a save button and bind save event to it
         */
        _addSaveButton: function(_target, label) {
            var self = this;
            this._saveLink = $(Mustache.render(this.options.saveLink, {
                'label': this._saveLabel
            }));

            this.element.before(this._saveLink);

            this._on(this._saveLink, {
                'click': function(event) {
                    event.preventDefault();
                    this._save(label);
                }
            });

        },

        /**
         *  remove link and it's event
         */
        _removeSaveButton: function() {
            this._saveLink.remove();
        },


        /**
         *  remove link and it's event
         */
        _cancel: function(_target) {
            _target.text(this._text);
            this._removeSaveButton();
        },

        // function to select a chunk of text in an editable field
        _select: function(element) {
            var doc = document,
                text = element,
                range, selection;
            if (doc.body.createTextRange) {
                range = document.body.createTextRange();
                range.moveToElementText(text);
                range.select();
            } else if (window.getSelection) {
                selection = window.getSelection();
                range = document.createRange();
                range.selectNodeContents(text);
                selection.removeAllRanges();
                selection.addRange(range);
            }
        },

        /**
         * Destroy
         */
        _destroy: function() {
            this._link.remove();
            this.element.show();
            this._target
                .removeClass(this.options.activeClass)
                .addClass(this.options.jsHideClass)
                .show();
        }
    });

})(jQuery, Mustache);
;
/**
 * @fileOverview A jQuery UI Widget to convert an input field from a disabled one into an editable one
 * @author Joel Mitchell
 * @name $.cx.edit_field_extended
 * @dependencies: jQuery, jQuery UI widget factory Mustache
 */

(function ($, Mustache) {
	'use strict';

	$.widget('cx.edit_field_extended', {
		options: {
			'editClass': '.js-editable',
			'triggerClass': 'js-edit-field--trigger',
			'activeClass': 'is-editable--active',
			'openClass': 'is-editable',
			'closedClass': 'is-uneditable',
			'saveSelector': 'data-edit-field-local-save',
			'saveMessage': 'updating address',
			'saveLink': '<button class="btn btn--secondary action-link btn--small">{{label}}</button>',
			'labels': ['Edit', 'cancel'],
			'editLink': '<a href="#" class="js-edit-field--trigger action-link">{{label}}</a>',
			'onlyEdit': 'data-edit-field-only-edit',
			'elementToReplaceWithLink': '.js-edit-field-link'

		},

		widgetEventPrefix: 'edit_field_extended_',
		/**
		 * Constructor
		 */
		_create: function () {
			var self = this;

			this._fields = {};

			$.each(this.element.find(this.options.editClass), function (index, item) {
				var $item = $(item);
				// make items disabled as an enhancement, not as default (for non JS users)
				$item.removeClass(self.options.openClass).addClass(self.options.closedClass).prop('disabled', true);

				self._fields[index] = {
					'item': $item,
					'id': item.id,
					'value': item.value,
					'index': index
				};
			});

			// only add a save / cancel button if there is a local save variable stated - data-edit-field-local-save
			this._localSave = this.element.attr(this.options.saveSelector) !== undefined;

			this._onlyEdit = this.element.attr(this.options.onlyEdit) !== undefined;

			// if no item abort
			if ($.isEmptyObject(this._fields)) {
				return;
			}

			this._open = false;

			this._label = this.element.data('edit-labels') ? this.element.data('edit-labels') : this.options.labels;
			this._saveLabel = this.element.data('save-label') || 'save';

			//  Luke
			if (this.element.is('[data-add-edit]') === true) {
				this.element.find(self.options.elementToReplaceWithLink).after(Mustache.render(self.options.editLink, {
					'label': this._label[0]
				}));
			}

			this._triggerLink = this.element.find('.' + this.options.triggerClass);

			/*
			 *  bind trigger link click
			 */
			this._on((function () {
				var events = {};
				events['click .' + self.options.triggerClass] = function (e) {
					self._editField(e);
				};
				return events;
			}()));

			this._on({
				'keydown': function (e) {
					this._keyboardHandler(e);
				}
			});

			this.element.addClass(this.options.activeClass);

			this._on({'edit_field_extended_toggle_field': function (event, data) {
				var elements = data.classes.split(',');
				for (var i = 0; i < elements.length; i++) {
					$(elements[i]).show();
				}
			}});

		},


		/**
		 *  bind keyboard handling
		 */
		_keyboardHandler: function (e) {
			var keyCode = e.keyCode;

			// if you click the escape key then cancel - need to prevent it from closing the overlay....
			if (keyCode === 27) {
				e.preventDefault();
				this._cancel(e);
			}

			if (keyCode === 13 && !this._onlyEdit) {
				e.preventDefault();
				this._editField(e);
			}

		},

		/**
		 * Toggle the field
		 */
		_editField: function (e, _target) {
			e.preventDefault();

			if (this._open) {
				this.close(_target);
				this._cancel(_target);

			} else {
				this.open(_target);
				// if this supports local saving then add a button
				if (this._localSave) {
					this._addSaveButton(_target);
				}
				if (this._onlyEdit) {
					this._removeEditLink(_target);
				}

				if (this.element.data('elements-to-reveal')) {
					this._trigger('toggle_field', null, {
						'classes': this.element.data('elements-to-reveal')
					});
				}

			}

		},

		/**
		 *  change the field to editable and swap link out
		 */
		open: function () {
			var self = this;

			$.each(this._fields, function (index, value) {
				value.item
					.removeClass(self.options.closedClass)
					.addClass(self.options.openClass)
					.prop('disabled', false);

			});

			// focus on the first field
			this._fields[0].item.focus();

			//select the text in the first field
			this._fields[0].item[0].select();

			// add overall active class to the panel
			this.element.addClass(this.options.activeClass);

			// update the trigger link text
			this._triggerLink.text(this._label[1]);

			this._open = true;

		},

		/**
		 *  change the field back
		 */
		close: function () {
			var self = this;

			// return all fields to their previous non-editable state
			$.each(this._fields, function (index, value) {
				value.item
					.removeClass(self.options.openClass)
					.addClass(self.options.closedClass)
					.prop('disabled', true);

			});

			this.element.removeClass(this.options.activeClass);

			this._triggerLink.text(this._label[0]);
			this._open = false;

			this._trigger('close', null, {
				'self': this
			});
		},


		/**
		 *  change the field back
		 */
		_save: function () {
			var self = this;
			//  get field values and make an array out of them
			var text = $.map(this._fields, function (value) {
				return value.item.val();
			});

			// saving to edit-fields.js in integration
			this._trigger('save', null, {
				'text': text
			});

		},

		/**
		 *  when we open the field and make it editable, add a save button and bind save event to it
		 */
		_addSaveButton: function () {

			this._saveLink = $(Mustache.render(this.options.saveLink, {
				'label': this._saveLabel
			}));

			this._triggerLink.before(this._saveLink);

			this._on(this._saveLink, {
				'click': function (event) {
					event.preventDefault();
					this._save(this._label);
				}
			});

		},

		/**
		 *  remove link and it's event
		 */
		_removeSaveButton: function () {
			this._saveLink.remove();
		},

		/**
		 *  remove edit link
		 */
		_removeEditLink: function () {
			var self = this;
			this.element.find('.' + self.options.triggerClass).remove();
		},


		/**
		 *  remove link and it's event
		 */
		_cancel: function () {

			// add in server return stuff to reset the text

			$.each(this._fields, function (index, value) {
				value.item.val(value.value);
			});

			this.close();

			if (this._localSave) {
				this._removeSaveButton();
			}

		},


		/**
		 * Destroy
		 */
		_destroy: function () {
			this._link.remove();
			this.element.show();
			this._target
				.removeClass(this.options.activeClass)
				.addClass(this.options.jsHideClass)
				.show();
		}
	});

})(jQuery, Mustache);
;
/**
 * @fileOverview A jQuery UI Widget to make deleteable blocks
 * @author Joel Mitchell
 * @name $.cx.removeable
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function($) {
	'use strict';

	$.widget('cx.removeable', {
		options: {
			'triggerClass': '.delete',
			'duration': 300
		},

		widgetEventPrefix: 'remove_',

		/**
		 * Constructor
		 */
		_create: function() {
			this._id = this.element.data('id');
			this._triggerLink = this.element.find(this.options.triggerClass);

			this._on(this._triggerLink, {
				'click': this._delete,
				'keydown': this._keyboardHandler
			});
		},


		/**
		 *	bind keyboard handling
		 */
		_keyboardHandler: function(e) {
			var keyCode = e.keyCode;

			if (keyCode === 32 || keyCode === 13) {
				e.preventDefault();
				this._delete();
			}

		},

		/**
		 *	change the field back
		 */
		_delete: function() {
			var self = this;

			// deleting edit-fields.js in integration
			this._trigger('delete', null, {
				'id': this.id,
				'complete': function(response) {
					if (response.status === 'success') {
						// self.element.slideUp(self.options.duration, function(){
						// 	self.element.remove();
						// });

					}
				}
			});

		},


		/**
		 * Destroy
		 */
		_destroy: function() {


		}
	});

})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to handle ajax form submission in a popup
 * @author Joel Mitchell
 * @name $.cx.email
 * @dependencies: jQuery, jQuery UI widget factory, Mustache
 */

(function($, Mustache) {
	'use strict';

	$.widget('cx.email', {
		options: {
			'submit': ':submit',
			'fieldClass': '.form__field--string',
			'waitingClass': 'btn--waiting',
			'fieldItem': '.form__item--string',
			'successClass': 'form__item--success',
			'errorClass': 'form__item--has-errors',
			'errorClassField': 'form__item__note form__item__note--error',
			'template': '<p class="{{class}}">{{message}}</p>',
			'messages': {
				'success': 'Thank you, we will be doing something soon',
				'failure_email': 'Please input a valid email address',
				'failure_server': 'Sorry there has been a server error'
			},

			// error from server
			'failure_server': function(e, ui) {
				var data = {
					'error': true,
					'message': ui.message || ui.self.options.messages.failure_server,
					'fieldClass': ui.self.options.errorClassField,
					'itemClass': ui.self.options.errorClass
				};
				ui.self._message(data);
			},

			// not a valid email
			'failure_email': function(e, ui) {
				var data = {
					'error': true,
					'message': ui.self.element.data('email-ajax-invalid') || ui.self.options.messages.failure_email,
					'fieldClass': ui.self.options.errorClassField,
					'itemClass': ui.self.options.errorClass
				};
				ui.self._message(data);
			},


			// not a valid email
			'success': function(e, ui) {
				var data = {
					'error': false,
					'message': ui.message || ui.self.options.messages.success,
					'fieldClass': ui.self.options.successClass,
					'itemClass': ui.self.options.errorClass
				};

				ui.fields.val('');
				ui.self._message(data);
			}
		},

		widgetEventPrefix: 'ajax_email_',

		/**
		 * Constructor
		 */
		_create: function() {

			this._submitButton = this.element.find(this.options.submit);
			this._fields = this.element.find(this.options.fieldClass);
			this._fieldItem = this._fields.closest(this.options.fieldItem);

			this._on(this._submitButton, {
				'keydown': this._keyboardHandler,
				'click': this._submit
			});


			// listen to the close event from the modal to clean the form up
			var self = this;
			$.subscribe('popup_close', function(e, context) {
				if (self.message) {
					self.message.remove();
				}
				self._cleanUp();
			});

		},


		/**
		 *	bind keyboard handling
		 */
		_keyboardHandler: function(event) {
			var keyCode = event.keyCode;
			if (keyCode === 32 || keyCode === 13) {
				event.preventDefault();
				this._submit(event);
			}
		},

		/**
		 *	remove previous error messages, make error message, populate it and move along
		 */
		_message: function(data) {
			var self = this;
			if (this.message) {
				this.message.remove();
			}

			this.message = $(Mustache.render(this.options.template, {
				'message': data.message,
				'class': data.fieldClass
			}));

			if (data.error) {
				this._fieldItem.addClass(data.itemClass);
				this._fields.after(this.message);
			} else {
				this._fieldItem.removeClass(data.itemClass);
				var $el = this.element.find('.form__group');
				$el
					.height(this.element.height())
					.fadeOut('fast', function() {
						$el.after(self.message);
					})
					.end()
					.focus();

			}
		},


		/**
		 *	bind submit function
		 */
		_submit: function(event) {
			event.preventDefault();
			var self = this;
			var value = this._fields.val();

			if (value.length <= 5) {
				this._trigger('failure_email', null, {
					'self': this
				});

				return;
			}

			this._submitButton
				.prop('disabled', true)
				.addClass('is-disabled')
				.addClass(this.options.waitingClass);

			// @devnote - add in client side validation as required here
			this._trigger('validation', null, {
				'value': value,
				'failure': function(response) {
					// handle failure here with own methods
				},
				'success': function() {
					// handle succes here with own methods
				}
			});

			// this function should * normally * be called from the validation function above on client side success
			this._submitting(value);

		},

		/*
		 *	actual submit to server - called after successful client side validation
		 */
		_submitting: function(value) {
			var self = this;

			this._trigger('submitting', null, {
				'value': value,
				'complete': function(response) {

					if (response.status === 'success') {
						self._trigger('success', null, {
							'self': self,
							'fields': self._fields,
							'message': response.message
						});

						self._cleanUp();
					} else {
						self._trigger('failure_server', null, {
							'self': self,
							'message': response.message
						});

						self._enableButton();
					}

				}
			});


		},

		// tidy up and make form good again
		_cleanUp: function() {

			this._enableButton();

			this._fieldItem.removeClass(this.options.itemClass);
			this._fields.val('');
			this.element
				.find('.form__group')
				.css({
					'height': 'auto'
				})
				.show();
		},

		_enableButton: function() {
			this._submitButton
				.prop('disabled', false)
				.removeClass('is-disabled')
				.removeClass(this.options.waitingClass);
		},

		/**
		 * Destroy
		 */
		_destroy: function() {

		}
	});

})(jQuery, Mustache);
;
/*global window, $, jQuery */

/**
 * @fileOverview A jQuery UI Widget to set headers and internal elements in a container to equal height
 * @author Andy Mantell and extended by Joel Mitchell - slightly odd pattern...
 * @name $.cx.equal_heights
 */

(function ($) {
    'use strict';

    $.widget('cx.equal_heights', {
        options: {
            resizeThrottle: 200,
            targetSelector: '.js-equal-heights-parent',
            targetHeading: '.js-equal-heights-child',
            targetChildId: '[class^="js-equal-heights-id"], [class*="js-equal-heights-id"]',
            targetHeadingActiveClass: 'js-equal-heights-child--active',
            targetMobileSelector: 'js-equal-heights-mobile',
            breakpoint: 850, // matches mobile max. when the grid collapses to full width
            resetOnMobile: 'options-resetonmobile',
            popupClass: '.mfp-hide',
            fadeClass: '.js-fade-trigger',
            filterClass: '[data-value]'
        },

        _create: function () {
            var self = this;

            this._windowWidth = 0;
            this._eventId = this.widgetFullName + this.uuid;
            this._events = [
                    'load',
                    'orientationchange.' + this._eventId,
                    'resize.' + this._eventId
            ];
            this._onmobile = this.element.hasClass(this.options.targetMobileSelector);
            this._target = this.element.find(this.options.targetSelector);
            this._child = this.element.find(this.options.targetHeading);
            this._childId = this.element.find(this.options.targetChildId);
            this._hasChild = this._child.length > 0;
            this._hasChildId = this._childId.length > 0;

            this.options.breakpoint = this.element.data(this.options.resetOnMobile) === false ? 0 : this.options.breakpoint;
            //this.element.addClass(this._eventId);

            // cover hidden instances
            //popups
            if (!this.element.is(':visible')) {

                this._popupContainer = this.element.closest(this.options.popupClass);

                this._popupId = this._popupContainer.length ? this._popupContainer.prop('id') : undefined;

                if (this._popupId) {
                    $(document.body).find('[href=#' + this._popupId + ']').on('click', function () {
                        self._resize(self._eventId);
                    });
                }
            }

            //fading
             if (this.options.fadeClass) {
                $(document.body).find(this.options.fadeClass).on('click', function () {
                    self._resize(self._eventId);
                });
            }

            //filtering
             if (this.options.filterClass) {
                $(document.body).find(this.options.filterClass).on('click', function () {
                    self._resize(self._eventId);
                });
            }

            //collapsing
            //@todo



            // bind events
            this.window.on(this._events.join(' '), function () {
                self._resize(self._eventId);
            });
        },

        _init: function () {
            this._resize(this._eventId);
        },

        /**
         * @description This function will be fired multiple times at once.
         * We will delay the event, for performance reasons, by eventId
         */
        _resize: function (eventId) {
            var self = this;

            this._delayedEvent(function () {
                self._windowWidth = window.innerWidth || $(window).width();
                //console.log( window.innerWidth, $(window).width());
                self._resetHeights();

                if (self._windowWidth > self.options.breakpoint) {
                    self._equalHeights();
                }
                else if (self._onmobile) {
                    self._equalHeights();
                }
            }, this.options.resizeThrottle, eventId);
        },

        _resetHeights: function () {
            this._target.height('auto');

            if (this._hasChild || this._hasChildId) {
                this._child.add(this._childId).height('auto').removeClass(this.options.targetHeadingActiveClass);
            }
        },

        _equalHeights: function () {
            var self = this,
                maxHeight = 0,
                maxHeightHeading = 0,
                itemHeadingHeight,
                groups = [];

            // reset calculation
            if (this._hasChild || this._hasChildId) {
                this._child.add(this._childId).addClass(this.options.targetHeadingActiveClass);
            }

            // loop the parents
            $.each(this._target, function (index, item) {
                var $item = $(item),
                    itemHeight = $item.height();

                if (self._hasChild) {
                    itemHeadingHeight = $item.find(self.options.targetHeading).innerHeight();

                    // gnnn bit nasty but given that there may not be a child element i dont want it freaking out
                    if (itemHeadingHeight > maxHeightHeading) {
                        maxHeightHeading = itemHeadingHeight;
                    }
                }

                if (itemHeight > maxHeight) {
                    maxHeight = itemHeight;
                }
            });

            // finish child elements
            if (this._hasChild) {
                this._target.find(this.options.targetHeading).height(maxHeightHeading);
            }

            //this._childId.find(self.options.targetHeadingId).height(maxHeightHeadingId);

            this._target.height(maxHeight);

            // EXTENSION
            // 1. divide children into groups, based on class id
            $.each(this._childId, function (idx, el) {
                var classList = el.classList;

                // loop an element 's current classlist
                $.each(classList, function (cIdx, cEl) {
                    var id;
                    if (/js-equal-heights-id-/i.test(cEl)) {
                        id = parseInt(cEl.replace('js-equal-heights-id-', ''), 10);
                        groups[id] = groups[id] || [];
                        groups[id].push(el);
                    }
                });
            });

            // 2. finish groups
            $.each(groups, function () {
                var children = $(this);

                maxHeightHeading = 0;

                $.each(children, function () {
                    itemHeadingHeight = $(this).innerHeight();

                    if (itemHeadingHeight > maxHeightHeading) {
                        maxHeightHeading = itemHeadingHeight;
                    }
                });


                children.filter(':visible').css({ height: maxHeightHeading });

            });
        },

        _delayedEvent: (function () {
            var timers = {};

            return function (func, wait, id) {
                wait = wait || 200;
                id = id || 'anonymous';

                if (timers[id]) {
                    clearTimeout(timers[id]);
                }

                timers[id] = setTimeout(func, wait);
            };
        })()

    });
}(jQuery));
;
/**
 * @fileOverview A jQuery UI Widget to create static heading on mobile version of compare table
 * @author Joel Mitchell
 * @name $.cx.compare_table_headings
 * @dependencies: jQuery, jQuery UI widget factory
 */
// @todo: subclass the collapsing behaviour to share across all the collapsibles


(function ($) {
    'use strict';
    $.widget('cx.compare_table_headings', {
        options: {
            'subheadingClass': '.compare-table__subheading',
            'headingClass': '.compare-table__heading',
            'throttle': 100,
            'staticClass': 'is-subheading--static',
            'activeClass': 'is-subheading--active',
            'margin': 20,
            'peepage': [{
                'width': [0, 370],
                'table': 650
            }, {
                'width': [371, 439],
                'table': 800
            }, {
                'width': [440, 650],
                'table': 850
            }, {
                'width': [651, 890],
                'table': 900
            }]
        },

        widgetEventPrefix: 'compare_headings_',

        // the constructor
        _create: function () {
            var self = this;

            this._subheadings = $('.compare-table__subheading, .compare-table__heading', this.element); // not sure how else to join these two using the options reference
            this._col = this.element.find('.compare-table__column--first');
            this._col.hide();
            this.element.addClass(this.options.activeClass);

            // timeout for resize event
            this._timeoutId = null;
            this._wrapperWidth = 0;
            this._left_offset = 0;

            // add a wrapper reference to allow the left offset calculation
            // not thrilled
            this._tableWrap = this.element.parent();
            this._tableWrapParent = this._tableWrap.parent();

            this._on(this.window, {
                'resize': this._resizeHandler.bind(self),
                'orientationchange': this._resizeHandler.bind(self)
            });

            // bind to the table wrapper as this monitors the table leftpos
            this._on(this._tableWrap, {
                'scroll': function(ev) {
                    this._scrollHandler(false); 
                }
            });

            this._subheadings.css({
                'left': 'auto',
                'width': 'auto'
            });

            this._resizeHandler();

        },

        // can be refactored from EC.fn but I didn't want to make a dependency out of it
        // possibly we can add this functionality into $.fn to make it independant.
        _throttle: (function () {
            var timers = {};

            return function (func, wait, id) {
                wait = wait || 200;
                id = id || 'anonymous';

                if (timers[id]) {
                    clearTimeout(timers[id]);
                }

                timers[id] = setTimeout(func, wait);
            };
        })(),

        _resizeHandler: function () {
            var self = this;

            this._throttle(function () {
                var width = self.window.width(),
                    isWideView = width > 620;

                self._wrapperWidth = width;
                self._left_offset = isWideView ? parseInt(self._tableWrapParent.css('marginLeft'), 10) : 0;
                self._scrollHandler(isWideView);
                self._setPeepage();
            }, this.options.throttle, 'resize-handler');
        },

        /**
         * measures offset from left of parent container
         * @param {Boolean} isReset : clear
         */
        _scrollHandler: function (isReset) {
            var self = this;

            this._throttle(function() {
                var css = isReset ? {
                    left: 'auto',
                    width: 'auto'
                } : {
                    left: self._tableWrap.scrollLeft() - self._left_offset,
                    width: self._wrapperWidth - self.options.margin
                };

                self._subheadings.css(css);
            }, 200, 'scroll-handler');
        },

        //  we always want to see a chunk of the next column to ensure that
        //  there is a visual clue as to whether there are more
        _setPeepage: function () {
            var self = this;

            $.grep(self.options.peepage, function (index) {
                if (self._numberInRange(self._wrapperWidth, index.width[0], index.width[1])) {
                    self.element.css({
                        width: index.table
                    });
                }
            });
        },

        /*
         *  test whether a number is within a range, if yes, return true
         */
        _numberInRange: function (number, lower, upper) {
            return number > lower && number < upper;
        },

        _destroy: function () {
            this._off(this.window, 'resize orientationchange load');
            this._off(this._tableWrap, 'scroll');
            this.element.removeClass(this.options.activeClass).css({
                width: 'auto'
            });
            this._col.show();
            this._scrollHandler(true);
        }
    });

})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to handle lazy loaded video
 * @author Joel Mitchell
 * @name $.cx.video
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function ($) {
  'use strict';

  $.widget('cx.video', {

    options: {
      'mode': 'desktop',
      'wrapTemplate': '<div class="video-wrap"></div>',
      'defaultHeight': 415,
      'defaultWidth': 650
    },

    // Add an underscore to the event prefix
    widgetEventPrefix: 'video_',

    /**
     * Constructor
     */
    _create: function () {
      var self = this,
        options = self.options;

      // Construct the iframe wrapper tag
      self._video = $('<iframe />', {
        'src': self.element.data('src'),
        'id': self.widgetFullName+self.uuid,
        'frameborder': 0,
        'allowfullscreen': true,
        'title': self.element.data('title') // Inherit all from the div
      }).prop({
          'width': self.element.data('width') || options.defaultWidth,
          'height': self.element.data('height') || options.defaultHeight
        }); // not added at creation to avoid width adding pixel value to it

      // Append the video
      self.element.before(self._video);

      //wrap it in the responsive wrapper - return the whole this to self._video for easy removal
      self._video = self._video.wrap(self.options.wrapTemplate);

      self._video.closest('.video-wrap').css('width', self.element.data('width') || options.defaultWidth);

    },


    /**
     * Destroy
     */
    _destroy: function () {
      var self = this;

      self._video.remove();

    }
  });

})(jQuery);
;
/**
 * @fileOverview A jQuery UI Widget to create an autosuggest base that extends the native ui autocomplete
 * @author Joel Mitchell
 * @name $.cx.autosuggest
 * @dependencies: jQuery, jQuery UI widget factory, Mustache
 */

(function($, Mustache) {
    'use strict';

    $.widget('cx.autosuggest', $.ui.autocomplete, {
        options: {

            // classes
            'autosuggestClass': '.js-autosuggest--field',
            'activeClass': 'autosuggest--active',
            'waitingClass': 'autosuggest__icon--waiting icon-waiting',
            'waitingIconClass': 'icon-waiting',
            'minLength': 2, //
            'searchThrottle': 300,
            'serverTimeoutDelay': 10000,
            'maxItems': 5,
            'baseUrl': '//' + location.host + '/Common/Data/companies.json?search=',
            'messages': {
                'noResults': 'No results returned',
                'searching': 'Retrieving results',
                'timeout': 'Sorry, we lost connection. Please try again.',
                'error': 'Sorry, we could not complete this request. Please try again.'
            },
            'templates': {
                'listWrap': '<div class="autosuggest__items"></div>',
                'indicator': '<span class="autosuggest__icon {{class}}"><span class="visuallyhidden">{{text}}</span></span>',
                'item': '<li class="autosuggest__item"><a class="autosuggest__link">{{{result}}}<br><span class="autosuggest__title zeta">{{address}}</span><i class="icon-chevron-right delta"></i></a></li>',
                'message': '<li class="autosuggest__item centi no-item"><p class="autosuggest__prompt">{{{result}}}</p></li>',
                'complete': '<span class="autosuggest__complete delta"><span class="visuallyhidden">Selected</span><i class="icon-tick"></i></span>',
                'timeout': '<div class="autosuggest__error delta">{{{text}}}</div>',

                'extra_single': '{{number}} more result.',
                'extra_multiple': '{{number}} more results.'

            },
            'server_timeout': function(event, ui) {},
            'server_error': function(event, ui) {}
        },

        widgetEventPrefix: 'autosuggest_',

        /**
         * Standard jQuery UI create function
         */
        _create: function() {
            var self = this;
            var max = self.options.maxItems;

            this._cache = {};
            this._resultText = '';
            this._totalResults = 0;
            this._search = this.element.find(this.options.autosuggestClass);
            this._actualMin = this.options.minLength;

            // adding this in to set a max width on the field wrapping to contain the icon
            //  also appending the list container for the returned items
            this._search.after(this.options.templates.listWrap);

            this.element.css({
                'maxWidth': parseInt(this._search.css('maxWidth'), 10)
            });

            var container = this.element.find('.autosuggest__items');

            // the icon
            this._indicator = $(Mustache.render(self.options.templates.indicator, {
                'class': self.options.waitingIconClass,
                'text': self.options.messages.searching,
                'icon_name': self.options.waitingIconClass
            }));

            // include the icon after the search field
            this._search.after(this._indicator);


            //  grab initial value of field - reverts to this on blur
            this._resetValue = this._search.val();

            // check if a complete icon should be shown
            if (this._resetValue !== '') {
                this._addComplete();
            }


            //  this is here to allow for extending before being initialised
            this._options = {
                'maxNumber': self.options.maxItems,
                'delay': self.options.searchThrottle,
                response: function(event, ui) {

                    var dif;

                    self._testForComplete();

                    //  add the extra companies section on the end
                    if (self._totalResults > 0) {
                        if (self._totalResults > self.options.maxItems) {
                            dif = self._totalResults - self.options.maxItems;
                            self._resultText = (dif === 1) ?

                                Mustache.render(self.options.templates.extra_single, {
                                    'number': dif
                                }) :
                                Mustache.render(self.options.templates.extra_multiple, {
                                    'number': dif
                                });

                        } else {
                            self._resultText = '';
                        }
                    } else {
                        self._resultText = '';
                    }

                },
                'source': function(request, response) {

                    var term = request.term;

                    //  check for a response less than 2
                    //  this needs doing like this as the field can be pre-filled and it ignores the
                    //  delete response when fewer than the minLength
                    if (term.length < self._actualMin) {
                        response();
                        return;
                    }

                    if (term in self._cache) {
                        self._totalResults = self._cache[term].totalResults;
                        response(self._responseMap(self._cache[term]));
                        return;
                    }

                    self._progressIndicator();

                    // request - grabs base URL and attaches request term
                    var ajax = $.ajax({
                        url: self.options.baseUrl + request.term,
                        timeout: self.options.serverTimeoutDelay
                    });

                    // done state
                    ajax.done(function(data, textStatus, jqXHR) {

                        if (textStatus === 'nocontent') {
                            self._totalResults = 0;
                            response([self.options.messages.noResults]);
                            return;
                        }

                        // caching
                        self._cache[term] = data;

                        self._totalResults = data.totalResults;

                        //  mapping it to label/value, value as per the example - feel this might make it a bit more standard
                        response(self._responseMap(data));

                    });

                    ajax.fail(function(jqXHR, textStatus, errorThrown) {

                        self._totalResults = 0;

                        switch (jqXHR.status) {
                            case 0:
                                response([{
                                    'label': self.options.messages.timeout,
                                    'value': -1
                                }]);
                                self._trigger('error', null, {
                                    'message': self.options.messages.timeout,
                                    'template': self.options.templates.timeout
                                });
                                break;

                            case 500:
                                response([{
                                    'label': self.options.messages.error,
                                    'value': -1
                                }]);
                                self._trigger('server_error', null, {
                                    'message': self.options.messages.error,
                                    'template': self.options.templates.timeout
                                });
                                break;
                        }
                    });

                },
                minLength: 0,
                appendTo: container,
                close: function() {

                    /*
                     * You can't search for the same thing twice in
                     * a row with the autocomplete widget.
                     */
                    $(this).data().uiAutocomplete.term = null;

                    //  remove the active class
                    self.element.removeClass(self.options.activeClass);
                    self._testForComplete();
                },
                select: function(event, ui) {
                    self._resetValue = ui.item.value;
                    // self._testForComplete();
                },
                focus: function(event, ui) {
                    self.inst.cancelBlur = false;
                    if (ui.item === '') {
                        //  if this isnt a regular list item look inside the active item for a link
                        //  bind a keyboard handler
                        var _a = self.inst.menu.active.find('a');
                        if (_a) {
                            self.inst.cancelBlur = true;
                            _a.focus()
                                .on('keydown.item', {
                                    a: self.inst
                                }, self._keyboardHandler);
                        }
                    }


                },
                change: function(event, ui) {

                    if (self._search.val() !== self._resetValue) {
                        self._search.val(self._resetValue);
                        self._testForComplete();
                    }
                }
            };

        },

        /**
         * Map values from the server to be label / value as per the demo
         */
        _responseMap: function(data) {
            return $.map(data.results, function(value) {
                return {
                    'id': value.id,
                    'label': value.address,
                    'value': value.title
                };

            });

        },

        /**
         *  bind keyboard handling for items in the added, non-standard links
         *
         */
        _keyboardHandler: function(e, item) {
            var keyCode = e.keyCode;

            //  when you tab, remove the cancel event and allow the focus to move on
            if (keyCode === 9) {
                e.preventDefault();
                e.data.a.close();
                return false;
            }

            //  press enter - select the link and navigate there
            if (keyCode === 13) {
                e.preventDefault();
                window.location.href = $(e.target).prop('href');
            }

        },

        _init: function() {
            var self = this;

            //  create autocomplete instance that can be used to extend render methods
            //  applied to 'this' to allow for inheriting correctly
            this.inst = this._search.autocomplete(this._options).data('ui-autocomplete');

            this.inst.menu.element.addClass('autosuggest__list');

            // extend renderMenu method to allow new options
            this.inst._renderMenu = function(ul, items) {
                var that = this;
                self.element.addClass(self.options.activeClass);

                if (!items.length || self._totalResults === 0) {
                    self.inst._renderNote(ul, items[0].label);
                } else {
                    $.each(items, function(index, item) {
                        self.inst._renderItem(ul, item);
                    });

                    if (self._resultText !== '') {
                        self.inst._renderNote(ul, self._resultText);
                    }

                }
                self._progressIndicatorRemove();
            };

            //  the custom item render function - returns the item template for populated results
            this.inst._renderItem = function(ul, item) {
                return $(Mustache.render(self.options.templates.item, {
                        'result': item.value,
                        'address': item.label,
                        'id': item.id
                    }))
                    .data('ui-autocomplete-item', item)
                    .appendTo(ul);
            };

            //  returns the note template for messages - returns null for data- attributes
            this.inst._renderNote = function(ul, result) {
                return $(Mustache.render(self.options.templates.message, {
                        'result': result
                    }))
                    .data('ui-autocomplete-item', '')
                    .appendTo(ul);
            };

        },


        _testForComplete: function() {
            this._delay(function() {
                if (this._search.val() === this._resetValue && this._resetValue !== '') {
                    this._addComplete();
                } else {
                    this._removeComplete();
                }
            }, 0);

        },

        _addComplete: function() {
            this._indicator
                .removeClass(this.options.waitingIconClass)
                .addClass('autosuggest__icon--complete')
                .addClass('icon-tick');

        },

        _removeComplete: function() {

            this._indicator
                .addClass(this.options.waitingIconClass)
                .removeClass('autosuggest__icon--complete')
                .removeClass('icon-tick');
        },

        _progressIndicator: function() {
            this._indicator.addClass(this.options.waitingClass);
        },

        _progressIndicatorRemove: function() {
            this._indicator.removeClass(this.options.waitingClass);
        },

        /**
         * Tear everything down again.
         */
        _destroy: function() {
            this._super('destroy');
        }
    });

})(jQuery, Mustache);
;
/**
 * @fileOverview A jQuery UI Widget to auto-hide elements after an amount of time
 * @author Riccardo Farina, Alessandro Saitta
 * @name $.cx.autohide
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function ($) {
    'use strict';

    $.widget('cx.autohide', {
        options: {
        		selectorClass: '.js-autohide',
            timeout: '0', //default autohide off
            closeButtons: '.popup-close',
            fadeOutOptions: 'slow'
        },

        /**
         * Standard jQuery UI create function
         */
        _create: function () {
            var self = this;

            self._timeout = self.options.timeout;
            self._closeButtons = $(self.options.closeButtons);
            self._selector = $(self.options.selectorClass);
            self._fadeOutOptions = self.options.fadeOutOptions;

            // set autoHide interval
          	if(self._timeout>0) {
	            setInterval(function () {
	                self.element.fadeOut(self._fadeOutOptions);
	           		}, self._timeout);
            }

            //  close button function binding
            self._on(self._closeButtons, {
                'click': function (event) {
                    $( event.target ).closest(self._selector).hide();
                }
            });
        }
    });

})(jQuery);;
/**
 * @fileOverview A jQuery UI Widget to extend autosuggest and create a company lookup
 * @author Joel Mitchell
 * @name $.cx.company_lookup
 * @dependencies: jQuery, jQuery UI widget factory, Mustache
 */

(function ($, Mustache) {
	'use strict';
	$.widget('cx.company_lookup', $.cx.autosuggest, {
		options: {
			'baseUrl': 'http://int.euroconsumer.cxdev.co.uk/api/api/v0.1/companies/lookup?search=',
			'redirectUrl': '/patterns/autosuggest',
			'messages': {
				'noResults': 'Sorry we couldn\'t find this company. <br><a class="autosuggest__link--internal" href="/">Add the company details yourself.</a>',
				'timeout': 'Sorry, we lost connection. Please try again. <br><a class="autosuggest__link--internal" href="/">Add the company details yourself.</a>',
				'error': 'Sorry, we could not complete this request. Please try again. <br><a class="autosuggest__link--internal" href="/">Add the company details yourself.</a>',
				'note': '<a href="/">Add the company details yourself.</a>'
			},
			'templates': {
				'extra_single': '{{number}} more company found. Keep typing.',
				'extra_multiple': '{{number}} more companies found. Keep typing.'
			},
			'select_function': function (event, ui) {
				window.location.href = (ui.redirect + '?cid=' + ui.data.id);
			}
		},

		/**
		 * Create the plugin
		 */
		_create: function () {
			var self = this;

			this._super('create');

			this._extend = {

				select: function (event, ui) {

					setTimeout(function () {

						if (ui.item.label !== undefined) {
							//  set input to the selected value
							self._trigger('select_function', null, {
								'data': ui.item,
								'redirect': self.options.redirectUrl
							});
						}
					}, 0);
				}
			};

			this._options = $.extend(this._options, this._extend);

		},


		_init: function () {
			var self = this;

			// summoning the base class init to kick it all off.
			this._super('init');

			//  extend renderMenu method to allow new options
			this.inst._renderMenu = function (ul, items) {
				self.element.addClass(self.options.activeClass);

				if (!items.length || self._totalResults === 0) {
					self.inst._renderNote(ul, items[0].label);

				} else {
					$.each(items, function (index, item) {
						self.inst._renderItem(ul, item);
					});

					if (self._resultText !== '') {
						self.inst._renderNote(ul, self._resultText);
					}

					// added 'add you own' link
					self.inst._renderExtraItem(ul);
				}

				self._progressIndicatorRemove();

			};

			this.inst._renderExtraItem = function (ul) {
				return $(Mustache.render(self.options.templates.message, {
					'result': self.options.messages.note
				}))
				.data('ui-autocomplete-item', '')
				.appendTo(ul);
			};

		},

		destroy: function () {
			this._super('destroy');
		}

	});

})(jQuery, Mustache);
;
/*global window, $, jQuery */

/**
 * @fileOverview A jQuery UI Widget to override default browser hash behaviour
 * @author Luke Taylor [additional by Joel Mitchell]
 * @name $.cx.scrollToTarget
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function($) {
    'use strict';

    $.widget('cx.scrollToTarget', {

        options: {
            'duration': 450,
            'offset': 50,
            'delay': 100,
            'click_link': function(event, ui) {},
            'focus_field': function(event, ui) {
                var field = ui.element.find('input, textarea').eq(0);
                if (field.length > 0) {
                    field.focus();
                } else {
                    ui.element
                        .addClass('has-focus-element')
                        .prop('tabindex', -1)
                        .focus();
                }
            }
        },

        widgetEventPrefix: 'scroll_to_target_',

        _create: function() {

            var self = this;
            var options = self.options;
            var hash = window.location.hash;
            var element = self.element;

            this._on({
                'click': function(event) {
                    var target = $(event.currentTarget);
                    var href = target.prop('href');
                    var hash;

                    event.preventDefault();

                    if (href !== undefined) {
                        hash = href.substring(href.indexOf('#'));
                        self.animateTo(element, options, hash, true);
                    }
                }
            });




            // short timeout else the jump still occurs
            setTimeout(function() {
                // show the element (we have hidden it with CSS to stop default hash jump occuring)
                element.show();
                // only proceed if there is a hash
                if (hash) {
                    self.animateTo(element, options, hash);
                }
            }, options.delay);

        },

        /**
         * Scroll to the target element
         *
         * @param element
         * @param options
         * @param hash
         * @param bool
         */
        animateTo: function(element, options, hash, bool) {

            var $hash;
            var settings;

            // only proceed if hash value matches the id of the element
            if (hash.replace('#', '') !== element.attr('id') && bool !== true) {
                return;
            }

            $hash = $(hash);

            //  if no element, bug out
            if ($hash.length === 0) {
                return;
            }

            // make settings object
            settings = {};
            settings.options = options;

            // get top of target element
            settings.top = $hash.offset().top;
            settings.element = $hash;

            settings.duration = options.duration;

            // if element is flagged as use native scroll, set duration to zero
            if (element.is('[data-native-scroll]')) {
                settings.duration = 0;
            }

            //  call animate function
            this._animateTo(settings);
        },

        /**
         * Animate the scroll
         *
         * @param settings
         * @private
         */
        _animateTo: function(settings) {
            var self = this;
            //   include both html and body for some browsers
            //   make a promise to prevent duplication of the animation
            $('html, body')
                .delay(settings.options.delay)
                .animate({
                        scrollTop: (settings.top - settings.options.offset)
                    },
                    settings.duration)
                .promise()
                .done(function() {
                    //  trigger field focus callback function
                    self._trigger('focus_field', null, {
                        'self': self,
                        'element': settings.element
                    });
                });

        },


        _destroy: function() {}

    });

}(jQuery));
;
/**
 * @fileOverview A jQuery UI Widget to extend Blue Imp file uploader
 * @author Joel Mitchell
 * @name $.cx.file_upload
 * @dependencies: jQuery, jQuery UI widget factory, Mustache
 */

(function($, Mustache) {
    'use strict';
    $.widget('cx.file_upload', {
        options: {

            // used to display server messages
            'templates': {
                'errorServer': '<div class="error error--server">{{{error_message}}}</div>'
            },

            // message / text to replace strings with
            'messages': {
                'uploading': 'Uploading...',
                '_continue': 'Continue',
                'extra_file': 'Choose another file to upload'
            },

            // target elements
            'elements': {
                'fileButton': '.form__label--file',
                'fileField': '.form__field--file',
                'continueButton': '.js-continue-button'
            },

            // applicable classes
            '_classes': {
                'disabledButton': 'is-disabled btn--waiting',
                'uploadComplete': 'fileupload--is-complete',
                'delete': 'delete',
                'waiting': 'btn--waiting',
                'triggerLink': 'trigger-link',
                '_continue': 'btn btn--primary btn--large'
            },

            // blue imp settings / options
            'blue_imp': {
                'url': 'http://int.euroconsumer.cxdev.co.uk/api/api/v0.1/attachments',
                'maxChunkSize': 100000,
                'maxFileSize': 10000000,
                'maxNumberOfFiles': 10,
                'timeout': 30000,
                'messages': {
                    'maxFileSize': 'Maximum filesize exceeded, please select a smaller file.',
                    'acceptFileTypes': 'File format not allowed, please select a different file',
                    'maxNumberOfFiles': 'You have reached the maximum number of files you can upload',
                    'unknownError': 'We could not deal with your request at this time.',
                    'timeout': 'We could not deal with your request at this time.',
                    'internalServerError': 'We could not deal with your request at this time.'
                },
                'acceptFileTypes': /(\.|\/)(gif|jpeg|png|jpg|pdf)$/i,

                'uploadTemplate': function(data) {
                    var template = '{{#files}}<li class="fileupload__item template-upload{{#error}} fileupload__item--error{{/error}}"><div class="progress__bar"></div><div class="fileupload__item__inner"><div class="name delta">{{name}}{{^error}}<span class="icon__wrap"><i class="icon-waiting"></i></span>{{/error}}</div>{{#error}}<div class="fileupload__item__error zeta">{{error}}</div>{{/error}}</div><button class="btn--cancel cancel icon-x"><span class="visuallyhidden">Cancel</span></button></li>{{/files}}';
                    return Mustache.render(template, data);
                },
                'downloadTemplate': function(data) {

                    if (data.files[0].error) {
                        switch (data.files[0].error) {
                            case 'timeout':
                                data.files[0].error = data.options.messages.timeout;
                                break;
                            case 'Internal Server Error':
                                data.files[0].error = data.options.messages.internalServerError;
                                break;
                            case 'unknownError':
                                data.files[0].error = data.options.messages.unknownError;
                                break;
                        }
                    }

                    // Generate form field IDs for the delete checkbox
                    $.each(data.files, function(index, item) {
                        item.fileId = item.name.toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '') + '-' + (Math.floor(Math.random() * 10000000));
                    });

                    var template = '{{#files}}<li class="fileupload__item template-download{{^error}} fileupload__item--success{{/error}}{{#error}} fileupload__item--error{{/error}}"><div class="progress__bar"></div><div class="fileupload__item__inner">{{#error}}<div class="name delta">{{name}}</div><div class="fileupload__item__error zeta">{{error}}</div>{{/error}}{{^error}}<div class="name delta">{{name}}<span class="icon__wrap"><i class="icon-tick icon--large"></i></span></div>{{/error}}</div>{{#deleteUrl}}<button title="Delete {{name}}" class="btn--delete delete icon-x" data-type="{{deleteType}}" data-url="{{deleteUrl}}"><span class="visuallyhidden">Delete {{name}}</span></button><label class="visuallyhidden" for="{{fileId}}">Delete {{name}}</label><input tabindex="-1" id="{{fileId}}" type="checkbox" checked name="delete" value="{{name}}" class="visuallyhidden toggle">{{/deleteUrl}}{{^deleteUrl}}<button class="btn--cancel cancel icon-x"><span class="visuallyhidden">Cancel</span></button>{{/deleteUrl}}</li>{{/files}}';
                    return Mustache.render(template, data);
                }

            }

        },

        'widgetEventPrefix': 'file_upload_',

        _create: function() {

            var self = this;

            // bypassing file type validation limit (json error passing directly a regex)
            var customFileTypes = this.options.blue_imp.customFileTypes;
            if (customFileTypes != undefined && customFileTypes != null && customFileTypes != "") {
                this.options.blue_imp.acceptFileTypes = new RegExp("(\.|\/)(" + customFileTypes + ")$", "i");
            }
            
            // extending the options to not reveal the ones we don't altered
            var _opts = $.extend(this.options.blue_imp, {
                'dropZone': null,
                'singleFileUploads': false,
                'filesContainer': this.element.find('.fileupload__items'),
                'previewSourceFileTypes': /^disabled$/,
                'autoUpload': true,
                'uploadTemplateId': null,
                'downloadTemplateId': null
            });



            //  here is a config thing
            var _elements = {
                '_fileInput': this.element.find(this.options.elements.fileField),
                '_fileButton': this.element.find(this.options.elements.fileButton),
                '_fileContainer': this.options.blue_imp.filesContainer,
                '_continueButton': this.element.find(this.options.elements.continueButton),
                '_button': this.element.find('.fileupload')
            };


            //  bit of a retrofit to deal with the possible other states of the buttons for the reply page
            //  todo: add to documentation
            this.options._classes.addExtraFields = _elements._continueButton.data('extra-classes') || _elements._continueButton.attr('class');

            if (_elements._continueButton.data('alt-text')) {
                this.options.messages._continue = _elements._continueButton.data('alt-text');
            }

            //  extending config object
            _elements._continueButtonText = _elements._continueButton.html();
            _elements._fileButtonText = _elements._fileButton.html();
            _elements._maintain = _elements._continueButton.attr('data-maintain-state') !== undefined;

            //  if it is needed that there are multiple instances of the file input on a page
            //  uncomment the fields below
            _elements._id = this.widgetName + this.uuid;
            _elements._fileButton.prop('for', _elements._id);
            _elements._fileInput.prop('id', _elements._id);


            // initial state for the skip / continue button
            this._uploading = false;

            //  listened-for custom events
            //  outside of options callbacks to make use of 'this'
            this._on({
                'fileuploaddestroyed': function(event, ui) {
                    self._testFinished(_elements);
                }
            });

            //  get initial state of file buttons
            //  make sure that if files are already present then it recognises them
            this._continueButtonState(_elements);
            this._testFinished(_elements);

            this._trigger('files_added');

            _opts = $.extend({}, _opts, {
                filesChanged: function(num) {
                    if (num === 0) {
                        self._trigger('no_files_uploaded');
                    } else {
                        self._trigger('files_added');
                    }
                }
            });

            /*
             * Blue Imp plugin doesn't support IE8.
             * If the browser is IE8, use our bespoke iframe uploader.
             * Reuse the options as much as possible.
             */
            if ($('html').hasClass('ie8')) {
                this.element.iframeFileUpload(_opts);
                return;
            }

            /*
             * Otherwise carry on with the standard uploader
             */

            this.element.fileupload(this.options.blue_imp)
                .on('fileuploadprogress', function(e, data) {
                    // create individual progress bars
                    data.context
                        .find('.progress__bar')
                        .css({
                            'width': parseInt(data.loaded / data.total * 100, 10) + '%'
                        });
                })
                .on('fileuploadprogressall', function(e, data) {

                    self._uploading = true;

                    if (data.loaded === data.total) {
                        self._continueButtonState(_elements);
                        self._enableUploadButton(_elements);
                        self._uploading = false;
                    }
                })
                .on('fileuploadadd', function(e, data) {

                    var files = data.files;
                    var that = $(this).data('blueimp-fileupload');

                    //  re-call the renderUpload template to allow it to recognise the error: within the returned object
                    //  really shouldn't need to do this...
                    $(this).fileupload('process', data).done(function() {
                        data.context = that._renderUpload(files).data('data', data);
                        that._forceReflow(data.context);
                    });

                    //  broadcasts an event for the reply form widget to pick up
                    //  event to listen for 'file_upload_form_files_added'
                    self._trigger('files_added');

                })
                .on('fileuploadstart', function(e, data) {
                    self._disableContinueButton(_elements);
                })
                .on('fileuploadstop', function(e, data) {
                    self._uploading = false;
                    self._testFinished(_elements);
                })
                .on('fileuploadalways', function(e, data) {
                    self._testFinished(_elements);
                })
                .on('fileuploadprocessfail', function(e, data) {
                    self._continueButtonState(_elements);
                })
                .on('fileuploaddone', function(e, data) {
                    self._continueButtonState(_elements);
                    self._delay(function(){
                        self.element.find(self.options.elements.fileField).focus();
                    }, 25);
                });



        },

        //  returns the current successful file count
        _fileCounter: function(_elements) {
            return _elements._fileContainer
                .children('.template-download')
                .not('.fileupload__item--error')
                .length;
        },

        //  disable continue  button
        _disableContinueButton: function(_elements) {
            _elements._continueButton
                .html(this.options.messages.uploading)
                .addClass(this.options._classes._continue)
                .addClass(this.options._classes.disabledButton)
                .prop('tabindex', -1)
                .on('click.continue', function(event) {
                    event.preventDefault();
                });

        },

        _enableUploadButton: function(_elements) {

            if (!_elements._maintain) {
                _elements._continueButton.text(this.options.messages._continue);
            } else {
                _elements._continueButton.text(_elements._continueButtonText);
            }
            _elements._continueButton
                .removeClass(this.options._classes.disabledButton)
                .prop('tabindex', 0)
                .off('click.continue');

        },

        _enableUpload: function(_elements) {
            _elements._fileInput.show();
            _elements._fileButton.addClass('in');
        },

        _revertUploadButton: function(_elements) {
            _elements._continueButton
                .html(_elements._continueButtonText)
                .removeClass(this.options._classes.disabledButton)
                .prop('tabindex', 0)
                .off('click.continue');
        },

        _disableUpload: function(_elements) {
            _elements._fileInput.hide();
            _elements._fileButton
                .addClass('fade')
                .removeClass('in')
                .delay();
        },

        _continueButtonState: function(_elements) {

            // convert file button to a straightforward link when there are files present
            if (this._fileCounter(_elements) !== 0) {

                _elements._fileButton
                    .html(this.options.messages.extra_file)
                    .removeClass(this.options._classes.addExtraFields)
                    .addClass(this.options._classes.triggerLink);

                if (!_elements._maintain) {
                    _elements._continueButton.addClass(this.options._classes._continue);
                }

            } else {

                _elements._fileButton
                    .html(_elements._fileButtonText)
                    .addClass(this.options._classes.addExtraFields)
                    .removeClass(this.options._classes.triggerLink);

                if (!_elements._maintain) {
                    _elements._continueButton.removeClass(this.options._classes._continue);
                }

            }
        },

        _testFinished: function(_elements) {
            var self = this;

            // need a cheeky delay to pick up the file count correctly. again, a shame.
            this._delay(function() {
                var fileCount = this._fileCounter(_elements);
                this._continueButtonState(_elements);
                if (fileCount === 0) {
                    this._revertUploadButton(_elements);

                    //  broadcasts an event for the reply form widget to pick up
                    //  event to listen for 'file_upload_no_files_uploaded'
                    this._trigger('no_files_uploaded');
                } else {
                    if (!this._uploading) {
                        this._enableUploadButton(_elements);
                    }
                }

                if (fileCount >= this.options.blue_imp.maxNumberOfFiles) {
                    this._disableUpload(_elements);
                } else {
                    this._enableUpload(_elements);
                }

            }, 0);

        },

        destroy: function() {


        }
    });

})(jQuery, Mustache);
;
/**
 * @fileOverview A jQuery UI Widget to make a base class for partially saving a page with AJAX
 * @author Joel Mitchell
 * @name $.cx.partial_save
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function($, Mustache) {
    'use strict';

    $.widget('cx.partial_save', {

        options: {
            'timeout': 5000, // time that elapses before it reverts to 'save now' link
            'elements': {
                'tag': '.partial-save',
                'trigger': '.partial-save__trigger',
                'status': '.partial-save__status',
                'field_types': 'input, textarea, select'
            },

            // underscore to stop IE complaining about reserved words
            '_classes': {
                'saving': 'partial-save--progress',
                'error': 'partial-save--error',
                'saved': 'partial-save--done',
                'disabled': 'partial-save__trigger--disabled'
            },

            'messages': {
                'saving': 'Saving....',
                'saveNow': 'Save now',
                'message': '{{{message}}}',
                'status': 'Draft saved at {{time}}'
            },

            // callbacks - if you want 'em
            'click': function(event, data) {

            },

            // save event
            'save': function(event, data) {
                var self = data.self;

                //  remove classes prior to saving
                self._trigger('remove_classes', null, data);

                // change text and apply classes
                self._triggerLink.text(self.options.messages.saving);
                self._tag.addClass(self.options._classes.saving);

                //  raise the save event and pass the data to it
                self._trigger('ajax_save', null, {
                    'data': data,
                    'complete': function(response) {

                        //  remove the saving class
                        self._tag.removeClass(self.options._classes.saving);

                        if (response.status === '200') {
                            //  if successful update timestamp
                            self._status.text(Mustache.render(self.options.messages.status, {
                                'time': response.time
                            }));

                            //  send it to the success event if wanted / comment out if needed
                            self._trigger('success', null, {
                                'response': response,
                                'self': self
                            });

                            //  an error of sorts - show error message
                        } else {

                            //  send it to the error event if wanted
                            self._trigger('error', null, {
                                'response': response,
                                'self': self
                            });
                        }

                        //  update message
                        self._triggerLink.text(Mustache.render(self.options.messages.message, {
                            'message': response.message
                        }));

                        self._delay(function() {
                            // enable the widget again
                            self._enableLinks();
                        }, self.options.timeout);

                    }
                });

                //  disable the widget then prevent a click jumping the page
                self._disableLinks();
            },

            //  please, let us remove the classes
            'remove_classes': function(event, data) {
                data.self._tag
                    .removeClass(data.self.options._classes.error)
                    .removeClass(data.self.options._classes.saving)
                    .removeClass(data.self.options._classes.saved);
            },


            //  error calllback - add more in here
            'error': function(event, data) {
                data.self._tag.addClass(data.self.options._classes.error);
            },

            //  if error loop through things and output them
            //  using the name as the selector - dunno
            'success': function(event, data) {
                data.self._tag.addClass(data.self.options._classes.saved);
                //  removing for the moment as this is a stripped back version
            }

        },

        widgetEventPrefix: 'partial_save_',

        _create: function() {

            var self = this;

            //  elements
            this._tag = this.element.find(this.options.elements.tag);
            this._status = this._tag.find(this.options.elements.status);
            this._triggerLink = this._tag.find(this.options.elements.trigger);

            this._on((function() {
                var events = {};
                events['click ' + self.options.elements.trigger] = self._saveHandler;
                return events;
            }()));

        },

        _disableLinks: function() {

            this._triggerLink
                .addClass(this.options._classes.disabled)
                .on('click.block', function() {
                    return false;
                });

        },


        _enableLinks: function() {
            this._triggerLink
                .removeClass(this.options._classes.disabled)
                .off('click.block');

            this._trigger('remove_classes', null, {
                'self': this
            });

            this._triggerLink.text(this.options.messages.saveNow);
        },


        /**
         *  bind keyboard handling
         */
        _keyboardHandler: function(e) {
            var keyCode = e.keyCode;

            if (keyCode === 13) {
                e.preventDefault();
                this._saveHandler(e);
            }

        },

        /**
         *  saving
         */
        _saveHandler: function(e) {

            // this could be plumbed in to an auto save so may not have an event to prevent
            if (typeof e !== 'undefined') {
                e.preventDefault();
            }

            this._trigger('save', e, {
                'self': this
            });


        },


        _destroy: function() {


        }

    });

}(jQuery, Mustache));
;
/*global jQuery */

/**
 * @fileOverview A jQuery UI Widget to grow textareas as you type
 * @author Luke Taylor
 * @name $.cx.textareaGrow
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function ($) {
  'use strict';

  $.widget('cx.textareaGrow', {

    options: {},

    // Add an underscore to the event prefix to make event names easier to read
    widgetEventPrefix: 'textareaGrow_',

    /**
    * Constructor
    */
    _create: function () {
      var self = this;

      // only proceed if the element is a textarea
      if (self.element.is('textarea')) {

        // find line height
        self._lineHeight = parseInt(self.element.css('line-height'), 10);

        // find height
        self._height = parseInt(self.element.height(), 10);

        // available lines (round up)
        self._availableLines = Math.round(parseFloat(self._height / self._lineHeight));

        // set height of element to accomodate specific number of lines
        self.element.height(self._availableLines * self._lineHeight);

        self.element.css({
          'overflow': 'hidden',
          'resize': 'none'
        });

        self._grow();

        self._on({
          'keydown': function () {
            self._grow();
          }
        });

      }

    },

    /**
    * Grow the element
    */
    _grow: function () {
      var self = this;

      self._height = parseInt(self.element.css('height'), 10);

      //console.log(Math.abs(self.element.get(0).scrollHeight - self._height));

      if (self.element.get(0).scrollHeight >= self._height) {
        self.element.height(self._height + self._lineHeight);
      }

    }

  });

}(jQuery));;
/*global jQuery */

/**
 * @fileOverview A jQuery UI Widget to handle reply form interactivity
 * @author Luke Taylor
 * @name $.cx.replyForm
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function($) {
    'use strict';

    $.widget('cx.replyForm', {

        options: {
            'textareaClass': '.js-reply-form-textarea',
            'lineBreaks': 2,
            'buttonClass': '.js-reply-form-button',
            'disabledClass': 'is-disabled',
            'statusClass': '.js-reply-form-status',
            'fileUploadItems': '.fileupload__items li'
        },

        // Add an underscore to the event prefix to make event names easier to read
        widgetEventPrefix: 'replyForm_',

        /**
         * Constructor
         */
        _create: function() {
            var self = this;

            self._defaultMessage = this.element.data('default-message') || '';

            self._textarea = this.element.find(self.options.textareaClass);

            self._lineBreaks = '';

            for (var i = 0; i < self.options.lineBreaks; i++) {
                self._lineBreaks += ' \n';
            }

            self._on(self._textarea, {
                'focus': self._focus,
                'keyup': self._keyup
            });

            self._button = this.element.find(self.options.buttonClass);

            self._disableButton();

            self._status = this.element.find(self.options.statusClass);

            //  this event broadcast from the file upload plugin
            this._on({
                'file_upload_files_added': function() {
                    self._enableButton();
                }
            });

            this._on({
                'file_upload_no_files_uploaded': function() {
                    if (self._textarea.val() === '') {
                        self._disableButton();
                    }
                }
            });
        },

        /**
         * Focus
         */
        _focus: function() {
            var self = this;
            if (self._textarea.val() === '') {
                self._textarea.val(self._defaultMessage+ self._lineBreaks);
                self._status.show();
                self._enableButton();
                setTimeout(function() {
                    self._moveCaretToEnd();
                    self._textarea.trigger('keydown');
                }, 100);
            }
        },

        /**
         * Keydown
         */
        _keyup: function() {
            var self = this;
            if (self._textarea.val() !== '') {
                self._enableButton();
            } else {
                if (self.element.find(self.options.fileUploadItems).length < 1) {
                    self._disableButton();
                }
            }
        },

        /**
         * Disable Button
         */
        _disableButton: function() {
            var self = this;
            self._button.prop('disabled', true).addClass(self.options.disabledClass);
        },

        /**
         * Enable Button
         */
        _enableButton: function() {
            var self = this;
            self._button.prop('disabled', false).removeClass(self.options.disabledClass);
        },

        _moveCaretToEnd: function() {
            var self = this,
                el = self._textarea[0];

            if (typeof el.selectionStart === 'number') {
                el.selectionStart = el.selectionEnd = el.value.length;
            } else if (typeof el.createTextRange !== 'undefined') {
                el.focus();
                var range = el.createTextRange();
                range.collapse(false);
                range.select();
            }
        }

    });

}(jQuery));
;
/*global jQuery */

/**
* @fileOverview A jQuery UI Widget to scroll to the latest item in a set of items if it is not visible
* @author Andy Mantell
* @name $.cx.complaintThread
* @dependencies: jQuery, jQuery UI widget factory
*/

(function ($) {
	'use strict';

	$.widget('cx.scrollToLatest', {

		options: {
			latestSelector: '.js-scroll-to-latest',
			contentSelector: '.js-scroll-to-latest-content',
			duration: 500,
			peepage: 110	// How much of the previous post to show
		},

		// Add an underscore to the event prefix to make event names easier to read
		widgetEventPrefix: 'scrollToLatest_',

		/**
		* Constructor
		*/
		_create: function () {
			var self = this;

			var $latestPost = self.element.find(self.options.latestSelector);
			var $latestPostContent = $latestPost.find(self.options.contentSelector);

			if(!($latestPost.length > 0 && $latestPostContent.length > 0)) {
				return;
			}

			// If the bottom of the latest message isn't visible, scroll to it
			if($latestPostContent.offset().top + $latestPostContent.height() > self.window.height() + self.window.scrollTop()) {
				$('html,body')
					.animate(
						{
							scrollTop: $latestPost.offset().top - self.options.peepage
						},
						self.options.duration
					)
					.promise()
	                .done(function() {
	                    $latestPost.focus();
	                });
			}

		}
	});

}(jQuery));;
/**
 * @fileOverview A jQuery UI Widget to handle slideshows
 * @author Joel Mitchell
 * @name $.cx.carousel_desktop
 * @dependencies: jQuery, jQuery UI widget factory, Owl carousel, Mustache, Magnific popup
 */

(function ($, Mustache) {
    'use strict';

    $.widget('cx.carousel', {
        options: {
            templates: {
                pager: '<div class="carousel__pager carousel__items">' +
                '{{#data}}' +
                '<div class="carousel__item__thumbnail carousel__item{{#current}}  carousel__item--current{{/current}}" tabindex="0" style="background-image:url(\'{{img}}\');"></div>' +
                '{{/data}}' +
                '</div>',
                galleryCloseButton: '<button class="mfp-close icon-x"><span class="visuallyhidden">Close overlay</span></button>',
                galleryPagerWrap: '<div class="carousel__items__wrap"></div>',
                galleryArrowMarkup: '<button title="%title%" type="button" class="mfp-arrow mfp-arrow-%dir% icon-chevron2-%dir%"></button>',
                iconLeft: '<i class="icon-chevron2-left"></i>',
                iconRight: '<i class="icon-chevron2-right"></i>'
            },

            classes: {
                carouselContainer: '.carousel__container',
                carouselItem: 'carousel__item',
                carouselImage: '.carousel__image',
                currentItem: 'carousel__item--current',
                carouselLink: 'carousel__link',
                iconLeft: 'icon-chevron2-left',
                iconRight: 'icon-chevron2-right'
            },

            events: {
                clickCarouselItem: "click .carousel__pager .carousel__item",
                clickOwlButton: "click .owl-button",
                clickCarouselLink: "click .carousel__link",
                keydownCarouselContainer: "keydown .carousel__container",
                keydownCarouselItem: "keydown .carousel__item",
                customOwlNavLeft: 'click .js-owl-left',
                customOwlNavRight: 'click .js-owl-right'
            },

            returnFocus: true,

            owlOptions: {
                slideSpeed: 300,
                paginationSpeed: 400,
                rewindNav: false,
                pagination: false,
                responsive: true,
                lazyLoad: true,
                transitionStyle: 'fade',
                singleItem: true,
                navigation: true,
                navigationText: []
            },

            thumbData: false,

            useCarouselNav: false,

            owlNavOptions: {
                items: 5,
                pagination: false,
                navigation: true,
                navigationText: [],
                rewindNav: false,
                itemsScaleUp: true,
            },

            usePopupGallery: true
        },

        /** Add an underscore to the event prefix to make event names easier to read */
        widgetEventPrefix: 'carousel_',

        /**
         * Constructor
         */
        _create: function () {

            var self = this,
                opts = self.options;

            this._wrapper = this.element.find(opts.classes.carouselContainer);

            this._data = this._getThumbnailsData();
            if (!this._data.length) {
                return;
            }

            this._single = this._data.length < 2;

            // handle switch between desktop and mobile using media queries

            this._switchBetweenDesktopAndMobileHandler = function (matches) {
                self[matches ? '_initDesktop' : '_initMobile'].call(self);
            };

            EC.Mq.on(EC.Mq.tabletMin(), this._switchBetweenDesktopAndMobileHandler, true);

        },

        /**
         * Initializes the component for desktop, taking into account that it could have been in mobile mode before
         */
        _initDesktop: function () {

            var self = this, opts = this.options;

            // cleanup
            this._destroyPopupGallery();
            this._destroyPager();

            // when a single image exists, don't bother about the carousel and simply handle the popup gallery feature.
            if (this._single) {
                this._initPopupGallery();
                return;
            }

            this._pager = this._createPager(this._data).insertAfter(this._wrapper);
            this._pagerItems = this._pager.children();

            // kick off the carousel

            this._initMainCarousel({
                    pagination: false
                },
                null,
                function (e) {
                    self._carouselAfterMove.call(this, e, self);
                });

            // should we use owl for navigation as well ?

            if (opts.useCarouselNav) {

                var navOptions = $.extend(true, opts.owlNavOptions, {
                    afterInit: function (e) {
                        self._carouselAfterInit.call(this, e, self);
                    }
                });

                this._owlNav = this._pager.owlCarousel(navOptions);
                this._owlNavInstance = this._getOwlInstance(this._owlNav);

            }

            // bind events on main carousel and navigation

            this._unbindEvent();
            this._bindEventsDesktop();

            // create the popup gallery

            this._initPopupGallery();

        },

        /**
         * Initializes the component for mobile, taking into account that it could have been in desktop mode before
         */
        _initMobile: function () {

            // clean up

            this._unbindEvent();
            this._destroyPager();
            this._destroyPopupGallery();

            // init main carousel for mobile

            this._initMainCarousel({
                pagination: true
            });

            // (re)bind events

            this._bindEventsMobile();

        },

        /**
         * Initializes or updates the main carousel.
         * @param {object} carouselOptionOverrides  An object with custom options to use when instanciating Owl.
         * @param {Function} afterInit              A callback to execute in the afterInit of owl carousel.
         * @param {Function} afterMove              A callback to execute in the afterMove of owl carousel.
         */
        _initMainCarousel: function (carouselOptionOverrides, afterInit, afterMove) {

            var self = this;

            // double extend ^^':
            // - base options with overrides
            // - our necessary options

            var carouselOptions = $.extend(true, $.extend(true, self.options.owlOptions, carouselOptionOverrides), {
                afterInit: function (e) {
                    self._carouselAfterInit.call(this, e, self);
                    self._getSliderPageNumbers(this, e);
                    if ($.isFunction(afterInit)) {
                        afterInit.call(this, e);
                    }
                },
                afterMove: function (e) {
                    self._getSliderPageNumbers(this, e);
                    if ($.isFunction(afterMove)) {
                        afterMove.call(this, e);
                    }
                }
            });

            // if it was already initialized, just refresh, other create a new instance

            if (this._owlInstance) {
                this._owlInstance.reinit(carouselOptions);
            } else {
                this._owl = this._wrapper.owlCarousel(carouselOptions);
            }

            this._owlInstance = this._getOwlInstance(this._owl);

        },

        /**
         * Cleans up the main carousel
         */
        _destroyMainCarousel: function () {

            if (this._owlInstance) {
                this._owlInstance.destroy();
                delete this._owlInstance;
            }

            if (this._owl) {
                delete this._owl;
            }

        },

        /**
         * uses magnific popup to create a large image gallery
         * creates thumbnails, bind click handlers and keyboard states
         */
        _initPopupGallery: function () {

            var self = this;

            // if no popup gallery should be create, disable links clicking
            if (!self.options.usePopupGallery) {
                $('.' + self.options.classes.carouselLink).on('click', function (e) {
                    e.preventDefault();
                });
                return;
            }

            var galleryPager,
                galleryPagerItems,
                galleryInstance;

            // Event handler for "beforeOpen" event
            //
            var onGalleryBeforeOpen = function () {

                if (!self._single) {

                    //  add in the current selected link to the data
                    for (var i = 0; i < self._pagerItems.length; i++) {
                        self._data[i].current = $(self._pagerItems[i]).hasClass(self.options.classes.currentItem);
                    }

                    //  build pager and store instance in method variable above
                    galleryPager = self._createPager(self._data);
                    galleryPagerItems = galleryPager.children();

                }

                galleryInstance = $.magnificPopup.instance;

            };

            // Event handler for "change" event
            // Updates links and broadcast an event to update main thumbnails
            var onGalleryChange = function (e) {

                if (self._single) return;

                var newIndex = e.index;

                self._markPagerCurrentItem(galleryPagerItems, newIndex);
                self._syncCarousel(newIndex, false);
                self._syncPagination(newIndex, false);

            };

            // Event handler for "open" event
            // Adds the gallery pager to the gallery
            var onGalleryOpen = function () {

                // first open and add the pager element
                galleryInstance
                    .content
                    .append(galleryPager)
                    .on('click', '.' + self.options.classes.carouselItem, function (e) {
                        e.preventDefault();
                        var index = galleryPagerItems.index(e.currentTarget);
                        galleryInstance.goTo(index);
                    });

                // add some extra wrapper for styling purposes
                galleryPager.wrap(self.options.templates.galleryPagerWrap);

            };

            // Event handler for "afterClose" event
            // return focus to slideshow if options require it
            var onGalleryAfterClose = function () {

                if (!self.options.returnFocus) return;

                self._wrapper
                    .prop('tabindex', 0)
                    .focus();

            };

            // gallery options
            var galleryOptions = {
                closeMarkup: self.options.templates.galleryCloseButton,
                delegate: '.' + self.options.classes.carouselLink,
                type: 'image',
                tLoading: 'Loading image #%curr%...',
                mainClass: 'product-popup',
                gallery: {
                    enabled: true,
                    arrowMarkup: self.options.templates.galleryArrowMarkup,
                    preload: [0, 1]
                },
                callbacks: {
                    beforeOpen: onGalleryBeforeOpen,
                    change: onGalleryChange,
                    open: onGalleryOpen,
                    afterClose: onGalleryAfterClose
                }

            };

            // here we go, create the gallery
            this.element.magnificPopup(galleryOptions);

        },

        /**
         * Cleans up the magnific popup gallery
         */
        _destroyPopupGallery: function () {

            if (!this.options.usePopupGallery) {
                return;
            }

            this.element
                .magnificPopup('close')
                .off('click.magnificPopup')
                .removeData('magnificPopup');

        },

        /**
         * Renders out the pager element for the main thumbs list and the popup thumbnails.
         *
         * @param {object} data  A plain object with image data. Ex: { img: '...', alt: '' }
         * @returns {jQuery}
         */
        _createPager: function (data) {

            var result = Mustache.render(this.options.templates.pager, {
                data: data
            });

            return $(result);

        },

        /**
         * Cleans up the pager
         */
        _destroyPager: function () {

            if (this.options.useCarouselNav && this._owlNavInstance) {
                this._owlNavInstance.destroy();
                delete this._owlNavInstance;
            }

            if (this._pager) {
                this._pager.remove();
                delete this._pagerItems;
            }

        },

        /**
         * Binds events to the widget when initializing for desktop
         */
        _bindEventsDesktop: function () {

            var events = {}, eventOptions = this.options.events;

            events[eventOptions.clickCarouselItem] = this._onNavThumbClick;
            events[eventOptions.keydownCarouselContainer] = this._onKeydownCarousel;
            events[eventOptions.keydownCarouselItem] = this._onKeydownPager;

            events[eventOptions.clickOwlButton] = function (e) {
                $(e.target).prop('tabIndex', -1).focus();
            };

            this._on(this.element, events);

        },

        /**
         * Binds events to the widget when initializing for mobile
         */
        _bindEventsMobile: function () {

            var events = {}, eventOptions = this.options.events;

            events[eventOptions.keydownCarouselContainer] = this._onKeydownCarousel;
            events[eventOptions.clickCarouselLink] = function (e) {
                e.preventDefault();
            };
            events[eventOptions.customOwlNavLeft] = this._onOwlNavigateLeft;
            events[eventOptions.customOwlNavRight] = this._onOwlNavigateRight;

            this._on(this.element, events);

        },

        /**
         * Unbinds all events of the widget
         */
        _unbindEvent: function () {

            this._off(this.element, 'click keydown');

        },

        /**
         * Retrieves data from EC.thumbnails if provided or generates data from markup
         *
         * @returns {array}
         */
        _getThumbnailsData: function () {

            var data = [],
                opts = this.options;

            data = opts.thumbData === false && typeof EC != 'undefined' && typeof EC.thumbnails != 'undefined'
                ? EC.thumbnails
                : opts.thumbData;

            if (data.length > 0) {
                data[0].current = true;
            }

            return data;

        },

        /**
         * Handler executed when owl is initialized.
         *
         * @param {jQuery.Event}  e       The jQuery event object.
         * @param {jQuery.Widget} widget  The current widget instance.
         *
         * @this  Owl instance
         */
        _carouselAfterInit: function (e, widget) {

            var tmpls = widget.options.templates;

            this.buttonPrev.append(tmpls.iconLeft);
            this.buttonNext.append(tmpls.iconRight);

        },

        /**
         * Handler executed when owl is navigated.
         *
         * @param {jQuery.Event}  e       The jQuery event object.
         * @param {jQuery.Widget} widget  The current widget instance.
         *
         * @this  Owl instance
         */
        _carouselAfterMove: function (e, widget) {

            var owl = this.owl;
            var items = owl.userItems;

            // tinker with the tabindex and focusing
            items.prop('tabIndex', -1);

            widget._delay(function () {
                items.eq(owl.currentItem).prop('tabindex', 0).focus();
            }, owl.userOptions.slideSpeed);

            // update navigation
            widget._syncPagination(owl.currentItem);

        },

        /**
         * Event handler when a navigation thumbnail is clicked.
         *
         * @param {jQuery.Event} e  The jQuery event object.
         */
        _onNavThumbClick: function (e) {

            if (this._single) return;

            var newIndex = this._pagerItems.index(e.currentTarget);

            // switch "current" classes
            this._markPagerCurrentItem(this._pagerItems, newIndex);

            // move main carousel
            this._syncCarousel(newIndex);
        },

        /**
         * Returns the owl instance attached to the given element.
         *
         * @param {jQuery} $element  A jQuery-wrapped element on which the owl plugin is attached.
         * @returns {object}
         */
        _getOwlInstance: function ($element) {

            if (!$element || !$element.length) return null;
            return $element.data('owlCarousel');

        },

        /**
         * Switches the "current" css class on the pager elements.
         *
         * @param {jQuery} pagerItems  The pager items.
         * @param {number} index       The new index to sync the pagination.
         */
        _markPagerCurrentItem: function (pagerItems, index) {

            if (!pagerItems) {
                return;
            }

            pagerItems
                .removeClass(this.options.classes.currentItem)
                .eq(index).addClass(this.options.classes.currentItem);

        },

        /**
         * Syncs the main awl carousel to the given index.
         *
         * @param {number} index             The new index to sync the pagination.
         * @param {bool}   withoutAnimation  Indicates if we should jump to new indew or animate to it (when using owl).
         */
        _syncCarousel: function (index, withoutAnimation) {

            if (this._single) return;

            // move main carousel
            this._owlInstance[!!withoutAnimation ? "jumpTo" : "goTo"](index);

        },

        /**
         * Sync the pager to the given index.
         *
         * @param {number} index             The new index to sync the pagination.
         * @param {bool}   withoutAnimation  Indicates if we should jump to new indew or animate to it (when using owl).
         */
        _syncPagination: function (index, withoutAnimation) {

            if (this._single) return;

            // switch "current" classes
            this._markPagerCurrentItem(this._pagerItems, index);

            // move owl navigation
            if (this.options.useCarouselNav) {

                var visibleItems = this._owlNavInstance.visibleItems,
                    goToIndex = false,
                    found = false;

                for (var i in visibleItems) {
                    if (index === visibleItems[i]) {
                        found = true;
                    }
                }

                if (found === false) {
                    if (index > visibleItems[visibleItems.length - 1]) {
                        goToIndex = index - visibleItems.length + 2;
                    } else {
                        if (index - 1 === -1) {
                            goToIndex = 0;
                        }
                        this._owlNavInstance.goTo(index);
                    }
                } else if (index === visibleItems[visibleItems.length - 1]) {
                    goToIndex = visibleItems[1];
                } else if (index === visibleItems[0]) {
                    goToIndex = index - 1;
                }

                if (goToIndex !== false) {
                    this._owlNavInstance[!!withoutAnimation ? "jumpTo" : "goTo"](goToIndex);
                }
            }
        },

        /**
         * Keydown handlers for the main carousel.
         * Handles navigating left and right.
         *
         * @param {jQuery.Event} e  The jQuery event object.
         */
        _onKeydownCarousel: function (e) {

            var keyCode = e.keyCode,
                owl = this._owlInstance;

            switch (keyCode) {
                case 39: //keyCode.RIGHT:
                case 40: //keyCode.DOWN:
                    owl.next();
                    break;
                case 37: //:keyCode.LEFT:
                case 38: //keyCode.UP:
                    owl.prev();
                    break;
            }

        },

        /**
         * Keydown handler for the pager.
         * Handles tabindex and focus change as well as image switching.
         *
         * @param {jQuery.Event} e  The jQuery event object.
         */
        _onKeydownPager: function (e) {

            var self = this;

            if (e.altKey || e.ctrlKey) {
                return;
            }

            var keyCode = e.keyCode,
                pagerItems = self._pagerItems,
                len = pagerItems.length,
                currentTarget = e.currentTarget,
                newIndex = pagerItems.index(currentTarget),
                toFocus = false;

            switch (keyCode) {
                case 39: //keyCode.RIGHT:
                case 40: //keyCode.DOWN:
                    toFocus = pagerItems[(newIndex + 1) % len];
                    break;
                case 37: //keyCode.LEFT:
                case 38: //keyCode.UP:
                    toFocus = pagerItems[(newIndex - 1 + len) % len];
                    break;
                case 32: //keyCode.SPACE:
                case 13: //keyCode.ENTER:
                    toFocus = pagerItems[newIndex];
                    self._syncPagination(newIndex);
                    self._syncCarousel(newIndex);
                    e.preventDefault();
                    break;
                case 36: //keyCode.HOME:
                    toFocus = pagerItems[0];
                    break;
                case 35: //keyCode.END:
                    toFocus = pagerItems[len - 1];
                    break;
            }

            if (!toFocus) return;

            $(currentTarget).attr('tabIndex', -1);
            $(toFocus).attr('tabIndex', 0).focus();

            e.preventDefault();
        },

        /**
         * Numeric Pagination.
         * @param {Object} owlobject  The instance of the owl carousel.
         * @param {jQuery} selector
         */
        _getSliderPageNumbers: function (owlobject, selector) {
            var self = this;
            var Owl = owlobject,
                currentslide = Owl.currentItem,
                owlitems = Owl.options.items,
                owltotitems = Owl.itemsAmount,
                totSlides = Math.ceil(owltotitems / owlitems),
                currentPageNr = Math.ceil(currentslide / owlitems) + 1,
                visibleItems = Owl.visibleItems;

            if (currentPageNr < 1) {
                currentPageNr = 1;
            }
            selector.closest('.js-paged-slider').find('.js-currentpage').html(currentPageNr);
            selector.closest('.js-paged-slider').find('.js-totalpages').html(totSlides);
        },

        _onOwlNavigateLeft: function (e) {
            var owl = this._owlInstance;
            owl.prev();
        },
        _onOwlNavigateRight: function (e) {
            var owl = this._owlInstance;
            owl.next();
        },

        /**
         * Cleaning up.
         */
        _destroy: function () {

            if (this._switchBetweenDesktopAndMobileHandler) {
                EC.Mq.off(EC.Mq.tabletMin(), this._switchBetweenDesktopAndMobileHandler);
            }

            this._destroyPopupGallery();
            this._destroyPager();
            this._destroyMainCarousel();

        },

    });

})(jQuery, Mustache);
;
/**
 * @fileOverview A jQuery UI Widget to handle public / private toggle radio buttons stuff
 * @author Joel Mitchell
 * @name $.cx.public_private
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function($) {
    'use strict';

    $.widget('cx.public_private', {
        options: {
            'radioClass': 'form__field--radio',
            'labels': ['Submit', 'Cancel'],
            'buttonClass': '.js-visibility-submit'
        },

        widgetEventPrefix: 'public_private_',

        /**
         * Standard jQuery UI create function
         */
        _create: function() {
            var self = this;
            this._status = this.element.data('visibility');
            this._labels = this.element.data('visibility-labels').split(',') || this.options.labels;
            this._submit = this.element.find(this.options.buttonClass);
            this._radios = this.element.find('.' + this.options.radioClass);

            //	bind clicks and keyboard events
            this._on((function() {
                var events = {};
                events['click .' + self.options.radioClass] = self._testValue;
                events['keydown .' + self.options.radioClass] = self._keyboardHandler;
                return events;
            })());

            // 	test the thing on first load to make sure the button state is correct
            this._testValue();

        },


        //  filter the radio buttons / return the checked radio value and compare it
        // 	to the data- attribute on the component (which will get updated when it changes state)
        _testValue: function() {
            var val = this._radios.filter(':checked').data('visibility-state');

            //	if the radio value is the same then swap the text and
            // 	prevent the submitting of the button, instead closing the overlay
            if (val === this._status) {
                this._submit
                    .text(this._labels[1])
                    .on('click.submit', function(event) {
                        event.preventDefault();
                        $.magnificPopup.instance.close();
                    });

            } else {
                this._submit
                    .text(this._labels[0])
                    .off('click.submit');
            }

        },

        //  keyboardz - handling
        _keyboardHandler: function(e) {
            if (e.keyCode === 13) {
                e.preventDefault();
                this._testValue();
            }

        },


        /**
         * Break it down again.
         */
        _destroy: function() {


        }

    });

})(jQuery);
;
/**
 * @fileOverview A jQuery widget to toggle-fade between 2 different contents
 * @author Riccardo Farina, Alessandro Saitta
 * @name $.cx.fade
 * @dependencies: jQuery, jQuery UI widget factory, jQuery tinypubsub
 */

/**
 * Tabs
 */
(function($) {

    'use strict';

    $.widget('cx.fadeContentToggle', {

        options: {
            toggleSpeed: "normal",
            selectors: {
                selectorClass: '.js-fade',
                wrapperClass: '.js-fade-wrapper',
                targetClass: '.js-fade-target',
                responsiveClass: '.js-fade--responsive'
            },
            classes: {
                fadingClass: "is-fading"
            },
            events: {
                clickTrigger: 'click .js-fade-trigger',
            }
        },

        /**
         * Standard jQuery UI create function
         */
        _create: function() {

            var events = {};
            var self = this;
            var eventDefinitions = self.options.events;
            var selectors = self.options.selectors;
            var classes = self.options.classes;


            events[eventDefinitions.clickTrigger] = self.fadeThisContent;
            self._on(events);

            $(selectors.selectorClass).each(function() {
                $(this).find(selectors.targetClass).eq(0).show();
                $(this).find(selectors.targetClass).eq(1).hide();
            });

            //responsive class has inverse logic on mobile
            self._onMatchingTabletMaxHandler = function(matches) {
                $(selectors.responsiveClass).each(function() {
                    $(this).find(selectors.targetClass).eq(1).show();
                    $(this).find(selectors.targetClass).eq(0).hide();
                });
            };

            EC.Mq.on(EC.Mq.tabletMax(), self._onMatchingTabletMaxHandler, false);
        },

        fadeThisContent: function(e) {

            var self = this,
                opts = self.options,
                selectors = opts.selectors,
                classes = opts.classes;


            var wrapper;
            if (e) {
                wrapper = $(e.currentTarget);
            } else {
                wrapper = $(this.element);
            }

            var activeSelector = $(wrapper).closest(selectors.selectorClass);
            var target = activeSelector.find(selectors.targetClass); //find the children of the active selector and set them as targets
            var activeWrapper = activeSelector.find(selectors.wrapperClass); //find the child of the active selector and set it as wrapper

            var oldHeight = activeWrapper.height(); //measure container current height (before toggling)
            target.filter(':visible').addClass(classes.fadingClass); //add animation class to the target fading out element
            target.fadeToggle(opts.toggleSpeed, function() { //toggle both target elements
                target.removeClass(classes.fadingClass); //remove animation class
            });
            var newHeight = activeWrapper.height(); //measure current container height (after toggling)
            activeWrapper.height(oldHeight); //revert the container height to the previous one
            activeWrapper.animate({ height: newHeight }, opts.toggleSpeed, function() { //animate smoothly from old height to new height
                activeWrapper.height('auto'); //revert container height on auto
            });
        }
    });

})(jQuery);;
/**
 * @socialshare A jQuery UI Widget to share and get shared counters from social media
* @author Riccardo Farina, Alessandro Saitta, Riccardo Moschetti
 * @name $.cx.socialshare
 * @dependencies: jQuery, jQuery UI widget factory
 */

(function($) {
    'use strict';

    $.widget('cx.socialshare', {

        options: {
            permalink: window.location.href,
            twitterTitle: '',
            twitterTitlePrefix: '&text=',
            twitterHashtags: '',
            hashtagPrefix: '&hashtags=',

            targets: {
              facebook: '.js-facebook-count',
              twitter: '.js-twitter-count',
              google: '.js-google-count',
                whatsapp:'.js-whatsapp-count'
            },
          	selectors: {
            	facebook: '.js-facebook-share',
            	twitter: '.js-twitter-share',
            	google: '.js-google-share',
                whatsapp:'.js-whatsapp-share'
            },
            strings: {
            	thousands: "<span class='suffix'>k</span>",
            	millions: "<span class='suffix'>M</span>",
            	zero: "<span class='zero'>0</span>"
            },
            APIurl: {
            	facebook: "//www.facebook.com/sharer.php?u=",
            	twitter: "//twitter.com/share?url=",
            	google: "//plus.google.com/share?url=",
                whatsapp:"whatsapp://send?text="
            }
        },

        /**
		 * Constructor
		 	*/

        _create: function () {
	 		var events = {};
            var targets = this.options.targets;
            var selectors = this.options.selectors;
            var APIurl = this.options.APIurl;
            var permalink = encodeURIComponent(this.options.permalink);
            var hashtagPrefix = this.options.hashtagPrefix;
            var twitterHashtags = this.options.twitterHashtags;
            var twitterTitle = this.options.twitterTitle;
            var twitterTitlePrefix = this.options.twitterTitlePrefix;

            $.each(APIurl, function(key,value) {
            	//apis = { }
            	//	"_api" + key + "Url" = value + permalink;
            	//	}
						});

            this._apiFacebookUrl = APIurl.facebook + permalink;
            this._apiTwitterUrl = APIurl.twitter + permalink + hashtagPrefix + twitterHashtags + twitterTitlePrefix + twitterTitle;
						this._apiGoogleUrl = APIurl.google + permalink;
            this._apiWhatsappUrl = APIurl.whatsapp + permalink;
            			$.each(selectors, function(key,value) {
								var functionName = "_share" + key;
								events['click' + this] = functionName;
						});
 			//events['click' + selectors.facebook ] = this._shareFacebook;
	          //events['click' + selectors.twitter] = this._shareTwitter;
	          //events['click' + selectors.google] = this._shareGoogle;
	          this._on(events);

						this._getSocialCount();
        },

        _getSocialCount: function () {

        	var self = this;
        	var opts = self.options;
        	var targets = opts.targets;
        	var strings = opts.strings;
        	var permalink = opts.permalink;

        		//temporary blank to create the headspace (ext APIs can take few seconds to give back a number)
	          $.each(targets, function() {
	          	$(this).html("&nbsp;");
	        	});

	        var encodedPerma;
            try {
                encodedPerma = encodeURIComponent(permalink);
            } catch (err) {
                encodedPerma = "";
            }
           
            $.getJSON('https://api.facebook.com/method/links.getStats?format=json&urls=' + encodedPerma, function (data) {
                var shareCount = 0;
	              try {
	                  shareCount = data[0].share_count;
	              } catch (err) {
	                  shareCount = 0;
	              }
	              $(targets.facebook).html(formatCount(shareCount));
            });

            //uses opensharecount.com to bypass twitter's lack of count API. all additional domains need to be whitelisted on their website
            //@todo: find a way to extrapolate the data directly, perhaps performing the search query ourselves?
            $.getJSON('https://opensharecount.com/count.json?url=' + permalink, function (data) {
                
	            var shareCount = 0;
                try {
                    shareCount = data.count;
                } catch (err) {
                    shareCount = 0;
                }
                $(targets.twitter).html(formatCount(shareCount));
            });

            var gPlusInputData = '[{"method":"pos.plusones.get","id":"p","params":{"nolog":true,"id":"' + permalink + '","source":"widget","userId":"@viewer","groupId":"@self"},"jsonrpc":"2.0","key":"p","apiVersion":"v1"}]';
            $.ajax({
                url: 'https://clients6.google.com/rpc',
                type: 'POST',
                dataType: 'json',
                contentType: 'application/json',
                processData: false,
                data: gPlusInputData,
                success: function (gPlusReturnData) {
                    var shareCount = gPlusReturnData[0].result.metadata.globalCounts.count;
                    $(targets.google).html(formatCount(shareCount));
                },
                error: function () {
                    $(targets.google).html(formatCount("0"));
                }
            });
            
            var formatCount = function (shareCount) {
                if (shareCount == undefined) {
                	shareCount = 0;
                }
	            	if (shareCount < 1) {
	            		shareCount = strings.zero;
	            	}
                if (shareCount > 1000) {
                    shareCount = (shareCount / 1000).toFixed(0);
                    if (shareCount > 1000) shareCount = (shareCount / 1000).toFixed(0) + strings.millions;
                    else shareCount = shareCount + strings.thousands;
                }
                return shareCount;
            }
        },


        _sharefacebook: function (e) {
					e.preventDefault();
        	window.open(this._apiFacebookUrl, '_blank');
        },

        _sharetwitter: function (e) {
					e.preventDefault();
        	window.open(this._apiTwitterUrl, '_blank');
        },

        _sharegoogle: function (e) {
        	e.preventDefault();
        	window.open(this._apiGoogleUrl, '_blank');
        },
         _sharewhatsapp: function (e) {
        	//e.preventDefault();
        	window.open(this._apiWhatsappUrl);
        }

    });


    })(jQuery);
;
/**
 * @truncate A jQuery UI Widget to truncate texts at given lenghts
 * @author Riccardo Farina
 * @name $.cx.truncate
 * @dependencies: jQuery, jQuery UI widget factory
 */


 /*global jQuery*/
(function($) {
    'use strict';

    $.widget('cx.truncate', {
        options: {
            omission: '&hellip;'
        },

        /**
         * Standard jQuery UI create function
         */
        _create: function() {
            var self = this;
            if (this.element.data("chars") > 0) {
                self._truncateChars();
            }
            
            if (this.element.data("lines") > 0) {
                self._truncateLines();
            }
        },

		_truncateChars: function() {
			var self = this;
            var htmlElement = this.element[0];
            var charsNumber = $(htmlElement).data("chars") - 1;
			self._charsNumber = charsNumber;
			self._truncatedTextLength = 0;
			
			var truncatedText = "";
			var truncatedTextLength = 0;
			if($(htmlElement).text().trim().length > charsNumber){
				var childNodes = htmlElement.childNodes;
				var parentContainer = htmlElement;
				self._partialExtracts = [];

				self._recursiveInspection(childNodes, 0);
				self._replaceText(htmlElement);
			};
        },
		
		_replaceText: function(htmlElement){
			var self = this;
			$(htmlElement).html('');
				
			var htmlToInsert = "";
			for (var i = 0; i < self._partialExtracts.length; i++) {
				var container = self._partialExtracts[i].container;
				var content = self._partialExtracts[i].content;
				var level = self._partialExtracts[i].level;
				if (level === 0){
					htmlToInsert += content;
					
					$(htmlElement).append(htmlToInsert);
					htmlToInsert = "";
				}
				else{
					var temp = $(container).html(content);
					if (self._partialExtracts[i-1].level == level -1){
						var html = $(htmlToInsert).html();
						if (html != null){
							htmlToInsert = $(htmlToInsert).html(html + temp[0].outerHTML)[0].outerHTML;
						}
						else{
							htmlToInsert += temp[0].outerHTML;
						}
					}
					else{
						var html = $(htmlToInsert).html();
						htmlToInsert = $(htmlToInsert).html(html + temp[0].innerHTML)[0].outerHTML
					}
				}
			}
			
			if (htmlToInsert != ""){
				$(htmlElement).append(htmlToInsert);
			}
		},
				
		_recursiveInspection: function(childNodes, level){
			var self = this;
			$(childNodes).each(function(){		
				var	extract = "";
				if (this.nodeType == 3) {//it means the node is text	
					extract = self._extractTextFromNode(this, self._truncatedTextLength);
					if(extract == null){return false;}
					self._truncatedTextLength += extract.nodeLength;
					
					self._partialExtracts.push({ 
						container: this.parentNode,
						content: extract.nodeText,
						level: level
					});
				}
				else{//has childNodes so recall itself
					self._recursiveInspection(this.childNodes, level + 1);
				}
			});
		},
		
		_extractTextFromNode: function(node){
			var self = this;
			var nodeValue = node.nodeValue.replace(/[\n\r]/g, '');

			if(self._truncatedTextLength + nodeValue.length <= self._charsNumber) //get the whole text
			{					
				return { 
					nodeText: nodeValue, 
					nodeLength: nodeValue.length
				}
			}
			else{ //cut the text		
				var charsToTake = self._charsNumber - self._truncatedTextLength;
				if(charsToTake > 0){
					var txtSubString = nodeValue.substring(0, charsToTake);
					return {
						nodeText: txtSubString + self.options.omission,
						nodeLength: txtSubString.length
					};
				}
				else{
					return null;
				}
			}			
		},
		
        _truncateLines: function() {
            var options = this.options;
            var el = this.element;

            el.trunk8({
                lines: this.element.data("lines"),
                fill: options.omission
            });
        }
    });

})(jQuery);





;
!function(a){function b(a){d=a.originalEvent.touches[0].clientX,e=a.originalEvent.touches[0].clientY}function c(b){if(d&&e){var c=b.originalEvent.touches[0].clientX,f=b.originalEvent.touches[0].clientY,g=d-c,h=e-f;Math.abs(g)>Math.abs(h)&&g>0&&b.originalEvent.path.forEach(function(b){a(".mobile-menuopen-overlay").trigger("swipeleft")}),d=null,e=null}}if(a("html").hasClass("touchevents")){var d=null,e=null;a("body").on("touchstart",b),a("body").on("touchmove",c)}}(window.jQuery);;
jQuery.cachedScript=function(a,b){return b=$.extend(b||{},{dataType:"script",cache:!0,url:a}),jQuery.ajax(b)},function(a){"use strict";a.widget("ec.dateSelect",{options:{regional:a.datepicker.regional.nl,changeMonth:!0,changeYear:!0,constrainInput:!0,duration:100,minDate:null,numberOfMonths:1,showAnim:"show",defaultDate:"+lw",culture:"nl-be",dateFormat:"dd-mm-yy"},_create:function(){var a=this.element;return console.log(a),!a.hasClass("has-datepicker")&&void a.addClass("has-datepicker")},_init:function(){var a=this.element;this.settings=a.datepicker(this.options),this._subscribeToEvents(),this._setOptions(a),a.trigger("initialised")},_setCulture:function(b){var c,d=this;b=b||"nl-be",c="pt-br"===b.toLowerCase()||"nl-be"===b.toLowerCase()?b:b.split("-")[0],a.datepicker.regional[c]?this.settings.datepicker("option",a.datepicker.regional[c]):a.cachedScript("https://cdn.euroconsumers.org/vendor/jquery/jquery-ui/1.12.0-pre/ui/i18n/datepicker-"+c+".js").then(function(b,e,f){d.settings.datepicker("option",a.datepicker.regional[c])},function(a,b,c){console.error(b,c,a)}),this.element[0]&&void 0!==this.element.attr("value")&&this.settings.datepicker("setDate",this.element.attr("value"))},_setOptions:function(a){var b=this;this.settings.datepicker("option",{minDate:a[0]?a.attr("min"):b.options.minDate,maxDate:a[0]?a.attr("max"):b.options.maxDate,onSelect:function(c){b._onSelect(a,c)}}),a[0]&&this.settings.datepicker("option",{yearRange:a.attr("year-range")||this.options.yearRange})},_subscribeToEvents:function(){var b=this;a.subscribe("CultureChanged",function(a,c){b._setCulture(c)}),a(document).ready(function(){b._setCulture(a("html").attr("lang"))}),a(this.element).trigger("initialised")},_onSelect:function(b,c){a.publish("dateChanged",{el:b,date:c}),a(this.element).trigger("dateChanged",{el:b,date:c}),a(b).attr("value",c);var d=b.data("rel"),e=b.data("rel-pos");d&&("from"==e?a(d).datepicker("option","minDate",c):"to"==e&&a(d).datepicker("option","maxDate",c))}})}(jQuery);;
!function(a){"use strict";a.widget("ec.recommend",{options:{additionalHolder:".recommended__carousel-item__additional",additionalItem:".recommended__carousel-item__additional__item",expandButton:".js-recommend-expand",itemsNormalized:!1},_create:function(){this.element.addClass("has-plugin"),this.$expandButton=this.element.find(this.options.expandButton),this.options.hasOwnProperty("labelContracted")||(this.options.labelContracted="+ More"),this.options.hasOwnProperty("labelExpanded")||(this.options.labelExpanded="- Less"),this.$expandButton.find("span").html(this.options.labelContracted),this._bindEvents(),a(".js-equal-heights").equal_heights()},_bindEvents:function(){var a=this,b=this.element.find(this.options.expandButton);a._on(b,{click:function(b){b.preventDefault(),a._handleExpand()}})},_handleExpand:function(){var b=this;this.$expandButton.hasClass("isExpanded")?(this.contract(),a("html, body").animate({scrollTop:b.element.offset().top})):(this.element.find(b.options.additionalItem).slideDown(300,function(){b.options.itemsNormalized||(a(".js-equal-heights").equal_heights(),b.options.itemsNormalized=!0)}),this.$expandButton.addClass("isExpanded").find("span").html(this.options.labelExpanded),this.element.addClass("is-expanded"))},contract:function(){var b=this;a(this.options.additionalHolder).each(function(){a(this).find(b.options.additionalItem).not(":first").slideUp()}),this.$expandButton.removeClass("isExpanded").find("span").html(this.options.labelContracted),this.element.removeClass("is-expanded")}})}(jQuery);;
!function(a){"use strict";a.widget("ec.owlCarousel",{options:{items:5,itemsDesktop:[1199,4],itemsDesktopSmall:[979,3],itemsTablet:[768,2],itemsTabletSmall:!1,itemsMobile:[479,1],itemsCustom:!1,singleItem:!1,itemsScaleUp:!1,itemElement:!1,slideSpeed:200,paginationSpeed:800,rewindSpeed:1e3,autoPlay:!1,stopOnHover:!1,navigation:!1,navigationText:["prev","next"],rewindNav:!0,scrollPerPage:!1,pagination:"default",paginationNumbers:!1,responsive:!0,responsiveRefreshRate:200,responsiveBaseWidth:window,ofString:"of",tabString:"Item",baseClass:"owl-carousel",theme:"owl-theme",lazyLoad:!1,lazyFollow:!0,lazyEffect:"fade",autoHeight:!1,jsonPath:!1,jsonSuccess:!1,dragBeforeAnimFinish:!0,mouseDrag:!0,touchDrag:!0,addClassActive:!1,desktopOnly:!1,transitionStyle:!1,photoGallery:!1,hasModal:!1,slave:!1,master:!1,beforeUpdate:!1,afterUpdate:!1,beforeInit:!1,afterInit:!1,beforeMove:!1,afterMove:!1,afterAction:!1,startDragging:!1,afterLazyLoad:!1},_create:function(){this.element.addClass("has-plugin"),this.options.random=Math.round(1e4*Math.random()),this.$elem=a(this.element),this.$elem.attr("id")?this.options.customId=this.$elem.attr("id"):this.$elem.attr("id",this.options.random+"carousel"),this._loadContent(),this.$elem.trigger("initialised")},_loadContent:function(){function b(a){var b,c="";if("function"==typeof d.options.jsonSuccess)d.options.jsonSuccess.apply(this,[a]);else{for(b in a.owl)a.owl.hasOwnProperty(b)&&(c+=a.owl[b].item);d.$elem.html(c)}d._logIn()}var c,d=this;"function"==typeof d.options.beforeInit&&d.options.beforeInit.apply(this,[d.$elem]),"string"==typeof d.options.jsonPath?(c=d.options.jsonPath,a.getJSON(c,b)):d._logIn()},_logIn:function(){var b=this;b.$elem.data({"owl-originalStyles":b.$elem.attr("style"),"owl-originalClasses":b.$elem.attr("class")}),b.$elem.css({opacity:0}),b.originalItems=b.options.items,b._checkBrowser(),b.options.desktopOnly&&b.browser.isMobile?(b.$elem.css({opacity:1}),a(window).resize(function(){b.$elem.owlCarousel(b.options),b._destroy()})):(a(window).off("resize",b._create),b.wrapperWidth=0,b.checkVisible=null,b._setVars())},_setVars:function(){var b=this;return 0!==b.$elem.children().length&&(b._baseClass(),b._eventTypes(),b.$elem.first().is("ul")?b.$userItems=a(b.$elem.first()).children():b.options.itemElement!==!1?b.$userItems=b.$elem.children(b.options.itemElement):b.$userItems=b.$elem.children(),"carousel"===b.options.photoGallery&&(b.options.slave="#"+b.options.random+"slave"),b.itemsAmount=b.$userItems.length,b._wrapItems(),b.$owlItems=b.$elem.find(".owl-item"),b.$owlwrapper=b.$elem.find(".owl-wrapper"),b.playDirection="next",b.prevItem=0,b.prevArr=[0],b.currentItem=0,b._customEvents(),void b._onStartup())},_onStartup:function(){var b=this;if(b._updateItems(),b._calculateAll(),b._buildControls(),"string"==typeof b.options.photoGallery&&b._buildGalleryControls(),b._updateControls(),b._response(),b._moveEvents(),b._stopOnHover(),b._owlStatus(),a(".js-dummyblock").length&&a(".js-dummyblock").remove(),b.options.singleItem===!0&&(b.options.items=b.originalItems=1,b.options.itemsCustom=!1,b.options.itemsDesktop=!1,b.options.itemsDesktopSmall=!1,b.options.itemsTablet=!1,b.options.itemsTabletSmall=!1,b.options.itemsMobile=!1),b.options.transitionStyle!==!1&&b._transitionTypes(b.options.transitionStyle),b.options.autoPlay===!0&&(b.options.autoPlay=5e3),b.play(),b.$elem.find(".owl-wrapper").css("display","block"),b.$elem.is(":visible")?b.$elem.css("opacity",1):b._watchVisibility(),b._onStartup=!1,b._eachMoveUpdate(),"function"==typeof b.options.afterInit&&b.options.afterInit.apply(this,[b.$elem]),"string"==typeof b.options.photoGallery&&b.options.hasModal===!1){var c=a('<div id="'+b.options.random+'modal" class="mfp-hide product-popup photogalery-fullscreen-popup">'),d=a('<div class="popup__inner popup--full-screen">'),e=a("<div>"),f=b.$userItems.clone();f.each(function(){a(this).find("img").css("max-height","64vh")}),e.append(f),d.append(e),c.append(d),a("body").append(c),b.options.hasModal=!0;var g=Object.assign({},b.options);g.master="#"+b.$elem.attr("id"),g.singleItem=!0,e.owlCarousel(g)}},_eachMoveUpdate:function(){var a=this;a.options.lazyLoad===!0&&a._lazyLoad(),a.options.autoHeight===!0&&a._autoHeight(),a._onVisibleItems(),"function"==typeof a.options.afterAction&&a.options.afterAction.apply(this,[a.$elem])},_updateVars:function(){var b=this;"function"==typeof b.options.beforeUpdate&&b.options.beforeUpdate.apply(this,[b.$elem]),b._checkBrowser(),b.options.desktopOnly&&b.browser.isMobile?(b._unWrap(),a(window).resize(function(){b.$elem.owlCarousel(b.options),b._destroy()})):(a(window).off("resize",b._create),b._watchVisibility(),b._updateItems(),b._calculateAll(),b._updatePosition(),b._updateControls(),b._eachMoveUpdate(),b.options.galleryControls&&b._buildGalleryControls(),"function"==typeof b.options.afterUpdate&&b.options.afterUpdate.apply(this,[b.$elem]))},_reload:function(){var a=this;window.setTimeout(function(){a._updateVars()},0)},_watchVisibility:function(){var a=this;return a.$elem.is(":visible")===!1&&(a.$elem.css({opacity:0}),window.clearInterval(a.autoPlayInterval),window.clearInterval(a._checkVisible),void(a._checkVisible=window.setInterval(function(){a.$elem.is(":visible")&&(a._reload(),a.$elem.animate({opacity:1},200),window.clearInterval(a._checkVisible))},500)))},_wrapItems:function(){var a=this;a.$userItems.wrapAll('<div class="owl-wrapper">').wrap('<div class="owl-item"></div>'),a.$elem.find(".owl-wrapper").wrap('<div class="owl-wrapper-outer">'),a.wrapperOuter=a.$elem.find(".owl-wrapper-outer"),a.$elem.css("display","block")},_baseClass:function(){var a=this,b=a.$elem.hasClass(a.options.baseClass),c=a.$elem.hasClass(a.options.theme);b||a.$elem.addClass(a.options.baseClass),c||a.$elem.addClass(a.options.theme)},_updateItems:function(){var b,c,d=this;if(d.options.responsive===!1)return!1;if(d.options.singleItem===!0)return d.options.items=d.originalItems=1,d.options.itemsCustom=!1,d.options.itemsDesktop=!1,d.options.itemsDesktopSmall=!1,d.options.itemsTablet=!1,d.options.itemsTabletSmall=!1,d.options.itemsMobile=!1,!1;if(b=a(d.options.responsiveBaseWidth).width(),b>(d.options.itemsDesktop[0]||d.originalItems)&&(d.options.items=d.originalItems),d.options.itemsCustom!==!1)for(d.options.itemsCustom.sort(function(a,b){return a[0]-b[0]}),c=0;c<d.options.itemsCustom.length;c++)d.options.itemsCustom[c][0]<=b&&(d.options.items=d.options.itemsCustom[c][1]);else b<=d.options.itemsDesktop[0]&&d.options.itemsDesktop!==!1&&(d.options.items=d.options.itemsDesktop[1]),b<=d.options.itemsDesktopSmall[0]&&d.options.itemsDesktopSmall!==!1&&(d.options.items=d.options.itemsDesktopSmall[1]),b<=d.options.itemsTablet[0]&&d.options.itemsTablet!==!1&&(d.options.items=d.options.itemsTablet[1]),b<=d.options.itemsTabletSmall[0]&&d.options.itemsTabletSmall!==!1&&(d.options.items=d.options.itemsTabletSmall[1]),b<=d.options.itemsMobile[0]&&d.options.itemsMobile!==!1&&(d.options.items=d.options.itemsMobile[1]);d.options.items>d.itemsAmount&&d.options.itemsScaleUp===!0&&(d.options.items=d.itemsAmount)},_response:function(){var b,c,d=this;return d.options.responsive===!0&&(c=a(window).width(),d.resizer=function(){a(window).width()!==c&&(d.options.autoPlay!==!1&&window.clearInterval(d.autoPlayInterval),window.clearTimeout(b),b=window.setTimeout(function(){c=a(window).width(),d._updateVars()},d.options.responsiveRefreshRate))},void a(window).resize(d.resizer))},_updatePosition:function(){var a=this;a.jumpTo(a.currentItem),a.options.autoPlay!==!1&&a._checkAp()},_appendItemsSizes:function(){var b=this,c=0,d=b.itemsAmount-b.options.items;b.$owlItems.each(function(e){var f=a(this);f.css({width:b.itemWidth}).data("owl-item",Number(e)),e%b.options.items!==0&&e!==d||e>d||(c+=1),f.data("owl-roundPages",c)})},_appendWrapperSizes:function(){var a=this,b=a.$owlItems.length*a.itemWidth;a.$owlwrapper.css({width:2*b,left:0}),a._appendItemsSizes()},_calculateAll:function(){var a=this;a._calculateWidth(),a._appendWrapperSizes(),a._loops(),a._max()},_calculateWidth:function(){var a=this;a.itemWidth=Math.round(a.$elem.width()/a.options.items)},_max:function(){var a=this,b=(a.itemsAmount*a.itemWidth-a.options.items*a.itemWidth)*-1;return a.options.items>a.itemsAmount?(a.maximumItem=0,b=0,a.maximumPixels=0):(a.maximumItem=a.itemsAmount-a.options.items,a.maximumPixels=b),b},_min:function(){return 0},_loops:function(){var b,c,d,e=this,f=0,g=0;for(e.positionsInArray=[0],e.pagesInArray=[],b=0;b<e.itemsAmount;b++)g+=e.itemWidth,e.positionsInArray.push(-g),e.options.scrollPerPage===!0&&(c=a(e.$owlItems[b]),d=c.data("owl-roundPages"),d!==f&&(e.pagesInArray[f]=e.positionsInArray[b],f=d))},_buildControls:function(){var b=this;b.options.navigation!==!0&&"string"!=typeof b.options.pagination||(b.owlControls=a('<div class="owl-controls">').toggleClass("clickable",!b.browser.isTouch).appendTo(b.$elem)),"string"==typeof b.options.pagination&&b._buildPagination(),b.options.navigation===!0&&b._buildButtons()},_buildButtons:function(){var b=this,c=a('<div class="owl-buttons">');b.owlControls.append(c),b.buttonPrev=a("<div>",{class:"owl-prev",html:b.options.navigationText[0]||""}),b.buttonNext=a("<div>",{class:"owl-next",html:b.options.navigationText[1]||""}),c.append(b.buttonPrev).append(b.buttonNext),c.on("touchstart.owlControls mousedown.owlControls",'div[class^="owl"]',function(a){a.preventDefault()}),c.on("touchend.owlControls mouseup.owlControls",'div[class^="owl"]',function(c){c.preventDefault(),a(this).hasClass("owl-next")?b.next():b.prev()})},_buildPagination:function(){var b=this;if("default"===b.options.pagination)b.paginationWrapper=a('<div class="owl-pagination">'),b.owlControls.append(b.paginationWrapper),b.paginationWrapper.on("touchend.owlControls mouseup.owlControls",".owl-page",function(c){c.preventDefault(),Number(a(this).data("owl-page"))!==b.currentItem&&b.goTo(Number(a(this).data("owl-page")),!0)});else if("tabs"===b.options.pagination){if(b.browser.isMobile===!1){b.paginationWrapper=a("<div>",{class:"related-content__category-list"});for(var c=a("<ul>",{style:"text-align: center"}),d=0;d<b.itemsAmount;d++){var e=a("<li>",{text:a(b.$owlItems[d].firstChild).data("title"),class:"related-content__category-list-item","data-item-index":d});0===d&&e.addClass("is-active"),e.on("click",function(){b.goTo(a(this).data("item-index")),b.paginationWrapper.find(".is-active").removeClass("is-active"),a(this).addClass("is-active")}),c.append(e),b.paginationWrapper.append(c)}}else{b.paginationWrapper=a("<div>",{class:"related-content__category-list--mobile"});var f=a("<div>",{class:"has-uniform"}),g=a("<span>",{text:a(b.$owlItems[0].firstChild).data("title")}),h=a("<select>",{class:"form__field--select"});for(d=0;d<b.itemsAmount;d++){var i=a("<option>",{text:a(b.$owlItems[d].firstChild).data("title"),value:d});h.append(i)}h.change(function(){b.goTo(this.value),g.text(a(b.$owlItems[this.value].firstChild).data("title"))}),f.append(g),f.append(h),b.paginationWrapper.append(f)}b.element.prepend(b.paginationWrapper)}else{b.paginationWrapper=a("<div>"),b.paginationWrapper2=a("<div>",{class:"navigators navigators--compact-right navigators--grey js-pull-actions"}),b.controlPrev=a("<span>",{class:"navigator navigator__nav-left owl-ctrl-prev"}),b.controlNext=a("<span>",{class:"navigator navigator__nav-right owl-ctrl-next"}),g=a("<span>"),b.controlCurrPage=a("<span>",{html:Math.ceil(b.currentItem/b.options.items)+1,id:b.options.random+"currPage"});var j=" "+this.options.ofString+" ";b.controlTotalPages=a("<span>",{html:Math.ceil(b.itemsAmount/b.options.items)}),g.append(b.controlCurrPage,j,b.controlTotalPages),b.paginationWrapper2.append(b.controlPrev,b.controlNext,g),b.paginationWrapper.append(b.paginationWrapper2),b.controlPrev.on("touchstart mousedown",function(a){a.preventDefault()}),b.controlPrev.on("touchend mouseup",function(a){a.preventDefault(),b.prev()}),b.controlNext.on("touchstart mousedown",function(a){a.preventDefault()}),b.controlNext.on("touchend mouseup",function(a){a.preventDefault(),b.next()}),"#"===b.options.pagination[0]?a(b.options.pagination).append(b.paginationWrapper):b.owlControls.append(b.paginationWrapper)}},_buildGalleryControls:function(){var b=this,c=a("body").find("#"+b.options.random+"modal");if(b.browser.isMobile)return b.galleryControls&&b.galleryControls.remove(),c.length&&c.remove(),"grid"===b.options.photoGallery?b.options.pagination="custom":"carousel"===b.options.photoGallery&&(b.options.pagination="default"),b._destroyControls(),b._buildControls(),!1;b.options.pagination=!1,b._destroyControls(),b._buildControls(),b.paginationWrapper&&b.paginationWrapper.remove(),b.galleryControls=a("<div>",{class:"carousel__pager carousel__items"});for(var d=0;d<b.itemsAmount;d++){var e=a("<div>",{class:"carousel__item__thumbnail carousel__item",style:"background-image:url('"+a(b.$owlItems[d]).find("img").data("owl-thumb")+"');","data-item-index":d});e.on("click",function(){b.goTo(a(this).data("item-index"))}),b.options.master||a(b.$owlItems[d]).attr("href","#"+b.options.random+"modal").modal(),b.galleryControls.append(e)}var f=b.$elem.parent();f.append(b.galleryControls),"carousel"===b.options.photoGallery&&b.galleryControls.owlCarousel({items:5,navigation:!0,navigationText:['<i class="icon-chevron2-left"></i>','<i class="icon-chevron2-right"></i>'],pagination:!1}),b._updateGallery()},_updatePagination:function(){var b,c,d,e,f,g=this,h=0;if("custom"===g.options.pagination)g.owlControls.find("#"+g.options.random+"currPage").html(Math.ceil(g.currentItem/g.options.items)+1);else if("#"===g.options.pagination[0])a(g.options.pagination).find("#"+g.options.random+"currPage").html(Math.ceil(g.currentItem/g.options.items)+1);else if("default"===g.options.pagination)for(g.paginationWrapper.html(""),b=g.itemsAmount-g.itemsAmount%g.options.items,d=0;d<g.itemsAmount;d++)d%g.options.items===0&&(h++,b===d&&(c=g.itemsAmount-g.options.items),e=a("<div>",{class:"owl-page"}),f=a("<span>",{text:g.options.paginationNumbers===!0?h:"",class:g.options.paginationNumbers===!0?"owl-numbers":""}),e.data("owl-page",b===d?c:d),e.data("owl-roundPages",h),e.append(f),g.paginationWrapper.append(e),g._checkPagination())},_checkPagination:function(){var b=this;return"string"==typeof b.options.photoGallery&&b._updateGallery(),b.options.pagination!==!1&&("custom"===b.options.pagination||"#"===b.options.pagination[0]?void b._updateControls():void b.paginationWrapper.find(".owl-page").each(function(){a(this).data("owl-roundPages")===a(b.$owlItems[b.currentItem]).data("owl-roundPages")&&(b.paginationWrapper.find(".owl-page").removeClass("active"),a(this).addClass("active"))}))},_checkNavigation:function(){var a=this;return a.options.navigation!==!1&&void(a.options.rewindNav===!1&&(0===a.currentItem&&0===a.maximumItem?(a.buttonPrev.addClass("disabled"),a.buttonNext.addClass("disabled")):0===a.currentItem&&0!==a.maximumItem?(a.buttonPrev.addClass("disabled"),a.buttonNext.removeClass("disabled")):a.currentItem===a.maximumItem?(a.buttonPrev.removeClass("disabled"),a.buttonNext.addClass("disabled")):0!==a.currentItem&&a.currentItem!==a.maximumItem&&(a.buttonPrev.removeClass("disabled"),a.buttonNext.removeClass("disabled"))))},_updateControls:function(){var a=this;a._updatePagination(),a._checkNavigation(),a.owlControls&&(a.$elem.hasClass("carousel__pager")?(a.owlControls.show(),a.options.items>=a.itemsAmount&&(a.buttonPrev.addClass("disabled"),a.buttonNext.addClass("disabled"))):a.options.items>=a.itemsAmount?a.owlControls.hide():a.owlControls.show())},_updateGallery:function(){var b=this;if(b.browser.isMobile)return!1;if(b.galleryControls){if(b.galleryControls.find(".carousel__item--current").removeClass("carousel__item--current"),"grid"===b.options.photoGallery)var c=b.galleryControls.children();else"carousel"===b.options.photoGallery&&(c=b.galleryControls.find(".carousel__item"));a(c[b.currentItem]).addClass("carousel__item--current")}},_destroyControls:function(){var a=this;a.owlControls&&a.owlControls.remove()},next:function(a){var b=this;if(b.isTransition)return!1;if(b.currentItem+=b.options.scrollPerPage===!0?b.options.items:1,b.currentItem>b.maximumItem+(b.options.scrollPerPage===!0?b.options.items-1:0)){if(b.options.rewindNav!==!0)return b.currentItem=b.maximumItem,!1;b.currentItem=0,a="rewind"}b.goTo(b.currentItem,a)},prev:function(a){var b=this;if(b.isTransition)return!1;if(b.options.scrollPerPage===!0&&b.currentItem>0&&b.currentItem<b.options.items?b.currentItem=0:b.currentItem-=b.options.scrollPerPage===!0?b.options.items:1,b.currentItem<0){if(b.options.rewindNav!==!0)return b.currentItem=0,!1;b.currentItem=b.maximumItem,a="rewind"}b.goTo(b.currentItem,a)},goTo:function(a,b,c){var d,e=this;return!e.isTransition&&(a!==e.owl.currentItem&&("function"==typeof e.options.beforeMove&&e.options.beforeMove.apply(this,[e.$elem]),a>=e.maximumItem?a=e.maximumItem:a<=0&&(a=0),e.currentItem=e.owl.currentItem=a,e.options.transitionStyle!==!1&&"drag"!==c&&1===e.options.items&&e.browser.support3d===!0?(e._swapSpeed(0),e.browser.support3d===!0?e._transition3d(e.positionsInArray[a]):e._css2slide(e.positionsInArray[a]),e._afterGo(),e._singleItemTransition(),!1):(d=e.positionsInArray[a],e.browser.support3d===!0?(e.isCss3Finish=!1,b===!0?(e._swapSpeed("paginationSpeed"),window.setTimeout(function(){e.isCss3Finish=!0},e.options.paginationSpeed)):"rewind"===b?(e._swapSpeed(e.options.rewindSpeed),window.setTimeout(function(){e.isCss3Finish=!0},e.options.rewindSpeed)):(e._swapSpeed("slideSpeed"),window.setTimeout(function(){e.isCss3Finish=!0},e.options.slideSpeed)),e._transition3d(d)):b===!0?e._css2slide(d,e.options.paginationSpeed):"rewind"===b?e._css2slide(d,e.options.rewindSpeed):e._css2slide(d,e.options.slideSpeed),void e._afterGo())))},jumpTo:function(a){var b=this;return"function"==typeof b.options.beforeMove&&b.options.beforeMove.apply(this,[b.$elem]),a!==b.owl.currentItem&&(a>=b.maximumItem||a===-1?a=b.maximumItem:a<=0&&(a=0),b._swapSpeed(0),b.browser.support3d===!0?b._transition3d(b.positionsInArray[a]):b._css2slide(b.positionsInArray[a],1),b.currentItem=b.owl.currentItem=a,void b._afterGo())},_afterGo:function(){var b=this;b.prevArr.push(b.currentItem),b.prevItem=b.owl.prevItem=b.prevArr[b.prevArr.length-2],b.prevArr.shift(),b.prevItem!==b.currentItem&&(b._checkPagination(),b._checkNavigation(),b._eachMoveUpdate(),b.options.autoPlay!==!1&&b._checkAp()),b.options.slave!==!1&&a(b.options.slave).owlCarousel("goTo",b.currentItem),b.options.master&&a(b.options.master).owlCarousel("jumpTo",b.currentItem),b.options.photoGallery!==!1&&a("#"+b.options.random+"modal").find(".owl-carousel").owlCarousel("jumpTo",b.currentItem),"function"==typeof b.options.afterMove&&b.prevItem!==b.currentItem&&b.options.afterMove.apply(this,[b.$elem,b.currentItem])},stop:function(){var a=this;a.apStatus="stop",window.clearInterval(a.autoPlayInterval)},_checkAp:function(){var a=this;"stop"!==a.apStatus&&a.play()},play:function(){var a=this;return a.apStatus="play",a.options.autoPlay!==!1&&(window.clearInterval(a.autoPlayInterval),void(a.autoPlayInterval=window.setInterval(function(){a.next(!0)},a.options.autoPlay)))},_swapSpeed:function(a){var b=this;"slideSpeed"===a?b.$owlwrapper.css(b._addCssSpeed(b.options.slideSpeed)):"paginationSpeed"===a?b.$owlwrapper.css(b._addCssSpeed(b.options.paginationSpeed)):"string"!=typeof a&&b.$owlwrapper.css(b._addCssSpeed(a))},_addCssSpeed:function(a){return{"-webkit-transition":"all "+.5*a+"ms ease-out","-moz-transition":"all "+.5*a+"ms ease-out","-o-transition":"all "+.5*a+"ms ease-out",transition:"all "+.5*a+"ms ease-out"}},_removeTransition:function(){return{"-webkit-transition":"","-moz-transition":"","-o-transition":"",transition:""}},_doTranslate:function(a){return{"-webkit-transform":"translate3d("+a+"px, 0px, 0px)","-moz-transform":"translate3d("+a+"px, 0px, 0px)","-o-transform":"translate3d("+a+"px, 0px, 0px)","-ms-transform":"translate3d("+a+"px, 0px, 0px)",transform:"translate3d("+a+"px, 0px,0px)"}},_transition3d:function(a){var b=this;b.$owlwrapper.css(b._doTranslate(a))},_css2move:function(a){this.$owlwrapper.css({left:a})},_css2slide:function(a,b){var c=this;c.isCss3Finish=!1,c.$owlwrapper.stop(!0,!0).animate({left:a},{duration:b||c.options.slideSpeed,complete:function(){c.isCssFinis=!0}})},_checkBrowser:function(){var b=a("html");this.browser={support3d:b.hasClass("csstransforms3d"),isTouch:b.hasClass("touchevents"),isMobile:window.matchMedia("only screen and (max-width: 53.125em)").matches}},_moveEvents:function(){var a=this;a.options.mouseDrag===!1&&a.options.touchDrag===!1||(a._gestures(),a._disabledEvents())},_eventTypes:function(){var a=this,b=["a","b","c"];a.ev_types={},a.options.mouseDrag===!0&&a.options.touchDrag===!0?b=["touchstart.owl mousedown.owl","touchmove.owl mousemove.owl","touchend.owl touchcancel.owl mouseup.owl"]:a.options.mouseDrag===!1&&a.options.touchDrag===!0?b=["touchstart.owl","touchmove.owl","touchend.owl touchcancel.owl"]:a.options.mouseDrag===!0&&a.options.touchDrag===!1&&(b=["mousedown.owl","mousemove.owl","mouseup.owl"]),a.ev_types.start=b[0],a.ev_types.move=b[1],a.ev_types.end=b[2]},_disabledEvents:function(){var b=this;b.$elem.on("dragstart.owl",function(a){a.preventDefault()}),b.$elem.on("mousedown.disableTextSelect",function(b){return a(b.target).is("input, textarea, select, option")})},_gestures:function(){function b(a){if(void 0!==a.touches)return{x:a.touches[0].pageX,y:a.touches[0].pageY};if(void 0===a.touches){if(void 0!==a.pageX)return{x:a.pageX,y:a.pageY};if(void 0===a.pageX)return{x:a.clientX,y:a.clientY}}}function c(b){var c=a(document);"on"===b?(c.on(g.ev_types.move,e),c.on(g.ev_types.end,f)):"off"===b&&(c.off(g.ev_types.move),c.off(g.ev_types.end))}function d(d){var e,f=d.originalEvent||d||window.event;if(3===f.which)return!1;if(!(g.itemsAmount<=g.options.items)){if(g.isCssFinish===!1&&!g.options.dragBeforeAnimFinish)return!1;if(g.isCss3Finish===!1&&!g.options.dragBeforeAnimFinish)return!1;g.options.autoPlay!==!1&&window.clearInterval(g.autoPlayInterval),g.browser.isTouch===!0||g.$owlwrapper.hasClass("grabbing")||g.$owlwrapper.addClass("grabbing"),g.newPosX=0,g.newRelativeX=0,a(this).css(g._removeTransition()),e=a(this).position(),h.relativePos=e.left,h.offsetX=b(f).x-e.left,h.offsetY=b(f).y-e.top,c("on"),h.sliding=!1,h.targetElement=f.target||f.srcElement}}function e(c){var d,e,f=c.originalEvent||c||window.event;g.newPosX=b(f).x-h.offsetX,g.newPosY=b(f).y-h.offsetY,g.newRelativeX=g.newPosX-h.relativePos,"function"==typeof g.options.startDragging&&h.dragging!==!0&&0!==g.newRelativeX&&(h.dragging=!0,g.options.startDragging.apply(g,[g.$elem])),(g.newRelativeX>8||g.newRelativeX<-8)&&g.browser.isTouch===!0&&(void 0!==f.preventDefault()?f.preventDefault():f.returnValue=!1,h.sliding=!0),(g.newPosY>10||g.newPosY<-10)&&h.sliding===!1&&a(document).off("touchmove.owl"),d=function(){return g.newRelativeX/5},e=function(){return g.maximumPixels+g.newRelativeX/5},g.newPosX=Math.max(Math.min(g.newPosX,d()),e()),g.browser.support3d===!0?g._transition3d(g.newPosX):g._css2move(g.newPosX)}function f(b){var d,e,f,i,j=b.originalEvent||b||window.event;i=j.target||j.srcElement,h.dragging=!1,g.browser.isTouch!==!0&&g.$owlwrapper.removeClass("grabbing"),g.newRelativeX<0?g.dragDirection=g.owl.dragDirection="left":g.dragDirection=g.owl.dragDirection="right",0!==g.newRelativeX&&(d=g._getNewPosition(),g.goTo(d,!1,"drag"),h.targetElement===i&&g.browser.isTouch!==!0&&(a(i).on("click.disable",function(b){b.stopImmediatePropagation(),b.stopPropagation(),b.preventDefault(),a(i).off("click.disable")}),e=a._data(i,"events").click,f=e.pop(),e.splice(0,0,f))),c("off")}var g=this,h={offsetX:0,offsetY:0,baseElWidth:0,relativePos:0,position:null,minSwipe:null,maxSwipe:null,sliding:null,dragging:null,targetElement:null};g.isCssFinish=!0,g.$elem.on(g.ev_types.start,".owl-wrapper",d)},_getNewPosition:function(){var a=this,b=a._closestItem();return b>a.maximumItem?(a.currentItem=a.maximumItem,b=a.maximumItem):a.newPosX>=0&&(b=0,a.currentItem=0),b},_closestItem:function(){var b=this,c=b.options.scrollPerPage===!0&&b.browser.isMobile===!1?b.pagesInArray:b.positionsInArray,d=b.newPosX,e=null;return a.each(c,function(f,g){d-b.itemWidth/20>c[f+1]&&d-b.itemWidth/20<g&&"left"===b._moveDirection()?(e=g,b.options.scrollPerPage===!0&&b.browser.isMobile===!1?b.currentItem=a.inArray(e,b.positionsInArray):b.currentItem=f):d+b.itemWidth/20<g&&d+b.itemWidth/20>(c[f+1]||c[f]-b.itemWidth)&&"right"===b._moveDirection()&&(b.options.scrollPerPage===!0&&b.browser.isMobile===!1?(e=c[f+1]||c[c.length-1],b.currentItem=a.inArray(e,b.positionsInArray)):(e=c[f+1],b.currentItem=f+1))}),b.currentItem},_moveDirection:function(){var a,b=this;return b.newRelativeX<0?(a="right",b.playDirection="next"):(a="left",b.playDirection="prev"),a},_customEvents:function(){var a=this;a.$elem.on("owl.next",function(){a.next()}),a.$elem.on("owl.prev",function(){a.prev()}),a.$elem.on("owl.play",function(b,c){a.options.autoplay=c,a.play(),a.hoverStatus="play"}),a.$elem.on("owl.stop",function(){a.stop(),a.hoverStatus="stop"}),a.$elem.on("owl.goTo",function(b,c){a.goTo(c)}),a.$elem.on("owl.jumpTo",function(b,c){a.jumpTo(c)})},_stopOnHover:function(){var a=this;a.options.stopOnHover===!0&&a.browser.isTouch!==!0&&a.options.autoPlay!==!1&&(a.$elem.on("mouseover",function(){a.stop()}),a.$elem.on("mouseout",function(){"stop"!==a.hoverStatus&&a.play()}))},_lazyLoad:function(){var b,c,d,e,f,g=this;if(g.options.lazyLoad===!1)return!1;for(b=0;b<g.itemsAmount;b++)c=a(g.$owlItems[b]),"loaded"!==c.data("owl-loaded")&&(d=c.data("owl-item"),e=c.find(".lazyOwl"),"string"==typeof e.data("src")?(void 0===c.data("owl.loaded")&&(e.hide(),c.addClass("loading").data("owl-loaded","checked")),f=g.options.lazyFollow!==!0||d>=g.currentItem,f&&d<g.currentItem+g.options.items&&e.length&&e.each(function(){g._lazyPreload(c,a(this))})):c.data("owl-loaded","loaded"))},_lazyPreload:function(a,b){function c(){a.data("owl-loaded","loaded").removeClass("loading"),b.removeAttr("data-src"),"fade"===f.options.lazyEffect?b.fadeIn(400):b.show(),"function"==typeof f.options.afterLazyLoad&&f.options.afterLazyLoad.apply(this,[f.$elem])}function d(){g+=1,f._completeImg(b.get(0))||e===!0?c():g<=100?window.setTimeout(d,100):c()}var e,f=this,g=0;"DIV"===b.prop("tagName")?(b.css("background-image","url("+b.data("src")+")"),e=!0):b[0].src=b.data("src"),d()},_autoHeight:function(){function b(){var b=a(e.$owlItems[e.currentItem]).height();e.wrapperOuter.css("height",b+"px"),e.wrapperOuter.hasClass("autoHeight")||window.setTimeout(function(){e.wrapperOuter.addClass("autoHeight")},0)}function c(){d++,e._completeImg(f.get(0))?b():d<=100?window.setTimeout(c,100):e.wrapperOuter.css("height","")}var d,e=this,f=a(e.$owlItems[e.currentItem]).find("img");void 0!==f.get(0)?(d=0,c()):b()},_completeImg:function(a){var b;return!!a.complete&&(b=typeof a.naturalWidth,!("undefined"!==b&&0===a.naturalWidth))},_onVisibleItems:function(){var b,c=this;for(c.options.addClassActive===!0&&c.$owlItems.removeClass("active"),c.visibleItems=[],b=c.currentItem;b<c.currentItem+c.options.items;b++)c.visibleItems.push(b),c.options.addClassActive===!0&&a(c.$owlItems[b].addClass("active"));c.owl.visibleItems=c.visibleItems},_transitionTypes:function(a){var b=this;b.outClass="owl-"+a+"-out",b.inClass="owl-"+a+"-in"},_singleItemTransition:function(){function a(a){return{position:"relative",left:a+"px"}}var b=this,c=b.outClass,d=b.inClass,e=b.$owlItems.eq(b.currentItem),f=b.$owlItems.eq(b.prevItem),g=Math.abs(b.positionsInArray[b.currentItem])+b.positionsInArray[b.prevItem],h=Math.abs(b.positionsInArray[b.currentItem])+b.itemWidth/2,i="webkitAnimationEnd oAnimationEnd MSAnimationEnd animationend";b.isTransition=!0,b.$owlwrapper.addClass("owl-origin").css({"-webkit-transform-origin":h+"px","-moz-perspective-origin":h+"px","perspective-origin":h+"px"}),f.css(a(g,10)).addClass(c).on(i,function(){b.endPrev=!0,f.off(i),b._clearTransStyle(f,c)}),e.addClass(d).on(i,function(){b.endCurrent=!0,e.off(i),b._clearTransStyle(e,d)})},_clearTransStyle:function(a,b){var c=this;a.css({position:"",left:""}).removeClass(b),c.endPrev&&c.endCurrent&&(c.$owlwrapper.removeClass("owl-origin"),c.endPrev=!1,c.endCurrent=!1,c.isTransition=!1)},_owlStatus:function(){var a=this;a.owl={userOptions:a.options,baseElement:a.$elem,userItems:a.$userItems,owlItems:a.$owlItems,currentItem:a.currentItem,prevItem:a.prevItem,visibleItems:a.visibleItems,isTouch:a.browser.isTouch,browser:a.browser,dragDirection:a.dragDirection}},_clearEvents:function(){var b=this;b.$elem.off(".owl owl mousedown.disableTextSelect"),a(document).off(".owl owl"),a(window).off("resize",b.resizer)},_unWrap:function(){var a=this;0!==a.$elem.children().length&&(a.$owlwrapper.unwrap(),a.$userItems.unwrap().unwrap(),a.owlControls&&a.owlControls.remove(),a.galleryControls&&a.galleryControls.remove()),a._clearEvents(),a.$elem.attr({style:a.$elem.data("owl-originalStyles")||"",class:a.$elem.data("owl-originalClasses")})},_destroy:function(){var a=this;a.stop(),window.clearInterval(a._checkVisible),a._unWrap(),a.$elem.removeData()},reinit:function(b){var c=this,d=a.extend({},c.userOptions,b);c._unWrap(),c._loadContent(d,c.$elem)},addItem:function(a,b){var c,d=this;return!!a&&(0===d.$elem.children().length?(d.$elem.append(a),d._setVars(),!1):(d._unWrap(),c=void 0===b||b===-1?-1:b,c>=d.$userItems.length||c===-1?d.$userItems.eq(-1).after(a):d.$userItems.eq(c).before(a),void d._setVars()))},removeItem:function(a){var b,c=this;return 0!==c.$elem.children().length&&(b=void 0===a||a===-1?-1:a,c._unWrap(),c.$userItems.eq(b).remove(),void c._setVars())}})}(jQuery);;
function snakeToCamel(a){return a.replace(/(\-\w)/g,function(a){return a[1].toUpperCase()})}jQuery.cachedScript=function(a,b){return b=$.extend(b||{},{dataType:"script",cache:!0,url:a}),jQuery.ajax(b)},void 0!==window.EC&&"Object"==typeof EC.Mq?window.mobilecheck=function(a){var b=!0;return EC.Mq.on(a,function(){b=!1}),b}:window.mobilecheck=function(){var a=!1;return function(b){(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(b)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(b.substr(0,4)))&&(a=!0)}(navigator.userAgent||navigator.vendor||window.opera),a},function(a){function b(b,c){var d=snakeToCamel(b),e=[],f=g+"/"+h+"-"+d+"/"+i+"/dependencies.json";a.ajax({url:f,success:function(d){var f=Object.keys(d);if(f.length>0){for(var g=0;g<f.length;g++){var h=f[g];a[h]||e.push(a.cachedScript(d[h]))}a.when.apply(a,e).then(function(){c(b)})}else c(b)},error:function(a){c(b)}})}function c(c){var f=snakeToCamel(c);if("function"==typeof a[h][f])d(c,f,!1);else if("file_upload"===c)d(c,f,!0);else{if("socialshare"===c)return!1;b(c,function(b){var c=snakeToCamel(b),f=g+"/"+h+"-"+c+"/"+i+"/"+h+"-"+c+j;a.cachedScript(f).then(function(){d(b,c)},e)})}}function d(b,d,e){if(!k)for(var g=0;g<f[b].length;g++){var i,j=a(f[b][g]),l=j.data("plugin-settings");l&&(i=a.parseJSON(a(l).text())),e?"function"==typeof a.cx[d]?a.cx[d](i,j):c(b):"function"==typeof a[h][d]?a[h][d](i,j):c(b)}}function e(a,b,c){console.error(b,c,a)}var f,g,h,i,j,k=!1;a.fn.getWidgetManually=function(a){k=!0,b(a)},a.fn.initialiseWidgets=function(b){f={},b||(b={}),g=b.baseUrl||"/~/resource/javascript/common/vendor/euroconsumers",h=b.ns||"ec",i=b.src||"dist",j=b.ext||".min.js",void 0===a[h]&&(a[h]={});for(var d=a("[data-plugin]"),e="",k=0;k<d.length;k++){var l=a(d[k]);if(!l.hasClass("has-plugin")){e=l.data("plugin");var m=a(l.data("plugin-settings")).text();if(m){var n=JSON.parse(m);if(n.disabled){console.info(e+" has been intentionally disabled");var o='<div class="panel panel--red padding-small"><p>This widget had been disabled</p></div>';n.hidden?l.html(o):l.prepend(o);continue}}f.hasOwnProperty(e)?f[e].push(d[k]):f[e]=[d[k]]}}for(var p in f)f.hasOwnProperty(p)&&c(p)}}(jQuery);;
///<reference path="../../../Scripts/typings/jquery/jquery.d.ts"/>
var Common;
(function (Common) {
    var CookiePolicy = (function () {
        function CookiePolicy(enableCloseOnScroll) {
            var hideCookieWarning = document.cookie.replace(/(?:(?:^|.*;\s*)hideCookieWarning\s*\=\s*([^;]*).*$)|^.*$/, "$1");
            if (!hideCookieWarning) {
                var $cookiebanner = $('.cookie-banner');
                $cookiebanner.show();
                var removeHandler = function (e) {
                    var target = $(e.currentTarget);
                    if (target.parents('.cookie-banner').length && !target.hasClass('cookie-dismiss'))
                        return;
                    $cookiebanner.hide('fast', function () {
                        $cookiebanner.remove();
                    });
                    $(document.body).off('click', 'a,button', removeHandler);
                };
                // Rule 2: cookie is only visible in first session
                document.cookie = "hideCookieWarning=true; expires=Fri, 31 Dec 9999 23:59:59 GMT;path=/";
                // Rule 1 & 3: click 'OK' to hide. On site action: hide
                $(document.body).on('click', 'a,button', removeHandler);
                if (enableCloseOnScroll) {
                    $(window).scroll(removeHandler);
                }
            }
        }
        return CookiePolicy;
    })();
    Common.CookiePolicy = CookiePolicy;
})(Common || (Common = {}));
;
/// <reference path="jquery/jquery.d.ts" />
/// <reference path="jqueryui/jqueryui.d.ts" />
/**
 * LSV2 Settings
 *  
 *
 * EDITED VERSION!
 * Converted into a widget to enable access to some of its variables. 
 * Original version checked in but not part of the solution
 * 
 * 
 */
(function($) {

    $.widget("ec.lightsurvey", {
        options: {
            onOpened: $.noop,
            onClosed: $.noop,
            isAnswerValid: $.noop,
            processAnswer: $.noop,
            sendCancelLightSurveyToServer: $.noop,
            hasSubmittedWithSuccess: $.noop,
            usesteps: Boolean
        },
        /**
         * Basic set up and create function
         */
        _create: function() {
            var me = this;
            // Remove the mobile conversion banner if present (business ruled)
            if($('.js-conversion-sticky')) {
                $('.js-conversion-sticky').remove();
            };
            // DUMP OF ORIGINAL (with changes) LIGHTSURVEY code (should be adapted to full widget code)
            (function(container) {
                var $lsv2 = $(container);
                var $allPanes = $lsv2.find("div.LSV2_contentPane"); //Question blocks
                var $nav = $lsv2.find("div.LSV2_topBreadcrumb > span");
                var paneController = observable(0);

                paneController.bind("change", function(e, oldValue, newValue) {
                    //only change active panes when in mode "usesteps"
                    if (me.options.usesteps) {
                        //panes
                        var $oldpane = $allPanes.eq(oldValue);
                        var $newpane = $allPanes.eq(newValue);

                        $oldpane.removeClass('active').fadeOut();
                        $newpane.addClass('active').fadeIn();

                        //nav
                        $nav.eq(newValue).addClass("visited").animate({
                            "opacity": 1
                        });
                        $nav.eq(oldValue).animate({
                            "opacity": 0.5
                        });
                    }
                });

                //TODO : remove following comment ?
                //Following code (top breadcrumbs) only ment to be executed in usesteps mode. Otherwise all panes have to be looped
                $nav.each(function(index, item) {
                    $(item).click(function(e) {
                        me.options.processAnswer(e);

                        var activePane;
                        if (me.options.usesteps) {
                            activePane = $allPanes.closest(".LSV2_wrapper .LSV2_contentPane.active");

                            //added
                            if (me.options.isAnswerValid(activePane)) {
                                //move to the next tab
                                paneController.val(index);
                            }
                        } else {
                            //$(item) is the breadcrumb item here. so lookup the questionpane with the same index as this breadcrumb
                            //var currentQuestionIndex = $(this).index();
                            //activePane = $($allPanes[currentQuestionIndex]);

                            //Loop all panels to check for validation errors
                            var isAllValid = true;
                            $allPanes.each(function(index, pane) {
                                var jsPane = $(pane);
                                var isValid = me.options.isAnswerValid(jsPane);
                                if (!isValid) {
                                    isAllValid = false;
                                }
                            });
                        }
                    });
                });

                /**
                 * Open/close survey
                 */
                var $tooltip = $lsv2.find(".LSV2_maxify"),
                    $btnMinify = $lsv2.find(".LSV2_minify"),
                    $container = $lsv2.find("div.LSV2_wrapper"),
                    opened = observable(false),
                    $btnClose = $tooltip.find(".close"),
                    isOpening = false,
                    isClosing = false,
                    startdelay = 0;

                $tooltip.click($.proxy(opened.val, null, true));
                $btnMinify.click($.proxy(opened.val, null, false));
                $btnClose.click(function(e) {
                    $tooltip.hide();
                    if (!isClosing) {
                        isClosing = true;
                        $lsv2.fadeOut();
                        if (!me.options.hasSubmittedWithSuccess()) {
                            //Send cancel to server if the user has not yet finished the survey
                            me.options.sendCancelLightSurveyToServer();
                        }
                    }
                    e.stopPropagation();
                });

                opened.bind("change", function(e, oldValue, newValue) {
                    //newvalue=true: hide tooltip + show LSV.    newvalue=false : show tooltip + hide LSV
                    if (newValue) {
                        isOpening = true;
                        $container.animate({
                            width: 260,
                            //height: 540,
                            opacity: 1,
                            bottom: 0

                        }, {
                            complete: function() {
                                isOpening = false;
                                me.options.onOpened();
                                $container.css('position', 'static');
                                $container.css('height', 'auto');
                            }
                        });
                        $tooltip.fadeOut();
                    } else {
                        //close small tooltip (maybe also when clicked on the tooltip in order to open the survey ?)
                        if (me.options.hasSubmittedWithSuccess()) {
                            //popup is already closing after complete submit (survey filled in + send) ; close LSV instantly
                            $lsv2.hide();
                        } else {
                            //normal flow: occurs when open/close tooltip
                            isOpening = false;
                            $container.css('position', 'absolute');
                            $container.animate({
                                width: 60,
                                //height: 0,
                                opacity: 0,
                                bottom: -999
                            }, {
                                complete: function() {
                                    me.options.onClosed();
                                }
                            });

                            $tooltip.fadeIn();
                        }
                    }
                });

                $tooltip.delay(startdelay).fadeIn(function() {
                    $tooltip.effect("bounce", 100);
                });

                /**
                 * setup questions
                 */
                //used in graph Rating10 : LSV2_graph jq_lsv2Next
                $(".LSV2_button.jq_lsv2Next,.jq_graph .LSV2_graph.jq_lsv2Next,.LSV2_ratingselect .jq_lsv2Next").each(function() {
                    setupQuestion($(this), observable());
                });

                $(".LSV2_ratingselect select").bind("change", function(e) {
                    var value = $(this).val();

                    if (value === "") {
                        return;
                    }

                    $(this).closest("[data-selector='lsv2-questionpane']").find("[data-selector='answer']").val(value);

                    //var activePane = $('.LSV2_wrapper .LSV2_contentPane.active');
                    var activePane = $(this).closest(".LSV2_wrapper .LSV2_contentPane.active");
                    me.options.isAnswerValid(activePane);
                });

                /**
                 * utils
                 */
                function setupQuestion($btn, question) {
                    $btn.click(function(event) {
                        var clickedActiveElement = $btn.closest('.jq_lsv2Next');
                        clickedActiveElement.addClassSvg("active").siblings().removeClassSvg("active");

                        // check mandatory
                        me.options.processAnswer(event);
                        var activePane = $btn.closest('.LSV2_wrapper .LSV2_contentPane.active')
                        if (me.options.isAnswerValid(activePane)) {
                            question.val($(this).index());

                            //move to the next tab
                            paneController.val(paneController.val() + 1);
                        }
                    });
                }

                function observable(defaultValue) {

                    var _value = defaultValue || null;
                    var _eventMngr = jQuery({});

                    var val = function(newValue) {
                        if (arguments.length > 0 && _value != newValue) {
                            var oldValue = _value;
                            _value = newValue;
                            _eventMngr.trigger("change", [oldValue, newValue]);
                        }

                        return _value;
                    };

                    return {
                        val: val,
                        bind: jQuery.proxy(_eventMngr.bind, _eventMngr),
                        unbind: jQuery.proxy(_eventMngr.unbind, _eventMngr)
                    };
                }

            })(this.element);
        },

        _destroy: function() {
            this.element.empty();
        }
    });


    /**
     * utils
     */
    $.fn.addClassSvg = function(classe1 /*, classe2,..*/ ) {

        var newClasses = arguments,
            ln = newClasses.length;

        this.each(function() {
            var $this = $(this);
            var classes = $.trim($this.attr("class")).replace(/\s{2,}/g, ' ').split(' ');

            for (var i = 0; i < ln; i++) {
                if (classes.indexOf(newClasses[i]) == -1) {
                    classes.push(newClasses[i]);
                }
            }

            $this.attr("class", classes.join(' '));

        });

        return this;
    }

    $.fn.removeClassSvg = function(classe1 /*, classe2,..*/ ) {

        var oldClasses = arguments,
            ln = oldClasses.length;

        this.each(function() {
            var $this = $(this);
            var classes = $.trim($this.attr("class")).replace(/\s{2,}/g, ' ').split(' ');

            for (var i = 0; i < ln; i++) {
                var index = classes.indexOf(oldClasses[i]);
                if (index > -1) {
                    classes.splice(index, 1);
                }
            }

            $this.attr("class", classes.join(' '));

        });

        return this;
    }

})(jQuery);;
(function ($, Mustache) {
    'use strict';

    $.widget('ec.carouselslider', {

        options: {
            templates: {
                iconLeft: '<i class="icon-chevron2-left"></i>',
                iconRight: '<i class="icon-chevron2-right"></i>'
            },

            classes: {
                carouselContainer: this,
                carouselItem: '',
                currentItem: 'carousel__item--current',
                carouselLink: 'carousel__link',
                iconLeft: 'icon-chevron2-left',
                iconRight: 'icon-chevron2-right',


            },

            events: {

                customOwlNavLeft: 'click .js-owl-left',
                customOwlNavRight: 'mousedown .js-owl-right',
                resize: 'resize window'
            },

            returnFocus: true,

            owlOptions: {
                autoPlay: false,
                items: 3,
                itemsDesktop: [1199, 3],
                itemsDesktopSmall: [850, 1],
                itemsTablet: [768, 1],
                itemsMobile: [479, 1],
                itemsScaleUp: true,
                stagePadding: 100,
                navigation: true,
                pagination: true,
                navigationText: ["<i class='icon-chevron2-left'></i>", "<i class='icon-chevron2-right'></i>"]
            },

            owloptionsMobile: {
                scrollPerPage: false
            },
            owloptionsDesktop: {
                scrollPerPage: true
            },
            owlScEditorOptions: {
                touchDrag: false,
                mouseDrag: false
            },
            thumbData: false,
            useCarouselNav: false,
            afterInitAddLeftMargin: false,
            breakPointMobile: 850,
            hasResponsiveOwlOptions: false,
            syncPositionToSlave: false,
            syncSlave: "[data-class='synced-slider-slave']",
            showLoader: false,
            adaptiveOwl: false,
            adaptiveOwlItemMobile: 'div.child-block',
            adaptiveOwlItemDesktop: 'div.block',
            tabsSelector: '[data-class="tab-blocks"]',
            desktopOnly: false,

            /**
             * @namespace ec.carouselslider#enabledsupportsExperienceEditor
             * @property {bool}  enabled - A boolean indicating if the widget is supported in the Experience Editor in editing mode.
             * @default true
             */
            supportsExperienceEditor: true
        },

        /** Add an underscore to the event prefix to make event names easier to read */
        widgetEventPrefix: 'carousel_',

        /**
         * @description Returns true if we are in the context of the Experience Editor in editing mode.
         */
        _isExperienceEditorEditing: function () {
            return this.document.find('body').hasClass('sc-editor--editing');
        },

        /**
         * Constructor
         */
        _create: function () {

            var self = this,
                opts = self.options;

            opts = $.extend(true, self.options.syncPositionToSlave, self.options);
            opts = $.extend(true, self.options.showLoader, self.options);

            // exit if experience editor editing is not supported and we are in that mode
            if (!opts.supportsExperienceEditor && self._isExperienceEditorEditing()) {
                return;
            }

            this._wrapper = this.element;

            // handle switch between desktop and mobile using media queries ::

            //			this._switchBetweenDesktopAndMobileHandler = function (matches) {
            //				self[matches ? '_initDesktop' : '_initDesktop'].call(self);
            //			};
            //
            //			EC.Mq.on(EC.Mq.tabletMin(), this._switchBetweenDesktopAndMobileHandler, true);

            this._initDesktop();

        },

        /**
         * Initializes the component for desktop, taking into account that it could have been in mobile mode before
         */
        _initDesktop: function () {

            var self = this,
                opts = this.options;

            // kick off the carousel

            this._initMainCarousel();


            // bind events on main carousel and navigation

            this._unbindEvent();
            this._bindEventsDesktop();
            this._switchTabs();
            this._mobileOnlyCarousel();

            if (this.options.hasResponsiveOwlOptions) {
                this._doResponsiveOwlOptions(opts.owloptionsMobile, opts.owloptionsDesktop);
                self._refreshAfterResize();
            }

            //remove dummy block that fills the empty space when js is not loaded yet
            if ($('.js-dummyblock').length) {
                $('.js-dummyblock').remove();
            }


        },

        /**
         * Initializes the component for mobile, taking into account that it could have been in desktop mode before
         */
        _initMobile: function () {
            var self = this,
                opts = this.options;
            // clean up

            this._unbindEvent();
            //            this._destroyPager();
            //            this._destroyPopupGallery();

            // init main carousel for mobile

            this._initMainCarousel({
                afterMove: $.noop
            });


            // (re)bind events

            this._bindEventsMobile();

        },

        /**
         * Initializes or updates the main carousel.
         */
        _initMainCarousel: function (carouselOptionOverrides) {

            var self = this;
            var theOptions;

            // double extend ^^':
            // - base options with overrides
            // - our necessary options
            // check for sc-editor
            if ($(".sc-editor").length) {
                theOptions = $.extend(true, self.options.owlScEditorOptions, self.options.owlOptions);
            } else {
                theOptions = self.options.owlOptions;
            }

            var carouselOptions = $.extend(true, $.extend(true, theOptions, carouselOptionOverrides), {
                beforeInit: function (e) {
                    if (self.options.showLoader) {
                        self._addLoader.call(this, e);
                    }
                },
                afterInit: function (e) {
                    self._carouselAfterInit.call(this, e, self);
                    self._getSliderPageNumbers(this, e);
                    if (self.options.showLoader) {
                        self._removeLoader.call(this, e);
                    }

                },
                afterMove: function (e) {
                    self._getSliderPageNumbers(this, e);
                },
                afterAction: function (e) {
                    if (self.options.syncPositionToSlave) {
                        self._doSyncPosition(this, self.options.syncSlave);
                    }
                }
            });

            // if it was already initialized, just refresh, other create a new instance

            if (this._owlInstance) {
                this._owlInstance.reinit(carouselOptions);
            } else {
                this._owl = this._wrapper.owlCarousel(carouselOptions);
            }

            this._owlInstance = this._getOwlInstance(this._owl);

        },

        /**
         * Cleans up the main carousel
         */
        _destroyMainCarousel: function () {

            if (this._owlInstance) {
                this._owlInstance.destroy();
                delete this._owlInstance;
            }

            if (this._owl) {
                delete this._owl;
            }

        },


        /**
         * Binds events to the widget when initializing for desktop
         */
        _bindEventsDesktop: function () {

            var events = {},
                self = this,
                eventOptions = this.options.events;

            events[eventOptions.customOwlNavLeft] = this._onOwlNavigateLeft;
            events[eventOptions.customOwlNavRight] = this._onOwlNavigateRight;
            //events[eventOptions.resize] = this._refreshAfterResize;

            this._on(this.element.closest('.js-paged-slider'), events);

            $(window).on('load resize', function () {
                self._refreshAfterResize();
            });
        },

        /**
         * Binds events to the widget when initializing for mobile
         */
        _bindEventsMobile: function () {


        },

        /**
         * Unbinds all events of the widget
         */
        _unbindEvent: function () {

            this._off(this.element, 'click keydown');

        },
        _getSliderPageNumbers: function (owlobject, selector) {
            var self = this;
            var theOptions;
            var target = owlobject;
            var Owl = target,
                currentslide = Owl.currentItem,
                owlitems = Owl.options.items,
                owltotitems = Owl.itemsAmount,
                totSlides = Math.ceil(owltotitems / owlitems),
                currentPageNr = Math.ceil(currentslide / owlitems) + 1,
                visibleItems = Owl.visibleItems;

            if (currentPageNr < 1) {
                currentPageNr = 1;

            }

            //console.log('current: ' + currentslide + ' owlItems: ' + owltotitems + ' totPages: ' + totSlides + ' visibleArr: ' + visibleItems);
            selector.closest('.js-paged-slider').find('.js-currentpage').html(currentPageNr);
            selector.closest('.js-paged-slider').find('.js-totalpages').html(totSlides);
            selector.find(".owl-item").removeClass('js-owl-visible first last');

            //set visibility classes on visible items
            for (var i = 0; i < visibleItems.length; i++) {
                var item = selector.find(".owl-item").eq(visibleItems[i]);
                //console.log($(item).attr("class"));
                item.addClass('js-owl-visible');
                if (i == 0) {
                    item.addClass("first");
                }
                if (i == visibleItems.length - 1) {
                    item.addClass("last");
                }
            }
        },

        /**
         * Handler executed when owl is initialized.
         *
         * @param {jQuery.Event}  e       The jQuery event object.
         * @param {jQuery.Widget} widget  The current widget instance.
         *
         * @this  Owl instance
         */
        _carouselAfterInit: function (e, widget) {
            var self = this;
            var opts = widget.options;
            $(widget).show();

            if (opts.afterInitAddLeftMargin) {
                $(self.wrapperOuter).addClass("owl-marginleft--mobile");
            }
        },
        _addLoader: function (widget, e) {
            var self = this;
            var opts = self.options;
            $(widget).parent().prepend("<div class='loader loading'></div>");

        },
        _removeLoader: function (widget, e) {
            $(".loader.loading").remove();
        },

        /**
         * Handler executed when owl is navigated.
         *
         * @param {jQuery.Event}  e       The jQuery event object.
         * @param {jQuery.Widget} widget  The current widget instance.
         *
         * @this  Owl instance
         */
        _carouselAfterMove: function (e, widget) {

            var owl = this.owl;
            var items = owl.userItems;

        },

        /**
         * Event handler when a navigation thumbnail is clicked.
         *
         * @param {jQuery.Event} e  The jQuery event object.
         */
        _onNavThumbClick: function (e) {

            if (this._single) return;

            var newIndex = this._pagerItems.index(e.currentTarget);

            // switch "current" classes
            this._markPagerCurrentItem(this._pagerItems, newIndex);

            // move main carousel
            this._syncCarousel(newIndex);
        },
        _onOwlNavigateLeft: function (e) {
            var owl = this._owlInstance;
            owl.prev();
        },
        _onOwlNavigateRight: function (e) {
            var owl = this._owlInstance;
            owl.next();
        },
        _switchTabs: function (e) {
            var tabsblock = this.options.tabsSelector;
            //regular clickable tabs
            $('[data-tab="trigger"]').click(function () {
                var mId = $(this).attr('data-tab-id');
                $('[data-tab="trigger"]').removeClass('is-active');
                $(this).addClass('is-active');
                $(tabsblock).trigger('owl.goTo', (mId - 1));
            });

            // tabs in options select
            $(' [data-tab="select-trigger"]').change(function () {
                var mId = $('option:selected', this).attr('data-tab-id');
                $(tabsblock).trigger('owl.goTo', (mId - 1));
            });

        },

        //Mobile only paged-slider
        _mobileOnlyCarousel: function (e) {
            // responsive checking of viewport width
            var checkWidth = $(document).width(),
                self = this,
                breakpoint = self.options.breakPointMobile;

            if (checkWidth <= breakpoint) {
                if ($('[data-class="paged-slider-only-mobile"]').length) {
                    $('[data-class="paged-slider-only-mobile"]').owlCarousel({
                        items: 1,
                        responsive: false
                    });
                }
            }
        },
        _doResponsiveOwlOptions: function (optionsMobile, optionsDesktop) {
            var owl = this._owlInstance,
                checkWidth = $(document).width(),
                self = this,
                desktopOnly = self.options.desktopOnly,
                breakpoint = self.options.breakPointMobile;

            if (checkWidth <= breakpoint) {

                if (desktopOnly) {
                    owl.destroy();
                    return;
                }
                //Check if owl needs to adapt its structure
                if (self.options.adaptiveOwl) {
                    owl.destroy();
                    $(self.options.adaptiveOwlItemMobile).unwrap(); //important !
                }
                owl.reinit(optionsMobile);
            } else {

                owl.reinit(optionsDesktop);
                if (desktopOnly) {
                    self._initMainCarousel();
                }
            }

        },
        _refreshAfterResize: function () {
            var resizeTimer,
                self = this,
                settings = self.settings;
            self._doResponsiveOwlOptions;
            //			$(window).resize(function () {
            //				clearTimeout(resizeTimer);
            //				resizeTimer = setTimeout(function () {
            //
            //
            //					//console.log('refreshed')
            //
            //				}, 250);
            //			});
        },

        /**
         * Returns the owl instance attached to the given element.
         *
         * @param {jQuery} $element  A jQuery-wrapped element on which the owl plugin is attached.
         * @returns {object}
         */
        _getOwlInstance: function ($element) {
            if (!$element || !$element.length) return null;
            return $element.data('owlCarousel');

        },

        /**
         * Switches the "current" css class on the pager elements.
         *
         * @param {jQuery} pagerItems  The pager items.
         * @param {number} index       The new index to sync the pagination.
         */
        _markPagerCurrentItem: function (pagerItems, index) {

            if (!pagerItems) {
                return;
            }

            pagerItems
                .removeClass(this.options.classes.currentItem)
                .eq(index).addClass(this.options.classes.currentItem);

        },

        /**
         * Syncs the main awl carousel to the given index.
         *
         * @param {number} index             The new index to sync the pagination.
         * @param {bool}   withoutAnimation  Indicates if we should jump to new indew or animate to it (when using owl).
         */
        _syncCarousel: function (index, withoutAnimation) {

            if (this._single) return;

            // move main carousel
            this._owlInstance[!!withoutAnimation ? "jumpTo" : "goTo"](index);

        },

        _doSyncPosition: function (owl, slave) {
            var current = owl.currentItem;
            $(slave)
                .removeClass("synced")
                .eq(current)
                .addClass("synced")
            if ($(slave).data("owlCarousel") !== undefined) {

                $(slave).trigger("owl.goTo", current);
            }
        },


        /**
         * Sync the pager to the given index.
         *
         * @param {number} index             The new index to sync the pagination.
         * @param {bool}   withoutAnimation  Indicates if we should jump to new indew or animate to it (when using owl).
         */
        //        _syncPagination: function (index, withoutAnimation) {
        //            return;
        //            if (this._single) return;
        //
        //            // switch "current" classes
        //            this._markPagerCurrentItem(this._pagerItems, index);
        //
        //            // move owl navigation
        //            if (this.options.useCarouselNav) {
        //
        //                var visibleItems = this._owlNavInstance.visibleItems,
        //                    goToIndex = false,
        //                    found = false;
        //
        //                for (var i in visibleItems) {
        //                    if (index === visibleItems[i]) {
        //                        found = true;
        //                    }
        //                }
        //
        //                if (found === false) {
        //                    if (index > visibleItems[visibleItems.length - 1]) {
        //                        goToIndex = index - visibleItems.length + 2;
        //                    } else {
        //                        if (index - 1 === -1) {
        //                            goToIndex = 0;
        //                        }
        //                        this._owlNavInstance.goTo(index);
        //                    }
        //                } else if (index === visibleItems[visibleItems.length - 1]) {
        //                    goToIndex = visibleItems[1];
        //                } else if (index === visibleItems[0]) {
        //                    goToIndex = index - 1;
        //                }
        //
        //                if (goToIndex !== false) {
        //                    this._owlNavInstance[!!withoutAnimation ? "jumpTo" : "goTo"](goToIndex);
        //                }
        //            }
        //        },

        /**
         * Keydown handlers for the main carousel.
         * Handles navigating left and right.
         *
         * @param {jQuery.Event} e  The jQuery event object.
         */
        _onKeydownCarousel: function (e) {

            var keyCode = e.keyCode,
                owl = this._owlInstance;

            switch (keyCode) {
                case 39: //keyCode.RIGHT:
                case 40: //keyCode.DOWN:
                    owl.next();
                    break;
                case 37: //:keyCode.LEFT:
                case 38: //keyCode.UP:
                    owl.prev();
                    break;
            }

        },

        /**
         * Keydown handler for the pager.
         * Handles tabindex and focus change as well as image switching.
         *
         * @param {jQuery.Event} e  The jQuery event object.
         */
        _onKeydownPager: function (e) {

            var self = this;

            if (e.altKey || e.ctrlKey) {
                return;
            }

            var keyCode = e.keyCode,
                pagerItems = self._pagerItems,
                len = pagerItems.length,
                currentTarget = e.currentTarget,
                newIndex = pagerItems.index(currentTarget),
                toFocus = false;

            switch (keyCode) {
                case 39: //keyCode.RIGHT:
                case 40: //keyCode.DOWN:
                    toFocus = pagerItems[(newIndex + 1) % len];
                    break;
                case 37: //keyCode.LEFT:
                case 38: //keyCode.UP:
                    toFocus = pagerItems[(newIndex - 1 + len) % len];
                    break;
                case 32: //keyCode.SPACE:
                case 13: //keyCode.ENTER:
                    toFocus = pagerItems[newIndex];
                    self._syncPagination(newIndex);
                    self._syncCarousel(newIndex);
                    e.preventDefault();
                    break;
                case 36: //keyCode.HOME:
                    toFocus = pagerItems[0];
                    break;
                case 35: //keyCode.END:
                    toFocus = pagerItems[len - 1];
                    break;
            }

            if (!toFocus) return;

            $(currentTarget).attr('tabIndex', -1);
            $(toFocus).attr('tabIndex', 0).focus();

            e.preventDefault();
        },

        /**
         * Cleaning up.
         */
        _destroy: function () {

            //			if (this._switchBetweenDesktopAndMobileHandler) {
            //				EC.Mq.off(EC.Mq.tabletMin(), this._switchBetweenDesktopAndMobileHandler);
            //			}

            //this._destroyPopupGallery();
            //this._destroyPager();
            this._destroyMainCarousel();

        }
    });

})(jQuery, Mustache);;
/**
 * @author       Peter Revalk
 * @author       Didier Ghys
 * @version      1.0
 */

(function ($) {

	'use strict';

	$.widget('ec.dropdownactive', {

        options: {

            /**
             * @namespace ec.dropdownactive#labelSelector
             * @property {string}  labelSelector - The selector to get the label element.
             * @default .js-dropdownactive-label
             */
            labelSelector: '.js-dropdownactive-label',

            /**
             * @namespace ec.dropdownactive#miniLabelSelector
             * @property {string}  miniLabelSelector - The selector to get the min label element.
             * @default .js-dropdownactive-mini-label
             */
            miniLabelSelector: '.js-dropdownactive-mini-label',

            /**
             * @namespace ec.dropdownactive#itemSelector
             * @property {string}  itemSelector - The selector to get the menu item elements.
             * @default .js-dropdownactive-item
             */
            itemSelector: '.js-dropdownactive-item',

            /**
             * @namespace ec.dropdownactive#subMenuSelector
             * @property {string}  subMenuSelector - The selector to get the sub menu elements.
             * @default .js-dropdownactive-submenu
             */
            subMenuSelector: '.js-dropdownactive-submenu',

            /**
             * @namespace ec.dropdownactive#activeClass
             * @property {string}  activeClass - The css class name to add to active elements.
             * @default is-active
             */
            activeClass: 'is-active',

            /**
             * @namespace ec.dropdownactive#hiddenClass
             * @property {string}  hiddenClass - The css class name to add to elements to hide.
             * @default js-hide
             */
            hiddenClass: 'js-hide',

            /**
             * @namespace ec.dropdownactive#labelText
             * @property {string}  labelText - The default label text.
             * @default More
             */
            labelText: 'More'
        },

        widgetEventPrefix: 'dropdownactive:',

	    /**
         * @inheritdoc
         */
        _create: function() {

            var self = this, options = self.options;

            self.itemElements = self._getItems();

            // get the first active element, cleaning up multiple active
            self.currentItemElement = self.itemElements
                .filter('.' + options.activeClass)
                .not(':first')
                    .removeClass(options.activeClass)
                    .end()
                .first();

            self.labelElement = self.element.find(options.labelSelector);
            self.miniLabelElement = self.element.find(options.miniLabelSelector);
            self.subMenuElement = self.element.find(options.subMenuSelector);

            self._bindEvents();

        },

	    /**
         * @name ec.dropdownactive#_bindEvents
         * @function
         * @private
         * @description Binds the events the plugin requires
         */
        _bindEvents: function() {

            var self = this, options = self.options, handlers = {};

            // handler for clicking a menu item
            handlers['click ' + options.itemSelector] = function (e) {
                self._setActive($(e.currentTarget));
            };

            // handler for clicking the menu itself
            handlers['click'] = function (e) {
                self._showDropdown();
            };
            
            self._on(self.element, handlers);

            // when clicking anywhere else, hide the menu
            self._on(self.document, {
                'click': function() {
                    self._hideDropdown();
                }
            });

        },

        /**
         * @name ec.dropdownactive#_getItems
         * @function
         * @private
         * @description Gets the menu items.
         * @returns {jQuery}
         */
        _getItems: function() {
            return this.element.find(this.options.itemSelector);
        },

        /**
         * @name ec.dropdownactive#_showDropdown
         * @function
         * @private
         * @description Slides down the dropdown menu.
         * @fires ec.dropdownactive#open
		 */
        _showDropdown: function () {

            var self = this;

            if (self.subMenuElement && self.isOpen)
                return;

            this.subMenuElement.slideDown(150, function() {
                self.isOpen = true;

                /**
                 * @event ec.dropdownactive#open
                 * @property {Element} submenu
                 */
                self._trigger('open', null, { submenu: self.element });
            });
            
        },

        /**
         * @name ec.dropdownactive#_hideDropdown
         * @function
         * @private
         * @description Hides the dropdown menu
         * @fires ec.dropdownactive#close
		 */
        _hideDropdown: function () {

            var self = this;

            if (!self.subMenuElement || !self.isOpen)
                return;

            self.subMenuElement.hide(0, function () {
                self.isOpen = false;

                /**
                 * @event ec.dropdownactive#close
                 * @property {Element} submenu
                 */
                self._trigger('close', null, { submenu: self.element });
            });

        },

	    /**
         * @name ec.dropdownactive#_setActive
         * @function
         * @private
         * @description Sets the current active menu item
         * @param {jQuery} item - a jQuery object or a selector to the element to set as active.
         * @fires ec.dropdownactive#change
         */
		_setActive: function (item) {

		    var self = this, options = self.options;

		    if (!self.itemElements.is(item))
		        return;

		    if (self.currentItemElement) {
		        self.currentItemElement.removeClass(options.activeClass);
		    }

		    self.currentItemElement = item.addClass(options.activeClass);
		    self.labelElement.text(item.text());
		    self.miniLabelElement.removeClass(options.hiddenClass);

		    /**
             * @event ec.dropdownactive#change
             * @property {jQuery}  item
             * @property {Element} submenu
             */
		    self._trigger('change', null, { item: item, submenu: self.element });
            
		},

	    /**
         * @name ec.dropdownactive#_reset
         * @function
         * @private
         * @description Resets the menu.
         * @fires ec.dropdownactive#reset
         */
		_reset: function () {

		    var self = this, options = self.options;

		    if (self.currentItemElement) {
		        self.currentItemElement.removeClass(options.activeClass);
		        self.currentItemElement = null;
		    }

		    self.miniLabelElement.addClass(options.hiddenClass);
		    self.labelElement.text(options.labelText);

		    /**
             * @event ec.dropdownactive#reset
             * @property {Element} submenu
             */
		    self._trigger('reset', null, { submenu: self.element });
		},

	    /**
         * @name ec.dropdownactive#getActive
         * @function
         * @description Returns the current active menu item
         * @since 1.0
         * @returns {jQuery}
         */
		getActive: function() {

		    return this.currentItemElement ? this.currentItemElement : null;

		},

	    /**
         * @name ec.dropdownactive#setActive
         * @function
         * @description Sets the current active menu item
         * @since 1.0
         * @param {jQuery|Element|string} item - the element to set as active.
         */
		setActive: function (item) {

		    item = $(item);

		    if (item.length !== 1) return;

		    this._setActive(item);

		},

	    /**
         * @name ec.dropdownactive#has
         * @function
         * @description Returns true if the given item is part of the menu.
         * @since 1.0
         * @param {jQuery|Element|string} item -  the element to set as active.
         * @returns boolean
         */
        has: function(item) {
            
            item = $(item);

            return this.itemElements.is(item);

        },

	    /**
         * @name ec.dropdownactive#reset
         * @function
         * @description Resets the menu to its default state
         * @since 1.0
         */
        reset: function() {
            this._reset();
        }
        
	});

})(jQuery);;
/**
 * @author       Peter Revalk
 * @author       Didier Ghys
 * @version      1.0
 */

(function ($) {

	'use strict';

	$.widget('ec.navmenu', {

        options: {

            /**
             * @namespace ec.navmenu#itemSelector
             * @property {string}  itemSelector - The selector to get menu item elements.
             * @default .js-navmenu-item
             */
            itemSelector: '.js-navmenu-item',

            /**
             * @namespace ec.navmenu#subMenuSelector
             * @property {string}  subMenuSelector - The selector to get the sub menu elements.
             * @default .js-navmenu-hassubmenu
             */
            subMenuSelector: '.js-navmenu-hassubmenu',
            hamburgerSelector: '.js-hamburger',
            menuItem: '.navmenu__item',

            /**
             * @namespace ec.navmenu#activeClass
             * @property {string}  activeClass - The css class name to add to active elements.
             * @default is-active
             */
            activeClass: 'is-active',

            /**
             * @namespace ec.navmenu#hiddenClass
             * @property {string}  hiddenClass - The css class name to add to elements to hide.
             * @default js-hide
             */
            hiddenClass: 'js-hide',

            /**
             * @namespace ec.navmenu#preventDefault
             * @property {boolean}  preventDefault - If true, default behavior will be prevented when hitting a menu item.
             * @default false
             */
            preventDefault: false
        },

        widgetEventPrefix: 'navmenu:',

	    /**
         * @inheritdoc
         */
        _create: function() {

            var self = this, options = self.options;

            self.itemElements = self._getItems();

            // get the first active element, cleaning up multiple active
            self.currentItemElement = self.itemElements
                .filter('.' + options.activeClass)
                .not(':first')
                    .removeClass(options.activeClass)
                    .end()
                .first();

            // initializes submenu(s)
            self.subMenus = self.element.find(options.subMenuSelector).dropdownactive({
                itemSelector: '.block-list__item',
                miniLabelSelector: '.navmenu__item__mini-label',
                labelSelector: '.navmenu__item__label',
                subMenuSelector: '.navmenu__item__submenu',
            });

            self._bindEvents();

        },

	    /**
         * @name ec.navmenu#_bindEvents
         * @function
         * @private
         * @description Binds the events the plugin requires
         */
        _bindEvents: function() {

            var self = this, options = self.options, handlers = {};

            // handler for clicking a menu item
            handlers['click ' + options.itemSelector] = function (e) {
                if (options.preventDefault) {
                    e.preventDefault();
                }
                self._setActive($(e.target));
            };
            
             handlers['click ' + options.hamburgerSelector] = function (e) {
                if (options.preventDefault) {
                    e.preventDefault();
                }
                $(this.element).find(options.menuItem).toggle();
            };

            // handler when a submenu item is selected
            handlers['dropdownactive:change'] = function (e, data) {
                e.stopPropagation();
                self._onSubMenuChange(data);
            };

            // handler when a submenu item is selected
            handlers['dropdownactive:reset'] = function (e, data) {
                e.stopPropagation();
                self._onSubMenuReset(data);
            };

              self._on(this.element, handlers);

        },

	    /**
         * @name ec.navmenu#_getItems
         * @function
         * @private
         * @description Gets the menu items.
         * @returns {jQuery}
         */
        _getItems: function() {
            return this.element.find(this.options.itemSelector);
        },

	    /**
         * @name ec.navmenu#_onSubMenuChange
         * @function
         * @private
         * @description When a submenu changes we need to set active class on the title element
         * @parameter {Object} data - the event data
         * @property  {jQuery}  data.item
         * @property  {Element} data.submenu
         */
        _onSubMenuChange: function (data) {
            var self = this;
            data.submenu.find('.navmenu__item__title ').addClass(self.options.activeClass);
        },

	    /**
        * @name ec.navmenu#_onSubMenuChange
        * @function
        * @private
        * @description When a submenu changes we need to set active class on the title element
        * @parameter {Object} data - the event data
        * @property  {Element} data.submenu
        */
        _onSubMenuReset: function (data) {
            var self = this;
            data.submenu.find('.navmenu__item__title ').removeClass(self.options.activeClass);
        },

	    /**
         * @name ec.navmenu#_setActive
         * @function
         * @private
         * @description Sets the current active menu item
         * @param {jQuery} item - a jQuery object or a selector to the element to set as active.
         * @fires ec.dropdownactive#close
         */
		_setActive: function (item) {

		    var self = this, options = self.options, inSubMenu = false, subMenuIndex;

		    if (!self.itemElements.is(item))
		        return;

		    if (self.currentItemElement) {
		        self.currentItemElement.removeClass(options.activeClass);
		    }

		    self.subMenus.dropdownactive('reset');

		    self.currentItemElement = item;

		    for (subMenuIndex = 0; subMenuIndex < self.subMenus.length; subMenuIndex++) {
		        if ($(self.subMenus[subMenuIndex]).dropdownactive('has', item)) {
		            inSubMenu = true;
		            break;
		        }
		    }

		    if (inSubMenu) {
		        $(self.subMenus[subMenuIndex]).dropdownactive('setActive', item);
		    }
		    else {
                self.currentItemElement.addClass(options.activeClass).focus();
            }
            
		    /**
             * @event ec.navmenu#change
             * @property {jQuery}  item
             * @property {Element} submenu
             */
		    self._trigger('change', null, { item: item, navmenu: self.element });
            
		},
        
	    /**
         * @name ec.navmenu#getActive
         * @function
         * @description Returns the current active menu item
         * @since 1.0
         * @returns {jQuery}
         */
		getActive: function() {

		    return this.currentItemElement ? this.currentItemElement[0] : null;

		},

	    /**
         * @name ec.navmenu#setActive
         * @function
         * @description Sets the current active menu item
         * @since 1.0
         * @param {jQuery|Element|string} item - the element to set as active.
         */
		setActive: function (item) {

		    item = $(item);

		    if (item.length !== 1) return;

		    this._setActive(item);

		},
        
	});

})(jQuery);;
/**
 * @fileOverview A jQuery Widget to handle form inputs interactions
 * @author ext-acv
 * @name $.ec.forminteraction
 * @dependencies: jQuery, jQuery UI widget factory, Magnificpopup
 * @inspiration: https://api.jqueryui.com/jquery.widget/
 * @inspiration: https://learn.jquery.com/jquery-ui/widget-factory/how-to-use-the-widget-factory/
 * @debug: $(':ec-forminteraction').length
 * error handling object: as in ajaxGetDataResult in service selectors
 */

(function ($) {
    'use strict';

    $.widget('ec.forminteraction', {

        options: {
            loading: false,
            modal: {
                mainClass: 'popup',
                selector: '.modal-warning',
                messageHolder: '.modal-warning-message',
                errorHolder: '.modal-warning-error',
                closeButton: '<button class="mfp-close icon-x"><span class="visuallyhidden">Close overlay</span></button>'
            }
        },

        revertItemsToSavedState: function () {
            for (var prop in this.initialValues) {
                if (this.initialValues.hasOwnProperty(prop)) {
                    var item = this.element.find('[name="' + prop + '"]');
                    if (item.length > 1) { //its a radio button
                        item.val([this.initialValues[prop]]);
                    } else if (item.length == 1 && item[0].type == 'checkbox') { //its a checkbox
                        if (this.initialValues[prop])
                            item.prop('checked', true);
                    } else if (item.length == 1 && item[0].type == 'text') { //its a text field
                        item.val(this.initialValues[prop])
                    } else if (item.length == 1 && item[0].type == 'select-one') { //its a size 1 select
                        item.val(this.initialValues[prop]).change();
                    } else if (item.length == 1 && item[0].type == 'hidden') { //its a hidden input
                        item.val(this.initialValues[prop]);
                    } else {
                        console.log('Unknown type: ', item);
                    }
                }
            }
        },

        processItems: function () {
            var serializedObjArray = this.element.find('input, select').serializeArray();

            //append any custom parameters to the serializedarray object
            for (var property in this.options.postData) {
                if (this.options.postData.hasOwnProperty(property)) {
                    var toAdd = {
                        name: property,
                        value: this.options.postData[property]
                    };
                    serializedObjArray.push(toAdd);
                }
            }

            //convert to plain object to be passed as ajax data
            var myPostData = this._convertSerializedArrayToObject(serializedObjArray);

            //check if data-js-validatefn exists in DOM
            if (this.validatefn) {
                var validationObj = this.validatefn(this);
                if (validationObj.isValid) {
                    this._send(this.options.endpoint, myPostData);
                } else {
                    this.element.find(this.options.submit).removeClass('btn--waiting');
                    if (validationObj.type === 'modal') {
                        this._showMagnificPopup(validationObj.content);
                    } else if (validationObj.type === 'string') {
                        if (this.notvalidfn) {
                            this.notvalidfn.apply(null, [this, validationObj]);
                        } else {
                            console.log('NotvalidFn not defined. Carry on.');
                        }
                    } else {
                        console.log('validationObj.type is missing');
                    }
                }
            } else {
                this._send(this.options.endpoint, myPostData);
            }
        },

        validate: function () { //useful for multiple forminteraction instances
            if (this.validatefn) {
                var validationObj = this.validatefn(this);
                if (validationObj.isValid) {
                    this.element.removeClass('isInvalid').addClass('isValid');
                } else {
                    this.element.removeClass('isValid').addClass('isInvalid');
                }
                return validationObj;
            } else {
                console.log('To validate you need a ValidateFn... right?');
            }
        },

        getCurrentValues: function () {
            var serializedArray = this.element.find('input, select').serializeArray();
            return this._convertSerializedArrayToObject(serializedArray);
        },

        getDirtyStatus: function () {
            var currentState = this.getCurrentValues();
            var originalState = this.initialValues;
            var isDirty = false;
            this.element.removeClass('dirty');
            if (Object.keys(currentState).length !== Object.keys(originalState).length) {
                isDirty = true;
                this.element.addClass('dirty');
            } else {
                for (var property in currentState) {
                    if (currentState.hasOwnProperty(property)) {
                        var currentvalue = currentState[property];
                        var originalvalue = originalState[property];
                        if (currentvalue !== originalvalue) {
                            isDirty = true;
                            this.element.addClass('dirty');
                            break;
                        }
                    }
                }
            }
            return isDirty;
        },

        _create: function () {
            $.extend(this.options, {
                postData: {}
            });
            if (this.element.hasClass('js-forminteraction-loading'))
                this.options.loading = true;
            if (this.element.data('jsEndpoint'))
                this.options.endpoint = this.element.data('jsEndpoint');
            if (this.element.data('jsModalwarning'))
                this.options.modal.selector = this.element.data('jsModalwarning');
            if (this.element.data('jsGuid'))
                this.options.postData.guid = this.element.data('jsGuid');
            this._setupExternalMethods();
            this._updateSavedValues();
            this.options.item = this.options.item.replace(/(\.)/, '');
            if (this.element.find(this.options.submit).length > 0) {
                this._bindSubmitEvent();
            } else {
                this._bindSelfEvents();
            }
        },

        _updateSavedValues: function () {
            var currentValuesArray = this.element.find('input, select').serializeArray();
            this.initialValues = this._convertSerializedArrayToObject(currentValuesArray);
        },

        _deeptest: function (s) {
            s = s.split('.');
            var obj = window[s.shift()];
            while (obj && s.length) obj = obj[s.shift()];
            return obj;
        },

        _checkFunctionExistance: function (strfn, namefn) {
            if (strfn) {
                var func = this._deeptest(strfn);
                if (typeof func === "function") {
                    this[namefn] = func;
                } else {
                    console.log('### Referenced function non existant: ', strfn);
                }
            }
        },

        _setupExternalMethods: function () {
            if (this.element.data('jsErrorfn')) {
                this._checkFunctionExistance(this.element.data('jsErrorfn'), 'errorfn');
            }
            if (this.element.data('jsSuccessfn')) {
                this._checkFunctionExistance(this.element.data('jsSuccessfn'), 'successfn');
            }
            if (this.element.data('jsValidatefn')) {
                this._checkFunctionExistance(this.element.data('jsValidatefn'), 'validatefn');
            }
            if (this.element.data('jsNotvalidfn')) {
                this._checkFunctionExistance(this.element.data('jsNotvalidfn'), 'notvalidfn');
            }
        },

        _convertSerializedArrayToObject: function (serializedObjArray) {
            var plainObj = {};
            for (var i = 0; i < serializedObjArray.length; i++) {
                if (serializedObjArray[i].value === 'on') { //jquery serializes as ON but .NET wants TRUE
                    serializedObjArray[i].value = 'True';
                }
                plainObj[serializedObjArray[i].name] = serializedObjArray[i].value
            }
            return plainObj;
        },

        _bindSubmitEvent: function () {
            var self = this;
            var $submitButton = this.element.find(this.options.submit);
            var $cancelButton = this.element.find(this.options.cancel);
            this._on($cancelButton, {
                'click': function (evt) {
                    evt.preventDefault();
                    self.revertItemsToSavedState();
                }
            });
            this._on($submitButton, {
                'click': function (evt) {
                    evt.preventDefault();
                    $(evt.target).addClass('btn--waiting');
                    self.processItems();
                }
            });
            this._on({ //just to check if its dirty
                'change input, change select': function (evt) {
                    evt.preventDefault();
                    self.getDirtyStatus();
                }
            });
        },

        _bindSelfEvents: function () {
            var self = this;
            this._on({
                'click a': function (evt) {
                    evt.preventDefault();
                    var $me = $(evt.target);
                    if (!$me.hasClass(self.options.item.replace(/(\.)/, ''))) {
                        return;
                    }
                    self.processItems();
                }
            });
            this._on({
                'change': function (evt) {
                    var $me = $(evt.target);
                    if (!$me.hasClass(self.options.item.replace(/(\.)/, ''))) {
                        return;
                    }
                    self.processItems();
                }
            });
        },

        _showMagnificPopup: function (incoming) {
            var self = this;
            var $selector = $(this.options.modal.selector);
            var elementToShow;
            if ($selector.length > 0) {
                $selector.find(this.options.modal.errorHolder).remove();
                if ($.isArray(incoming)) {
                    $.each(incoming, function (index, value) {
                        $('<p/>', {
                            'class': self.options.modal.errorHolder.replace(/(\.)/, ''),
                            text: value
                        }).appendTo($selector.find('.popup__inner'));
                    });
                    elementToShow = this.options.modal.errorHolder;
                } else {
                    elementToShow = incoming;
                }
                $.magnificPopup.open({
                    mainClass: this.options.modal.mainClass,
                    closeMarkup: this.options.modal.closeButton,
                    items: {
                        src: this.options.modal.selector
                    },
                    callbacks: {
                        beforeOpen: function () {
                            this.popupInstance = $.magnificPopup.instance;
                        },
                        open: function () {
                            this.popupInstance.content.find(self.options.modal.messageHolder).hide();
                            this.popupInstance.content.find(elementToShow).show();
                        }
                    }
                });
            } else if ($(incoming).length > 0) {
                $.magnificPopup.open({
                    mainClass: this.options.modal.mainClass,
                    closeMarkup: this.options.modal.closeButton,
                    items: {
                        src: incoming
                    }
                });
            } else {
                console.log('Something is missing here... where is the modal?')
            }
        },

        _send: function (endpoint, params) {
            var self = this;
            if (this.options.loading) {
                jQuery.publish("spinner.open", [jQuery("body")]);
            }
            $.ajax({
                method: 'GET',
                url: endpoint,
                data: params
            }).done(function (data) {
                if (typeof data === 'string') { //if it's just HTML
                    if (self.successfn) {
                        self.successfn.apply(null, [self.element, data]);
                    } else {
                        console.log('You may define a SuccessFn.');
                    }
                } else { //if it's an object
                    if (!data.hasOwnProperty('Valid')) {
                        console.log('You need a Valid property.')
                    } else {
                        if (data.Valid !== 'false' && data.Valid) {
                            if (self.successfn) {
                                self.successfn.apply(null, [self.element, data]);
                            }
                            self._updateSavedValues();
                        } else {
                            if (self.errorfn) {
                                self.errorfn.apply(null, [self.element, data]);
                            } else {
                                console.log('You may define a ErrorFn.');
                            }
                        }
                    }
                }
            }).fail(function (jqXHR, textStatus, errorThrown) {
                console.log('Fail:', textStatus, errorThrown);
            }).always(function () {
                self.getDirtyStatus();
                self.element.find(self.options.submit).removeClass('btn--waiting');
                jQuery.publish("spinner.close", [jQuery("body")]);
            });
        }

    });

})(jQuery);;
(function ($, document, undefined) {

	'use strict';


	///////////

	function YouTube(videoUrl) {

	    var pattern = /^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/,
			matches;

		if (!(this instanceof YouTube)) {

			matches = videoUrl.match(pattern);
			if (!matches) {
				return undefined;
			}

			return new YouTube(videoUrl, matches[1]);
		}

		this._init.apply(this, arguments);
		return this;

	};

	YouTube.initializing = false;
	YouTube.initialized = false;

	YouTube.prototype._init = function (videoUrl, videoId) {

		var self = this;

		this.videoUrl = videoUrl;
		this.videoId = videoId;

		this._loadApi();

		$(document).on({
			'playerReady.playvideoapi': $.proxy(self._onPlayerReady, self),
			'startPlaying.playvideoapi': $.proxy(self._onStartPlaying, self)
		});

	};

	YouTube.prototype._onPlayerReady = function (e, videoId, player) {

		var self = this;

		if (videoId !== this.videoId) {
			return;
		}

		player.addEventListener('onStateChange', function (event) {
			if (event.data === YT.PlayerState.PLAYING) {
				$(document).triggerHandler('startPlaying.playvideoapi', [self.videoId]);
			}
		});

		this.player = player;

	};

	YouTube.prototype._onStartPlaying = function (e, videoId) {

		if (!this.player || this.videoId === videoId) {
			return;
		}

		this.player.pauseVideo();

	};

	YouTube.prototype._loadApi = function () {

		if (YouTube.initializing || YouTube.initialized) {
			return;
		}

		YouTube.initializing = true;

		var body = document.querySelector('body');

		var script = document.createElement('script');
		script.id = 'playVideoYouTubeApi';
		script.setAttribute('type', 'text/javascript');
		script.src = "//www.youtube.com/iframe_api";

		body.appendChild(script);

		YouTube.initializing = false;
		YouTube.initialized = true;

	}

	YouTube.prototype.createPlayer = function (replaceFunc) {

		var videoId = this.videoId,
			elementId = videoId + '_t';

		var div = document.createElement('div');
		div.id = elementId;
		div.setAttribute('data-videoid', videoId);

		//container.appendChild(div);

		replaceFunc(div);

		new YT.Player(elementId, {
			width: '100%',
			height: 'auto',
			videoId: videoId,
			playerVars: {
				'autoplay': 1,
				'controls': 1
			},
			events: {
				'onReady': function (event) {
					$(document).triggerHandler('playerReady.playvideoapi', [videoId, event.target]);
				}
			}
		});

	};

	YouTube.prototype.getPlayerElement = function () {
		return document.getElementById(this.videoId + '_t');
	};

	YouTube.prototype.pause = function () {

		if (!this.player) {
			return;
		}

		this.player.pauseVideo();

	};

	YouTube.prototype.play = function () {

		if (!this.player) {
			return;
		}

		this.player.playVideo();

	};

	///////////

	function FaceBook(videoUrl) {

		var pattern = /^.*(?:facebook\.(?:[a-z]+))(?:\/[a-z]+)?\/(?:video\.php\?v(?:i)?=|videos\/)([^#\&\?\/]*).*/,
			matches;


		if (!(this instanceof FaceBook)) {

			matches = videoUrl.match(pattern);
			if (!matches) {
				return undefined;
			}

			return new FaceBook(videoUrl, matches[1]);
		}

		this._init.apply(this, arguments);
		return this;

	};

	FaceBook.initializing = false;
	FaceBook.initialized = false;

	FaceBook.prototype._init = function (videoUrl, videoId) {

		var self = this;

		this.videoUrl = videoUrl;
		this.videoId = videoId;

		this._loadApi();

		$(document).on({
			'playerReady.playvideoapi': $.proxy(self._onPlayerReady, self),
			'startPlaying.playvideoapi': $.proxy(self._onStartPlaying, self)
		});

	};

	FaceBook.prototype._onPlayerReady = function (e, videoId, player) {

		var self = this;

		if (videoId !== this.videoId) {
			return;
		}

		player.subscribe('startedPlaying', function () {
			$(document).triggerHandler('startPlaying.playvideoapi', [self.videoId]);
		});

		this.player = player;

	};

	FaceBook.prototype._onStartPlaying = function (e, videoId) {

		if (!this.player || this.videoId === videoId) {
			return;
		}

		this.player.pause();

	};

	FaceBook.prototype._loadApi = function () {

		if (FaceBook.initializing || FaceBook.initialized) {
			return;
		}

		FaceBook.initializing = true;

		var body = document.querySelector('body');

		if (!document.querySelector('#fb-root')) {

			var div = document.createElement('div');
			div.id = 'fb-root';

			body.appendChild(div);

		};

		var script = document.createElement('script');
		script.id = 'playVideoFaceBookApi';
		script.innerHTML = '' +
			'window.fbAsyncInit = function() {' +
			'   FB.init({ version: "v2.7", xfbml: true });' +
			'   FB.Event.subscribe("xfbml.ready", function(msg) {' +
			'       if (msg.type !== "video") { return; }' +
			'       var videoId = document.getElementById(msg.id).getAttribute("data-videoid");' +
			'       $(document).triggerHandler("playerReady.playvideoapi", [videoId, msg.instance]);' +
			'   });' +
			'' +
			'};' +
			'(function(d, s, id) {' +
			'var js, fjs = d.getElementsByTagName(s)[0];' +
			'if (d.getElementById(id)) return;' +
			'js = d.createElement(s); js.id = id; ' +
			'js.src = "//connect.facebook.net/en_US/sdk.js";' +
			'fjs.parentNode.insertBefore(js, fjs);' +
			'}(document, "script", "facebook-jssdk"));';

		body.appendChild(script);

		FaceBook.initializing = false;
		FaceBook.initialized = true;

	};

	FaceBook.prototype.createPlayer = function (replaceFunc) {

		var videoId = this.videoId,
			elementId = videoId + '_t';

		var div = document.createElement('div');
		div.id = elementId;
		div.className = 'fb-video';
		div.setAttribute('data-href', 'https://www.facebook.com/facebook/videos/' + videoId);
		div.setAttribute('data-autoplay', 'true');
		div.setAttribute('data-allowfullscreen', 'true');
		div.setAttribute('data-videoid', videoId);

		//container.appendChild(div);

		var script = document.createElement('script');
		script.innerHTML = '(function() { FB.XFBML.parse(); })()';
		//container.appendChild(script);

		replaceFunc([div, script]);

	};

	FaceBook.prototype.getPlayerElement = function () {
		return document.getElementById(this.videoId + '_t');
	};

	FaceBook.prototype.pause = function () {

		if (!this.player) {
			return;
		}

		this.player.pause();

	};

	FaceBook.prototype.play = function () {

		if (!this.player) {
			return;
		}

		this.player.play();

	};

	///////////


	var apis = [YouTube, FaceBook];

	$.widget('ec.playvideo', {
		options: {

			/**
			 * @namespace ec.playvideo#videoUrl
			 * @property {string}  videoUrl - The url to the video
			 * @default null
			 */
			videoUrl: null,

			/**
			 * @namespace ec.playvideo#usePopup
			 * @property {boolean}  usePopup - Indicates that videos are played in a popup 'window'.
			 * @default true
			 */
			usePopup: true,

			/**
			 * @namespace ec.playvideo#disablePopupBreakpoint
			 * @property {number}  disablePopupBreakpoint - A width value (in pixels) under with videos are played inline.
			 * @default 850
			 */
			disablePopupBreakpoint: 850,

			/**
			 * @namespace ec.playvideo#footerSelector
			 * @property {string}  footerSelector - A selector to get the element to keep as a footer when replacing the player inline.
			 * @default .js-play-video-footer
			 */
			footerSelector: '.js-play-video-footer',

			/**
			 * @namespace ec.playvideo#popupFooter
			 * @property {string}  popupFooter - The content to be used as footer in the popup.
			 * @default ''
			 */
			popupFooter: '',

		},


		/**
		 * @name ec.playvideo#_create
		 * @function
		 * @private
		 */
		_create: function () {

			var self = this,
				options = self.options,
				element = self.element,
				handlers = {};

			self.videoUrl = options.videoUrl || element.attr('href');
			if (!self.videoUrl) {
				return;
			}

			// load the api
			self._loadApi();


			self.usePopup = document.body.clientWidth > options.disablePopupBreakpoint && options.usePopup;
			self.footerElement = element.find(options.footerSelector).clone();


			// bind some events

			handlers['click'] = function (e) {
				e.preventDefault();
				e.stopPropagation();
				self._doVideo();
			};

			self._on(element, handlers);
		},



		/**
		 * @name ec.playvideo#_loadApi
		 * @function
		 * @private
		 * @description Detects the api to use for handling the video and loads it.
		 */
		_loadApi: function () {

			var self = this,
				l = apis.length;

			for (var i = 0; i < l; i++) {

				var instance = apis[i](self.videoUrl);
				if (!instance) {
					continue;
				}

				self.api = instance;
				break;

			}

		},

		/**
		 * @name ec.playvideo#_doVideo
		 * @function
		 * @private
		 */
		_doVideo: function () {

			var self = this;
			self.element.addClass('video-loaded');
            
            
            // if we are in the sitecore experience editor don't load the video
			if (!$('.sc-editor').length) {
				if (self.usePopup) {
					self._popupVideo();
				} else {
					self._injectVideo();
				}
			}

		},

		/**
		 * @name ec.playvideo#_replaceContent
		 * @function
		 * @private
		 * @description Creates an inline player using the api.
		 */
		_injectVideo: function () {

			var self = this;

			if (self.api.player) {
				return;
			}

			// we only clean elements when the player is ready, in case there is an error with player initialization
			function hideTargetWhenPlayerReady() {
				self.element.children().not(self.api.getPlayerElement()).remove();
				self.footerElement.appendTo(self.element);
			};
			self.document.one('playerReady.playvideoapi', $.proxy(hideTargetWhenPlayerReady, self));

			self.api.createPlayer(function (elements) {
				self.element
					.children().addClass('js-hide').end()
					.prepend(elements);
			});

		},

		/**
		 * @name ec.playvideo#_popupContent
		 * @function
		 * @private
		 * @description Opens the video in a popup overlay.
		 */
		_popupVideo: function () {

			var self = this,
				videoUrl = self.videoUrl;

			$.magnificPopup.open({

				items: [{
					src: videoUrl,
                }],
				type: 'iframe',
				iframe: {
					markup: '<div class="mfp-iframe-scaler prvprv">' +
						'<div class="mfp-close"></div>' +
						'<iframe class="mfp-iframe" frameborder="0" allowfullscreen></iframe>' +
						'</div>' +
						'<p class="video-popup__footer"></p>',
					patterns: {
						facebook: {
							index: 'facebook.com',
							id: function (url) {
								return url;
							},
							src: 'https://www.facebook.com/v2.7/plugins/video.php?allowfullscreen=true&autoplay=true&href=%id%'
						},
						youtu: {
						    index: 'youtu.be',
						    id: function (url) {

						        // Capture everything after the hostname, excluding possible querystrings.
						        var m = url.match(/^.+youtu.be\/([^?]+)/);

						        if (null !== m) {
						            return m[1];
						        }

						        return null;

						    },
						    // Use the captured video ID in an embed URL. 
						    // Add/remove querystrings as desired.
						    src: '//www.youtube.com/embed/%id%?autoplay=1&rel=0'
						}
					}
				},
				callbacks: {
					open: function () {
						this.contentContainer.addClass("video-popup--has-header")
							.find('.video-popup__footer')
							.html(self.options.popupFooter);
					}
				}

			});

		},

		/**
		 * @name ec.playvideo#play
		 * @function
		 * @description Plays the video.
		 */
		play: function () {

			var self = this;

			self._doVideo();

			self.api.play();
		},

		/**
		 * @name ec.playvideo#pause
		 * @function
		 * @description Pauses the video.
		 */
		pause: function () {

			var self = this;

			if (!self.api) {
				return;
			}

			self.api.pause();
		}

	});

})(window.jQuery, document);;
/**
 *  SCROLL TO TOP NAVIGATION
 * 
 */

(function ($) {

	'use strict';

	$.widget('ec.scrolltotop', {


		options: {
			container: '#back-to-top',
			classes: {
				show: 'show'
			},
			events: {
				click: 'click',
				scroll: 'scroll'
			},
			scrollTrigger: 500
		},

		/**
		 * Leverages options set via data-* attributes
		 * Ex: data-generate-pager will match generatePager option.
		 */
		_getCreateOptions: function () {

			var elem = this.element,
				options = {};

			$.each(this.options, function (option) {
				var value = elem.data(option.replace(/[A-Z]/g, function (c) {
					return "-" + c.toLowerCase();
				}));
				if (value !== undefined) {
					options[option] = value;
				}
			});

			return options;
		},
		/**
		 * Constructor
		 */
		_create: function () {
			if (this.options.container.length) {
				//
				this._bindEvents()
			}

		},
		_scrollToTop: function (e) {
			$('html,body').animate({
				scrollTop: 0
			}, 700);
           // console.log('back to top') ;
		},
		_showMeOrNot: function () {
			var scrollTrigger = this.options.scrollTrigger,
				container = this.options.container, // px
				scrollTop = $(window).scrollTop();
			if (scrollTop > scrollTrigger) {
				$(container).addClass('show');
			} else {
				$(container).removeClass('show');
			}
		},
		_bindEvents: function () {

			var self = this,
				eventOptions = this.options.events;

			$(self.options.container).on(eventOptions.click, function (e) {
				e.preventDefault();
				self._scrollToTop();
			});

			$(window).on(eventOptions.scroll, function () {
				self._showMeOrNot();
			});
		}

	});

})(jQuery);;
// Use: blur images with the help of a canvas element
// IE does not support css filters yet (14-4-2016)

/*
 html example: 
 <div class="item">
          	<img data-canvas-image="" src="/Aline/MediaLibrary/homepage/petrol-gun.jpg" alt="img" style="visibility: hidden;">
            <canvas class="blurCanvas" id="blurCanvas0" width="576" height="330" data-canvas=""></canvas>
</div>
*/

// you have to specify width and height on the canvas element, otherwise it renders the new image on size 0

(function ($, stackBlur, undefined) {

    'use strict';


    /**
     * @name ec.blurcanvas
     * @description Creates a blurred version of the image element is applied on using a canvas.
     */
    $.widget('ec.blurcanvas', {

        options: {

            /**
             * @namespace ec.blurcanvas#imageSelector
             * @property {string}  imageSelector - The selector for the original image element.
             * @default [data-canvas-image]
             */
            imageSelector: '[data-canvas-image]',

            /**
             * @namespace ec.blurcanvas#blurRadius
             * @property {Number}  blurRadius - Adjust this value to make the canvas more or less blurry.
             * @default 10
             */
            blurRadius: 10

        },

        /**
         * @inheritdoc
         */
        _create: function () {

            var self = this;
        
            // create the blurred version
            
            self._doImage(self.element[0]);

            // bind events in case the image is lazy loaded

            self._on(self.element, {
                'load': function (e) {
                    self._doImage(e.currentTarget);
                }
            });

        },


        /**
         * @name ec.blurcanvas#_createBlurredCanvas
         * @function
         * @private
         * @description Creates the blurred version of the given image element.
         * @parameter {Element} imageElement - The image element
         */
        _doImage: function (imageElement) {

            var self = this,
		        options = this.options,
                $image,
                $canvas,
		        canvasId,
		        imageSrc;

            $image = $(imageElement);
            canvasId = $image.next('canvas').attr('id');
            $canvas = $('#' + canvasId).hide();
            imageSrc = imageElement.src;

            self._createBlurredCanvas(imageSrc, canvasId, options.blurRadius, function (isDone) {
                if (!isDone) { return; }
                $image.css('visibility', 'hidden');
                $canvas.fadeIn('slow');
            });
            
        },


        /**
         * @name ec.blurcanvas#_createBlurredCanvas
         * @function
         * @private
         * @description Creates the blurred version of the given image src and draws it on the canvas.
         * @parameter {string}   imageSrc - The source of the image
         * @parameter {string}   canvasId - The id of the canvas element
         * @parameter {number}   radius   - The blur radius
         * @parameter {Function} callback - A function to call after bluring is done
         */
        _createBlurredCanvas: function (imageSrc, canvasId, radius, callback) {

            var self = this, canvas, context, image, w, h;

            canvas = document.getElementById(canvasId);

            if (!canvas || !imageSrc) {
                if ($.isFunction(callback)) {
                    callback.call(self, false);
                }
                return;
            }

            context = canvas.getContext('2d');
            w = canvas.width;
            h = canvas.height;

            image = new Image();
            image.src = imageSrc;
            image.crossOrigin = "";

            image.onload = function () {

                context.drawImage(image, 0, 0, w, h);
                stackBlur.canvasRGB(canvas, 0, 0, w, h, radius);

                if ($.isFunction(callback)) {
                    callback.call(self, true);
                }

            };
        }

    });

})(window.jQuery, window.StackBlur);;
/**
 *  Enable Tables to be scrollable and leave the rowheader sticky 
 * 
		
 */

(function ($) {

	'use strict';

	$.widget('ec.mobilescrolltable', {

		options: {
			container: '.wysiwyg table.table',
			template: '<div class="js-scroll-table-wrapper"><div class="js-scroll-table scroll-table--horz"></div></div>',
			classes: {
				scrollTable: 'js-scroll-table',
				stickyTdTable: 'js-sticky-td-table',
                stickyTd : '.rowheader'
			}

		},

		/**
		 * Constructor
		 */
		_create: function () {
			if (this.options.container.length) {
				//
				this._wrapTable();
				this._createStickyArea();
			}

		},
		_wrapTable: function (e) {
			//wrap bare tables in scrollable div and a wrapper class
			var opts = this.options;
			$(this.element).wrap(opts.template);
		},
		_createStickyArea: function () {

			var opts = this.options,
				container = opts.container,
				classes = opts.classes;


			//wrap bare tables in scrollable div and a wrapper class
			var instance = $(this.element).closest('.' + classes.scrollTable),
				thecopy = instance.clone(),
				clonedtable;

			// create clone of the scrollable table
			$(instance).parent().prepend($(thecopy).addClass(classes.stickyTdTable).removeClass(classes.scrollTable + " , scroll-table--horz"));

			//make it sticky
			clonedtable = $(instance).parent().find('.' + classes.stickyTdTable);
			$(clonedtable).removeClass(classes.scrollTable);

			//hide everything from cloned table except rowheader, remove does not work because then the layout break
			$(clonedtable).find("tr td:not(" + classes.stickyTd + "), th:not(:first-child),  caption").css('visibility', 'hidden');


		}

	});

})(jQuery);;
/**
 *  switch between 2 elements
 * 
		//SWITCH BETWEEN ELEMENTS
		$('[data-switch]').click(function (e) {
			e.stopPropagation();
			$(this).closest('[data-class="element-switch"]').find('[data-switch]').toggleClass("js-hide");
		});
 */

(function ($) {

	'use strict';

	$.widget('ec.switchelements', {

		options: {
			container: '[data-class="element-switch"]',
			element: '[data-switch]',
			classes: {
				hidden: 'js-hide'
			},
			events: {
				click: 'click [data-switch]'
			}
		},

		/**
		 * Constructor
		 */
		_create: function () {
			if (this.options.container.length) {
				this._bindEvents();
			}

		},
		_bindEvents: function (e) {
			var click = this.options.events.click,
                self=this;
			
            this._on(this.element, { click : function (event) {
					event.stopPropagation();
					self._switch(event.currentTarget);
				}
			});

		},
		_switch: function (target) {
			var opts = this.options,
				container = opts.container,
				element = opts.element,
				classes = opts.classes;

			$(target).closest(container).find(element).toggleClass(classes.hidden);
		}

	});

})(jQuery);;
/**
 *  switch between 2 elements
 * 
		//MOBILE BROWSE MOBILE MENU NAVIGATION
		$('.js-mobile-menu-dropdown-navigation').on('change', function () {
			var url = $('option:selected', this).data('url');
			//console.log(url);
			window.location = url;
		});
 */

(function ($) {

	'use strict';

	$.widget('ec.dropdownnavigation', {

		options: {
			container: '.js-mobile-menu-dropdown-navigation',
			events: {
				change: 'change'
			}
		},

		/**
		 * Constructor
		 */
		_create: function () {
			if (this.options.container.length) {
				this._bindEvents();
			}
		},
		_bindEvents: function (e) {
			var change = this.options.events.change,
				self = this;

			$(this.element).on(change, function (event) {
				self._switchUrl(event.currentTarget);
			});

		},
		_switchUrl: function (target) {
            
			//look at the data-url attr and redirect
			var url = $('option:selected', target).data('url') || false;
			//console.log(url);
			if (url) {
				window.location = url;
			}

		}

	});

})(jQuery);;
/**
 *  
 * //RESET FILTERS ON FORMS- BTN FUNCTION

 */

(function ($) {

	'use strict';

	$.widget('ec.clearfilters', {

		options: {
			container: '.js-clear-filters'
		},

		/**
		 * Constructor
		 */
		_create: function () {
			if (this.options.container.length) {
				this._bindEvents();
			}
		},
		_bindEvents: function (e) {
			var self = this;
			self._on(this.element, {
				'click': self._clearfilters
			});
		},
		_clearfilters: function (event) {
			var form = $(this.element).closest('form');
			// undo selections
			form.find('option').attr('selected', false);
			// submit changed selection
			form.find('select').trigger('change');
			// retrigger uniform js to make sure conditional selects are initialized again
			form.find('select').uniform();
		}

	});

})(jQuery);;
// this script is usefull in a page with multiple video's. When a video gets started, the current playing video gets stopped.
// It also installs the youtube upi  in the header. 
// There is also a responsive functionality, cause videos are displayed in a popup(magnificPopup) on desktop and fullwidth inline on mobile.

(function ($) {
	'use strict';

	var youtubePlayers = [];

	$.widget('ec.zoomimage', {



		// 2. CONFIGURATION
		options: {
			cache: {
				container: '.js-zoom-image',

			},
			zoomcontainer: '.has-zoomicon',
			zoomicontemplate: '<div class="zoomicon"><span class="icon-stack"><i class="icon-bullet icon-stack-2x icon--inverse"></i><i class="icon-search icon-stack-1x "></i></span></div>',
			events: {
				click: 'click'
			},

			//for future responsive behavior
			popUpOnDesktop: true,
			//to overwrite this, use data-popUpOnDesktop='false' on the container,

			breakPointMobile: 850

		},


		/**
		 * Constructor
		 */
		_create: function () {

			this._initialize();

		},


		_initialize: function () {

			this.container = $(this.options.cache.container);
			if (this.container.length) {
				//console.log('init');
				// override the settings from data-attributes
				var self = this;
				self._createZoomIcon();
				self.bindEvents();

			}
		},
		openPopup: function (el) {
			//thre is some confusion about 'this'
			var self = this,
				settings = self.settings,
                //if there is a separate big image defined take that url, otherwise the same src as the thumbnail
				imgUrl =  $(el).data('bigimageSrc') || $(el).attr('src');

			$.magnificPopup.open({
				//               
				items: {
					src: imgUrl
				},
				type: 'image',
                overflowY: 'auto',
                image :{
                    verticalFit: false
                }
			})


		},
		_createZoomIcon: function () {
			var settings = this.options,
				zoomIcon = settings.zoomicontemplate,
				self = this;
			$(this.element).wrap('<figure class="has-zoomicon"></figure>').closest('.has-zoomicon').append(zoomIcon);

		},


		bindEvents: function () {
			var settings = this.options,
				self = this,
				options = settings.options,
				clickarea = $(self.options.zoomcontainer);


			this._on(clickarea, {
				'click .js-zoom-image': function (ev) {
					ev.preventDefault();
					var el = this.element;
					self.openPopup(ev.currentTarget);

				}
			});

            $('.zoomicon').on('click', function (ev) {
					ev.preventDefault();
					var el = $(this).parent().find('.js-zoom-image')[0];
					self.openPopup(el);
                    console.log('zoom');

				})

		}

	});

})(jQuery);;
/**
 *  
 * //dynamic links for use with tabs
example use = visually static 'see all' link, combined with dynamic created link in tab
 */


(function ($) {

	'use strict';

	$.widget('ec.dynamiclink', {

		options: {
			container: '[data-class="dynamic-link"]',
			linksrc: '[data-class="dynamic-link-source"]',
			sharedparent: '.tabpanes',
			visibleselector: '.js-owl-visible'
		},

		/**
		 * Constructor
		 */
		_create: function () {
			if (this.options.container.length) {
				this._bindEvents();
			}
		},
		_bindEvents: function (e) {
			var self = this;
			self._on(this.element, {
                //mouseover is to let the user see which link will be used
				'mouseover': function (e) {
					self._collectdynamiclink(e);
				}
			});
		},
		_collectdynamiclink: function (event) {
            
			var self = this,
				opts = self.options,
				parent = $(event.currentTarget).closest(opts.sharedparent),
                //find link in nearest visible elemnt in shared parent 
				link = parent.find(opts.visibleselector).find(opts.linksrc).attr('href');
                $(event.currentTarget).attr('href', link );
		}

	});

})(jQuery);;
/* 
 This is meant to provide basic keyboard control over the main navigation. You can use tab, and arrow keys to scroll through the navigation links
*/

(function ($) {
	'use strict';

	$.widget('ec.accessiblemenu', {

		// 2. CONFIGURATION
		options: {
			container: '.js-navmenu--main',
			accessibleoptions: {
				uuidPrefix: "accessible-megamenu",

				/* css class used to define the megamenu styling */
				//menuClass: "navmenu--main",

				/* css class for a top-level navigation item in the megamenu */
				topNavItemClass: "js-navmenu-hassubmenu",

				/* css class for a megamenu panel */
				panelClass: "navmenu__item__submenu",

				/* css class for a group of items within a megamenu panel */
				panelGroupClass: "block-list__item",

				/* css class for the hover state */
				hoverClass: "hover",

				/* css class for the focus state */
				focusClass: "focus",

				/* css class for the open state */
				openClass: "open"
			},

		},

		/**
		 * Constructor
		 */
		_create: function () {
			this._initialize();
			//console.log('hello there')
		},



		_initialize: function () {
			var options = this.options,
				container = $(options.container),
				focusClass = options.accessibleoptions.focusClass;
			if (container.length) {
				$(container).accessibleMegaMenu(options.accessibleoptions);





				$('.navmenu--main .js-accessible-submenu .block-list__item > a').on('focus', function (e) {
					$('.navmenu--main .js-accessible-submenu .block-list__item').removeClass('open');
					$(this).parent().addClass('open');
				});
				$('.navmenu--main .navmenu__item:not(:first) a').on('focus', function (e) {
					$('.navmenu--main .js-accessible-submenu .block-list__item').removeClass('open');
				});

				$('.navmenu--main .navmenu__item')
					.on('focus', function (e) {
						$('.navmenu--main .navmenu__item').removeClass(focusClass);
						$(this).addClass(focusClass);
					})


				$('.navmenu--main .navmenu__item ')
					.on('mouseout', function (e) {
						$('.navmenu--main *').removeClass(focusClass);
						$('.navmenu--main * ').removeClass('open');
					})


				//ipad, large touch devices rendering desktop view
				$('.navmenu--main .js-navmenu-hassubmenu > a').on('touchstart', function (e) {
					//toggle and reset other focused dropdown
					if ($(this).parent().hasClass(focusClass)) {
						$('.navmenu--main .js-navmenu-hassubmenu').removeClass(focusClass);
					} else {
						$('.navmenu--main .js-navmenu-hassubmenu').removeClass(focusClass);
						$('.block-list__item--pane-trigger').removeClass(focusClass);
						$(this).parent().addClass(focusClass);
					}
				});
				$('.block-list__item--pane-trigger').on('touchstart', function (e) {
					$('.block-list__item--pane-trigger').removeClass(focusClass);
					$(this).addClass(focusClass);
				});
                
                
              
				$('.nohover .block-list__item--pane-trigger').on('click', function (e) {
					$('.block-list__item--pane-trigger').removeClass(focusClass);
					$(this).addClass(focusClass);
				});

				
                //this one is explicit linking behavior for safari and stock browsers on Android
				$('.navmenu--main a.navigation-tiles__tile , .navmenu--main a.block-list__item').on('click', function (e) {
					var linktarget = $(this).attr('target') || "_self",
						linkurl = $(this).attr('href');
                    
					e.preventDefault();
					window.open(linkurl, linktarget);
				});



			}
		}

	});

})(jQuery);;
/**
 * @fileOverview A jQuery Widget to handle simple show hide interaction on click
 * @author ext-acv
 * @name $.ec.togglefade
 * @dependencies: jQuery, jQuery UI widget factory
 * @inspiration: https://api.jqueryui.com/jquery.widget/
 * @inspiration: https://learn.jquery.com/jquery-ui/widget-factory/how-to-use-the-widget-factory/
 */

(function ($) {
    'use strict';

    $.widget('ec.togglefade', {

        options: {
            trigger: '.js-togglefade-trigger',
            target: 'jsTogglefadeTarget'
        },

        _create: function () {
            this.trigger = this.element.find(this.options.trigger);
            this._bindEvents();
        },

        _bindEvents: function () {
            var self = this;
            this._on(this.trigger, {
                'click': function (evt) {
                    evt.preventDefault();
                    var toggleElement = $(evt.target).data(self.options.target);
                    self._toggleElement(toggleElement, $(evt.target));
                }
            });
        },

        _toggleElement: function (toggleElement, toggleTrigger) {
            var $toggleElement = this.element.find(toggleElement);
            $toggleElement.stop();
            if ($toggleElement.is(':visible')) {
                toggleTrigger.next().removeClass('icon-chevron-up').addClass('icon-chevron-down');
            } else {
                toggleTrigger.next().removeClass('icon-chevron-down').addClass('icon-chevron-up');
            }
            $toggleElement.fadeToggle(200);
        }

    });

})(jQuery);;
/**
 * @fileOverview A jQuery Widget to handle quality boxes modal with animation
 * @author ext-acv
 * @name $.ec.qualityboxes
 * @dependencies: jQuery, jQuery UI widget factory
 * @inspiration: https://api.jqueryui.com/jquery.widget/
 * @inspiration: https://learn.jquery.com/jquery-ui/widget-factory/how-to-use-the-widget-factory/
 */

(function ($) {
    'use strict';

    $.widget('ec.qualityboxes', {

        options: {
            indicator: '.quality-boxes__ruler__indicator',
            item: '.quality-boxes__indicators__item-bar',
            stars: '.icon-stars'
        },

        _create: function () {
            //console.log('create qualitybox', this.element);
        },

        start: function () {
            //console.log('start qualitybox', this.element);

            //determine if it's ruler or stars
            var self = this;
            if (this.element.find(this.options.stars).length > 0) {
                this.element.find(this.options.stars).each(function (index, el) {
                    $(el).attr('class', 'icon-stars');
                    setTimeout(function () {
                        self.buildStars(index, el);
                    }, (index + 1) * 600); //delay between star sets
                });
            } else {
                var $indicator = this.element.find(this.options.indicator);
                var boxValue = $indicator.data('value');
                $indicator.css({'left': '6%'}).animate({'left': boxValue + '%'}, 800, 'swing');
                this.element.find(this.options.item).each(function (index, el) {
                    $(el).css({'width': 0});
                    var itemValue = $(el).data('value');
                    $(el).stop(true, true).delay(350 * (index + 1)).animate({
                        'width': itemValue + '%'
                    }, 1000, 'swing');
                });
            }
        },

        buildStars: function (index, el) {
            var position = 0;
            var starsSize = $(el).data('stars');
            var myinterval = setInterval(function () {
                position++;
                $(el).removeClass('icon-stars--' + (position - 1)).addClass('icon-stars--' + position);
                if (position == starsSize) {
                    clearInterval(myinterval);
                }
            }, 120); //delay between stars
        }

    });

})(jQuery);;
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ┌────────────────────────────────────────────────────────────┐ \\
// │ Eve 0.4.2 - JavaScript Events Library                      │ \\
// ├────────────────────────────────────────────────────────────┤ \\
// │ Author Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) │ \\
// └────────────────────────────────────────────────────────────┘ \\
(function (glob, factory) {
    if (typeof define === "function" && define.amd) {
        define("eve", function() {
            return factory();
        });
    } else if (typeof exports === "object") {
        module.exports = factory();
    } else {
        glob.eve = factory();
    }
}(this, function(){
    var version = "0.4.2",
        has = "hasOwnProperty",
        separator = /[\.\/]/,
        wildcard = "*",
        fun = function () {},
        numsort = function (a, b) {
            return a - b;
        },
        current_event,
        stop,
        events = {n: {}},
    /*\
     * eve
     [ method ]

     * Fires event with given `name`, given scope and other parameters.

     > Arguments

     - name (string) name of the *event*, dot (`.`) or slash (`/`) separated
     - scope (object) context for the event handlers
     - varargs (...) the rest of arguments will be sent to event handlers

     = (object) array of returned values from the listeners
     \*/
        eve = function (name, scope) {
            name = String(name);
            var e = events,
                oldstop = stop,
                args = Array.prototype.slice.call(arguments, 2),
                listeners = eve.listeners(name),
                z = 0,
                f = false,
                l,
                indexed = [],
                queue = {},
                out = [],
                ce = current_event,
                errors = [];
            current_event = name;
            stop = 0;
            for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) {
                indexed.push(listeners[i].zIndex);
                if (listeners[i].zIndex < 0) {
                    queue[listeners[i].zIndex] = listeners[i];
                }
            }
            indexed.sort(numsort);
            while (indexed[z] < 0) {
                l = queue[indexed[z++]];
                out.push(l.apply(scope, args));
                if (stop) {
                    stop = oldstop;
                    return out;
                }
            }
            for (i = 0; i < ii; i++) {
                l = listeners[i];
                if ("zIndex" in l) {
                    if (l.zIndex == indexed[z]) {
                        out.push(l.apply(scope, args));
                        if (stop) {
                            break;
                        }
                        do {
                            z++;
                            l = queue[indexed[z]];
                            l && out.push(l.apply(scope, args));
                            if (stop) {
                                break;
                            }
                        } while (l)
                    } else {
                        queue[l.zIndex] = l;
                    }
                } else {
                    out.push(l.apply(scope, args));
                    if (stop) {
                        break;
                    }
                }
            }
            stop = oldstop;
            current_event = ce;
            return out.length ? out : null;
        };
    // Undocumented. Debug only.
    eve._events = events;
    /*\
     * eve.listeners
     [ method ]

     * Internal method which gives you array of all event handlers that will be triggered by the given `name`.

     > Arguments

     - name (string) name of the event, dot (`.`) or slash (`/`) separated

     = (array) array of event handlers
     \*/
    eve.listeners = function (name) {
        var names = name.split(separator),
            e = events,
            item,
            items,
            k,
            i,
            ii,
            j,
            jj,
            nes,
            es = [e],
            out = [];
        for (i = 0, ii = names.length; i < ii; i++) {
            nes = [];
            for (j = 0, jj = es.length; j < jj; j++) {
                e = es[j].n;
                items = [e[names[i]], e[wildcard]];
                k = 2;
                while (k--) {
                    item = items[k];
                    if (item) {
                        nes.push(item);
                        out = out.concat(item.f || []);
                    }
                }
            }
            es = nes;
        }
        return out;
    };

    /*\
     * eve.on
     [ method ]
     **
     * Binds given event handler with a given name. You can use wildcards “`*`” for the names:
     | eve.on("*.under.*", f);
     | eve("mouse.under.floor"); // triggers f
     * Use @eve to trigger the listener.
     **
     > Arguments
     **
     - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
     - f (function) event handler function
     **
     = (function) returned function accepts a single numeric parameter that represents z-index of the handler. It is an optional feature and only used when you need to ensure that some subset of handlers will be invoked in a given order, despite of the order of assignment.
     > Example:
     | eve.on("mouse", eatIt)(2);
     | eve.on("mouse", scream);
     | eve.on("mouse", catchIt)(1);
     * This will ensure that `catchIt()` function will be called before `eatIt()`.
     *
     * If you want to put your handler before non-indexed handlers, specify a negative value.
     * Note: I assume most of the time you don’t need to worry about z-index, but it’s nice to have this feature “just in case”.
     \*/
    eve.on = function (name, f) {
        name = String(name);
        if (typeof f != "function") {
            return function () {};
        }
        var names = name.split(separator),
            e = events;
        for (var i = 0, ii = names.length; i < ii; i++) {
            e = e.n;
            e = e.hasOwnProperty(names[i]) && e[names[i]] || (e[names[i]] = {n: {}});
        }
        e.f = e.f || [];
        for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
            return fun;
        }
        e.f.push(f);
        return function (zIndex) {
            if (+zIndex == +zIndex) {
                f.zIndex = +zIndex;
            }
        };
    };
    /*\
     * eve.f
     [ method ]
     **
     * Returns function that will fire given event with optional arguments.
     * Arguments that will be passed to the result function will be also
     * concated to the list of final arguments.
     | el.onclick = eve.f("click", 1, 2);
     | eve.on("click", function (a, b, c) {
     |     console.log(a, b, c); // 1, 2, [event object]
     | });
     > Arguments
     - event (string) event name
     - varargs (…) and any other arguments
     = (function) possible event handler function
     \*/
    eve.f = function (event) {
        var attrs = [].slice.call(arguments, 1);
        return function () {
            eve.apply(null, [event, null].concat(attrs).concat([].slice.call(arguments, 0)));
        };
    };
    /*\
     * eve.stop
     [ method ]
     **
     * Is used inside an event handler to stop the event, preventing any subsequent listeners from firing.
     \*/
    eve.stop = function () {
        stop = 1;
    };
    /*\
     * eve.nt
     [ method ]
     **
     * Could be used inside event handler to figure out actual name of the event.
     **
     > Arguments
     **
     - subname (string) #optional subname of the event
     **
     = (string) name of the event, if `subname` is not specified
     * or
     = (boolean) `true`, if current event’s name contains `subname`
     \*/
    eve.nt = function (subname) {
        if (subname) {
            return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(current_event);
        }
        return current_event;
    };
    /*\
     * eve.nts
     [ method ]
     **
     * Could be used inside event handler to figure out actual name of the event.
     **
     **
     = (array) names of the event
     \*/
    eve.nts = function () {
        return current_event.split(separator);
    };
    /*\
     * eve.off
     [ method ]
     **
     * Removes given function from the list of event listeners assigned to given name.
     * If no arguments specified all the events will be cleared.
     **
     > Arguments
     **
     - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
     - f (function) event handler function
     \*/
    /*\
     * eve.unbind
     [ method ]
     **
     * See @eve.off
     \*/
    eve.off = eve.unbind = function (name, f) {
        if (!name) {
            eve._events = events = {n: {}};
            return;
        }
        var names = name.split(separator),
            e,
            key,
            splice,
            i, ii, j, jj,
            cur = [events];
        for (i = 0, ii = names.length; i < ii; i++) {
            for (j = 0; j < cur.length; j += splice.length - 2) {
                splice = [j, 1];
                e = cur[j].n;
                if (names[i] != wildcard) {
                    if (e[names[i]]) {
                        splice.push(e[names[i]]);
                    }
                } else {
                    for (key in e) if (e[has](key)) {
                        splice.push(e[key]);
                    }
                }
                cur.splice.apply(cur, splice);
            }
        }
        for (i = 0, ii = cur.length; i < ii; i++) {
            e = cur[i];
            while (e.n) {
                if (f) {
                    if (e.f) {
                        for (j = 0, jj = e.f.length; j < jj; j++) if (e.f[j] == f) {
                            e.f.splice(j, 1);
                            break;
                        }
                        !e.f.length && delete e.f;
                    }
                    for (key in e.n) if (e.n[has](key) && e.n[key].f) {
                        var funcs = e.n[key].f;
                        for (j = 0, jj = funcs.length; j < jj; j++) if (funcs[j] == f) {
                            funcs.splice(j, 1);
                            break;
                        }
                        !funcs.length && delete e.n[key].f;
                    }
                } else {
                    delete e.f;
                    for (key in e.n) if (e.n[has](key) && e.n[key].f) {
                        delete e.n[key].f;
                    }
                }
                e = e.n;
            }
        }
    };
    /*\
     * eve.once
     [ method ]
     **
     * Binds given event handler with a given name to only run once then unbind itself.
     | eve.once("login", f);
     | eve("login"); // triggers f
     | eve("login"); // no listeners
     * Use @eve to trigger the listener.
     **
     > Arguments
     **
     - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
     - f (function) event handler function
     **
     = (function) same return function as @eve.on
     \*/
    eve.once = function (name, f) {
        var f2 = function () {
            eve.unbind(name, f2);
            return f.apply(this, arguments);
        };
        return eve.on(name, f2);
    };
    /*\
     * eve.version
     [ property (string) ]
     **
     * Current version of the library.
     \*/
    eve.version = version;
    eve.toString = function () {
        return "You are running Eve " + version;
    };

    return eve;
}));

// ┌────────────────────────────────────────────────────────────────────┐ \\
// │ Raphaël 2.1.4 - JavaScript Vector Library                      │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Core Module                                                        │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Licensed under the MIT (http://raphaeljs.com/license.html) license.│ \\
// └────────────────────────────────────────────────────────────────────┘ \\

(function (glob, factory) {
    if (typeof define === "function" && define.amd) {
        define("raphael.core", ["eve"], function(eve) {
            return factory(eve);
        });
    } else if (typeof exports === "object") {
        module.exports = factory(require("eve"));
    } else {
        glob.Raphael = factory(glob.eve);
    }
}(this, function (eve) {
    /*\
     * Raphael
     [ method ]
     **
     * Creates a canvas object on which to draw.
     * You must do this first, as all future calls to drawing methods
     * from this instance will be bound to this canvas.
     > Parameters
     **
     - container (HTMLElement|string) DOM element or its ID which is going to be a parent for drawing surface
     - width (number)
     - height (number)
     - callback (function) #optional callback function which is going to be executed in the context of newly created paper
     * or
     - x (number)
     - y (number)
     - width (number)
     - height (number)
     - callback (function) #optional callback function which is going to be executed in the context of newly created paper
     * or
     - all (array) (first 3 or 4 elements in the array are equal to [containerID, width, height] or [x, y, width, height]. The rest are element descriptions in format {type: type, <attributes>}). See @Paper.add.
     - callback (function) #optional callback function which is going to be executed in the context of newly created paper
     * or
     - onReadyCallback (function) function that is going to be called on DOM ready event. You can also subscribe to this event via Eve’s “DOMLoad” event. In this case method returns `undefined`.
     = (object) @Paper
     > Usage
     | // Each of the following examples create a canvas
     | // that is 320px wide by 200px high.
     | // Canvas is created at the viewport’s 10,50 coordinate.
     | var paper = Raphael(10, 50, 320, 200);
     | // Canvas is created at the top left corner of the #notepad element
     | // (or its top right corner in dir="rtl" elements)
     | var paper = Raphael(document.getElementById("notepad"), 320, 200);
     | // Same as above
     | var paper = Raphael("notepad", 320, 200);
     | // Image dump
     | var set = Raphael(["notepad", 320, 200, {
     |     type: "rect",
     |     x: 10,
     |     y: 10,
     |     width: 25,
     |     height: 25,
     |     stroke: "#f00"
     | }, {
     |     type: "text",
     |     x: 30,
     |     y: 40,
     |     text: "Dump"
     | }]);
    \*/
    function R(first) {
        if (R.is(first, "function")) {
            return loaded ? first() : eve.on("raphael.DOMload", first);
        } else if (R.is(first, array)) {
            return R._engine.create[apply](R, first.splice(0, 3 + R.is(first[0], nu))).add(first);
        } else {
            var args = Array.prototype.slice.call(arguments, 0);
            if (R.is(args[args.length - 1], "function")) {
                var f = args.pop();
                return loaded ? f.call(R._engine.create[apply](R, args)) : eve.on("raphael.DOMload", function () {
                    f.call(R._engine.create[apply](R, args));
                });
            } else {
                return R._engine.create[apply](R, arguments);
            }
        }
    }
    R.version = "2.1.4";
    R.eve = eve;
    var loaded,
        separator = /[, ]+/,
        elements = {circle: 1, rect: 1, path: 1, ellipse: 1, text: 1, image: 1},
        formatrg = /\{(\d+)\}/g,
        proto = "prototype",
        has = "hasOwnProperty",
        g = {
            doc: document,
            win: window
        },
        oldRaphael = {
            was: Object.prototype[has].call(g.win, "Raphael"),
            is: g.win.Raphael
        },
        Paper = function () {
            /*\
             * Paper.ca
             [ property (object) ]
             **
             * Shortcut for @Paper.customAttributes
            \*/
            /*\
             * Paper.customAttributes
             [ property (object) ]
             **
             * If you have a set of attributes that you would like to represent
             * as a function of some number you can do it easily with custom attributes:
             > Usage
             | paper.customAttributes.hue = function (num) {
             |     num = num % 1;
             |     return {fill: "hsb(" + num + ", 0.75, 1)"};
             | };
             | // Custom attribute “hue” will change fill
             | // to be given hue with fixed saturation and brightness.
             | // Now you can use it like this:
             | var c = paper.circle(10, 10, 10).attr({hue: .45});
             | // or even like this:
             | c.animate({hue: 1}, 1e3);
             |
             | // You could also create custom attribute
             | // with multiple parameters:
             | paper.customAttributes.hsb = function (h, s, b) {
             |     return {fill: "hsb(" + [h, s, b].join(",") + ")"};
             | };
             | c.attr({hsb: "0.5 .8 1"});
             | c.animate({hsb: [1, 0, 0.5]}, 1e3);
            \*/
            this.ca = this.customAttributes = {};
        },
        paperproto,
        appendChild = "appendChild",
        apply = "apply",
        concat = "concat",
        supportsTouch = ('ontouchstart' in g.win) || g.win.DocumentTouch && g.doc instanceof DocumentTouch, //taken from Modernizr touch test
        E = "",
        S = " ",
        Str = String,
        split = "split",
        events = "click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel"[split](S),
        touchMap = {
            mousedown: "touchstart",
            mousemove: "touchmove",
            mouseup: "touchend"
        },
        lowerCase = Str.prototype.toLowerCase,
        math = Math,
        mmax = math.max,
        mmin = math.min,
        abs = math.abs,
        pow = math.pow,
        PI = math.PI,
        nu = "number",
        string = "string",
        array = "array",
        toString = "toString",
        fillString = "fill",
        objectToString = Object.prototype.toString,
        paper = {},
        push = "push",
        ISURL = R._ISURL = /^url\(['"]?(.+?)['"]?\)$/i,
        colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,
        isnan = {"NaN": 1, "Infinity": 1, "-Infinity": 1},
        bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
        round = math.round,
        setAttribute = "setAttribute",
        toFloat = parseFloat,
        toInt = parseInt,
        upperCase = Str.prototype.toUpperCase,
        availableAttrs = R._availableAttrs = {
            "arrow-end": "none",
            "arrow-start": "none",
            blur: 0,
            "clip-rect": "0 0 1e9 1e9",
            cursor: "default",
            cx: 0,
            cy: 0,
            fill: "#fff",
            "fill-opacity": 1,
            font: '10px "Arial"',
            "font-family": '"Arial"',
            "font-size": "10",
            "font-style": "normal",
            "font-weight": 400,
            gradient: 0,
            height: 0,
            href: "http://raphaeljs.com/",
            "letter-spacing": 0,
            opacity: 1,
            path: "M0,0",
            r: 0,
            rx: 0,
            ry: 0,
            src: "",
            stroke: "#000",
            "stroke-dasharray": "",
            "stroke-linecap": "butt",
            "stroke-linejoin": "butt",
            "stroke-miterlimit": 0,
            "stroke-opacity": 1,
            "stroke-width": 1,
            target: "_blank",
            "text-anchor": "middle",
            title: "Raphael",
            transform: "",
            width: 0,
            x: 0,
            y: 0
        },
        availableAnimAttrs = R._availableAnimAttrs = {
            blur: nu,
            "clip-rect": "csv",
            cx: nu,
            cy: nu,
            fill: "colour",
            "fill-opacity": nu,
            "font-size": nu,
            height: nu,
            opacity: nu,
            path: "path",
            r: nu,
            rx: nu,
            ry: nu,
            stroke: "colour",
            "stroke-opacity": nu,
            "stroke-width": nu,
            transform: "transform",
            width: nu,
            x: nu,
            y: nu
        },
        whitespace = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]/g,
        commaSpaces = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/,
        hsrg = {hs: 1, rg: 1},
        p2s = /,?([achlmqrstvxz]),?/gi,
        pathCommand = /([achlmrqstvz])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig,
        tCommand = /([rstm])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig,
        pathValues = /(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/ig,
        radial_gradient = R._radial_gradient = /^r(?:\(([^,]+?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*([^\)]+?)\))?/,
        eldata = {},
        sortByKey = function (a, b) {
            return a.key - b.key;
        },
        sortByNumber = function (a, b) {
            return toFloat(a) - toFloat(b);
        },
        fun = function () {},
        pipe = function (x) {
            return x;
        },
        rectPath = R._rectPath = function (x, y, w, h, r) {
            if (r) {
                return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]];
            }
            return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
        },
        ellipsePath = function (x, y, rx, ry) {
            if (ry == null) {
                ry = rx;
            }
            return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]];
        },
        getPath = R._getPath = {
            path: function (el) {
                return el.attr("path");
            },
            circle: function (el) {
                var a = el.attrs;
                return ellipsePath(a.cx, a.cy, a.r);
            },
            ellipse: function (el) {
                var a = el.attrs;
                return ellipsePath(a.cx, a.cy, a.rx, a.ry);
            },
            rect: function (el) {
                var a = el.attrs;
                return rectPath(a.x, a.y, a.width, a.height, a.r);
            },
            image: function (el) {
                var a = el.attrs;
                return rectPath(a.x, a.y, a.width, a.height);
            },
            text: function (el) {
                var bbox = el._getBBox();
                return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
            },
            set : function(el) {
                var bbox = el._getBBox();
                return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
            }
        },
        /*\
         * Raphael.mapPath
         [ method ]
         **
         * Transform the path string with given matrix.
         > Parameters
         - path (string) path string
         - matrix (object) see @Matrix
         = (string) transformed path string
        \*/
        mapPath = R.mapPath = function (path, matrix) {
            if (!matrix) {
                return path;
            }
            var x, y, i, j, ii, jj, pathi;
            path = path2curve(path);
            for (i = 0, ii = path.length; i < ii; i++) {
                pathi = path[i];
                for (j = 1, jj = pathi.length; j < jj; j += 2) {
                    x = matrix.x(pathi[j], pathi[j + 1]);
                    y = matrix.y(pathi[j], pathi[j + 1]);
                    pathi[j] = x;
                    pathi[j + 1] = y;
                }
            }
            return path;
        };

    R._g = g;
    /*\
     * Raphael.type
     [ property (string) ]
     **
     * Can be “SVG”, “VML” or empty, depending on browser support.
    \*/
    R.type = (g.win.SVGAngle || g.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML");
    if (R.type == "VML") {
        var d = g.doc.createElement("div"),
            b;
        d.innerHTML = '<v:shape adj="1"/>';
        b = d.firstChild;
        b.style.behavior = "url(#default#VML)";
        if (!(b && typeof b.adj == "object")) {
            return (R.type = E);
        }
        d = null;
    }
    /*\
     * Raphael.svg
     [ property (boolean) ]
     **
     * `true` if browser supports SVG.
    \*/
    /*\
     * Raphael.vml
     [ property (boolean) ]
     **
     * `true` if browser supports VML.
    \*/
    R.svg = !(R.vml = R.type == "VML");
    R._Paper = Paper;
    /*\
     * Raphael.fn
     [ property (object) ]
     **
     * You can add your own method to the canvas. For example if you want to draw a pie chart,
     * you can create your own pie chart function and ship it as a Raphaël plugin. To do this
     * you need to extend the `Raphael.fn` object. You should modify the `fn` object before a
     * Raphaël instance is created, otherwise it will take no effect. Please note that the
     * ability for namespaced plugins was removed in Raphael 2.0. It is up to the plugin to
     * ensure any namespacing ensures proper context.
     > Usage
     | Raphael.fn.arrow = function (x1, y1, x2, y2, size) {
     |     return this.path( ... );
     | };
     | // or create namespace
     | Raphael.fn.mystuff = {
     |     arrow: function () {…},
     |     star: function () {…},
     |     // etc…
     | };
     | var paper = Raphael(10, 10, 630, 480);
     | // then use it
     | paper.arrow(10, 10, 30, 30, 5).attr({fill: "#f00"});
     | paper.mystuff.arrow();
     | paper.mystuff.star();
    \*/
    R.fn = paperproto = Paper.prototype = R.prototype;
    R._id = 0;
    R._oid = 0;
    /*\
     * Raphael.is
     [ method ]
     **
     * Handful of replacements for `typeof` operator.
     > Parameters
     - o (…) any object or primitive
     - type (string) name of the type, i.e. “string”, “function”, “number”, etc.
     = (boolean) is given value is of given type
    \*/
    R.is = function (o, type) {
        type = lowerCase.call(type);
        if (type == "finite") {
            return !isnan[has](+o);
        }
        if (type == "array") {
            return o instanceof Array;
        }
        return  (type == "null" && o === null) ||
                (type == typeof o && o !== null) ||
                (type == "object" && o === Object(o)) ||
                (type == "array" && Array.isArray && Array.isArray(o)) ||
                objectToString.call(o).slice(8, -1).toLowerCase() == type;
    };

    function clone(obj) {
        if (typeof obj == "function" || Object(obj) !== obj) {
            return obj;
        }
        var res = new obj.constructor;
        for (var key in obj) if (obj[has](key)) {
            res[key] = clone(obj[key]);
        }
        return res;
    }

    /*\
     * Raphael.angle
     [ method ]
     **
     * Returns angle between two or three points
     > Parameters
     - x1 (number) x coord of first point
     - y1 (number) y coord of first point
     - x2 (number) x coord of second point
     - y2 (number) y coord of second point
     - x3 (number) #optional x coord of third point
     - y3 (number) #optional y coord of third point
     = (number) angle in degrees.
    \*/
    R.angle = function (x1, y1, x2, y2, x3, y3) {
        if (x3 == null) {
            var x = x1 - x2,
                y = y1 - y2;
            if (!x && !y) {
                return 0;
            }
            return (180 + math.atan2(-y, -x) * 180 / PI + 360) % 360;
        } else {
            return R.angle(x1, y1, x3, y3) - R.angle(x2, y2, x3, y3);
        }
    };
    /*\
     * Raphael.rad
     [ method ]
     **
     * Transform angle to radians
     > Parameters
     - deg (number) angle in degrees
     = (number) angle in radians.
    \*/
    R.rad = function (deg) {
        return deg % 360 * PI / 180;
    };
    /*\
     * Raphael.deg
     [ method ]
     **
     * Transform angle to degrees
     > Parameters
     - rad (number) angle in radians
     = (number) angle in degrees.
    \*/
    R.deg = function (rad) {
        return Math.round ((rad * 180 / PI% 360)* 1000) / 1000;
    };
    /*\
     * Raphael.snapTo
     [ method ]
     **
     * Snaps given value to given grid.
     > Parameters
     - values (array|number) given array of values or step of the grid
     - value (number) value to adjust
     - tolerance (number) #optional tolerance for snapping. Default is `10`.
     = (number) adjusted value.
    \*/
    R.snapTo = function (values, value, tolerance) {
        tolerance = R.is(tolerance, "finite") ? tolerance : 10;
        if (R.is(values, array)) {
            var i = values.length;
            while (i--) if (abs(values[i] - value) <= tolerance) {
                return values[i];
            }
        } else {
            values = +values;
            var rem = value % values;
            if (rem < tolerance) {
                return value - rem;
            }
            if (rem > values - tolerance) {
                return value - rem + values;
            }
        }
        return value;
    };

    /*\
     * Raphael.createUUID
     [ method ]
     **
     * Returns RFC4122, version 4 ID
    \*/
    var createUUID = R.createUUID = (function (uuidRegEx, uuidReplacer) {
        return function () {
            return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(uuidRegEx, uuidReplacer).toUpperCase();
        };
    })(/[xy]/g, function (c) {
        var r = math.random() * 16 | 0,
            v = c == "x" ? r : (r & 3 | 8);
        return v.toString(16);
    });

    /*\
     * Raphael.setWindow
     [ method ]
     **
     * Used when you need to draw in `&lt;iframe>`. Switched window to the iframe one.
     > Parameters
     - newwin (window) new window object
    \*/
    R.setWindow = function (newwin) {
        eve("raphael.setWindow", R, g.win, newwin);
        g.win = newwin;
        g.doc = g.win.document;
        if (R._engine.initWin) {
            R._engine.initWin(g.win);
        }
    };
    var toHex = function (color) {
        if (R.vml) {
            // http://dean.edwards.name/weblog/2009/10/convert-any-colour-value-to-hex-in-msie/
            var trim = /^\s+|\s+$/g;
            var bod;
            try {
                var docum = new ActiveXObject("htmlfile");
                docum.write("<body>");
                docum.close();
                bod = docum.body;
            } catch(e) {
                bod = createPopup().document.body;
            }
            var range = bod.createTextRange();
            toHex = cacher(function (color) {
                try {
                    bod.style.color = Str(color).replace(trim, E);
                    var value = range.queryCommandValue("ForeColor");
                    value = ((value & 255) << 16) | (value & 65280) | ((value & 16711680) >>> 16);
                    return "#" + ("000000" + value.toString(16)).slice(-6);
                } catch(e) {
                    return "none";
                }
            });
        } else {
            var i = g.doc.createElement("i");
            i.title = "Rapha\xebl Colour Picker";
            i.style.display = "none";
            g.doc.body.appendChild(i);
            toHex = cacher(function (color) {
                i.style.color = color;
                return g.doc.defaultView.getComputedStyle(i, E).getPropertyValue("color");
            });
        }
        return toHex(color);
    },
    hsbtoString = function () {
        return "hsb(" + [this.h, this.s, this.b] + ")";
    },
    hsltoString = function () {
        return "hsl(" + [this.h, this.s, this.l] + ")";
    },
    rgbtoString = function () {
        return this.hex;
    },
    prepareRGB = function (r, g, b) {
        if (g == null && R.is(r, "object") && "r" in r && "g" in r && "b" in r) {
            b = r.b;
            g = r.g;
            r = r.r;
        }
        if (g == null && R.is(r, string)) {
            var clr = R.getRGB(r);
            r = clr.r;
            g = clr.g;
            b = clr.b;
        }
        if (r > 1 || g > 1 || b > 1) {
            r /= 255;
            g /= 255;
            b /= 255;
        }

        return [r, g, b];
    },
    packageRGB = function (r, g, b, o) {
        r *= 255;
        g *= 255;
        b *= 255;
        var rgb = {
            r: r,
            g: g,
            b: b,
            hex: R.rgb(r, g, b),
            toString: rgbtoString
        };
        R.is(o, "finite") && (rgb.opacity = o);
        return rgb;
    };

    /*\
     * Raphael.color
     [ method ]
     **
     * Parses the color string and returns object with all values for the given color.
     > Parameters
     - clr (string) color string in one of the supported formats (see @Raphael.getRGB)
     = (object) Combined RGB & HSB object in format:
     o {
     o     r (number) red,
     o     g (number) green,
     o     b (number) blue,
     o     hex (string) color in HTML/CSS format: #••••••,
     o     error (boolean) `true` if string can’t be parsed,
     o     h (number) hue,
     o     s (number) saturation,
     o     v (number) value (brightness),
     o     l (number) lightness
     o }
    \*/
    R.color = function (clr) {
        var rgb;
        if (R.is(clr, "object") && "h" in clr && "s" in clr && "b" in clr) {
            rgb = R.hsb2rgb(clr);
            clr.r = rgb.r;
            clr.g = rgb.g;
            clr.b = rgb.b;
            clr.hex = rgb.hex;
        } else if (R.is(clr, "object") && "h" in clr && "s" in clr && "l" in clr) {
            rgb = R.hsl2rgb(clr);
            clr.r = rgb.r;
            clr.g = rgb.g;
            clr.b = rgb.b;
            clr.hex = rgb.hex;
        } else {
            if (R.is(clr, "string")) {
                clr = R.getRGB(clr);
            }
            if (R.is(clr, "object") && "r" in clr && "g" in clr && "b" in clr) {
                rgb = R.rgb2hsl(clr);
                clr.h = rgb.h;
                clr.s = rgb.s;
                clr.l = rgb.l;
                rgb = R.rgb2hsb(clr);
                clr.v = rgb.b;
            } else {
                clr = {hex: "none"};
                clr.r = clr.g = clr.b = clr.h = clr.s = clr.v = clr.l = -1;
            }
        }
        clr.toString = rgbtoString;
        return clr;
    };
    /*\
     * Raphael.hsb2rgb
     [ method ]
     **
     * Converts HSB values to RGB object.
     > Parameters
     - h (number) hue
     - s (number) saturation
     - v (number) value or brightness
     = (object) RGB object in format:
     o {
     o     r (number) red,
     o     g (number) green,
     o     b (number) blue,
     o     hex (string) color in HTML/CSS format: #••••••
     o }
    \*/
    R.hsb2rgb = function (h, s, v, o) {
        if (this.is(h, "object") && "h" in h && "s" in h && "b" in h) {
            v = h.b;
            s = h.s;
            o = h.o;
            h = h.h;
        }
        h *= 360;
        var R, G, B, X, C;
        h = (h % 360) / 60;
        C = v * s;
        X = C * (1 - abs(h % 2 - 1));
        R = G = B = v - C;

        h = ~~h;
        R += [C, X, 0, 0, X, C][h];
        G += [X, C, C, X, 0, 0][h];
        B += [0, 0, X, C, C, X][h];
        return packageRGB(R, G, B, o);
    };
    /*\
     * Raphael.hsl2rgb
     [ method ]
     **
     * Converts HSL values to RGB object.
     > Parameters
     - h (number) hue
     - s (number) saturation
     - l (number) luminosity
     = (object) RGB object in format:
     o {
     o     r (number) red,
     o     g (number) green,
     o     b (number) blue,
     o     hex (string) color in HTML/CSS format: #••••••
     o }
    \*/
    R.hsl2rgb = function (h, s, l, o) {
        if (this.is(h, "object") && "h" in h && "s" in h && "l" in h) {
            l = h.l;
            s = h.s;
            h = h.h;
        }
        if (h > 1 || s > 1 || l > 1) {
            h /= 360;
            s /= 100;
            l /= 100;
        }
        h *= 360;
        var R, G, B, X, C;
        h = (h % 360) / 60;
        C = 2 * s * (l < .5 ? l : 1 - l);
        X = C * (1 - abs(h % 2 - 1));
        R = G = B = l - C / 2;

        h = ~~h;
        R += [C, X, 0, 0, X, C][h];
        G += [X, C, C, X, 0, 0][h];
        B += [0, 0, X, C, C, X][h];
        return packageRGB(R, G, B, o);
    };
    /*\
     * Raphael.rgb2hsb
     [ method ]
     **
     * Converts RGB values to HSB object.
     > Parameters
     - r (number) red
     - g (number) green
     - b (number) blue
     = (object) HSB object in format:
     o {
     o     h (number) hue
     o     s (number) saturation
     o     b (number) brightness
     o }
    \*/
    R.rgb2hsb = function (r, g, b) {
        b = prepareRGB(r, g, b);
        r = b[0];
        g = b[1];
        b = b[2];

        var H, S, V, C;
        V = mmax(r, g, b);
        C = V - mmin(r, g, b);
        H = (C == 0 ? null :
             V == r ? (g - b) / C :
             V == g ? (b - r) / C + 2 :
                      (r - g) / C + 4
            );
        H = ((H + 360) % 6) * 60 / 360;
        S = C == 0 ? 0 : C / V;
        return {h: H, s: S, b: V, toString: hsbtoString};
    };
    /*\
     * Raphael.rgb2hsl
     [ method ]
     **
     * Converts RGB values to HSL object.
     > Parameters
     - r (number) red
     - g (number) green
     - b (number) blue
     = (object) HSL object in format:
     o {
     o     h (number) hue
     o     s (number) saturation
     o     l (number) luminosity
     o }
    \*/
    R.rgb2hsl = function (r, g, b) {
        b = prepareRGB(r, g, b);
        r = b[0];
        g = b[1];
        b = b[2];

        var H, S, L, M, m, C;
        M = mmax(r, g, b);
        m = mmin(r, g, b);
        C = M - m;
        H = (C == 0 ? null :
             M == r ? (g - b) / C :
             M == g ? (b - r) / C + 2 :
                      (r - g) / C + 4);
        H = ((H + 360) % 6) * 60 / 360;
        L = (M + m) / 2;
        S = (C == 0 ? 0 :
             L < .5 ? C / (2 * L) :
                      C / (2 - 2 * L));
        return {h: H, s: S, l: L, toString: hsltoString};
    };
    R._path2string = function () {
        return this.join(",").replace(p2s, "$1");
    };
    function repush(array, item) {
        for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
            return array.push(array.splice(i, 1)[0]);
        }
    }
    function cacher(f, scope, postprocessor) {
        function newf() {
            var arg = Array.prototype.slice.call(arguments, 0),
                args = arg.join("\u2400"),
                cache = newf.cache = newf.cache || {},
                count = newf.count = newf.count || [];
            if (cache[has](args)) {
                repush(count, args);
                return postprocessor ? postprocessor(cache[args]) : cache[args];
            }
            count.length >= 1e3 && delete cache[count.shift()];
            count.push(args);
            cache[args] = f[apply](scope, arg);
            return postprocessor ? postprocessor(cache[args]) : cache[args];
        }
        return newf;
    }

    var preload = R._preload = function (src, f) {
        var img = g.doc.createElement("img");
        img.style.cssText = "position:absolute;left:-9999em;top:-9999em";
        img.onload = function () {
            f.call(this);
            this.onload = null;
            g.doc.body.removeChild(this);
        };
        img.onerror = function () {
            g.doc.body.removeChild(this);
        };
        g.doc.body.appendChild(img);
        img.src = src;
    };

    function clrToString() {
        return this.hex;
    }

    /*\
     * Raphael.getRGB
     [ method ]
     **
     * Parses colour string as RGB object
     > Parameters
     - colour (string) colour string in one of formats:
     # <ul>
     #     <li>Colour name (“<code>red</code>”, “<code>green</code>”, “<code>cornflowerblue</code>”, etc)</li>
     #     <li>#••• — shortened HTML colour: (“<code>#000</code>”, “<code>#fc0</code>”, etc)</li>
     #     <li>#•••••• — full length HTML colour: (“<code>#000000</code>”, “<code>#bd2300</code>”)</li>
     #     <li>rgb(•••, •••, •••) — red, green and blue channels’ values: (“<code>rgb(200,&nbsp;100,&nbsp;0)</code>”)</li>
     #     <li>rgb(•••%, •••%, •••%) — same as above, but in %: (“<code>rgb(100%,&nbsp;175%,&nbsp;0%)</code>”)</li>
     #     <li>hsb(•••, •••, •••) — hue, saturation and brightness values: (“<code>hsb(0.5,&nbsp;0.25,&nbsp;1)</code>”)</li>
     #     <li>hsb(•••%, •••%, •••%) — same as above, but in %</li>
     #     <li>hsl(•••, •••, •••) — same as hsb</li>
     #     <li>hsl(•••%, •••%, •••%) — same as hsb</li>
     # </ul>
     = (object) RGB object in format:
     o {
     o     r (number) red,
     o     g (number) green,
     o     b (number) blue
     o     hex (string) color in HTML/CSS format: #••••••,
     o     error (boolean) true if string can’t be parsed
     o }
    \*/
    R.getRGB = cacher(function (colour) {
        if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) {
            return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString};
        }
        if (colour == "none") {
            return {r: -1, g: -1, b: -1, hex: "none", toString: clrToString};
        }
        !(hsrg[has](colour.toLowerCase().substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour));
        var res,
            red,
            green,
            blue,
            opacity,
            t,
            values,
            rgb = colour.match(colourRegExp);
        if (rgb) {
            if (rgb[2]) {
                blue = toInt(rgb[2].substring(5), 16);
                green = toInt(rgb[2].substring(3, 5), 16);
                red = toInt(rgb[2].substring(1, 3), 16);
            }
            if (rgb[3]) {
                blue = toInt((t = rgb[3].charAt(3)) + t, 16);
                green = toInt((t = rgb[3].charAt(2)) + t, 16);
                red = toInt((t = rgb[3].charAt(1)) + t, 16);
            }
            if (rgb[4]) {
                values = rgb[4][split](commaSpaces);
                red = toFloat(values[0]);
                values[0].slice(-1) == "%" && (red *= 2.55);
                green = toFloat(values[1]);
                values[1].slice(-1) == "%" && (green *= 2.55);
                blue = toFloat(values[2]);
                values[2].slice(-1) == "%" && (blue *= 2.55);
                rgb[1].toLowerCase().slice(0, 4) == "rgba" && (opacity = toFloat(values[3]));
                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
            }
            if (rgb[5]) {
                values = rgb[5][split](commaSpaces);
                red = toFloat(values[0]);
                values[0].slice(-1) == "%" && (red *= 2.55);
                green = toFloat(values[1]);
                values[1].slice(-1) == "%" && (green *= 2.55);
                blue = toFloat(values[2]);
                values[2].slice(-1) == "%" && (blue *= 2.55);
                (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
                rgb[1].toLowerCase().slice(0, 4) == "hsba" && (opacity = toFloat(values[3]));
                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
                return R.hsb2rgb(red, green, blue, opacity);
            }
            if (rgb[6]) {
                values = rgb[6][split](commaSpaces);
                red = toFloat(values[0]);
                values[0].slice(-1) == "%" && (red *= 2.55);
                green = toFloat(values[1]);
                values[1].slice(-1) == "%" && (green *= 2.55);
                blue = toFloat(values[2]);
                values[2].slice(-1) == "%" && (blue *= 2.55);
                (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
                rgb[1].toLowerCase().slice(0, 4) == "hsla" && (opacity = toFloat(values[3]));
                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
                return R.hsl2rgb(red, green, blue, opacity);
            }
            rgb = {r: red, g: green, b: blue, toString: clrToString};
            rgb.hex = "#" + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1);
            R.is(opacity, "finite") && (rgb.opacity = opacity);
            return rgb;
        }
        return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString};
    }, R);
    /*\
     * Raphael.hsb
     [ method ]
     **
     * Converts HSB values to hex representation of the colour.
     > Parameters
     - h (number) hue
     - s (number) saturation
     - b (number) value or brightness
     = (string) hex representation of the colour.
    \*/
    R.hsb = cacher(function (h, s, b) {
        return R.hsb2rgb(h, s, b).hex;
    });
    /*\
     * Raphael.hsl
     [ method ]
     **
     * Converts HSL values to hex representation of the colour.
     > Parameters
     - h (number) hue
     - s (number) saturation
     - l (number) luminosity
     = (string) hex representation of the colour.
    \*/
    R.hsl = cacher(function (h, s, l) {
        return R.hsl2rgb(h, s, l).hex;
    });
    /*\
     * Raphael.rgb
     [ method ]
     **
     * Converts RGB values to hex representation of the colour.
     > Parameters
     - r (number) red
     - g (number) green
     - b (number) blue
     = (string) hex representation of the colour.
    \*/
    R.rgb = cacher(function (r, g, b) {
        function round(x) { return (x + 0.5) | 0; }
        return "#" + (16777216 | round(b) | (round(g) << 8) | (round(r) << 16)).toString(16).slice(1);
    });
    /*\
     * Raphael.getColor
     [ method ]
     **
     * On each call returns next colour in the spectrum. To reset it back to red call @Raphael.getColor.reset
     > Parameters
     - value (number) #optional brightness, default is `0.75`
     = (string) hex representation of the colour.
    \*/
    R.getColor = function (value) {
        var start = this.getColor.start = this.getColor.start || {h: 0, s: 1, b: value || .75},
            rgb = this.hsb2rgb(start.h, start.s, start.b);
        start.h += .075;
        if (start.h > 1) {
            start.h = 0;
            start.s -= .2;
            start.s <= 0 && (this.getColor.start = {h: 0, s: 1, b: start.b});
        }
        return rgb.hex;
    };
    /*\
     * Raphael.getColor.reset
     [ method ]
     **
     * Resets spectrum position for @Raphael.getColor back to red.
    \*/
    R.getColor.reset = function () {
        delete this.start;
    };

    // http://schepers.cc/getting-to-the-point
    function catmullRom2bezier(crp, z) {
        var d = [];
        for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
            var p = [
                        {x: +crp[i - 2], y: +crp[i - 1]},
                        {x: +crp[i],     y: +crp[i + 1]},
                        {x: +crp[i + 2], y: +crp[i + 3]},
                        {x: +crp[i + 4], y: +crp[i + 5]}
                    ];
            if (z) {
                if (!i) {
                    p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]};
                } else if (iLen - 4 == i) {
                    p[3] = {x: +crp[0], y: +crp[1]};
                } else if (iLen - 2 == i) {
                    p[2] = {x: +crp[0], y: +crp[1]};
                    p[3] = {x: +crp[2], y: +crp[3]};
                }
            } else {
                if (iLen - 4 == i) {
                    p[3] = p[2];
                } else if (!i) {
                    p[0] = {x: +crp[i], y: +crp[i + 1]};
                }
            }
            d.push(["C",
                  (-p[0].x + 6 * p[1].x + p[2].x) / 6,
                  (-p[0].y + 6 * p[1].y + p[2].y) / 6,
                  (p[1].x + 6 * p[2].x - p[3].x) / 6,
                  (p[1].y + 6*p[2].y - p[3].y) / 6,
                  p[2].x,
                  p[2].y
            ]);
        }

        return d;
    }
    /*\
     * Raphael.parsePathString
     [ method ]
     **
     * Utility method
     **
     * Parses given path string into an array of arrays of path segments.
     > Parameters
     - pathString (string|array) path string or array of segments (in the last case it will be returned straight away)
     = (array) array of segments.
    \*/
    R.parsePathString = function (pathString) {
        if (!pathString) {
            return null;
        }
        var pth = paths(pathString);
        if (pth.arr) {
            return pathClone(pth.arr);
        }

        var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0},
            data = [];
        if (R.is(pathString, array) && R.is(pathString[0], array)) { // rough assumption
            data = pathClone(pathString);
        }
        if (!data.length) {
            Str(pathString).replace(pathCommand, function (a, b, c) {
                var params = [],
                    name = b.toLowerCase();
                c.replace(pathValues, function (a, b) {
                    b && params.push(+b);
                });
                if (name == "m" && params.length > 2) {
                    data.push([b][concat](params.splice(0, 2)));
                    name = "l";
                    b = b == "m" ? "l" : "L";
                }
                if (name == "r") {
                    data.push([b][concat](params));
                } else while (params.length >= paramCounts[name]) {
                    data.push([b][concat](params.splice(0, paramCounts[name])));
                    if (!paramCounts[name]) {
                        break;
                    }
                }
            });
        }
        data.toString = R._path2string;
        pth.arr = pathClone(data);
        return data;
    };
    /*\
     * Raphael.parseTransformString
     [ method ]
     **
     * Utility method
     **
     * Parses given path string into an array of transformations.
     > Parameters
     - TString (string|array) transform string or array of transformations (in the last case it will be returned straight away)
     = (array) array of transformations.
    \*/
    R.parseTransformString = cacher(function (TString) {
        if (!TString) {
            return null;
        }
        var paramCounts = {r: 3, s: 4, t: 2, m: 6},
            data = [];
        if (R.is(TString, array) && R.is(TString[0], array)) { // rough assumption
            data = pathClone(TString);
        }
        if (!data.length) {
            Str(TString).replace(tCommand, function (a, b, c) {
                var params = [],
                    name = lowerCase.call(b);
                c.replace(pathValues, function (a, b) {
                    b && params.push(+b);
                });
                data.push([b][concat](params));
            });
        }
        data.toString = R._path2string;
        return data;
    });
    // PATHS
    var paths = function (ps) {
        var p = paths.ps = paths.ps || {};
        if (p[ps]) {
            p[ps].sleep = 100;
        } else {
            p[ps] = {
                sleep: 100
            };
        }
        setTimeout(function () {
            for (var key in p) if (p[has](key) && key != ps) {
                p[key].sleep--;
                !p[key].sleep && delete p[key];
            }
        });
        return p[ps];
    };
    /*\
     * Raphael.findDotsAtSegment
     [ method ]
     **
     * Utility method
     **
     * Find dot coordinates on the given cubic bezier curve at the given t.
     > Parameters
     - p1x (number) x of the first point of the curve
     - p1y (number) y of the first point of the curve
     - c1x (number) x of the first anchor of the curve
     - c1y (number) y of the first anchor of the curve
     - c2x (number) x of the second anchor of the curve
     - c2y (number) y of the second anchor of the curve
     - p2x (number) x of the second point of the curve
     - p2y (number) y of the second point of the curve
     - t (number) position on the curve (0..1)
     = (object) point information in format:
     o {
     o     x: (number) x coordinate of the point
     o     y: (number) y coordinate of the point
     o     m: {
     o         x: (number) x coordinate of the left anchor
     o         y: (number) y coordinate of the left anchor
     o     }
     o     n: {
     o         x: (number) x coordinate of the right anchor
     o         y: (number) y coordinate of the right anchor
     o     }
     o     start: {
     o         x: (number) x coordinate of the start of the curve
     o         y: (number) y coordinate of the start of the curve
     o     }
     o     end: {
     o         x: (number) x coordinate of the end of the curve
     o         y: (number) y coordinate of the end of the curve
     o     }
     o     alpha: (number) angle of the curve derivative at the point
     o }
    \*/
    R.findDotsAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
        var t1 = 1 - t,
            t13 = pow(t1, 3),
            t12 = pow(t1, 2),
            t2 = t * t,
            t3 = t2 * t,
            x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x,
            y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y,
            mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
            my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
            nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
            ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
            ax = t1 * p1x + t * c1x,
            ay = t1 * p1y + t * c1y,
            cx = t1 * c2x + t * p2x,
            cy = t1 * c2y + t * p2y,
            alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI);
        (mx > nx || my < ny) && (alpha += 180);
        return {
            x: x,
            y: y,
            m: {x: mx, y: my},
            n: {x: nx, y: ny},
            start: {x: ax, y: ay},
            end: {x: cx, y: cy},
            alpha: alpha
        };
    };
    /*\
     * Raphael.bezierBBox
     [ method ]
     **
     * Utility method
     **
     * Return bounding box of a given cubic bezier curve
     > Parameters
     - p1x (number) x of the first point of the curve
     - p1y (number) y of the first point of the curve
     - c1x (number) x of the first anchor of the curve
     - c1y (number) y of the first anchor of the curve
     - c2x (number) x of the second anchor of the curve
     - c2y (number) y of the second anchor of the curve
     - p2x (number) x of the second point of the curve
     - p2y (number) y of the second point of the curve
     * or
     - bez (array) array of six points for bezier curve
     = (object) point information in format:
     o {
     o     min: {
     o         x: (number) x coordinate of the left point
     o         y: (number) y coordinate of the top point
     o     }
     o     max: {
     o         x: (number) x coordinate of the right point
     o         y: (number) y coordinate of the bottom point
     o     }
     o }
    \*/
    R.bezierBBox = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
        if (!R.is(p1x, "array")) {
            p1x = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y];
        }
        var bbox = curveDim.apply(null, p1x);
        return {
            x: bbox.min.x,
            y: bbox.min.y,
            x2: bbox.max.x,
            y2: bbox.max.y,
            width: bbox.max.x - bbox.min.x,
            height: bbox.max.y - bbox.min.y
        };
    };
    /*\
     * Raphael.isPointInsideBBox
     [ method ]
     **
     * Utility method
     **
     * Returns `true` if given point is inside bounding boxes.
     > Parameters
     - bbox (string) bounding box
     - x (string) x coordinate of the point
     - y (string) y coordinate of the point
     = (boolean) `true` if point inside
    \*/
    R.isPointInsideBBox = function (bbox, x, y) {
        return x >= bbox.x && x <= bbox.x2 && y >= bbox.y && y <= bbox.y2;
    };
    /*\
     * Raphael.isBBoxIntersect
     [ method ]
     **
     * Utility method
     **
     * Returns `true` if two bounding boxes intersect
     > Parameters
     - bbox1 (string) first bounding box
     - bbox2 (string) second bounding box
     = (boolean) `true` if they intersect
    \*/
    R.isBBoxIntersect = function (bbox1, bbox2) {
        var i = R.isPointInsideBBox;
        return i(bbox2, bbox1.x, bbox1.y)
            || i(bbox2, bbox1.x2, bbox1.y)
            || i(bbox2, bbox1.x, bbox1.y2)
            || i(bbox2, bbox1.x2, bbox1.y2)
            || i(bbox1, bbox2.x, bbox2.y)
            || i(bbox1, bbox2.x2, bbox2.y)
            || i(bbox1, bbox2.x, bbox2.y2)
            || i(bbox1, bbox2.x2, bbox2.y2)
            || (bbox1.x < bbox2.x2 && bbox1.x > bbox2.x || bbox2.x < bbox1.x2 && bbox2.x > bbox1.x)
            && (bbox1.y < bbox2.y2 && bbox1.y > bbox2.y || bbox2.y < bbox1.y2 && bbox2.y > bbox1.y);
    };
    function base3(t, p1, p2, p3, p4) {
        var t1 = -3 * p1 + 9 * p2 - 9 * p3 + 3 * p4,
            t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3;
        return t * t2 - 3 * p1 + 3 * p2;
    }
    function bezlen(x1, y1, x2, y2, x3, y3, x4, y4, z) {
        if (z == null) {
            z = 1;
        }
        z = z > 1 ? 1 : z < 0 ? 0 : z;
        var z2 = z / 2,
            n = 12,
            Tvalues = [-0.1252,0.1252,-0.3678,0.3678,-0.5873,0.5873,-0.7699,0.7699,-0.9041,0.9041,-0.9816,0.9816],
            Cvalues = [0.2491,0.2491,0.2335,0.2335,0.2032,0.2032,0.1601,0.1601,0.1069,0.1069,0.0472,0.0472],
            sum = 0;
        for (var i = 0; i < n; i++) {
            var ct = z2 * Tvalues[i] + z2,
                xbase = base3(ct, x1, x2, x3, x4),
                ybase = base3(ct, y1, y2, y3, y4),
                comb = xbase * xbase + ybase * ybase;
            sum += Cvalues[i] * math.sqrt(comb);
        }
        return z2 * sum;
    }
    function getTatLen(x1, y1, x2, y2, x3, y3, x4, y4, ll) {
        if (ll < 0 || bezlen(x1, y1, x2, y2, x3, y3, x4, y4) < ll) {
            return;
        }
        var t = 1,
            step = t / 2,
            t2 = t - step,
            l,
            e = .01;
        l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
        while (abs(l - ll) > e) {
            step /= 2;
            t2 += (l < ll ? 1 : -1) * step;
            l = bezlen(x1, y1, x2, y2, x3, y3, x4, y4, t2);
        }
        return t2;
    }
    function intersect(x1, y1, x2, y2, x3, y3, x4, y4) {
        if (
            mmax(x1, x2) < mmin(x3, x4) ||
            mmin(x1, x2) > mmax(x3, x4) ||
            mmax(y1, y2) < mmin(y3, y4) ||
            mmin(y1, y2) > mmax(y3, y4)
        ) {
            return;
        }
        var nx = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4),
            ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4),
            denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);

        if (!denominator) {
            return;
        }
        var px = nx / denominator,
            py = ny / denominator,
            px2 = +px.toFixed(2),
            py2 = +py.toFixed(2);
        if (
            px2 < +mmin(x1, x2).toFixed(2) ||
            px2 > +mmax(x1, x2).toFixed(2) ||
            px2 < +mmin(x3, x4).toFixed(2) ||
            px2 > +mmax(x3, x4).toFixed(2) ||
            py2 < +mmin(y1, y2).toFixed(2) ||
            py2 > +mmax(y1, y2).toFixed(2) ||
            py2 < +mmin(y3, y4).toFixed(2) ||
            py2 > +mmax(y3, y4).toFixed(2)
        ) {
            return;
        }
        return {x: px, y: py};
    }
    function inter(bez1, bez2) {
        return interHelper(bez1, bez2);
    }
    function interCount(bez1, bez2) {
        return interHelper(bez1, bez2, 1);
    }
    function interHelper(bez1, bez2, justCount) {
        var bbox1 = R.bezierBBox(bez1),
            bbox2 = R.bezierBBox(bez2);
        if (!R.isBBoxIntersect(bbox1, bbox2)) {
            return justCount ? 0 : [];
        }
        var l1 = bezlen.apply(0, bez1),
            l2 = bezlen.apply(0, bez2),
            n1 = mmax(~~(l1 / 5), 1),
            n2 = mmax(~~(l2 / 5), 1),
            dots1 = [],
            dots2 = [],
            xy = {},
            res = justCount ? 0 : [];
        for (var i = 0; i < n1 + 1; i++) {
            var p = R.findDotsAtSegment.apply(R, bez1.concat(i / n1));
            dots1.push({x: p.x, y: p.y, t: i / n1});
        }
        for (i = 0; i < n2 + 1; i++) {
            p = R.findDotsAtSegment.apply(R, bez2.concat(i / n2));
            dots2.push({x: p.x, y: p.y, t: i / n2});
        }
        for (i = 0; i < n1; i++) {
            for (var j = 0; j < n2; j++) {
                var di = dots1[i],
                    di1 = dots1[i + 1],
                    dj = dots2[j],
                    dj1 = dots2[j + 1],
                    ci = abs(di1.x - di.x) < .001 ? "y" : "x",
                    cj = abs(dj1.x - dj.x) < .001 ? "y" : "x",
                    is = intersect(di.x, di.y, di1.x, di1.y, dj.x, dj.y, dj1.x, dj1.y);
                if (is) {
                    if (xy[is.x.toFixed(4)] == is.y.toFixed(4)) {
                        continue;
                    }
                    xy[is.x.toFixed(4)] = is.y.toFixed(4);
                    var t1 = di.t + abs((is[ci] - di[ci]) / (di1[ci] - di[ci])) * (di1.t - di.t),
                        t2 = dj.t + abs((is[cj] - dj[cj]) / (dj1[cj] - dj[cj])) * (dj1.t - dj.t);
                    if (t1 >= 0 && t1 <= 1.001 && t2 >= 0 && t2 <= 1.001) {
                        if (justCount) {
                            res++;
                        } else {
                            res.push({
                                x: is.x,
                                y: is.y,
                                t1: mmin(t1, 1),
                                t2: mmin(t2, 1)
                            });
                        }
                    }
                }
            }
        }
        return res;
    }
    /*\
     * Raphael.pathIntersection
     [ method ]
     **
     * Utility method
     **
     * Finds intersections of two paths
     > Parameters
     - path1 (string) path string
     - path2 (string) path string
     = (array) dots of intersection
     o [
     o     {
     o         x: (number) x coordinate of the point
     o         y: (number) y coordinate of the point
     o         t1: (number) t value for segment of path1
     o         t2: (number) t value for segment of path2
     o         segment1: (number) order number for segment of path1
     o         segment2: (number) order number for segment of path2
     o         bez1: (array) eight coordinates representing beziér curve for the segment of path1
     o         bez2: (array) eight coordinates representing beziér curve for the segment of path2
     o     }
     o ]
    \*/
    R.pathIntersection = function (path1, path2) {
        return interPathHelper(path1, path2);
    };
    R.pathIntersectionNumber = function (path1, path2) {
        return interPathHelper(path1, path2, 1);
    };
    function interPathHelper(path1, path2, justCount) {
        path1 = R._path2curve(path1);
        path2 = R._path2curve(path2);
        var x1, y1, x2, y2, x1m, y1m, x2m, y2m, bez1, bez2,
            res = justCount ? 0 : [];
        for (var i = 0, ii = path1.length; i < ii; i++) {
            var pi = path1[i];
            if (pi[0] == "M") {
                x1 = x1m = pi[1];
                y1 = y1m = pi[2];
            } else {
                if (pi[0] == "C") {
                    bez1 = [x1, y1].concat(pi.slice(1));
                    x1 = bez1[6];
                    y1 = bez1[7];
                } else {
                    bez1 = [x1, y1, x1, y1, x1m, y1m, x1m, y1m];
                    x1 = x1m;
                    y1 = y1m;
                }
                for (var j = 0, jj = path2.length; j < jj; j++) {
                    var pj = path2[j];
                    if (pj[0] == "M") {
                        x2 = x2m = pj[1];
                        y2 = y2m = pj[2];
                    } else {
                        if (pj[0] == "C") {
                            bez2 = [x2, y2].concat(pj.slice(1));
                            x2 = bez2[6];
                            y2 = bez2[7];
                        } else {
                            bez2 = [x2, y2, x2, y2, x2m, y2m, x2m, y2m];
                            x2 = x2m;
                            y2 = y2m;
                        }
                        var intr = interHelper(bez1, bez2, justCount);
                        if (justCount) {
                            res += intr;
                        } else {
                            for (var k = 0, kk = intr.length; k < kk; k++) {
                                intr[k].segment1 = i;
                                intr[k].segment2 = j;
                                intr[k].bez1 = bez1;
                                intr[k].bez2 = bez2;
                            }
                            res = res.concat(intr);
                        }
                    }
                }
            }
        }
        return res;
    }
    /*\
     * Raphael.isPointInsidePath
     [ method ]
     **
     * Utility method
     **
     * Returns `true` if given point is inside a given closed path.
     > Parameters
     - path (string) path string
     - x (number) x of the point
     - y (number) y of the point
     = (boolean) true, if point is inside the path
    \*/
    R.isPointInsidePath = function (path, x, y) {
        var bbox = R.pathBBox(path);
        return R.isPointInsideBBox(bbox, x, y) &&
               interPathHelper(path, [["M", x, y], ["H", bbox.x2 + 10]], 1) % 2 == 1;
    };
    R._removedFactory = function (methodname) {
        return function () {
            eve("raphael.log", null, "Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object", methodname);
        };
    };
    /*\
     * Raphael.pathBBox
     [ method ]
     **
     * Utility method
     **
     * Return bounding box of a given path
     > Parameters
     - path (string) path string
     = (object) bounding box
     o {
     o     x: (number) x coordinate of the left top point of the box
     o     y: (number) y coordinate of the left top point of the box
     o     x2: (number) x coordinate of the right bottom point of the box
     o     y2: (number) y coordinate of the right bottom point of the box
     o     width: (number) width of the box
     o     height: (number) height of the box
     o     cx: (number) x coordinate of the center of the box
     o     cy: (number) y coordinate of the center of the box
     o }
    \*/
    var pathDimensions = R.pathBBox = function (path) {
        var pth = paths(path);
        if (pth.bbox) {
            return clone(pth.bbox);
        }
        if (!path) {
            return {x: 0, y: 0, width: 0, height: 0, x2: 0, y2: 0};
        }
        path = path2curve(path);
        var x = 0,
            y = 0,
            X = [],
            Y = [],
            p;
        for (var i = 0, ii = path.length; i < ii; i++) {
            p = path[i];
            if (p[0] == "M") {
                x = p[1];
                y = p[2];
                X.push(x);
                Y.push(y);
            } else {
                var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
                X = X[concat](dim.min.x, dim.max.x);
                Y = Y[concat](dim.min.y, dim.max.y);
                x = p[5];
                y = p[6];
            }
        }
        var xmin = mmin[apply](0, X),
            ymin = mmin[apply](0, Y),
            xmax = mmax[apply](0, X),
            ymax = mmax[apply](0, Y),
            width = xmax - xmin,
            height = ymax - ymin,
                bb = {
                x: xmin,
                y: ymin,
                x2: xmax,
                y2: ymax,
                width: width,
                height: height,
                cx: xmin + width / 2,
                cy: ymin + height / 2
            };
        pth.bbox = clone(bb);
        return bb;
    },
        pathClone = function (pathArray) {
            var res = clone(pathArray);
            res.toString = R._path2string;
            return res;
        },
        pathToRelative = R._pathToRelative = function (pathArray) {
            var pth = paths(pathArray);
            if (pth.rel) {
                return pathClone(pth.rel);
            }
            if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
                pathArray = R.parsePathString(pathArray);
            }
            var res = [],
                x = 0,
                y = 0,
                mx = 0,
                my = 0,
                start = 0;
            if (pathArray[0][0] == "M") {
                x = pathArray[0][1];
                y = pathArray[0][2];
                mx = x;
                my = y;
                start++;
                res.push(["M", x, y]);
            }
            for (var i = start, ii = pathArray.length; i < ii; i++) {
                var r = res[i] = [],
                    pa = pathArray[i];
                if (pa[0] != lowerCase.call(pa[0])) {
                    r[0] = lowerCase.call(pa[0]);
                    switch (r[0]) {
                        case "a":
                            r[1] = pa[1];
                            r[2] = pa[2];
                            r[3] = pa[3];
                            r[4] = pa[4];
                            r[5] = pa[5];
                            r[6] = +(pa[6] - x).toFixed(3);
                            r[7] = +(pa[7] - y).toFixed(3);
                            break;
                        case "v":
                            r[1] = +(pa[1] - y).toFixed(3);
                            break;
                        case "m":
                            mx = pa[1];
                            my = pa[2];
                        default:
                            for (var j = 1, jj = pa.length; j < jj; j++) {
                                r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
                            }
                    }
                } else {
                    r = res[i] = [];
                    if (pa[0] == "m") {
                        mx = pa[1] + x;
                        my = pa[2] + y;
                    }
                    for (var k = 0, kk = pa.length; k < kk; k++) {
                        res[i][k] = pa[k];
                    }
                }
                var len = res[i].length;
                switch (res[i][0]) {
                    case "z":
                        x = mx;
                        y = my;
                        break;
                    case "h":
                        x += +res[i][len - 1];
                        break;
                    case "v":
                        y += +res[i][len - 1];
                        break;
                    default:
                        x += +res[i][len - 2];
                        y += +res[i][len - 1];
                }
            }
            res.toString = R._path2string;
            pth.rel = pathClone(res);
            return res;
        },
        pathToAbsolute = R._pathToAbsolute = function (pathArray) {
            var pth = paths(pathArray);
            if (pth.abs) {
                return pathClone(pth.abs);
            }
            if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
                pathArray = R.parsePathString(pathArray);
            }
            if (!pathArray || !pathArray.length) {
                return [["M", 0, 0]];
            }
            var res = [],
                x = 0,
                y = 0,
                mx = 0,
                my = 0,
                start = 0;
            if (pathArray[0][0] == "M") {
                x = +pathArray[0][1];
                y = +pathArray[0][2];
                mx = x;
                my = y;
                start++;
                res[0] = ["M", x, y];
            }
            var crz = pathArray.length == 3 && pathArray[0][0] == "M" && pathArray[1][0].toUpperCase() == "R" && pathArray[2][0].toUpperCase() == "Z";
            for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
                res.push(r = []);
                pa = pathArray[i];
                if (pa[0] != upperCase.call(pa[0])) {
                    r[0] = upperCase.call(pa[0]);
                    switch (r[0]) {
                        case "A":
                            r[1] = pa[1];
                            r[2] = pa[2];
                            r[3] = pa[3];
                            r[4] = pa[4];
                            r[5] = pa[5];
                            r[6] = +(pa[6] + x);
                            r[7] = +(pa[7] + y);
                            break;
                        case "V":
                            r[1] = +pa[1] + y;
                            break;
                        case "H":
                            r[1] = +pa[1] + x;
                            break;
                        case "R":
                            var dots = [x, y][concat](pa.slice(1));
                            for (var j = 2, jj = dots.length; j < jj; j++) {
                                dots[j] = +dots[j] + x;
                                dots[++j] = +dots[j] + y;
                            }
                            res.pop();
                            res = res[concat](catmullRom2bezier(dots, crz));
                            break;
                        case "M":
                            mx = +pa[1] + x;
                            my = +pa[2] + y;
                        default:
                            for (j = 1, jj = pa.length; j < jj; j++) {
                                r[j] = +pa[j] + ((j % 2) ? x : y);
                            }
                    }
                } else if (pa[0] == "R") {
                    dots = [x, y][concat](pa.slice(1));
                    res.pop();
                    res = res[concat](catmullRom2bezier(dots, crz));
                    r = ["R"][concat](pa.slice(-2));
                } else {
                    for (var k = 0, kk = pa.length; k < kk; k++) {
                        r[k] = pa[k];
                    }
                }
                switch (r[0]) {
                    case "Z":
                        x = mx;
                        y = my;
                        break;
                    case "H":
                        x = r[1];
                        break;
                    case "V":
                        y = r[1];
                        break;
                    case "M":
                        mx = r[r.length - 2];
                        my = r[r.length - 1];
                    default:
                        x = r[r.length - 2];
                        y = r[r.length - 1];
                }
            }
            res.toString = R._path2string;
            pth.abs = pathClone(res);
            return res;
        },
        l2c = function (x1, y1, x2, y2) {
            return [x1, y1, x2, y2, x2, y2];
        },
        q2c = function (x1, y1, ax, ay, x2, y2) {
            var _13 = 1 / 3,
                _23 = 2 / 3;
            return [
                    _13 * x1 + _23 * ax,
                    _13 * y1 + _23 * ay,
                    _13 * x2 + _23 * ax,
                    _13 * y2 + _23 * ay,
                    x2,
                    y2
                ];
        },
        a2c = function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
            // for more information of where this math came from visit:
            // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
            var _120 = PI * 120 / 180,
                rad = PI / 180 * (+angle || 0),
                res = [],
                xy,
                rotate = cacher(function (x, y, rad) {
                    var X = x * math.cos(rad) - y * math.sin(rad),
                        Y = x * math.sin(rad) + y * math.cos(rad);
                    return {x: X, y: Y};
                });
            if (!recursive) {
                xy = rotate(x1, y1, -rad);
                x1 = xy.x;
                y1 = xy.y;
                xy = rotate(x2, y2, -rad);
                x2 = xy.x;
                y2 = xy.y;
                var cos = math.cos(PI / 180 * angle),
                    sin = math.sin(PI / 180 * angle),
                    x = (x1 - x2) / 2,
                    y = (y1 - y2) / 2;
                var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
                if (h > 1) {
                    h = math.sqrt(h);
                    rx = h * rx;
                    ry = h * ry;
                }
                var rx2 = rx * rx,
                    ry2 = ry * ry,
                    k = (large_arc_flag == sweep_flag ? -1 : 1) *
                        math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
                    cx = k * rx * y / ry + (x1 + x2) / 2,
                    cy = k * -ry * x / rx + (y1 + y2) / 2,
                    f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
                    f2 = math.asin(((y2 - cy) / ry).toFixed(9));

                f1 = x1 < cx ? PI - f1 : f1;
                f2 = x2 < cx ? PI - f2 : f2;
                f1 < 0 && (f1 = PI * 2 + f1);
                f2 < 0 && (f2 = PI * 2 + f2);
                if (sweep_flag && f1 > f2) {
                    f1 = f1 - PI * 2;
                }
                if (!sweep_flag && f2 > f1) {
                    f2 = f2 - PI * 2;
                }
            } else {
                f1 = recursive[0];
                f2 = recursive[1];
                cx = recursive[2];
                cy = recursive[3];
            }
            var df = f2 - f1;
            if (abs(df) > _120) {
                var f2old = f2,
                    x2old = x2,
                    y2old = y2;
                f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
                x2 = cx + rx * math.cos(f2);
                y2 = cy + ry * math.sin(f2);
                res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
            }
            df = f2 - f1;
            var c1 = math.cos(f1),
                s1 = math.sin(f1),
                c2 = math.cos(f2),
                s2 = math.sin(f2),
                t = math.tan(df / 4),
                hx = 4 / 3 * rx * t,
                hy = 4 / 3 * ry * t,
                m1 = [x1, y1],
                m2 = [x1 + hx * s1, y1 - hy * c1],
                m3 = [x2 + hx * s2, y2 - hy * c2],
                m4 = [x2, y2];
            m2[0] = 2 * m1[0] - m2[0];
            m2[1] = 2 * m1[1] - m2[1];
            if (recursive) {
                return [m2, m3, m4][concat](res);
            } else {
                res = [m2, m3, m4][concat](res).join()[split](",");
                var newres = [];
                for (var i = 0, ii = res.length; i < ii; i++) {
                    newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
                }
                return newres;
            }
        },
        findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
            var t1 = 1 - t;
            return {
                x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
                y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y
            };
        },
        curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
            var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
                b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
                c = p1x - c1x,
                t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a,
                t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a,
                y = [p1y, p2y],
                x = [p1x, p2x],
                dot;
            abs(t1) > "1e12" && (t1 = .5);
            abs(t2) > "1e12" && (t2 = .5);
            if (t1 > 0 && t1 < 1) {
                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
                x.push(dot.x);
                y.push(dot.y);
            }
            if (t2 > 0 && t2 < 1) {
                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
                x.push(dot.x);
                y.push(dot.y);
            }
            a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
            b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
            c = p1y - c1y;
            t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a;
            t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a;
            abs(t1) > "1e12" && (t1 = .5);
            abs(t2) > "1e12" && (t2 = .5);
            if (t1 > 0 && t1 < 1) {
                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
                x.push(dot.x);
                y.push(dot.y);
            }
            if (t2 > 0 && t2 < 1) {
                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
                x.push(dot.x);
                y.push(dot.y);
            }
            return {
                min: {x: mmin[apply](0, x), y: mmin[apply](0, y)},
                max: {x: mmax[apply](0, x), y: mmax[apply](0, y)}
            };
        }),
        path2curve = R._path2curve = cacher(function (path, path2) {
            var pth = !path2 && paths(path);
            if (!path2 && pth.curve) {
                return pathClone(pth.curve);
            }
            var p = pathToAbsolute(path),
                p2 = path2 && pathToAbsolute(path2),
                attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
                attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
                processPath = function (path, d, pcom) {
                    var nx, ny, tq = {T:1, Q:1};
                    if (!path) {
                        return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
                    }
                    !(path[0] in tq) && (d.qx = d.qy = null);
                    switch (path[0]) {
                        case "M":
                            d.X = path[1];
                            d.Y = path[2];
                            break;
                        case "A":
                            path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1))));
                            break;
                        case "S":
                            if (pcom == "C" || pcom == "S") { // In "S" case we have to take into account, if the previous command is C/S.
                                nx = d.x * 2 - d.bx;          // And reflect the previous
                                ny = d.y * 2 - d.by;          // command's control point relative to the current point.
                            }
                            else {                            // or some else or nothing
                                nx = d.x;
                                ny = d.y;
                            }
                            path = ["C", nx, ny][concat](path.slice(1));
                            break;
                        case "T":
                            if (pcom == "Q" || pcom == "T") { // In "T" case we have to take into account, if the previous command is Q/T.
                                d.qx = d.x * 2 - d.qx;        // And make a reflection similar
                                d.qy = d.y * 2 - d.qy;        // to case "S".
                            }
                            else {                            // or something else or nothing
                                d.qx = d.x;
                                d.qy = d.y;
                            }
                            path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
                            break;
                        case "Q":
                            d.qx = path[1];
                            d.qy = path[2];
                            path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4]));
                            break;
                        case "L":
                            path = ["C"][concat](l2c(d.x, d.y, path[1], path[2]));
                            break;
                        case "H":
                            path = ["C"][concat](l2c(d.x, d.y, path[1], d.y));
                            break;
                        case "V":
                            path = ["C"][concat](l2c(d.x, d.y, d.x, path[1]));
                            break;
                        case "Z":
                            path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y));
                            break;
                    }
                    return path;
                },
                fixArc = function (pp, i) {
                    if (pp[i].length > 7) {
                        pp[i].shift();
                        var pi = pp[i];
                        while (pi.length) {
                            pcoms1[i]="A"; // if created multiple C:s, their original seg is saved
                            p2 && (pcoms2[i]="A"); // the same as above
                            pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6)));
                        }
                        pp.splice(i, 1);
                        ii = mmax(p.length, p2 && p2.length || 0);
                    }
                },
                fixM = function (path1, path2, a1, a2, i) {
                    if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
                        path2.splice(i, 0, ["M", a2.x, a2.y]);
                        a1.bx = 0;
                        a1.by = 0;
                        a1.x = path1[i][1];
                        a1.y = path1[i][2];
                        ii = mmax(p.length, p2 && p2.length || 0);
                    }
                },
                pcoms1 = [], // path commands of original path p
                pcoms2 = [], // path commands of original path p2
                pfirst = "", // temporary holder for original path command
                pcom = ""; // holder for previous path command of original path
            for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) {
                p[i] && (pfirst = p[i][0]); // save current path command

                if (pfirst != "C") // C is not saved yet, because it may be result of conversion
                {
                    pcoms1[i] = pfirst; // Save current path command
                    i && ( pcom = pcoms1[i-1]); // Get previous path command pcom
                }
                p[i] = processPath(p[i], attrs, pcom); // Previous path command is inputted to processPath

                if (pcoms1[i] != "A" && pfirst == "C") pcoms1[i] = "C"; // A is the only command
                // which may produce multiple C:s
                // so we have to make sure that C is also C in original path

                fixArc(p, i); // fixArc adds also the right amount of A:s to pcoms1

                if (p2) { // the same procedures is done to p2
                    p2[i] && (pfirst = p2[i][0]);
                    if (pfirst != "C")
                    {
                        pcoms2[i] = pfirst;
                        i && (pcom = pcoms2[i-1]);
                    }
                    p2[i] = processPath(p2[i], attrs2, pcom);

                    if (pcoms2[i]!="A" && pfirst=="C") pcoms2[i]="C";

                    fixArc(p2, i);
                }
                fixM(p, p2, attrs, attrs2, i);
                fixM(p2, p, attrs2, attrs, i);
                var seg = p[i],
                    seg2 = p2 && p2[i],
                    seglen = seg.length,
                    seg2len = p2 && seg2.length;
                attrs.x = seg[seglen - 2];
                attrs.y = seg[seglen - 1];
                attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
                attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
                attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x);
                attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y);
                attrs2.x = p2 && seg2[seg2len - 2];
                attrs2.y = p2 && seg2[seg2len - 1];
            }
            if (!p2) {
                pth.curve = pathClone(p);
            }
            return p2 ? [p, p2] : p;
        }, null, pathClone),
        parseDots = R._parseDots = cacher(function (gradient) {
            var dots = [];
            for (var i = 0, ii = gradient.length; i < ii; i++) {
                var dot = {},
                    par = gradient[i].match(/^([^:]*):?([\d\.]*)/);
                dot.color = R.getRGB(par[1]);
                if (dot.color.error) {
                    return null;
                }
                dot.opacity = dot.color.opacity;
                dot.color = dot.color.hex;
                par[2] && (dot.offset = par[2] + "%");
                dots.push(dot);
            }
            for (i = 1, ii = dots.length - 1; i < ii; i++) {
                if (!dots[i].offset) {
                    var start = toFloat(dots[i - 1].offset || 0),
                        end = 0;
                    for (var j = i + 1; j < ii; j++) {
                        if (dots[j].offset) {
                            end = dots[j].offset;
                            break;
                        }
                    }
                    if (!end) {
                        end = 100;
                        j = ii;
                    }
                    end = toFloat(end);
                    var d = (end - start) / (j - i + 1);
                    for (; i < j; i++) {
                        start += d;
                        dots[i].offset = start + "%";
                    }
                }
            }
            return dots;
        }),
        tear = R._tear = function (el, paper) {
            el == paper.top && (paper.top = el.prev);
            el == paper.bottom && (paper.bottom = el.next);
            el.next && (el.next.prev = el.prev);
            el.prev && (el.prev.next = el.next);
        },
        tofront = R._tofront = function (el, paper) {
            if (paper.top === el) {
                return;
            }
            tear(el, paper);
            el.next = null;
            el.prev = paper.top;
            paper.top.next = el;
            paper.top = el;
        },
        toback = R._toback = function (el, paper) {
            if (paper.bottom === el) {
                return;
            }
            tear(el, paper);
            el.next = paper.bottom;
            el.prev = null;
            paper.bottom.prev = el;
            paper.bottom = el;
        },
        insertafter = R._insertafter = function (el, el2, paper) {
            tear(el, paper);
            el2 == paper.top && (paper.top = el);
            el2.next && (el2.next.prev = el);
            el.next = el2.next;
            el.prev = el2;
            el2.next = el;
        },
        insertbefore = R._insertbefore = function (el, el2, paper) {
            tear(el, paper);
            el2 == paper.bottom && (paper.bottom = el);
            el2.prev && (el2.prev.next = el);
            el.prev = el2.prev;
            el2.prev = el;
            el.next = el2;
        },
        /*\
         * Raphael.toMatrix
         [ method ]
         **
         * Utility method
         **
         * Returns matrix of transformations applied to a given path
         > Parameters
         - path (string) path string
         - transform (string|array) transformation string
         = (object) @Matrix
        \*/
        toMatrix = R.toMatrix = function (path, transform) {
            var bb = pathDimensions(path),
                el = {
                    _: {
                        transform: E
                    },
                    getBBox: function () {
                        return bb;
                    }
                };
            extractTransform(el, transform);
            return el.matrix;
        },
        /*\
         * Raphael.transformPath
         [ method ]
         **
         * Utility method
         **
         * Returns path transformed by a given transformation
         > Parameters
         - path (string) path string
         - transform (string|array) transformation string
         = (string) path
        \*/
        transformPath = R.transformPath = function (path, transform) {
            return mapPath(path, toMatrix(path, transform));
        },
        extractTransform = R._extractTransform = function (el, tstr) {
            if (tstr == null) {
                return el._.transform;
            }
            tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || E);
            var tdata = R.parseTransformString(tstr),
                deg = 0,
                dx = 0,
                dy = 0,
                sx = 1,
                sy = 1,
                _ = el._,
                m = new Matrix;
            _.transform = tdata || [];
            if (tdata) {
                for (var i = 0, ii = tdata.length; i < ii; i++) {
                    var t = tdata[i],
                        tlen = t.length,
                        command = Str(t[0]).toLowerCase(),
                        absolute = t[0] != command,
                        inver = absolute ? m.invert() : 0,
                        x1,
                        y1,
                        x2,
                        y2,
                        bb;
                    if (command == "t" && tlen == 3) {
                        if (absolute) {
                            x1 = inver.x(0, 0);
                            y1 = inver.y(0, 0);
                            x2 = inver.x(t[1], t[2]);
                            y2 = inver.y(t[1], t[2]);
                            m.translate(x2 - x1, y2 - y1);
                        } else {
                            m.translate(t[1], t[2]);
                        }
                    } else if (command == "r") {
                        if (tlen == 2) {
                            bb = bb || el.getBBox(1);
                            m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2);
                            deg += t[1];
                        } else if (tlen == 4) {
                            if (absolute) {
                                x2 = inver.x(t[2], t[3]);
                                y2 = inver.y(t[2], t[3]);
                                m.rotate(t[1], x2, y2);
                            } else {
                                m.rotate(t[1], t[2], t[3]);
                            }
                            deg += t[1];
                        }
                    } else if (command == "s") {
                        if (tlen == 2 || tlen == 3) {
                            bb = bb || el.getBBox(1);
                            m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2);
                            sx *= t[1];
                            sy *= t[tlen - 1];
                        } else if (tlen == 5) {
                            if (absolute) {
                                x2 = inver.x(t[3], t[4]);
                                y2 = inver.y(t[3], t[4]);
                                m.scale(t[1], t[2], x2, y2);
                            } else {
                                m.scale(t[1], t[2], t[3], t[4]);
                            }
                            sx *= t[1];
                            sy *= t[2];
                        }
                    } else if (command == "m" && tlen == 7) {
                        m.add(t[1], t[2], t[3], t[4], t[5], t[6]);
                    }
                    _.dirtyT = 1;
                    el.matrix = m;
                }
            }

            /*\
             * Element.matrix
             [ property (object) ]
             **
             * Keeps @Matrix object, which represents element transformation
            \*/
            el.matrix = m;

            _.sx = sx;
            _.sy = sy;
            _.deg = deg;
            _.dx = dx = m.e;
            _.dy = dy = m.f;

            if (sx == 1 && sy == 1 && !deg && _.bbox) {
                _.bbox.x += +dx;
                _.bbox.y += +dy;
            } else {
                _.dirtyT = 1;
            }
        },
        getEmpty = function (item) {
            var l = item[0];
            switch (l.toLowerCase()) {
                case "t": return [l, 0, 0];
                case "m": return [l, 1, 0, 0, 1, 0, 0];
                case "r": if (item.length == 4) {
                    return [l, 0, item[2], item[3]];
                } else {
                    return [l, 0];
                }
                case "s": if (item.length == 5) {
                    return [l, 1, 1, item[3], item[4]];
                } else if (item.length == 3) {
                    return [l, 1, 1];
                } else {
                    return [l, 1];
                }
            }
        },
        equaliseTransform = R._equaliseTransform = function (t1, t2) {
            t2 = Str(t2).replace(/\.{3}|\u2026/g, t1);
            t1 = R.parseTransformString(t1) || [];
            t2 = R.parseTransformString(t2) || [];
            var maxlength = mmax(t1.length, t2.length),
                from = [],
                to = [],
                i = 0, j, jj,
                tt1, tt2;
            for (; i < maxlength; i++) {
                tt1 = t1[i] || getEmpty(t2[i]);
                tt2 = t2[i] || getEmpty(tt1);
                if ((tt1[0] != tt2[0]) ||
                    (tt1[0].toLowerCase() == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) ||
                    (tt1[0].toLowerCase() == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4]))
                    ) {
                    return;
                }
                from[i] = [];
                to[i] = [];
                for (j = 0, jj = mmax(tt1.length, tt2.length); j < jj; j++) {
                    j in tt1 && (from[i][j] = tt1[j]);
                    j in tt2 && (to[i][j] = tt2[j]);
                }
            }
            return {
                from: from,
                to: to
            };
        };
    R._getContainer = function (x, y, w, h) {
        var container;
        container = h == null && !R.is(x, "object") ? g.doc.getElementById(x) : x;
        if (container == null) {
            return;
        }
        if (container.tagName) {
            if (y == null) {
                return {
                    container: container,
                    width: container.style.pixelWidth || container.offsetWidth,
                    height: container.style.pixelHeight || container.offsetHeight
                };
            } else {
                return {
                    container: container,
                    width: y,
                    height: w
                };
            }
        }
        return {
            container: 1,
            x: x,
            y: y,
            width: w,
            height: h
        };
    };
    /*\
     * Raphael.pathToRelative
     [ method ]
     **
     * Utility method
     **
     * Converts path to relative form
     > Parameters
     - pathString (string|array) path string or array of segments
     = (array) array of segments.
    \*/
    R.pathToRelative = pathToRelative;
    R._engine = {};
    /*\
     * Raphael.path2curve
     [ method ]
     **
     * Utility method
     **
     * Converts path to a new path where all segments are cubic bezier curves.
     > Parameters
     - pathString (string|array) path string or array of segments
     = (array) array of segments.
    \*/
    R.path2curve = path2curve;
    /*\
     * Raphael.matrix
     [ method ]
     **
     * Utility method
     **
     * Returns matrix based on given parameters.
     > Parameters
     - a (number)
     - b (number)
     - c (number)
     - d (number)
     - e (number)
     - f (number)
     = (object) @Matrix
    \*/
    R.matrix = function (a, b, c, d, e, f) {
        return new Matrix(a, b, c, d, e, f);
    };
    function Matrix(a, b, c, d, e, f) {
        if (a != null) {
            this.a = +a;
            this.b = +b;
            this.c = +c;
            this.d = +d;
            this.e = +e;
            this.f = +f;
        } else {
            this.a = 1;
            this.b = 0;
            this.c = 0;
            this.d = 1;
            this.e = 0;
            this.f = 0;
        }
    }
    (function (matrixproto) {
        /*\
         * Matrix.add
         [ method ]
         **
         * Adds given matrix to existing one.
         > Parameters
         - a (number)
         - b (number)
         - c (number)
         - d (number)
         - e (number)
         - f (number)
         or
         - matrix (object) @Matrix
        \*/
        matrixproto.add = function (a, b, c, d, e, f) {
            var out = [[], [], []],
                m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]],
                matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
                x, y, z, res;

            if (a && a instanceof Matrix) {
                matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]];
            }

            for (x = 0; x < 3; x++) {
                for (y = 0; y < 3; y++) {
                    res = 0;
                    for (z = 0; z < 3; z++) {
                        res += m[x][z] * matrix[z][y];
                    }
                    out[x][y] = res;
                }
            }
            this.a = out[0][0];
            this.b = out[1][0];
            this.c = out[0][1];
            this.d = out[1][1];
            this.e = out[0][2];
            this.f = out[1][2];
        };
        /*\
         * Matrix.invert
         [ method ]
         **
         * Returns inverted version of the matrix
         = (object) @Matrix
        \*/
        matrixproto.invert = function () {
            var me = this,
                x = me.a * me.d - me.b * me.c;
            return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x);
        };
        /*\
         * Matrix.clone
         [ method ]
         **
         * Returns copy of the matrix
         = (object) @Matrix
        \*/
        matrixproto.clone = function () {
            return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
        };
        /*\
         * Matrix.translate
         [ method ]
         **
         * Translate the matrix
         > Parameters
         - x (number)
         - y (number)
        \*/
        matrixproto.translate = function (x, y) {
            this.add(1, 0, 0, 1, x, y);
        };
        /*\
         * Matrix.scale
         [ method ]
         **
         * Scales the matrix
         > Parameters
         - x (number)
         - y (number) #optional
         - cx (number) #optional
         - cy (number) #optional
        \*/
        matrixproto.scale = function (x, y, cx, cy) {
            y == null && (y = x);
            (cx || cy) && this.add(1, 0, 0, 1, cx, cy);
            this.add(x, 0, 0, y, 0, 0);
            (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy);
        };
        /*\
         * Matrix.rotate
         [ method ]
         **
         * Rotates the matrix
         > Parameters
         - a (number)
         - x (number)
         - y (number)
        \*/
        matrixproto.rotate = function (a, x, y) {
            a = R.rad(a);
            x = x || 0;
            y = y || 0;
            var cos = +math.cos(a).toFixed(9),
                sin = +math.sin(a).toFixed(9);
            this.add(cos, sin, -sin, cos, x, y);
            this.add(1, 0, 0, 1, -x, -y);
        };
        /*\
         * Matrix.x
         [ method ]
         **
         * Return x coordinate for given point after transformation described by the matrix. See also @Matrix.y
         > Parameters
         - x (number)
         - y (number)
         = (number) x
        \*/
        matrixproto.x = function (x, y) {
            return x * this.a + y * this.c + this.e;
        };
        /*\
         * Matrix.y
         [ method ]
         **
         * Return y coordinate for given point after transformation described by the matrix. See also @Matrix.x
         > Parameters
         - x (number)
         - y (number)
         = (number) y
        \*/
        matrixproto.y = function (x, y) {
            return x * this.b + y * this.d + this.f;
        };
        matrixproto.get = function (i) {
            return +this[Str.fromCharCode(97 + i)].toFixed(4);
        };
        matrixproto.toString = function () {
            return R.svg ?
                "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" :
                [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join();
        };
        matrixproto.toFilter = function () {
            return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) +
                ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) +
                ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmethod='auto expand')";
        };
        matrixproto.offset = function () {
            return [this.e.toFixed(4), this.f.toFixed(4)];
        };
        function norm(a) {
            return a[0] * a[0] + a[1] * a[1];
        }
        function normalize(a) {
            var mag = math.sqrt(norm(a));
            a[0] && (a[0] /= mag);
            a[1] && (a[1] /= mag);
        }
        /*\
         * Matrix.split
         [ method ]
         **
         * Splits matrix into primitive transformations
         = (object) in format:
         o dx (number) translation by x
         o dy (number) translation by y
         o scalex (number) scale by x
         o scaley (number) scale by y
         o shear (number) shear
         o rotate (number) rotation in deg
         o isSimple (boolean) could it be represented via simple transformations
        \*/
        matrixproto.split = function () {
            var out = {};
            // translation
            out.dx = this.e;
            out.dy = this.f;

            // scale and shear
            var row = [[this.a, this.c], [this.b, this.d]];
            out.scalex = math.sqrt(norm(row[0]));
            normalize(row[0]);

            out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
            row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];

            out.scaley = math.sqrt(norm(row[1]));
            normalize(row[1]);
            out.shear /= out.scaley;

            // rotation
            var sin = -row[0][1],
                cos = row[1][1];
            if (cos < 0) {
                out.rotate = R.deg(math.acos(cos));
                if (sin < 0) {
                    out.rotate = 360 - out.rotate;
                }
            } else {
                out.rotate = R.deg(math.asin(sin));
            }

            out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate);
            out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate;
            out.noRotation = !+out.shear.toFixed(9) && !out.rotate;
            return out;
        };
        /*\
         * Matrix.toTransformString
         [ method ]
         **
         * Return transform string that represents given matrix
         = (string) transform string
        \*/
        matrixproto.toTransformString = function (shorter) {
            var s = shorter || this[split]();
            if (s.isSimple) {
                s.scalex = +s.scalex.toFixed(4);
                s.scaley = +s.scaley.toFixed(4);
                s.rotate = +s.rotate.toFixed(4);
                return  (s.dx || s.dy ? "t" + [s.dx, s.dy] : E) +
                        (s.scalex != 1 || s.scaley != 1 ? "s" + [s.scalex, s.scaley, 0, 0] : E) +
                        (s.rotate ? "r" + [s.rotate, 0, 0] : E);
            } else {
                return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)];
            }
        };
    })(Matrix.prototype);

    var preventDefault = function () {
        this.returnValue = false;
    },
    preventTouch = function () {
        return this.originalEvent.preventDefault();
    },
    stopPropagation = function () {
        this.cancelBubble = true;
    },
    stopTouch = function () {
        return this.originalEvent.stopPropagation();
    },
    getEventPosition = function (e) {
        var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
            scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft;

        return {
            x: e.clientX + scrollX,
            y: e.clientY + scrollY
        };
    },
    addEvent = (function () {
        if (g.doc.addEventListener) {
            return function (obj, type, fn, element) {
                var f = function (e) {
                    var pos = getEventPosition(e);
                    return fn.call(element, e, pos.x, pos.y);
                };
                obj.addEventListener(type, f, false);

                if (supportsTouch && touchMap[type]) {
                    var _f = function (e) {
                        var pos = getEventPosition(e),
                            olde = e;

                        for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
                            if (e.targetTouches[i].target == obj) {
                                e = e.targetTouches[i];
                                e.originalEvent = olde;
                                e.preventDefault = preventTouch;
                                e.stopPropagation = stopTouch;
                                break;
                            }
                        }

                        return fn.call(element, e, pos.x, pos.y);
                    };
                    obj.addEventListener(touchMap[type], _f, false);
                }

                return function () {
                    obj.removeEventListener(type, f, false);

                    if (supportsTouch && touchMap[type])
                        obj.removeEventListener(touchMap[type], _f, false);

                    return true;
                };
            };
        } else if (g.doc.attachEvent) {
            return function (obj, type, fn, element) {
                var f = function (e) {
                    e = e || g.win.event;
                    var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
                        scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
                        x = e.clientX + scrollX,
                        y = e.clientY + scrollY;
                    e.preventDefault = e.preventDefault || preventDefault;
                    e.stopPropagation = e.stopPropagation || stopPropagation;
                    return fn.call(element, e, x, y);
                };
                obj.attachEvent("on" + type, f);
                var detacher = function () {
                    obj.detachEvent("on" + type, f);
                    return true;
                };
                return detacher;
            };
        }
    })(),
    drag = [],
    dragMove = function (e) {
        var x = e.clientX,
            y = e.clientY,
            scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
            scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
            dragi,
            j = drag.length;
        while (j--) {
            dragi = drag[j];
            if (supportsTouch && e.touches) {
                var i = e.touches.length,
                    touch;
                while (i--) {
                    touch = e.touches[i];
                    if (touch.identifier == dragi.el._drag.id) {
                        x = touch.clientX;
                        y = touch.clientY;
                        (e.originalEvent ? e.originalEvent : e).preventDefault();
                        break;
                    }
                }
            } else {
                e.preventDefault();
            }
            var node = dragi.el.node,
                o,
                next = node.nextSibling,
                parent = node.parentNode,
                display = node.style.display;
            g.win.opera && parent.removeChild(node);
            node.style.display = "none";
            o = dragi.el.paper.getElementByPoint(x, y);
            node.style.display = display;
            g.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node));
            o && eve("raphael.drag.over." + dragi.el.id, dragi.el, o);
            x += scrollX;
            y += scrollY;
            eve("raphael.drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e);
        }
    },
    dragUp = function (e) {
        R.unmousemove(dragMove).unmouseup(dragUp);
        var i = drag.length,
            dragi;
        while (i--) {
            dragi = drag[i];
            dragi.el._drag = {};
            eve("raphael.drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e);
        }
        drag = [];
    },
    /*\
     * Raphael.el
     [ property (object) ]
     **
     * You can add your own method to elements. This is usefull when you want to hack default functionality or
     * want to wrap some common transformation or attributes in one method. In difference to canvas methods,
     * you can redefine element method at any time. Expending element methods wouldn’t affect set.
     > Usage
     | Raphael.el.red = function () {
     |     this.attr({fill: "#f00"});
     | };
     | // then use it
     | paper.circle(100, 100, 20).red();
    \*/
    elproto = R.el = {};
    /*\
     * Element.click
     [ method ]
     **
     * Adds event handler for click for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.unclick
     [ method ]
     **
     * Removes event handler for click for the element.
     > Parameters
     - handler (function) #optional handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.dblclick
     [ method ]
     **
     * Adds event handler for double click for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.undblclick
     [ method ]
     **
     * Removes event handler for double click for the element.
     > Parameters
     - handler (function) #optional handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.mousedown
     [ method ]
     **
     * Adds event handler for mousedown for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.unmousedown
     [ method ]
     **
     * Removes event handler for mousedown for the element.
     > Parameters
     - handler (function) #optional handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.mousemove
     [ method ]
     **
     * Adds event handler for mousemove for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.unmousemove
     [ method ]
     **
     * Removes event handler for mousemove for the element.
     > Parameters
     - handler (function) #optional handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.mouseout
     [ method ]
     **
     * Adds event handler for mouseout for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.unmouseout
     [ method ]
     **
     * Removes event handler for mouseout for the element.
     > Parameters
     - handler (function) #optional handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.mouseover
     [ method ]
     **
     * Adds event handler for mouseover for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.unmouseover
     [ method ]
     **
     * Removes event handler for mouseover for the element.
     > Parameters
     - handler (function) #optional handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.mouseup
     [ method ]
     **
     * Adds event handler for mouseup for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.unmouseup
     [ method ]
     **
     * Removes event handler for mouseup for the element.
     > Parameters
     - handler (function) #optional handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.touchstart
     [ method ]
     **
     * Adds event handler for touchstart for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.untouchstart
     [ method ]
     **
     * Removes event handler for touchstart for the element.
     > Parameters
     - handler (function) #optional handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.touchmove
     [ method ]
     **
     * Adds event handler for touchmove for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.untouchmove
     [ method ]
     **
     * Removes event handler for touchmove for the element.
     > Parameters
     - handler (function) #optional handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.touchend
     [ method ]
     **
     * Adds event handler for touchend for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.untouchend
     [ method ]
     **
     * Removes event handler for touchend for the element.
     > Parameters
     - handler (function) #optional handler for the event
     = (object) @Element
    \*/

    /*\
     * Element.touchcancel
     [ method ]
     **
     * Adds event handler for touchcancel for the element.
     > Parameters
     - handler (function) handler for the event
     = (object) @Element
    \*/
    /*\
     * Element.untouchcancel
     [ method ]
     **
     * Removes event handler for touchcancel for the element.
     > Parameters
     - handler (function) #optional handler for the event
     = (object) @Element
    \*/
    for (var i = events.length; i--;) {
        (function (eventName) {
            R[eventName] = elproto[eventName] = function (fn, scope) {
                if (R.is(fn, "function")) {
                    this.events = this.events || [];
                    this.events.push({name: eventName, f: fn, unbind: addEvent(this.shape || this.node || g.doc, eventName, fn, scope || this)});
                }
                return this;
            };
            R["un" + eventName] = elproto["un" + eventName] = function (fn) {
                var events = this.events || [],
                    l = events.length;
                while (l--){
                    if (events[l].name == eventName && (R.is(fn, "undefined") || events[l].f == fn)) {
                        events[l].unbind();
                        events.splice(l, 1);
                        !events.length && delete this.events;
                    }
                }
                return this;
            };
        })(events[i]);
    }

    /*\
     * Element.data
     [ method ]
     **
     * Adds or retrieves given value asociated with given key.
     **
     * See also @Element.removeData
     > Parameters
     - key (string) key to store data
     - value (any) #optional value to store
     = (object) @Element
     * or, if value is not specified:
     = (any) value
     * or, if key and value are not specified:
     = (object) Key/value pairs for all the data associated with the element.
     > Usage
     | for (var i = 0, i < 5, i++) {
     |     paper.circle(10 + 15 * i, 10, 10)
     |          .attr({fill: "#000"})
     |          .data("i", i)
     |          .click(function () {
     |             alert(this.data("i"));
     |          });
     | }
    \*/
    elproto.data = function (key, value) {
        var data = eldata[this.id] = eldata[this.id] || {};
        if (arguments.length == 0) {
            return data;
        }
        if (arguments.length == 1) {
            if (R.is(key, "object")) {
                for (var i in key) if (key[has](i)) {
                    this.data(i, key[i]);
                }
                return this;
            }
            eve("raphael.data.get." + this.id, this, data[key], key);
            return data[key];
        }
        data[key] = value;
        eve("raphael.data.set." + this.id, this, value, key);
        return this;
    };
    /*\
     * Element.removeData
     [ method ]
     **
     * Removes value associated with an element by given key.
     * If key is not provided, removes all the data of the element.
     > Parameters
     - key (string) #optional key
     = (object) @Element
    \*/
    elproto.removeData = function (key) {
        if (key == null) {
            eldata[this.id] = {};
        } else {
            eldata[this.id] && delete eldata[this.id][key];
        }
        return this;
    };
     /*\
     * Element.getData
     [ method ]
     **
     * Retrieves the element data
     = (object) data
    \*/
    elproto.getData = function () {
        return clone(eldata[this.id] || {});
    };
    /*\
     * Element.hover
     [ method ]
     **
     * Adds event handlers for hover for the element.
     > Parameters
     - f_in (function) handler for hover in
     - f_out (function) handler for hover out
     - icontext (object) #optional context for hover in handler
     - ocontext (object) #optional context for hover out handler
     = (object) @Element
    \*/
    elproto.hover = function (f_in, f_out, scope_in, scope_out) {
        return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in);
    };
    /*\
     * Element.unhover
     [ method ]
     **
     * Removes event handlers for hover for the element.
     > Parameters
     - f_in (function) handler for hover in
     - f_out (function) handler for hover out
     = (object) @Element
    \*/
    elproto.unhover = function (f_in, f_out) {
        return this.unmouseover(f_in).unmouseout(f_out);
    };
    var draggable = [];
    /*\
     * Element.drag
     [ method ]
     **
     * Adds event handlers for drag of the element.
     > Parameters
     - onmove (function) handler for moving
     - onstart (function) handler for drag start
     - onend (function) handler for drag end
     - mcontext (object) #optional context for moving handler
     - scontext (object) #optional context for drag start handler
     - econtext (object) #optional context for drag end handler
     * Additionaly following `drag` events will be triggered: `drag.start.<id>` on start,
     * `drag.end.<id>` on end and `drag.move.<id>` on every move. When element will be dragged over another element
     * `drag.over.<id>` will be fired as well.
     *
     * Start event and start handler will be called in specified context or in context of the element with following parameters:
     o x (number) x position of the mouse
     o y (number) y position of the mouse
     o event (object) DOM event object
     * Move event and move handler will be called in specified context or in context of the element with following parameters:
     o dx (number) shift by x from the start point
     o dy (number) shift by y from the start point
     o x (number) x position of the mouse
     o y (number) y position of the mouse
     o event (object) DOM event object
     * End event and end handler will be called in specified context or in context of the element with following parameters:
     o event (object) DOM event object
     = (object) @Element
    \*/
    elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) {
        function start(e) {
            (e.originalEvent || e).preventDefault();
            var x = e.clientX,
                y = e.clientY,
                scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
                scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft;
            this._drag.id = e.identifier;
            if (supportsTouch && e.touches) {
                var i = e.touches.length, touch;
                while (i--) {
                    touch = e.touches[i];
                    this._drag.id = touch.identifier;
                    if (touch.identifier == this._drag.id) {
                        x = touch.clientX;
                        y = touch.clientY;
                        break;
                    }
                }
            }
            this._drag.x = x + scrollX;
            this._drag.y = y + scrollY;
            !drag.length && R.mousemove(dragMove).mouseup(dragUp);
            drag.push({el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope});
            onstart && eve.on("raphael.drag.start." + this.id, onstart);
            onmove && eve.on("raphael.drag.move." + this.id, onmove);
            onend && eve.on("raphael.drag.end." + this.id, onend);
            eve("raphael.drag.start." + this.id, start_scope || move_scope || this, e.clientX + scrollX, e.clientY + scrollY, e);
        }
        this._drag = {};
        draggable.push({el: this, start: start});
        this.mousedown(start);
        return this;
    };
    /*\
     * Element.onDragOver
     [ method ]
     **
     * Shortcut for assigning event handler for `drag.over.<id>` event, where id is id of the element (see @Element.id).
     > Parameters
     - f (function) handler for event, first argument would be the element you are dragging over
    \*/
    elproto.onDragOver = function (f) {
        f ? eve.on("raphael.drag.over." + this.id, f) : eve.unbind("raphael.drag.over." + this.id);
    };
    /*\
     * Element.undrag
     [ method ]
     **
     * Removes all drag event handlers from given element.
    \*/
    elproto.undrag = function () {
        var i = draggable.length;
        while (i--) if (draggable[i].el == this) {
            this.unmousedown(draggable[i].start);
            draggable.splice(i, 1);
            eve.unbind("raphael.drag.*." + this.id);
        }
        !draggable.length && R.unmousemove(dragMove).unmouseup(dragUp);
        drag = [];
    };
    /*\
     * Paper.circle
     [ method ]
     **
     * Draws a circle.
     **
     > Parameters
     **
     - x (number) x coordinate of the centre
     - y (number) y coordinate of the centre
     - r (number) radius
     = (object) Raphaël element object with type “circle”
     **
     > Usage
     | var c = paper.circle(50, 50, 40);
    \*/
    paperproto.circle = function (x, y, r) {
        var out = R._engine.circle(this, x || 0, y || 0, r || 0);
        this.__set__ && this.__set__.push(out);
        return out;
    };
    /*\
     * Paper.rect
     [ method ]
     *
     * Draws a rectangle.
     **
     > Parameters
     **
     - x (number) x coordinate of the top left corner
     - y (number) y coordinate of the top left corner
     - width (number) width
     - height (number) height
     - r (number) #optional radius for rounded corners, default is 0
     = (object) Raphaël element object with type “rect”
     **
     > Usage
     | // regular rectangle
     | var c = paper.rect(10, 10, 50, 50);
     | // rectangle with rounded corners
     | var c = paper.rect(40, 40, 50, 50, 10);
    \*/
    paperproto.rect = function (x, y, w, h, r) {
        var out = R._engine.rect(this, x || 0, y || 0, w || 0, h || 0, r || 0);
        this.__set__ && this.__set__.push(out);
        return out;
    };
    /*\
     * Paper.ellipse
     [ method ]
     **
     * Draws an ellipse.
     **
     > Parameters
     **
     - x (number) x coordinate of the centre
     - y (number) y coordinate of the centre
     - rx (number) horizontal radius
     - ry (number) vertical radius
     = (object) Raphaël element object with type “ellipse”
     **
     > Usage
     | var c = paper.ellipse(50, 50, 40, 20);
    \*/
    paperproto.ellipse = function (x, y, rx, ry) {
        var out = R._engine.ellipse(this, x || 0, y || 0, rx || 0, ry || 0);
        this.__set__ && this.__set__.push(out);
        return out;
    };
    /*\
     * Paper.path
     [ method ]
     **
     * Creates a path element by given path data string.
     > Parameters
     - pathString (string) #optional path string in SVG format.
     * Path string consists of one-letter commands, followed by comma seprarated arguments in numercal form. Example:
     | "M10,20L30,40"
     * Here we can see two commands: “M”, with arguments `(10, 20)` and “L” with arguments `(30, 40)`. Upper case letter mean command is absolute, lower case—relative.
     *
     # <p>Here is short list of commands available, for more details see <a href="http://www.w3.org/TR/SVG/paths.html#PathData" title="Details of a path's data attribute's format are described in the SVG specification.">SVG path string format</a>.</p>
     # <table><thead><tr><th>Command</th><th>Name</th><th>Parameters</th></tr></thead><tbody>
     # <tr><td>M</td><td>moveto</td><td>(x y)+</td></tr>
     # <tr><td>Z</td><td>closepath</td><td>(none)</td></tr>
     # <tr><td>L</td><td>lineto</td><td>(x y)+</td></tr>
     # <tr><td>H</td><td>horizontal lineto</td><td>x+</td></tr>
     # <tr><td>V</td><td>vertical lineto</td><td>y+</td></tr>
     # <tr><td>C</td><td>curveto</td><td>(x1 y1 x2 y2 x y)+</td></tr>
     # <tr><td>S</td><td>smooth curveto</td><td>(x2 y2 x y)+</td></tr>
     # <tr><td>Q</td><td>quadratic Bézier curveto</td><td>(x1 y1 x y)+</td></tr>
     # <tr><td>T</td><td>smooth quadratic Bézier curveto</td><td>(x y)+</td></tr>
     # <tr><td>A</td><td>elliptical arc</td><td>(rx ry x-axis-rotation large-arc-flag sweep-flag x y)+</td></tr>
     # <tr><td>R</td><td><a href="http://en.wikipedia.org/wiki/Catmull–Rom_spline#Catmull.E2.80.93Rom_spline">Catmull-Rom curveto</a>*</td><td>x1 y1 (x y)+</td></tr></tbody></table>
     * * “Catmull-Rom curveto” is a not standard SVG command and added in 2.0 to make life easier.
     * Note: there is a special case when path consist of just three commands: “M10,10R…z”. In this case path will smoothly connects to its beginning.
     > Usage
     | var c = paper.path("M10 10L90 90");
     | // draw a diagonal line:
     | // move to 10,10, line to 90,90
     * For example of path strings, check out these icons: http://raphaeljs.com/icons/
    \*/
    paperproto.path = function (pathString) {
        pathString && !R.is(pathString, string) && !R.is(pathString[0], array) && (pathString += E);
        var out = R._engine.path(R.format[apply](R, arguments), this);
        this.__set__ && this.__set__.push(out);
        return out;
    };
    /*\
     * Paper.image
     [ method ]
     **
     * Embeds an image into the surface.
     **
     > Parameters
     **
     - src (string) URI of the source image
     - x (number) x coordinate position
     - y (number) y coordinate position
     - width (number) width of the image
     - height (number) height of the image
     = (object) Raphaël element object with type “image”
     **
     > Usage
     | var c = paper.image("apple.png", 10, 10, 80, 80);
    \*/
    paperproto.image = function (src, x, y, w, h) {
        var out = R._engine.image(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0);
        this.__set__ && this.__set__.push(out);
        return out;
    };
    /*\
     * Paper.text
     [ method ]
     **
     * Draws a text string. If you need line breaks, put “\n” in the string.
     **
     > Parameters
     **
     - x (number) x coordinate position
     - y (number) y coordinate position
     - text (string) The text string to draw
     = (object) Raphaël element object with type “text”
     **
     > Usage
     | var t = paper.text(50, 50, "Raphaël\nkicks\nbutt!");
    \*/
    paperproto.text = function (x, y, text) {
        var out = R._engine.text(this, x || 0, y || 0, Str(text));
        this.__set__ && this.__set__.push(out);
        return out;
    };
    /*\
     * Paper.set
     [ method ]
     **
     * Creates array-like object to keep and operate several elements at once.
     * Warning: it doesn’t create any elements for itself in the page, it just groups existing elements.
     * Sets act as pseudo elements — all methods available to an element can be used on a set.
     = (object) array-like object that represents set of elements
     **
     > Usage
     | var st = paper.set();
     | st.push(
     |     paper.circle(10, 10, 5),
     |     paper.circle(30, 10, 5)
     | );
     | st.attr({fill: "red"}); // changes the fill of both circles
    \*/
    paperproto.set = function (itemsArray) {
        !R.is(itemsArray, "array") && (itemsArray = Array.prototype.splice.call(arguments, 0, arguments.length));
        var out = new Set(itemsArray);
        this.__set__ && this.__set__.push(out);
        out["paper"] = this;
        out["type"] = "set";
        return out;
    };
    /*\
     * Paper.setStart
     [ method ]
     **
     * Creates @Paper.set. All elements that will be created after calling this method and before calling
     * @Paper.setFinish will be added to the set.
     **
     > Usage
     | paper.setStart();
     | paper.circle(10, 10, 5),
     | paper.circle(30, 10, 5)
     | var st = paper.setFinish();
     | st.attr({fill: "red"}); // changes the fill of both circles
    \*/
    paperproto.setStart = function (set) {
        this.__set__ = set || this.set();
    };
    /*\
     * Paper.setFinish
     [ method ]
     **
     * See @Paper.setStart. This method finishes catching and returns resulting set.
     **
     = (object) set
    \*/
    paperproto.setFinish = function (set) {
        var out = this.__set__;
        delete this.__set__;
        return out;
    };
    /*\
     * Paper.getSize
     [ method ]
     **
     * Obtains current paper actual size.
     **
     = (object)
     \*/
    paperproto.getSize = function () {
        var container = this.canvas.parentNode;
        return {
            width: container.offsetWidth,
            height: container.offsetHeight
                };
        };
    /*\
     * Paper.setSize
     [ method ]
     **
     * If you need to change dimensions of the canvas call this method
     **
     > Parameters
     **
     - width (number) new width of the canvas
     - height (number) new height of the canvas
    \*/
    paperproto.setSize = function (width, height) {
        return R._engine.setSize.call(this, width, height);
    };
    /*\
     * Paper.setViewBox
     [ method ]
     **
     * Sets the view box of the paper. Practically it gives you ability to zoom and pan whole paper surface by
     * specifying new boundaries.
     **
     > Parameters
     **
     - x (number) new x position, default is `0`
     - y (number) new y position, default is `0`
     - w (number) new width of the canvas
     - h (number) new height of the canvas
     - fit (boolean) `true` if you want graphics to fit into new boundary box
    \*/
    paperproto.setViewBox = function (x, y, w, h, fit) {
        return R._engine.setViewBox.call(this, x, y, w, h, fit);
    };
    /*\
     * Paper.top
     [ property ]
     **
     * Points to the topmost element on the paper
    \*/
    /*\
     * Paper.bottom
     [ property ]
     **
     * Points to the bottom element on the paper
    \*/
    paperproto.top = paperproto.bottom = null;
    /*\
     * Paper.raphael
     [ property ]
     **
     * Points to the @Raphael object/function
    \*/
    paperproto.raphael = R;
    var getOffset = function (elem) {
        var box = elem.getBoundingClientRect(),
            doc = elem.ownerDocument,
            body = doc.body,
            docElem = doc.documentElement,
            clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
            top  = box.top  + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop,
            left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft;
        return {
            y: top,
            x: left
        };
    };
    /*\
     * Paper.getElementByPoint
     [ method ]
     **
     * Returns you topmost element under given point.
     **
     = (object) Raphaël element object
     > Parameters
     **
     - x (number) x coordinate from the top left corner of the window
     - y (number) y coordinate from the top left corner of the window
     > Usage
     | paper.getElementByPoint(mouseX, mouseY).attr({stroke: "#f00"});
    \*/
    paperproto.getElementByPoint = function (x, y) {
        var paper = this,
            svg = paper.canvas,
            target = g.doc.elementFromPoint(x, y);
        if (g.win.opera && target.tagName == "svg") {
            var so = getOffset(svg),
                sr = svg.createSVGRect();
            sr.x = x - so.x;
            sr.y = y - so.y;
            sr.width = sr.height = 1;
            var hits = svg.getIntersectionList(sr, null);
            if (hits.length) {
                target = hits[hits.length - 1];
            }
        }
        if (!target) {
            return null;
        }
        while (target.parentNode && target != svg.parentNode && !target.raphael) {
            target = target.parentNode;
        }
        target == paper.canvas.parentNode && (target = svg);
        target = target && target.raphael ? paper.getById(target.raphaelid) : null;
        return target;
    };

    /*\
     * Paper.getElementsByBBox
     [ method ]
     **
     * Returns set of elements that have an intersecting bounding box
     **
     > Parameters
     **
     - bbox (object) bbox to check with
     = (object) @Set
     \*/
    paperproto.getElementsByBBox = function (bbox) {
        var set = this.set();
        this.forEach(function (el) {
            if (R.isBBoxIntersect(el.getBBox(), bbox)) {
                set.push(el);
            }
        });
        return set;
    };

    /*\
     * Paper.getById
     [ method ]
     **
     * Returns you element by its internal ID.
     **
     > Parameters
     **
     - id (number) id
     = (object) Raphaël element object
    \*/
    paperproto.getById = function (id) {
        var bot = this.bottom;
        while (bot) {
            if (bot.id == id) {
                return bot;
            }
            bot = bot.next;
        }
        return null;
    };
    /*\
     * Paper.forEach
     [ method ]
     **
     * Executes given function for each element on the paper
     *
     * If callback function returns `false` it will stop loop running.
     **
     > Parameters
     **
     - callback (function) function to run
     - thisArg (object) context object for the callback
     = (object) Paper object
     > Usage
     | paper.forEach(function (el) {
     |     el.attr({ stroke: "blue" });
     | });
    \*/
    paperproto.forEach = function (callback, thisArg) {
        var bot = this.bottom;
        while (bot) {
            if (callback.call(thisArg, bot) === false) {
                return this;
            }
            bot = bot.next;
        }
        return this;
    };
    /*\
     * Paper.getElementsByPoint
     [ method ]
     **
     * Returns set of elements that have common point inside
     **
     > Parameters
     **
     - x (number) x coordinate of the point
     - y (number) y coordinate of the point
     = (object) @Set
    \*/
    paperproto.getElementsByPoint = function (x, y) {
        var set = this.set();
        this.forEach(function (el) {
            if (el.isPointInside(x, y)) {
                set.push(el);
            }
        });
        return set;
    };
    function x_y() {
        return this.x + S + this.y;
    }
    function x_y_w_h() {
        return this.x + S + this.y + S + this.width + " \xd7 " + this.height;
    }
    /*\
     * Element.isPointInside
     [ method ]
     **
     * Determine if given point is inside this element’s shape
     **
     > Parameters
     **
     - x (number) x coordinate of the point
     - y (number) y coordinate of the point
     = (boolean) `true` if point inside the shape
    \*/
    elproto.isPointInside = function (x, y) {
        var rp = this.realPath = getPath[this.type](this);
        if (this.attr('transform') && this.attr('transform').length) {
            rp = R.transformPath(rp, this.attr('transform'));
        }
        return R.isPointInsidePath(rp, x, y);
    };
    /*\
     * Element.getBBox
     [ method ]
     **
     * Return bounding box for a given element
     **
     > Parameters
     **
     - isWithoutTransform (boolean) flag, `true` if you want to have bounding box before transformations. Default is `false`.
     = (object) Bounding box object:
     o {
     o     x: (number) top left corner x
     o     y: (number) top left corner y
     o     x2: (number) bottom right corner x
     o     y2: (number) bottom right corner y
     o     width: (number) width
     o     height: (number) height
     o }
    \*/
    elproto.getBBox = function (isWithoutTransform) {
        if (this.removed) {
            return {};
        }
        var _ = this._;
        if (isWithoutTransform) {
            if (_.dirty || !_.bboxwt) {
                this.realPath = getPath[this.type](this);
                _.bboxwt = pathDimensions(this.realPath);
                _.bboxwt.toString = x_y_w_h;
                _.dirty = 0;
            }
            return _.bboxwt;
        }
        if (_.dirty || _.dirtyT || !_.bbox) {
            if (_.dirty || !this.realPath) {
                _.bboxwt = 0;
                this.realPath = getPath[this.type](this);
            }
            _.bbox = pathDimensions(mapPath(this.realPath, this.matrix));
            _.bbox.toString = x_y_w_h;
            _.dirty = _.dirtyT = 0;
        }
        return _.bbox;
    };
    /*\
     * Element.clone
     [ method ]
     **
     = (object) clone of a given element
     **
    \*/
    elproto.clone = function () {
        if (this.removed) {
            return null;
        }
        var out = this.paper[this.type]().attr(this.attr());
        this.__set__ && this.__set__.push(out);
        return out;
    };
    /*\
     * Element.glow
     [ method ]
     **
     * Return set of elements that create glow-like effect around given element. See @Paper.set.
     *
     * Note: Glow is not connected to the element. If you change element attributes it won’t adjust itself.
     **
     > Parameters
     **
     - glow (object) #optional parameters object with all properties optional:
     o {
     o     width (number) size of the glow, default is `10`
     o     fill (boolean) will it be filled, default is `false`
     o     opacity (number) opacity, default is `0.5`
     o     offsetx (number) horizontal offset, default is `0`
     o     offsety (number) vertical offset, default is `0`
     o     color (string) glow colour, default is `black`
     o }
     = (object) @Paper.set of elements that represents glow
    \*/
    elproto.glow = function (glow) {
        if (this.type == "text") {
            return null;
        }
        glow = glow || {};
        var s = {
            width: (glow.width || 10) + (+this.attr("stroke-width") || 1),
            fill: glow.fill || false,
            opacity: glow.opacity == null ? .5 : glow.opacity,
            offsetx: glow.offsetx || 0,
            offsety: glow.offsety || 0,
            color: glow.color || "#000"
        },
            c = s.width / 2,
            r = this.paper,
            out = r.set(),
            path = this.realPath || getPath[this.type](this);
        path = this.matrix ? mapPath(path, this.matrix) : path;
        for (var i = 1; i < c + 1; i++) {
            out.push(r.path(path).attr({
                stroke: s.color,
                fill: s.fill ? s.color : "none",
                "stroke-linejoin": "round",
                "stroke-linecap": "round",
                "stroke-width": +(s.width / c * i).toFixed(3),
                opacity: +(s.opacity / c).toFixed(3)
            }));
        }
        return out.insertBefore(this).translate(s.offsetx, s.offsety);
    };
    var curveslengths = {},
    getPointAtSegmentLength = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) {
        if (length == null) {
            return bezlen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
        } else {
            return R.findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, getTatLen(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length));
        }
    },
    getLengthFactory = function (istotal, subpath) {
        return function (path, length, onlystart) {
            path = path2curve(path);
            var x, y, p, l, sp = "", subpaths = {}, point,
                len = 0;
            for (var i = 0, ii = path.length; i < ii; i++) {
                p = path[i];
                if (p[0] == "M") {
                    x = +p[1];
                    y = +p[2];
                } else {
                    l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
                    if (len + l > length) {
                        if (subpath && !subpaths.start) {
                            point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
                            sp += ["C" + point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y];
                            if (onlystart) {return sp;}
                            subpaths.start = sp;
                            sp = ["M" + point.x, point.y + "C" + point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]].join();
                            len += l;
                            x = +p[5];
                            y = +p[6];
                            continue;
                        }
                        if (!istotal && !subpath) {
                            point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
                            return {x: point.x, y: point.y, alpha: point.alpha};
                        }
                    }
                    len += l;
                    x = +p[5];
                    y = +p[6];
                }
                sp += p.shift() + p;
            }
            subpaths.end = sp;
            point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1);
            point.alpha && (point = {x: point.x, y: point.y, alpha: point.alpha});
            return point;
        };
    };
    var getTotalLength = getLengthFactory(1),
        getPointAtLength = getLengthFactory(),
        getSubpathsAtLength = getLengthFactory(0, 1);
    /*\
     * Raphael.getTotalLength
     [ method ]
     **
     * Returns length of the given path in pixels.
     **
     > Parameters
     **
     - path (string) SVG path string.
     **
     = (number) length.
    \*/
    R.getTotalLength = getTotalLength;
    /*\
     * Raphael.getPointAtLength
     [ method ]
     **
     * Return coordinates of the point located at the given length on the given path.
     **
     > Parameters
     **
     - path (string) SVG path string
     - length (number)
     **
     = (object) representation of the point:
     o {
     o     x: (number) x coordinate
     o     y: (number) y coordinate
     o     alpha: (number) angle of derivative
     o }
    \*/
    R.getPointAtLength = getPointAtLength;
    /*\
     * Raphael.getSubpath
     [ method ]
     **
     * Return subpath of a given path from given length to given length.
     **
     > Parameters
     **
     - path (string) SVG path string
     - from (number) position of the start of the segment
     - to (number) position of the end of the segment
     **
     = (string) pathstring for the segment
    \*/
    R.getSubpath = function (path, from, to) {
        if (this.getTotalLength(path) - to < 1e-6) {
            return getSubpathsAtLength(path, from).end;
        }
        var a = getSubpathsAtLength(path, to, 1);
        return from ? getSubpathsAtLength(a, from).end : a;
    };
    /*\
     * Element.getTotalLength
     [ method ]
     **
     * Returns length of the path in pixels. Only works for element of “path” type.
     = (number) length.
    \*/
    elproto.getTotalLength = function () {
        var path = this.getPath();
        if (!path) {
            return;
        }

        if (this.node.getTotalLength) {
            return this.node.getTotalLength();
        }

        return getTotalLength(path);
    };
    /*\
     * Element.getPointAtLength
     [ method ]
     **
     * Return coordinates of the point located at the given length on the given path. Only works for element of “path” type.
     **
     > Parameters
     **
     - length (number)
     **
     = (object) representation of the point:
     o {
     o     x: (number) x coordinate
     o     y: (number) y coordinate
     o     alpha: (number) angle of derivative
     o }
    \*/
    elproto.getPointAtLength = function (length) {
        var path = this.getPath();
        if (!path) {
            return;
        }

        return getPointAtLength(path, length);
    };
    /*\
     * Element.getPath
     [ method ]
     **
     * Returns path of the element. Only works for elements of “path” type and simple elements like circle.
     = (object) path
     **
    \*/
    elproto.getPath = function () {
        var path,
            getPath = R._getPath[this.type];

        if (this.type == "text" || this.type == "set") {
            return;
        }

        if (getPath) {
            path = getPath(this);
        }

        return path;
    };
    /*\
     * Element.getSubpath
     [ method ]
     **
     * Return subpath of a given element from given length to given length. Only works for element of “path” type.
     **
     > Parameters
     **
     - from (number) position of the start of the segment
     - to (number) position of the end of the segment
     **
     = (string) pathstring for the segment
    \*/
    elproto.getSubpath = function (from, to) {
        var path = this.getPath();
        if (!path) {
            return;
        }

        return R.getSubpath(path, from, to);
    };
    /*\
     * Raphael.easing_formulas
     [ property ]
     **
     * Object that contains easing formulas for animation. You could extend it with your own. By default it has following list of easing:
     # <ul>
     #     <li>“linear”</li>
     #     <li>“&lt;” or “easeIn” or “ease-in”</li>
     #     <li>“>” or “easeOut” or “ease-out”</li>
     #     <li>“&lt;>” or “easeInOut” or “ease-in-out”</li>
     #     <li>“backIn” or “back-in”</li>
     #     <li>“backOut” or “back-out”</li>
     #     <li>“elastic”</li>
     #     <li>“bounce”</li>
     # </ul>
     # <p>See also <a href="http://raphaeljs.com/easing.html">Easing demo</a>.</p>
    \*/
    var ef = R.easing_formulas = {
        linear: function (n) {
            return n;
        },
        "<": function (n) {
            return pow(n, 1.7);
        },
        ">": function (n) {
            return pow(n, .48);
        },
        "<>": function (n) {
            var q = .48 - n / 1.04,
                Q = math.sqrt(.1734 + q * q),
                x = Q - q,
                X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1),
                y = -Q - q,
                Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1),
                t = X + Y + .5;
            return (1 - t) * 3 * t * t + t * t * t;
        },
        backIn: function (n) {
            var s = 1.70158;
            return n * n * ((s + 1) * n - s);
        },
        backOut: function (n) {
            n = n - 1;
            var s = 1.70158;
            return n * n * ((s + 1) * n + s) + 1;
        },
        elastic: function (n) {
            if (n == !!n) {
                return n;
            }
            return pow(2, -10 * n) * math.sin((n - .075) * (2 * PI) / .3) + 1;
        },
        bounce: function (n) {
            var s = 7.5625,
                p = 2.75,
                l;
            if (n < (1 / p)) {
                l = s * n * n;
            } else {
                if (n < (2 / p)) {
                    n -= (1.5 / p);
                    l = s * n * n + .75;
                } else {
                    if (n < (2.5 / p)) {
                        n -= (2.25 / p);
                        l = s * n * n + .9375;
                    } else {
                        n -= (2.625 / p);
                        l = s * n * n + .984375;
                    }
                }
            }
            return l;
        }
    };
    ef.easeIn = ef["ease-in"] = ef["<"];
    ef.easeOut = ef["ease-out"] = ef[">"];
    ef.easeInOut = ef["ease-in-out"] = ef["<>"];
    ef["back-in"] = ef.backIn;
    ef["back-out"] = ef.backOut;

    var animationElements = [],
        requestAnimFrame = window.requestAnimationFrame       ||
                           window.webkitRequestAnimationFrame ||
                           window.mozRequestAnimationFrame    ||
                           window.oRequestAnimationFrame      ||
                           window.msRequestAnimationFrame     ||
                           function (callback) {
                               setTimeout(callback, 16);
                           },
        animation = function () {
            var Now = +new Date,
                l = 0;
            for (; l < animationElements.length; l++) {
                var e = animationElements[l];
                if (e.el.removed || e.paused) {
                    continue;
                }
                var time = Now - e.start,
                    ms = e.ms,
                    easing = e.easing,
                    from = e.from,
                    diff = e.diff,
                    to = e.to,
                    t = e.t,
                    that = e.el,
                    set = {},
                    now,
                    init = {},
                    key;
                if (e.initstatus) {
                    time = (e.initstatus * e.anim.top - e.prev) / (e.percent - e.prev) * ms;
                    e.status = e.initstatus;
                    delete e.initstatus;
                    e.stop && animationElements.splice(l--, 1);
                } else {
                    e.status = (e.prev + (e.percent - e.prev) * (time / ms)) / e.anim.top;
                }
                if (time < 0) {
                    continue;
                }
                if (time < ms) {
                    var pos = easing(time / ms);
                    for (var attr in from) if (from[has](attr)) {
                        switch (availableAnimAttrs[attr]) {
                            case nu:
                                now = +from[attr] + pos * ms * diff[attr];
                                break;
                            case "colour":
                                now = "rgb(" + [
                                    upto255(round(from[attr].r + pos * ms * diff[attr].r)),
                                    upto255(round(from[attr].g + pos * ms * diff[attr].g)),
                                    upto255(round(from[attr].b + pos * ms * diff[attr].b))
                                ].join(",") + ")";
                                break;
                            case "path":
                                now = [];
                                for (var i = 0, ii = from[attr].length; i < ii; i++) {
                                    now[i] = [from[attr][i][0]];
                                    for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
                                        now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j];
                                    }
                                    now[i] = now[i].join(S);
                                }
                                now = now.join(S);
                                break;
                            case "transform":
                                if (diff[attr].real) {
                                    now = [];
                                    for (i = 0, ii = from[attr].length; i < ii; i++) {
                                        now[i] = [from[attr][i][0]];
                                        for (j = 1, jj = from[attr][i].length; j < jj; j++) {
                                            now[i][j] = from[attr][i][j] + pos * ms * diff[attr][i][j];
                                        }
                                    }
                                } else {
                                    var get = function (i) {
                                        return +from[attr][i] + pos * ms * diff[attr][i];
                                    };
                                    // now = [["r", get(2), 0, 0], ["t", get(3), get(4)], ["s", get(0), get(1), 0, 0]];
                                    now = [["m", get(0), get(1), get(2), get(3), get(4), get(5)]];
                                }
                                break;
                            case "csv":
                                if (attr == "clip-rect") {
                                    now = [];
                                    i = 4;
                                    while (i--) {
                                        now[i] = +from[attr][i] + pos * ms * diff[attr][i];
                                    }
                                }
                                break;
                            default:
                                var from2 = [][concat](from[attr]);
                                now = [];
                                i = that.paper.customAttributes[attr].length;
                                while (i--) {
                                    now[i] = +from2[i] + pos * ms * diff[attr][i];
                                }
                                break;
                        }
                        set[attr] = now;
                    }
                    that.attr(set);
                    (function (id, that, anim) {
                        setTimeout(function () {
                            eve("raphael.anim.frame." + id, that, anim);
                        });
                    })(that.id, that, e.anim);
                } else {
                    (function(f, el, a) {
                        setTimeout(function() {
                            eve("raphael.anim.frame." + el.id, el, a);
                            eve("raphael.anim.finish." + el.id, el, a);
                            R.is(f, "function") && f.call(el);
                        });
                    })(e.callback, that, e.anim);
                    that.attr(to);
                    animationElements.splice(l--, 1);
                    if (e.repeat > 1 && !e.next) {
                        for (key in to) if (to[has](key)) {
                            init[key] = e.totalOrigin[key];
                        }
                        e.el.attr(init);
                        runAnimation(e.anim, e.el, e.anim.percents[0], null, e.totalOrigin, e.repeat - 1);
                    }
                    if (e.next && !e.stop) {
                        runAnimation(e.anim, e.el, e.next, null, e.totalOrigin, e.repeat);
                    }
                }
            }
            animationElements.length && requestAnimFrame(animation);
        },
        upto255 = function (color) {
            return color > 255 ? 255 : color < 0 ? 0 : color;
        };
    /*\
     * Element.animateWith
     [ method ]
     **
     * Acts similar to @Element.animate, but ensure that given animation runs in sync with another given element.
     **
     > Parameters
     **
     - el (object) element to sync with
     - anim (object) animation to sync with
     - params (object) #optional final attributes for the element, see also @Element.attr
     - ms (number) #optional number of milliseconds for animation to run
     - easing (string) #optional easing type. Accept on of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
     - callback (function) #optional callback function. Will be called at the end of animation.
     * or
     - element (object) element to sync with
     - anim (object) animation to sync with
     - animation (object) #optional animation object, see @Raphael.animation
     **
     = (object) original element
    \*/
    elproto.animateWith = function (el, anim, params, ms, easing, callback) {
        var element = this;
        if (element.removed) {
            callback && callback.call(element);
            return element;
        }
        var a = params instanceof Animation ? params : R.animation(params, ms, easing, callback),
            x, y;
        runAnimation(a, element, a.percents[0], null, element.attr());
        for (var i = 0, ii = animationElements.length; i < ii; i++) {
            if (animationElements[i].anim == anim && animationElements[i].el == el) {
                animationElements[ii - 1].start = animationElements[i].start;
                break;
            }
        }
        return element;
        //
        //
        // var a = params ? R.animation(params, ms, easing, callback) : anim,
        //     status = element.status(anim);
        // return this.animate(a).status(a, status * anim.ms / a.ms);
    };
    function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration) {
        var cx = 3 * p1x,
            bx = 3 * (p2x - p1x) - cx,
            ax = 1 - cx - bx,
            cy = 3 * p1y,
            by = 3 * (p2y - p1y) - cy,
            ay = 1 - cy - by;
        function sampleCurveX(t) {
            return ((ax * t + bx) * t + cx) * t;
        }
        function solve(x, epsilon) {
            var t = solveCurveX(x, epsilon);
            return ((ay * t + by) * t + cy) * t;
        }
        function solveCurveX(x, epsilon) {
            var t0, t1, t2, x2, d2, i;
            for(t2 = x, i = 0; i < 8; i++) {
                x2 = sampleCurveX(t2) - x;
                if (abs(x2) < epsilon) {
                    return t2;
                }
                d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
                if (abs(d2) < 1e-6) {
                    break;
                }
                t2 = t2 - x2 / d2;
            }
            t0 = 0;
            t1 = 1;
            t2 = x;
            if (t2 < t0) {
                return t0;
            }
            if (t2 > t1) {
                return t1;
            }
            while (t0 < t1) {
                x2 = sampleCurveX(t2);
                if (abs(x2 - x) < epsilon) {
                    return t2;
                }
                if (x > x2) {
                    t0 = t2;
                } else {
                    t1 = t2;
                }
                t2 = (t1 - t0) / 2 + t0;
            }
            return t2;
        }
        return solve(t, 1 / (200 * duration));
    }
    elproto.onAnimation = function (f) {
        f ? eve.on("raphael.anim.frame." + this.id, f) : eve.unbind("raphael.anim.frame." + this.id);
        return this;
    };
    function Animation(anim, ms) {
        var percents = [],
            newAnim = {};
        this.ms = ms;
        this.times = 1;
        if (anim) {
            for (var attr in anim) if (anim[has](attr)) {
                newAnim[toFloat(attr)] = anim[attr];
                percents.push(toFloat(attr));
            }
            percents.sort(sortByNumber);
        }
        this.anim = newAnim;
        this.top = percents[percents.length - 1];
        this.percents = percents;
    }
    /*\
     * Animation.delay
     [ method ]
     **
     * Creates a copy of existing animation object with given delay.
     **
     > Parameters
     **
     - delay (number) number of ms to pass between animation start and actual animation
     **
     = (object) new altered Animation object
     | var anim = Raphael.animation({cx: 10, cy: 20}, 2e3);
     | circle1.animate(anim); // run the given animation immediately
     | circle2.animate(anim.delay(500)); // run the given animation after 500 ms
    \*/
    Animation.prototype.delay = function (delay) {
        var a = new Animation(this.anim, this.ms);
        a.times = this.times;
        a.del = +delay || 0;
        return a;
    };
    /*\
     * Animation.repeat
     [ method ]
     **
     * Creates a copy of existing animation object with given repetition.
     **
     > Parameters
     **
     - repeat (number) number iterations of animation. For infinite animation pass `Infinity`
     **
     = (object) new altered Animation object
    \*/
    Animation.prototype.repeat = function (times) {
        var a = new Animation(this.anim, this.ms);
        a.del = this.del;
        a.times = math.floor(mmax(times, 0)) || 1;
        return a;
    };
    function runAnimation(anim, element, percent, status, totalOrigin, times) {
        percent = toFloat(percent);
        var params,
            isInAnim,
            isInAnimSet,
            percents = [],
            next,
            prev,
            timestamp,
            ms = anim.ms,
            from = {},
            to = {},
            diff = {};
        if (status) {
            for (i = 0, ii = animationElements.length; i < ii; i++) {
                var e = animationElements[i];
                if (e.el.id == element.id && e.anim == anim) {
                    if (e.percent != percent) {
                        animationElements.splice(i, 1);
                        isInAnimSet = 1;
                    } else {
                        isInAnim = e;
                    }
                    element.attr(e.totalOrigin);
                    break;
                }
            }
        } else {
            status = +to; // NaN
        }
        for (var i = 0, ii = anim.percents.length; i < ii; i++) {
            if (anim.percents[i] == percent || anim.percents[i] > status * anim.top) {
                percent = anim.percents[i];
                prev = anim.percents[i - 1] || 0;
                ms = ms / anim.top * (percent - prev);
                next = anim.percents[i + 1];
                params = anim.anim[percent];
                break;
            } else if (status) {
                element.attr(anim.anim[anim.percents[i]]);
            }
        }
        if (!params) {
            return;
        }
        if (!isInAnim) {
            for (var attr in params) if (params[has](attr)) {
                if (availableAnimAttrs[has](attr) || element.paper.customAttributes[has](attr)) {
                    from[attr] = element.attr(attr);
                    (from[attr] == null) && (from[attr] = availableAttrs[attr]);
                    to[attr] = params[attr];
                    switch (availableAnimAttrs[attr]) {
                        case nu:
                            diff[attr] = (to[attr] - from[attr]) / ms;
                            break;
                        case "colour":
                            from[attr] = R.getRGB(from[attr]);
                            var toColour = R.getRGB(to[attr]);
                            diff[attr] = {
                                r: (toColour.r - from[attr].r) / ms,
                                g: (toColour.g - from[attr].g) / ms,
                                b: (toColour.b - from[attr].b) / ms
                            };
                            break;
                        case "path":
                            var pathes = path2curve(from[attr], to[attr]),
                                toPath = pathes[1];
                            from[attr] = pathes[0];
                            diff[attr] = [];
                            for (i = 0, ii = from[attr].length; i < ii; i++) {
                                diff[attr][i] = [0];
                                for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
                                    diff[attr][i][j] = (toPath[i][j] - from[attr][i][j]) / ms;
                                }
                            }
                            break;
                        case "transform":
                            var _ = element._,
                                eq = equaliseTransform(_[attr], to[attr]);
                            if (eq) {
                                from[attr] = eq.from;
                                to[attr] = eq.to;
                                diff[attr] = [];
                                diff[attr].real = true;
                                for (i = 0, ii = from[attr].length; i < ii; i++) {
                                    diff[attr][i] = [from[attr][i][0]];
                                    for (j = 1, jj = from[attr][i].length; j < jj; j++) {
                                        diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
                                    }
                                }
                            } else {
                                var m = (element.matrix || new Matrix),
                                    to2 = {
                                        _: {transform: _.transform},
                                        getBBox: function () {
                                            return element.getBBox(1);
                                        }
                                    };
                                from[attr] = [
                                    m.a,
                                    m.b,
                                    m.c,
                                    m.d,
                                    m.e,
                                    m.f
                                ];
                                extractTransform(to2, to[attr]);
                                to[attr] = to2._.transform;
                                diff[attr] = [
                                    (to2.matrix.a - m.a) / ms,
                                    (to2.matrix.b - m.b) / ms,
                                    (to2.matrix.c - m.c) / ms,
                                    (to2.matrix.d - m.d) / ms,
                                    (to2.matrix.e - m.e) / ms,
                                    (to2.matrix.f - m.f) / ms
                                ];
                                // from[attr] = [_.sx, _.sy, _.deg, _.dx, _.dy];
                                // var to2 = {_:{}, getBBox: function () { return element.getBBox(); }};
                                // extractTransform(to2, to[attr]);
                                // diff[attr] = [
                                //     (to2._.sx - _.sx) / ms,
                                //     (to2._.sy - _.sy) / ms,
                                //     (to2._.deg - _.deg) / ms,
                                //     (to2._.dx - _.dx) / ms,
                                //     (to2._.dy - _.dy) / ms
                                // ];
                            }
                            break;
                        case "csv":
                            var values = Str(params[attr])[split](separator),
                                from2 = Str(from[attr])[split](separator);
                            if (attr == "clip-rect") {
                                from[attr] = from2;
                                diff[attr] = [];
                                i = from2.length;
                                while (i--) {
                                    diff[attr][i] = (values[i] - from[attr][i]) / ms;
                                }
                            }
                            to[attr] = values;
                            break;
                        default:
                            values = [][concat](params[attr]);
                            from2 = [][concat](from[attr]);
                            diff[attr] = [];
                            i = element.paper.customAttributes[attr].length;
                            while (i--) {
                                diff[attr][i] = ((values[i] || 0) - (from2[i] || 0)) / ms;
                            }
                            break;
                    }
                }
            }
            var easing = params.easing,
                easyeasy = R.easing_formulas[easing];
            if (!easyeasy) {
                easyeasy = Str(easing).match(bezierrg);
                if (easyeasy && easyeasy.length == 5) {
                    var curve = easyeasy;
                    easyeasy = function (t) {
                        return CubicBezierAtTime(t, +curve[1], +curve[2], +curve[3], +curve[4], ms);
                    };
                } else {
                    easyeasy = pipe;
                }
            }
            timestamp = params.start || anim.start || +new Date;
            e = {
                anim: anim,
                percent: percent,
                timestamp: timestamp,
                start: timestamp + (anim.del || 0),
                status: 0,
                initstatus: status || 0,
                stop: false,
                ms: ms,
                easing: easyeasy,
                from: from,
                diff: diff,
                to: to,
                el: element,
                callback: params.callback,
                prev: prev,
                next: next,
                repeat: times || anim.times,
                origin: element.attr(),
                totalOrigin: totalOrigin
            };
            animationElements.push(e);
            if (status && !isInAnim && !isInAnimSet) {
                e.stop = true;
                e.start = new Date - ms * status;
                if (animationElements.length == 1) {
                    return animation();
                }
            }
            if (isInAnimSet) {
                e.start = new Date - e.ms * status;
            }
            animationElements.length == 1 && requestAnimFrame(animation);
        } else {
            isInAnim.initstatus = status;
            isInAnim.start = new Date - isInAnim.ms * status;
        }
        eve("raphael.anim.start." + element.id, element, anim);
    }
    /*\
     * Raphael.animation
     [ method ]
     **
     * Creates an animation object that can be passed to the @Element.animate or @Element.animateWith methods.
     * See also @Animation.delay and @Animation.repeat methods.
     **
     > Parameters
     **
     - params (object) final attributes for the element, see also @Element.attr
     - ms (number) number of milliseconds for animation to run
     - easing (string) #optional easing type. Accept one of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
     - callback (function) #optional callback function. Will be called at the end of animation.
     **
     = (object) @Animation
    \*/
    R.animation = function (params, ms, easing, callback) {
        if (params instanceof Animation) {
            return params;
        }
        if (R.is(easing, "function") || !easing) {
            callback = callback || easing || null;
            easing = null;
        }
        params = Object(params);
        ms = +ms || 0;
        var p = {},
            json,
            attr;
        for (attr in params) if (params[has](attr) && toFloat(attr) != attr && toFloat(attr) + "%" != attr) {
            json = true;
            p[attr] = params[attr];
        }
        if (!json) {
            // if percent-like syntax is used and end-of-all animation callback used
            if(callback){
                // find the last one
                var lastKey = 0;
                for(var i in params){
                    var percent = toInt(i);
                    if(params[has](i) && percent > lastKey){
                        lastKey = percent;
                    }
                }
                lastKey += '%';
                // if already defined callback in the last keyframe, skip
                !params[lastKey].callback && (params[lastKey].callback = callback);
            }
          return new Animation(params, ms);
        } else {
            easing && (p.easing = easing);
            callback && (p.callback = callback);
            return new Animation({100: p}, ms);
        }
    };
    /*\
     * Element.animate
     [ method ]
     **
     * Creates and starts animation for given element.
     **
     > Parameters
     **
     - params (object) final attributes for the element, see also @Element.attr
     - ms (number) number of milliseconds for animation to run
     - easing (string) #optional easing type. Accept one of @Raphael.easing_formulas or CSS format: `cubic&#x2010;bezier(XX,&#160;XX,&#160;XX,&#160;XX)`
     - callback (function) #optional callback function. Will be called at the end of animation.
     * or
     - animation (object) animation object, see @Raphael.animation
     **
     = (object) original element
    \*/
    elproto.animate = function (params, ms, easing, callback) {
        var element = this;
        if (element.removed) {
            callback && callback.call(element);
            return element;
        }
        var anim = params instanceof Animation ? params : R.animation(params, ms, easing, callback);
        runAnimation(anim, element, anim.percents[0], null, element.attr());
        return element;
    };
    /*\
     * Element.setTime
     [ method ]
     **
     * Sets the status of animation of the element in milliseconds. Similar to @Element.status method.
     **
     > Parameters
     **
     - anim (object) animation object
     - value (number) number of milliseconds from the beginning of the animation
     **
     = (object) original element if `value` is specified
     * Note, that during animation following events are triggered:
     *
     * On each animation frame event `anim.frame.<id>`, on start `anim.start.<id>` and on end `anim.finish.<id>`.
    \*/
    elproto.setTime = function (anim, value) {
        if (anim && value != null) {
            this.status(anim, mmin(value, anim.ms) / anim.ms);
        }
        return this;
    };
    /*\
     * Element.status
     [ method ]
     **
     * Gets or sets the status of animation of the element.
     **
     > Parameters
     **
     - anim (object) #optional animation object
     - value (number) #optional 0 – 1. If specified, method works like a setter and sets the status of a given animation to the value. This will cause animation to jump to the given position.
     **
     = (number) status
     * or
     = (array) status if `anim` is not specified. Array of objects in format:
     o {
     o     anim: (object) animation object
     o     status: (number) status
     o }
     * or
     = (object) original element if `value` is specified
    \*/
    elproto.status = function (anim, value) {
        var out = [],
            i = 0,
            len,
            e;
        if (value != null) {
            runAnimation(anim, this, -1, mmin(value, 1));
            return this;
        } else {
            len = animationElements.length;
            for (; i < len; i++) {
                e = animationElements[i];
                if (e.el.id == this.id && (!anim || e.anim == anim)) {
                    if (anim) {
                        return e.status;
                    }
                    out.push({
                        anim: e.anim,
                        status: e.status
                    });
                }
            }
            if (anim) {
                return 0;
            }
            return out;
        }
    };
    /*\
     * Element.pause
     [ method ]
     **
     * Stops animation of the element with ability to resume it later on.
     **
     > Parameters
     **
     - anim (object) #optional animation object
     **
     = (object) original element
    \*/
    elproto.pause = function (anim) {
        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
            if (eve("raphael.anim.pause." + this.id, this, animationElements[i].anim) !== false) {
                animationElements[i].paused = true;
            }
        }
        return this;
    };
    /*\
     * Element.resume
     [ method ]
     **
     * Resumes animation if it was paused with @Element.pause method.
     **
     > Parameters
     **
     - anim (object) #optional animation object
     **
     = (object) original element
    \*/
    elproto.resume = function (anim) {
        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
            var e = animationElements[i];
            if (eve("raphael.anim.resume." + this.id, this, e.anim) !== false) {
                delete e.paused;
                this.status(e.anim, e.status);
            }
        }
        return this;
    };
    /*\
     * Element.stop
     [ method ]
     **
     * Stops animation of the element.
     **
     > Parameters
     **
     - anim (object) #optional animation object
     **
     = (object) original element
    \*/
    elproto.stop = function (anim) {
        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
            if (eve("raphael.anim.stop." + this.id, this, animationElements[i].anim) !== false) {
                animationElements.splice(i--, 1);
            }
        }
        return this;
    };
    function stopAnimation(paper) {
        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.paper == paper) {
            animationElements.splice(i--, 1);
        }
    }
    eve.on("raphael.remove", stopAnimation);
    eve.on("raphael.clear", stopAnimation);
    elproto.toString = function () {
        return "Rapha\xebl\u2019s object";
    };

    // Set
    var Set = function (items) {
        this.items = [];
        this.length = 0;
        this.type = "set";
        if (items) {
            for (var i = 0, ii = items.length; i < ii; i++) {
                if (items[i] && (items[i].constructor == elproto.constructor || items[i].constructor == Set)) {
                    this[this.items.length] = this.items[this.items.length] = items[i];
                    this.length++;
                }
            }
        }
    },
    setproto = Set.prototype;
    /*\
     * Set.push
     [ method ]
     **
     * Adds each argument to the current set.
     = (object) original element
    \*/
    setproto.push = function () {
        var item,
            len;
        for (var i = 0, ii = arguments.length; i < ii; i++) {
            item = arguments[i];
            if (item && (item.constructor == elproto.constructor || item.constructor == Set)) {
                len = this.items.length;
                this[len] = this.items[len] = item;
                this.length++;
            }
        }
        return this;
    };
    /*\
     * Set.pop
     [ method ]
     **
     * Removes last element and returns it.
     = (object) element
    \*/
    setproto.pop = function () {
        this.length && delete this[this.length--];
        return this.items.pop();
    };
    /*\
     * Set.forEach
     [ method ]
     **
     * Executes given function for each element in the set.
     *
     * If function returns `false` it will stop loop running.
     **
     > Parameters
     **
     - callback (function) function to run
     - thisArg (object) context object for the callback
     = (object) Set object
    \*/
    setproto.forEach = function (callback, thisArg) {
        for (var i = 0, ii = this.items.length; i < ii; i++) {
            if (callback.call(thisArg, this.items[i], i) === false) {
                return this;
            }
        }
        return this;
    };
    for (var method in elproto) if (elproto[has](method)) {
        setproto[method] = (function (methodname) {
            return function () {
                var arg = arguments;
                return this.forEach(function (el) {
                    el[methodname][apply](el, arg);
                });
            };
        })(method);
    }
    setproto.attr = function (name, value) {
        if (name && R.is(name, array) && R.is(name[0], "object")) {
            for (var j = 0, jj = name.length; j < jj; j++) {
                this.items[j].attr(name[j]);
            }
        } else {
            for (var i = 0, ii = this.items.length; i < ii; i++) {
                this.items[i].attr(name, value);
            }
        }
        return this;
    };
    /*\
     * Set.clear
     [ method ]
     **
     * Removes all elements from the set
    \*/
    setproto.clear = function () {
        while (this.length) {
            this.pop();
        }
    };
    /*\
     * Set.splice
     [ method ]
     **
     * Removes given element from the set
     **
     > Parameters
     **
     - index (number) position of the deletion
     - count (number) number of element to remove
     - insertion… (object) #optional elements to insert
     = (object) set elements that were deleted
    \*/
    setproto.splice = function (index, count, insertion) {
        index = index < 0 ? mmax(this.length + index, 0) : index;
        count = mmax(0, mmin(this.length - index, count));
        var tail = [],
            todel = [],
            args = [],
            i;
        for (i = 2; i < arguments.length; i++) {
            args.push(arguments[i]);
        }
        for (i = 0; i < count; i++) {
            todel.push(this[index + i]);
        }
        for (; i < this.length - index; i++) {
            tail.push(this[index + i]);
        }
        var arglen = args.length;
        for (i = 0; i < arglen + tail.length; i++) {
            this.items[index + i] = this[index + i] = i < arglen ? args[i] : tail[i - arglen];
        }
        i = this.items.length = this.length -= count - arglen;
        while (this[i]) {
            delete this[i++];
        }
        return new Set(todel);
    };
    /*\
     * Set.exclude
     [ method ]
     **
     * Removes given element from the set
     **
     > Parameters
     **
     - element (object) element to remove
     = (boolean) `true` if object was found & removed from the set
    \*/
    setproto.exclude = function (el) {
        for (var i = 0, ii = this.length; i < ii; i++) if (this[i] == el) {
            this.splice(i, 1);
            return true;
        }
    };
    setproto.animate = function (params, ms, easing, callback) {
        (R.is(easing, "function") || !easing) && (callback = easing || null);
        var len = this.items.length,
            i = len,
            item,
            set = this,
            collector;
        if (!len) {
            return this;
        }
        callback && (collector = function () {
            !--len && callback.call(set);
        });
        easing = R.is(easing, string) ? easing : collector;
        var anim = R.animation(params, ms, easing, collector);
        item = this.items[--i].animate(anim);
        while (i--) {
            this.items[i] && !this.items[i].removed && this.items[i].animateWith(item, anim, anim);
            (this.items[i] && !this.items[i].removed) || len--;
        }
        return this;
    };
    setproto.insertAfter = function (el) {
        var i = this.items.length;
        while (i--) {
            this.items[i].insertAfter(el);
        }
        return this;
    };
    setproto.getBBox = function () {
        var x = [],
            y = [],
            x2 = [],
            y2 = [];
        for (var i = this.items.length; i--;) if (!this.items[i].removed) {
            var box = this.items[i].getBBox();
            x.push(box.x);
            y.push(box.y);
            x2.push(box.x + box.width);
            y2.push(box.y + box.height);
        }
        x = mmin[apply](0, x);
        y = mmin[apply](0, y);
        x2 = mmax[apply](0, x2);
        y2 = mmax[apply](0, y2);
        return {
            x: x,
            y: y,
            x2: x2,
            y2: y2,
            width: x2 - x,
            height: y2 - y
        };
    };
    setproto.clone = function (s) {
        s = this.paper.set();
        for (var i = 0, ii = this.items.length; i < ii; i++) {
            s.push(this.items[i].clone());
        }
        return s;
    };
    setproto.toString = function () {
        return "Rapha\xebl\u2018s set";
    };

    setproto.glow = function(glowConfig) {
        var ret = this.paper.set();
        this.forEach(function(shape, index){
            var g = shape.glow(glowConfig);
            if(g != null){
                g.forEach(function(shape2, index2){
                    ret.push(shape2);
                });
            }
        });
        return ret;
    };


    /*\
     * Set.isPointInside
     [ method ]
     **
     * Determine if given point is inside this set’s elements
     **
     > Parameters
     **
     - x (number) x coordinate of the point
     - y (number) y coordinate of the point
     = (boolean) `true` if point is inside any of the set's elements
     \*/
    setproto.isPointInside = function (x, y) {
        var isPointInside = false;
        this.forEach(function (el) {
            if (el.isPointInside(x, y)) {
                isPointInside = true;
                return false; // stop loop
            }
        });
        return isPointInside;
    };

    /*\
     * Raphael.registerFont
     [ method ]
     **
     * Adds given font to the registered set of fonts for Raphaël. Should be used as an internal call from within Cufón’s font file.
     * Returns original parameter, so it could be used with chaining.
     # <a href="http://wiki.github.com/sorccu/cufon/about">More about Cufón and how to convert your font form TTF, OTF, etc to JavaScript file.</a>
     **
     > Parameters
     **
     - font (object) the font to register
     = (object) the font you passed in
     > Usage
     | Cufon.registerFont(Raphael.registerFont({…}));
    \*/
    R.registerFont = function (font) {
        if (!font.face) {
            return font;
        }
        this.fonts = this.fonts || {};
        var fontcopy = {
                w: font.w,
                face: {},
                glyphs: {}
            },
            family = font.face["font-family"];
        for (var prop in font.face) if (font.face[has](prop)) {
            fontcopy.face[prop] = font.face[prop];
        }
        if (this.fonts[family]) {
            this.fonts[family].push(fontcopy);
        } else {
            this.fonts[family] = [fontcopy];
        }
        if (!font.svg) {
            fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10);
            for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) {
                var path = font.glyphs[glyph];
                fontcopy.glyphs[glyph] = {
                    w: path.w,
                    k: {},
                    d: path.d && "M" + path.d.replace(/[mlcxtrv]/g, function (command) {
                            return {l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M";
                        }) + "z"
                };
                if (path.k) {
                    for (var k in path.k) if (path[has](k)) {
                        fontcopy.glyphs[glyph].k[k] = path.k[k];
                    }
                }
            }
        }
        return font;
    };
    /*\
     * Paper.getFont
     [ method ]
     **
     * Finds font object in the registered fonts by given parameters. You could specify only one word from the font name, like “Myriad” for “Myriad Pro”.
     **
     > Parameters
     **
     - family (string) font family name or any word from it
     - weight (string) #optional font weight
     - style (string) #optional font style
     - stretch (string) #optional font stretch
     = (object) the font object
     > Usage
     | paper.print(100, 100, "Test string", paper.getFont("Times", 800), 30);
    \*/
    paperproto.getFont = function (family, weight, style, stretch) {
        stretch = stretch || "normal";
        style = style || "normal";
        weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400;
        if (!R.fonts) {
            return;
        }
        var font = R.fonts[family];
        if (!font) {
            var name = new RegExp("(^|\\s)" + family.replace(/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i");
            for (var fontName in R.fonts) if (R.fonts[has](fontName)) {
                if (name.test(fontName)) {
                    font = R.fonts[fontName];
                    break;
                }
            }
        }
        var thefont;
        if (font) {
            for (var i = 0, ii = font.length; i < ii; i++) {
                thefont = font[i];
                if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) {
                    break;
                }
            }
        }
        return thefont;
    };
    /*\
     * Paper.print
     [ method ]
     **
     * Creates path that represent given text written using given font at given position with given size.
     * Result of the method is path element that contains whole text as a separate path.
     **
     > Parameters
     **
     - x (number) x position of the text
     - y (number) y position of the text
     - string (string) text to print
     - font (object) font object, see @Paper.getFont
     - size (number) #optional size of the font, default is `16`
     - origin (string) #optional could be `"baseline"` or `"middle"`, default is `"middle"`
     - letter_spacing (number) #optional number in range `-1..1`, default is `0`
     - line_spacing (number) #optional number in range `1..3`, default is `1`
     = (object) resulting path element, which consist of all letters
     > Usage
     | var txt = r.print(10, 50, "print", r.getFont("Museo"), 30).attr({fill: "#fff"});
    \*/
    paperproto.print = function (x, y, string, font, size, origin, letter_spacing, line_spacing) {
        origin = origin || "middle"; // baseline|middle
        letter_spacing = mmax(mmin(letter_spacing || 0, 1), -1);
        line_spacing = mmax(mmin(line_spacing || 1, 3), 1);
        var letters = Str(string)[split](E),
            shift = 0,
            notfirst = 0,
            path = E,
            scale;
        R.is(font, "string") && (font = this.getFont(font));
        if (font) {
            scale = (size || 16) / font.face["units-per-em"];
            var bb = font.face.bbox[split](separator),
                top = +bb[0],
                lineHeight = bb[3] - bb[1],
                shifty = 0,
                height = +bb[1] + (origin == "baseline" ? lineHeight + (+font.face.descent) : lineHeight / 2);
            for (var i = 0, ii = letters.length; i < ii; i++) {
                if (letters[i] == "\n") {
                    shift = 0;
                    curr = 0;
                    notfirst = 0;
                    shifty += lineHeight * line_spacing;
                } else {
                    var prev = notfirst && font.glyphs[letters[i - 1]] || {},
                        curr = font.glyphs[letters[i]];
                    shift += notfirst ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) + (font.w * letter_spacing) : 0;
                    notfirst = 1;
                }
                if (curr && curr.d) {
                    path += R.transformPath(curr.d, ["t", shift * scale, shifty * scale, "s", scale, scale, top, height, "t", (x - top) / scale, (y - height) / scale]);
                }
            }
        }
        return this.path(path).attr({
            fill: "#000",
            stroke: "none"
        });
    };

    /*\
     * Paper.add
     [ method ]
     **
     * Imports elements in JSON array in format `{type: type, <attributes>}`
     **
     > Parameters
     **
     - json (array)
     = (object) resulting set of imported elements
     > Usage
     | paper.add([
     |     {
     |         type: "circle",
     |         cx: 10,
     |         cy: 10,
     |         r: 5
     |     },
     |     {
     |         type: "rect",
     |         x: 10,
     |         y: 10,
     |         width: 10,
     |         height: 10,
     |         fill: "#fc0"
     |     }
     | ]);
    \*/
    paperproto.add = function (json) {
        if (R.is(json, "array")) {
            var res = this.set(),
                i = 0,
                ii = json.length,
                j;
            for (; i < ii; i++) {
                j = json[i] || {};
                elements[has](j.type) && res.push(this[j.type]().attr(j));
            }
        }
        return res;
    };

    /*\
     * Raphael.format
     [ method ]
     **
     * Simple format function. Replaces construction of type “`{<number>}`” to the corresponding argument.
     **
     > Parameters
     **
     - token (string) string to format
     - … (string) rest of arguments will be treated as parameters for replacement
     = (string) formated string
     > Usage
     | var x = 10,
     |     y = 20,
     |     width = 40,
     |     height = 50;
     | // this will draw a rectangular shape equivalent to "M10,20h40v50h-40z"
     | paper.path(Raphael.format("M{0},{1}h{2}v{3}h{4}z", x, y, width, height, -width));
    \*/
    R.format = function (token, params) {
        var args = R.is(params, array) ? [0][concat](params) : arguments;
        token && R.is(token, string) && args.length - 1 && (token = token.replace(formatrg, function (str, i) {
            return args[++i] == null ? E : args[i];
        }));
        return token || E;
    };
    /*\
     * Raphael.fullfill
     [ method ]
     **
     * A little bit more advanced format function than @Raphael.format. Replaces construction of type “`{<name>}`” to the corresponding argument.
     **
     > Parameters
     **
     - token (string) string to format
     - json (object) object which properties will be used as a replacement
     = (string) formated string
     > Usage
     | // this will draw a rectangular shape equivalent to "M10,20h40v50h-40z"
     | paper.path(Raphael.fullfill("M{x},{y}h{dim.width}v{dim.height}h{dim['negative width']}z", {
     |     x: 10,
     |     y: 20,
     |     dim: {
     |         width: 40,
     |         height: 50,
     |         "negative width": -40
     |     }
     | }));
    \*/
    R.fullfill = (function () {
        var tokenRegex = /\{([^\}]+)\}/g,
            objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties
            replacer = function (all, key, obj) {
                var res = obj;
                key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) {
                    name = name || quotedName;
                    if (res) {
                        if (name in res) {
                            res = res[name];
                        }
                        typeof res == "function" && isFunc && (res = res());
                    }
                });
                res = (res == null || res == obj ? all : res) + "";
                return res;
            };
        return function (str, obj) {
            return String(str).replace(tokenRegex, function (all, key) {
                return replacer(all, key, obj);
            });
        };
    })();
    /*\
     * Raphael.ninja
     [ method ]
     **
     * If you want to leave no trace of Raphaël (Well, Raphaël creates only one global variable `Raphael`, but anyway.) You can use `ninja` method.
     * Beware, that in this case plugins could stop working, because they are depending on global variable existence.
     **
     = (object) Raphael object
     > Usage
     | (function (local_raphael) {
     |     var paper = local_raphael(10, 10, 320, 200);
     |     …
     | })(Raphael.ninja());
    \*/
    R.ninja = function () {
        oldRaphael.was ? (g.win.Raphael = oldRaphael.is) : delete Raphael;
        return R;
    };
    /*\
     * Raphael.st
     [ property (object) ]
     **
     * You can add your own method to elements and sets. It is wise to add a set method for each element method
     * you added, so you will be able to call the same method on sets too.
     **
     * See also @Raphael.el.
     > Usage
     | Raphael.el.red = function () {
     |     this.attr({fill: "#f00"});
     | };
     | Raphael.st.red = function () {
     |     this.forEach(function (el) {
     |         el.red();
     |     });
     | };
     | // then use it
     | paper.set(paper.circle(100, 100, 20), paper.circle(110, 100, 20)).red();
    \*/
    R.st = setproto;

    eve.on("raphael.DOMload", function () {
        loaded = true;
    });

    // Firefox <3.6 fix: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
    (function (doc, loaded, f) {
        if (doc.readyState == null && doc.addEventListener){
            doc.addEventListener(loaded, f = function () {
                doc.removeEventListener(loaded, f, false);
                doc.readyState = "complete";
            }, false);
            doc.readyState = "loading";
        }
        function isLoaded() {
            (/in/).test(doc.readyState) ? setTimeout(isLoaded, 9) : R.eve("raphael.DOMload");
        }
        isLoaded();
    })(document, "DOMContentLoaded");

    return R;
}));

// ┌─────────────────────────────────────────────────────────────────────┐ \\
// │ Raphaël 2.1.4 - JavaScript Vector Library                       │ \\
// ├─────────────────────────────────────────────────────────────────────┤ \\
// │ SVG Module                                                          │ \\
// ├─────────────────────────────────────────────────────────────────────┤ \\
// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
// └─────────────────────────────────────────────────────────────────────┘ \\

(function (glob, factory) {
    if (typeof define === "function" && define.amd) {
        define("raphael.svg", ["raphael.core"], function(raphael) {
            return factory(raphael);
        });
    } else if (typeof exports === "object") {
        factory(require("./raphael.core"));
    } else {
        factory(glob.Raphael);
    }
}(this, function(R) {
    if (R && !R.svg) {
        return;
    }

    var has = "hasOwnProperty",
        Str = String,
        toFloat = parseFloat,
        toInt = parseInt,
        math = Math,
        mmax = math.max,
        abs = math.abs,
        pow = math.pow,
        separator = /[, ]+/,
        eve = R.eve,
        E = "",
        S = " ";
    var xlink = "http://www.w3.org/1999/xlink",
        markers = {
            block: "M5,0 0,2.5 5,5z",
            classic: "M5,0 0,2.5 5,5 3.5,3 3.5,2z",
            diamond: "M2.5,0 5,2.5 2.5,5 0,2.5z",
            open: "M6,1 1,3.5 6,6",
            oval: "M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"
        },
        markerCounter = {};
    R.toString = function () {
        return  "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version;
    };
    var $ = function (el, attr) {
        if (attr) {
            if (typeof el == "string") {
                el = $(el);
            }
            for (var key in attr) if (attr[has](key)) {
                if (key.substring(0, 6) == "xlink:") {
                    el.setAttributeNS(xlink, key.substring(6), Str(attr[key]));
                } else {
                    el.setAttribute(key, Str(attr[key]));
                }
            }
        } else {
            el = R._g.doc.createElementNS("http://www.w3.org/2000/svg", el);
            el.style && (el.style.webkitTapHighlightColor = "rgba(0,0,0,0)");
        }
        return el;
    },
    addGradientFill = function (element, gradient) {
        var type = "linear",
            id = element.id + gradient,
            fx = .5, fy = .5,
            o = element.node,
            SVG = element.paper,
            s = o.style,
            el = R._g.doc.getElementById(id);
        if (!el) {
            gradient = Str(gradient).replace(R._radial_gradient, function (all, _fx, _fy) {
                type = "radial";
                if (_fx && _fy) {
                    fx = toFloat(_fx);
                    fy = toFloat(_fy);
                    var dir = ((fy > .5) * 2 - 1);
                    pow(fx - .5, 2) + pow(fy - .5, 2) > .25 &&
                        (fy = math.sqrt(.25 - pow(fx - .5, 2)) * dir + .5) &&
                        fy != .5 &&
                        (fy = fy.toFixed(5) - 1e-5 * dir);
                }
                return E;
            });
            gradient = gradient.split(/\s*\-\s*/);
            if (type == "linear") {
                var angle = gradient.shift();
                angle = -toFloat(angle);
                if (isNaN(angle)) {
                    return null;
                }
                var vector = [0, 0, math.cos(R.rad(angle)), math.sin(R.rad(angle))],
                    max = 1 / (mmax(abs(vector[2]), abs(vector[3])) || 1);
                vector[2] *= max;
                vector[3] *= max;
                if (vector[2] < 0) {
                    vector[0] = -vector[2];
                    vector[2] = 0;
                }
                if (vector[3] < 0) {
                    vector[1] = -vector[3];
                    vector[3] = 0;
                }
            }
            var dots = R._parseDots(gradient);
            if (!dots) {
                return null;
            }
            id = id.replace(/[\(\)\s,\xb0#]/g, "_");

            if (element.gradient && id != element.gradient.id) {
                SVG.defs.removeChild(element.gradient);
                delete element.gradient;
            }

            if (!element.gradient) {
                el = $(type + "Gradient", {id: id});
                element.gradient = el;
                $(el, type == "radial" ? {
                    fx: fx,
                    fy: fy
                } : {
                    x1: vector[0],
                    y1: vector[1],
                    x2: vector[2],
                    y2: vector[3],
                    gradientTransform: element.matrix.invert()
                });
                SVG.defs.appendChild(el);
                for (var i = 0, ii = dots.length; i < ii; i++) {
                    el.appendChild($("stop", {
                        offset: dots[i].offset ? dots[i].offset : i ? "100%" : "0%",
                        "stop-color": dots[i].color || "#fff",
                        "stop-opacity": isFinite(dots[i].opacity) ? dots[i].opacity : 1
                    }));
                }
            }
        }
        $(o, {
            fill: "url('" + document.location.origin + document.location.pathname + "#" + id + "')",
            opacity: 1,
            "fill-opacity": 1
        });
        s.fill = E;
        s.opacity = 1;
        s.fillOpacity = 1;
        return 1;
    },
    updatePosition = function (o) {
        var bbox = o.getBBox(1);
        $(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"});
    },
    addArrow = function (o, value, isEnd) {
        if (o.type == "path") {
            var values = Str(value).toLowerCase().split("-"),
                p = o.paper,
                se = isEnd ? "end" : "start",
                node = o.node,
                attrs = o.attrs,
                stroke = attrs["stroke-width"],
                i = values.length,
                type = "classic",
                from,
                to,
                dx,
                refX,
                attr,
                w = 3,
                h = 3,
                t = 5;
            while (i--) {
                switch (values[i]) {
                    case "block":
                    case "classic":
                    case "oval":
                    case "diamond":
                    case "open":
                    case "none":
                        type = values[i];
                        break;
                    case "wide": h = 5; break;
                    case "narrow": h = 2; break;
                    case "long": w = 5; break;
                    case "short": w = 2; break;
                }
            }
            if (type == "open") {
                w += 2;
                h += 2;
                t += 2;
                dx = 1;
                refX = isEnd ? 4 : 1;
                attr = {
                    fill: "none",
                    stroke: attrs.stroke
                };
            } else {
                refX = dx = w / 2;
                attr = {
                    fill: attrs.stroke,
                    stroke: "none"
                };
            }
            if (o._.arrows) {
                if (isEnd) {
                    o._.arrows.endPath && markerCounter[o._.arrows.endPath]--;
                    o._.arrows.endMarker && markerCounter[o._.arrows.endMarker]--;
                } else {
                    o._.arrows.startPath && markerCounter[o._.arrows.startPath]--;
                    o._.arrows.startMarker && markerCounter[o._.arrows.startMarker]--;
                }
            } else {
                o._.arrows = {};
            }
            if (type != "none") {
                var pathId = "raphael-marker-" + type,
                    markerId = "raphael-marker-" + se + type + w + h + "-obj" + o.id;
                if (!R._g.doc.getElementById(pathId)) {
                    p.defs.appendChild($($("path"), {
                        "stroke-linecap": "round",
                        d: markers[type],
                        id: pathId
                    }));
                    markerCounter[pathId] = 1;
                } else {
                    markerCounter[pathId]++;
                }
                var marker = R._g.doc.getElementById(markerId),
                    use;
                if (!marker) {
                    marker = $($("marker"), {
                        id: markerId,
                        markerHeight: h,
                        markerWidth: w,
                        orient: "auto",
                        refX: refX,
                        refY: h / 2
                    });
                    use = $($("use"), {
                        "xlink:href": "#" + pathId,
                        transform: (isEnd ? "rotate(180 " + w / 2 + " " + h / 2 + ") " : E) + "scale(" + w / t + "," + h / t + ")",
                        "stroke-width": (1 / ((w / t + h / t) / 2)).toFixed(4)
                    });
                    marker.appendChild(use);
                    p.defs.appendChild(marker);
                    markerCounter[markerId] = 1;
                } else {
                    markerCounter[markerId]++;
                    use = marker.getElementsByTagName("use")[0];
                }
                $(use, attr);
                var delta = dx * (type != "diamond" && type != "oval");
                if (isEnd) {
                    from = o._.arrows.startdx * stroke || 0;
                    to = R.getTotalLength(attrs.path) - delta * stroke;
                } else {
                    from = delta * stroke;
                    to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
                }
                attr = {};
                attr["marker-" + se] = "url(#" + markerId + ")";
                if (to || from) {
                    attr.d = R.getSubpath(attrs.path, from, to);
                }
                $(node, attr);
                o._.arrows[se + "Path"] = pathId;
                o._.arrows[se + "Marker"] = markerId;
                o._.arrows[se + "dx"] = delta;
                o._.arrows[se + "Type"] = type;
                o._.arrows[se + "String"] = value;
            } else {
                if (isEnd) {
                    from = o._.arrows.startdx * stroke || 0;
                    to = R.getTotalLength(attrs.path) - from;
                } else {
                    from = 0;
                    to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
                }
                o._.arrows[se + "Path"] && $(node, {d: R.getSubpath(attrs.path, from, to)});
                delete o._.arrows[se + "Path"];
                delete o._.arrows[se + "Marker"];
                delete o._.arrows[se + "dx"];
                delete o._.arrows[se + "Type"];
                delete o._.arrows[se + "String"];
            }
            for (attr in markerCounter) if (markerCounter[has](attr) && !markerCounter[attr]) {
                var item = R._g.doc.getElementById(attr);
                item && item.parentNode.removeChild(item);
            }
        }
    },
    dasharray = {
        "-": [3, 1],
        ".": [1, 1],
        "-.": [3, 1, 1, 1],
        "-..": [3, 1, 1, 1, 1, 1],
        ". ": [1, 3],
        "- ": [4, 3],
        "--": [8, 3],
        "- .": [4, 3, 1, 3],
        "--.": [8, 3, 1, 3],
        "--..": [8, 3, 1, 3, 1, 3]
    },
    addDashes = function (o, value, params) {
        value = dasharray[Str(value).toLowerCase()];
        if (value) {
            var width = o.attrs["stroke-width"] || "1",
                butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0,
                dashes = [],
                i = value.length;
            while (i--) {
                dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt;
            }
            $(o.node, {"stroke-dasharray": dashes.join(",")});
        }
        else {
          $(o.node, {"stroke-dasharray": "none"});
        }
    },
    setFillAndStroke = function (o, params) {
        var node = o.node,
            attrs = o.attrs,
            vis = node.style.visibility;
        node.style.visibility = "hidden";
        for (var att in params) {
            if (params[has](att)) {
                if (!R._availableAttrs[has](att)) {
                    continue;
                }
                var value = params[att];
                attrs[att] = value;
                switch (att) {
                    case "blur":
                        o.blur(value);
                        break;
                    case "title":
                        var title = node.getElementsByTagName("title");

                        // Use the existing <title>.
                        if (title.length && (title = title[0])) {
                          title.firstChild.nodeValue = value;
                        } else {
                          title = $("title");
                          var val = R._g.doc.createTextNode(value);
                          title.appendChild(val);
                          node.appendChild(title);
                        }
                        break;
                    case "href":
                    case "target":
                        var pn = node.parentNode;
                        if (pn.tagName.toLowerCase() != "a") {
                            var hl = $("a");
                            pn.insertBefore(hl, node);
                            hl.appendChild(node);
                            pn = hl;
                        }
                        if (att == "target") {
                            pn.setAttributeNS(xlink, "show", value == "blank" ? "new" : value);
                        } else {
                            pn.setAttributeNS(xlink, att, value);
                        }
                        break;
                    case "cursor":
                        node.style.cursor = value;
                        break;
                    case "transform":
                        o.transform(value);
                        break;
                    case "arrow-start":
                        addArrow(o, value);
                        break;
                    case "arrow-end":
                        addArrow(o, value, 1);
                        break;
                    case "clip-rect":
                        var rect = Str(value).split(separator);
                        if (rect.length == 4) {
                            o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode);
                            var el = $("clipPath"),
                                rc = $("rect");
                            el.id = R.createUUID();
                            $(rc, {
                                x: rect[0],
                                y: rect[1],
                                width: rect[2],
                                height: rect[3]
                            });
                            el.appendChild(rc);
                            o.paper.defs.appendChild(el);
                            $(node, {"clip-path": "url(#" + el.id + ")"});
                            o.clip = rc;
                        }
                        if (!value) {
                            var path = node.getAttribute("clip-path");
                            if (path) {
                                var clip = R._g.doc.getElementById(path.replace(/(^url\(#|\)$)/g, E));
                                clip && clip.parentNode.removeChild(clip);
                                $(node, {"clip-path": E});
                                delete o.clip;
                            }
                        }
                    break;
                    case "path":
                        if (o.type == "path") {
                            $(node, {d: value ? attrs.path = R._pathToAbsolute(value) : "M0,0"});
                            o._.dirty = 1;
                            if (o._.arrows) {
                                "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
                                "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
                            }
                        }
                        break;
                    case "width":
                        node.setAttribute(att, value);
                        o._.dirty = 1;
                        if (attrs.fx) {
                            att = "x";
                            value = attrs.x;
                        } else {
                            break;
                        }
                    case "x":
                        if (attrs.fx) {
                            value = -attrs.x - (attrs.width || 0);
                        }
                    case "rx":
                        if (att == "rx" && o.type == "rect") {
                            break;
                        }
                    case "cx":
                        node.setAttribute(att, value);
                        o.pattern && updatePosition(o);
                        o._.dirty = 1;
                        break;
                    case "height":
                        node.setAttribute(att, value);
                        o._.dirty = 1;
                        if (attrs.fy) {
                            att = "y";
                            value = attrs.y;
                        } else {
                            break;
                        }
                    case "y":
                        if (attrs.fy) {
                            value = -attrs.y - (attrs.height || 0);
                        }
                    case "ry":
                        if (att == "ry" && o.type == "rect") {
                            break;
                        }
                    case "cy":
                        node.setAttribute(att, value);
                        o.pattern && updatePosition(o);
                        o._.dirty = 1;
                        break;
                    case "r":
                        if (o.type == "rect") {
                            $(node, {rx: value, ry: value});
                        } else {
                            node.setAttribute(att, value);
                        }
                        o._.dirty = 1;
                        break;
                    case "src":
                        if (o.type == "image") {
                            node.setAttributeNS(xlink, "href", value);
                        }
                        break;
                    case "stroke-width":
                        if (o._.sx != 1 || o._.sy != 1) {
                            value /= mmax(abs(o._.sx), abs(o._.sy)) || 1;
                        }
                        node.setAttribute(att, value);
                        if (attrs["stroke-dasharray"]) {
                            addDashes(o, attrs["stroke-dasharray"], params);
                        }
                        if (o._.arrows) {
                            "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
                            "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
                        }
                        break;
                    case "stroke-dasharray":
                        addDashes(o, value, params);
                        break;
                    case "fill":
                        var isURL = Str(value).match(R._ISURL);
                        if (isURL) {
                            el = $("pattern");
                            var ig = $("image");
                            el.id = R.createUUID();
                            $(el, {x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1});
                            $(ig, {x: 0, y: 0, "xlink:href": isURL[1]});
                            el.appendChild(ig);

                            (function (el) {
                                R._preload(isURL[1], function () {
                                    var w = this.offsetWidth,
                                        h = this.offsetHeight;
                                    $(el, {width: w, height: h});
                                    $(ig, {width: w, height: h});
                                });
                            })(el);
                            o.paper.defs.appendChild(el);
                            $(node, {fill: "url(#" + el.id + ")"});
                            o.pattern = el;
                            o.pattern && updatePosition(o);
                            break;
                        }
                        var clr = R.getRGB(value);
                        if (!clr.error) {
                            delete params.gradient;
                            delete attrs.gradient;
                            !R.is(attrs.opacity, "undefined") &&
                                R.is(params.opacity, "undefined") &&
                                $(node, {opacity: attrs.opacity});
                            !R.is(attrs["fill-opacity"], "undefined") &&
                                R.is(params["fill-opacity"], "undefined") &&
                                $(node, {"fill-opacity": attrs["fill-opacity"]});
                        } else if ((o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value)) {
                            if ("opacity" in attrs || "fill-opacity" in attrs) {
                                var gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
                                if (gradient) {
                                    var stops = gradient.getElementsByTagName("stop");
                                    $(stops[stops.length - 1], {"stop-opacity": ("opacity" in attrs ? attrs.opacity : 1) * ("fill-opacity" in attrs ? attrs["fill-opacity"] : 1)});
                                }
                            }
                            attrs.gradient = value;
                            attrs.fill = "none";
                            break;
                        }
                        clr[has]("opacity") && $(node, {"fill-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
                    case "stroke":
                        clr = R.getRGB(value);
                        node.setAttribute(att, clr.hex);
                        att == "stroke" && clr[has]("opacity") && $(node, {"stroke-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
                        if (att == "stroke" && o._.arrows) {
                            "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
                            "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
                        }
                        break;
                    case "gradient":
                        (o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value);
                        break;
                    case "opacity":
                        if (attrs.gradient && !attrs[has]("stroke-opacity")) {
                            $(node, {"stroke-opacity": value > 1 ? value / 100 : value});
                        }
                        // fall
                    case "fill-opacity":
                        if (attrs.gradient) {
                            gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
                            if (gradient) {
                                stops = gradient.getElementsByTagName("stop");
                                $(stops[stops.length - 1], {"stop-opacity": value});
                            }
                            break;
                        }
                    default:
                        att == "font-size" && (value = toInt(value, 10) + "px");
                        var cssrule = att.replace(/(\-.)/g, function (w) {
                            return w.substring(1).toUpperCase();
                        });
                        node.style[cssrule] = value;
                        o._.dirty = 1;
                        node.setAttribute(att, value);
                        break;
                }
            }
        }

        tuneText(o, params);
        node.style.visibility = vis;
    },
    leading = 1.2,
    tuneText = function (el, params) {
        if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) {
            return;
        }
        var a = el.attrs,
            node = el.node,
            fontSize = node.firstChild ? toInt(R._g.doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10;

        if (params[has]("text")) {
            a.text = params.text;
            while (node.firstChild) {
                node.removeChild(node.firstChild);
            }
            var texts = Str(params.text).split("\n"),
                tspans = [],
                tspan;
            for (var i = 0, ii = texts.length; i < ii; i++) {
                tspan = $("tspan");
                i && $(tspan, {dy: fontSize * leading, x: a.x});
                tspan.appendChild(R._g.doc.createTextNode(texts[i]));
                node.appendChild(tspan);
                tspans[i] = tspan;
            }
        } else {
            tspans = node.getElementsByTagName("tspan");
            for (i = 0, ii = tspans.length; i < ii; i++) if (i) {
                $(tspans[i], {dy: fontSize * leading, x: a.x});
            } else {
                $(tspans[0], {dy: 0});
            }
        }
        $(node, {x: a.x, y: a.y});
        el._.dirty = 1;
        var bb = el._getBBox(),
            dif = a.y - (bb.y + bb.height / 2);
        dif && R.is(dif, "finite") && $(tspans[0], {dy: dif});
    },
    getRealNode = function (node) {
        if (node.parentNode && node.parentNode.tagName.toLowerCase() === "a") {
            return node.parentNode;
        } else {
            return node;
        }
    },
    Element = function (node, svg) {
        var X = 0,
            Y = 0;
        /*\
         * Element.node
         [ property (object) ]
         **
         * Gives you a reference to the DOM object, so you can assign event handlers or just mess around.
         **
         * Note: Don’t mess with it.
         > Usage
         | // draw a circle at coordinate 10,10 with radius of 10
         | var c = paper.circle(10, 10, 10);
         | c.node.onclick = function () {
         |     c.attr("fill", "red");
         | };
        \*/
        this[0] = this.node = node;
        /*\
         * Element.raphael
         [ property (object) ]
         **
         * Internal reference to @Raphael object. In case it is not available.
         > Usage
         | Raphael.el.red = function () {
         |     var hsb = this.paper.raphael.rgb2hsb(this.attr("fill"));
         |     hsb.h = 1;
         |     this.attr({fill: this.paper.raphael.hsb2rgb(hsb).hex});
         | }
        \*/
        node.raphael = true;
        /*\
         * Element.id
         [ property (number) ]
         **
         * Unique id of the element. Especially useful when you want to listen to events of the element,
         * because all events are fired in format `<module>.<action>.<id>`. Also useful for @Paper.getById method.
        \*/
        this.id = R._oid++;
        node.raphaelid = this.id;
        this.matrix = R.matrix();
        this.realPath = null;
        /*\
         * Element.paper
         [ property (object) ]
         **
         * Internal reference to “paper” where object drawn. Mainly for use in plugins and element extensions.
         > Usage
         | Raphael.el.cross = function () {
         |     this.attr({fill: "red"});
         |     this.paper.path("M10,10L50,50M50,10L10,50")
         |         .attr({stroke: "red"});
         | }
        \*/
        this.paper = svg;
        this.attrs = this.attrs || {};
        this._ = {
            transform: [],
            sx: 1,
            sy: 1,
            deg: 0,
            dx: 0,
            dy: 0,
            dirty: 1
        };
        !svg.bottom && (svg.bottom = this);
        /*\
         * Element.prev
         [ property (object) ]
         **
         * Reference to the previous element in the hierarchy.
        \*/
        this.prev = svg.top;
        svg.top && (svg.top.next = this);
        svg.top = this;
        /*\
         * Element.next
         [ property (object) ]
         **
         * Reference to the next element in the hierarchy.
        \*/
        this.next = null;
    },
    elproto = R.el;

    Element.prototype = elproto;
    elproto.constructor = Element;

    R._engine.path = function (pathString, SVG) {
        var el = $("path");
        SVG.canvas && SVG.canvas.appendChild(el);
        var p = new Element(el, SVG);
        p.type = "path";
        setFillAndStroke(p, {
            fill: "none",
            stroke: "#000",
            path: pathString
        });
        return p;
    };
    /*\
     * Element.rotate
     [ method ]
     **
     * Deprecated! Use @Element.transform instead.
     * Adds rotation by given angle around given point to the list of
     * transformations of the element.
     > Parameters
     - deg (number) angle in degrees
     - cx (number) #optional x coordinate of the centre of rotation
     - cy (number) #optional y coordinate of the centre of rotation
     * If cx & cy aren’t specified centre of the shape is used as a point of rotation.
     = (object) @Element
    \*/
    elproto.rotate = function (deg, cx, cy) {
        if (this.removed) {
            return this;
        }
        deg = Str(deg).split(separator);
        if (deg.length - 1) {
            cx = toFloat(deg[1]);
            cy = toFloat(deg[2]);
        }
        deg = toFloat(deg[0]);
        (cy == null) && (cx = cy);
        if (cx == null || cy == null) {
            var bbox = this.getBBox(1);
            cx = bbox.x + bbox.width / 2;
            cy = bbox.y + bbox.height / 2;
        }
        this.transform(this._.transform.concat([["r", deg, cx, cy]]));
        return this;
    };
    /*\
     * Element.scale
     [ method ]
     **
     * Deprecated! Use @Element.transform instead.
     * Adds scale by given amount relative to given point to the list of
     * transformations of the element.
     > Parameters
     - sx (number) horisontal scale amount
     - sy (number) vertical scale amount
     - cx (number) #optional x coordinate of the centre of scale
     - cy (number) #optional y coordinate of the centre of scale
     * If cx & cy aren’t specified centre of the shape is used instead.
     = (object) @Element
    \*/
    elproto.scale = function (sx, sy, cx, cy) {
        if (this.removed) {
            return this;
        }
        sx = Str(sx).split(separator);
        if (sx.length - 1) {
            sy = toFloat(sx[1]);
            cx = toFloat(sx[2]);
            cy = toFloat(sx[3]);
        }
        sx = toFloat(sx[0]);
        (sy == null) && (sy = sx);
        (cy == null) && (cx = cy);
        if (cx == null || cy == null) {
            var bbox = this.getBBox(1);
        }
        cx = cx == null ? bbox.x + bbox.width / 2 : cx;
        cy = cy == null ? bbox.y + bbox.height / 2 : cy;
        this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
        return this;
    };
    /*\
     * Element.translate
     [ method ]
     **
     * Deprecated! Use @Element.transform instead.
     * Adds translation by given amount to the list of transformations of the element.
     > Parameters
     - dx (number) horisontal shift
     - dy (number) vertical shift
     = (object) @Element
    \*/
    elproto.translate = function (dx, dy) {
        if (this.removed) {
            return this;
        }
        dx = Str(dx).split(separator);
        if (dx.length - 1) {
            dy = toFloat(dx[1]);
        }
        dx = toFloat(dx[0]) || 0;
        dy = +dy || 0;
        this.transform(this._.transform.concat([["t", dx, dy]]));
        return this;
    };
    /*\
     * Element.transform
     [ method ]
     **
     * Adds transformation to the element which is separate to other attributes,
     * i.e. translation doesn’t change `x` or `y` of the rectange. The format
     * of transformation string is similar to the path string syntax:
     | "t100,100r30,100,100s2,2,100,100r45s1.5"
     * Each letter is a command. There are four commands: `t` is for translate, `r` is for rotate, `s` is for
     * scale and `m` is for matrix.
     *
     * There are also alternative “absolute” translation, rotation and scale: `T`, `R` and `S`. They will not take previous transformation into account. For example, `...T100,0` will always move element 100 px horisontally, while `...t100,0` could move it vertically if there is `r90` before. Just compare results of `r90t100,0` and `r90T100,0`.
     *
     * So, the example line above could be read like “translate by 100, 100; rotate 30° around 100, 100; scale twice around 100, 100;
     * rotate 45° around centre; scale 1.5 times relative to centre”. As you can see rotate and scale commands have origin
     * coordinates as optional parameters, the default is the centre point of the element.
     * Matrix accepts six parameters.
     > Usage
     | var el = paper.rect(10, 20, 300, 200);
     | // translate 100, 100, rotate 45°, translate -100, 0
     | el.transform("t100,100r45t-100,0");
     | // if you want you can append or prepend transformations
     | el.transform("...t50,50");
     | el.transform("s2...");
     | // or even wrap
     | el.transform("t50,50...t-50-50");
     | // to reset transformation call method with empty string
     | el.transform("");
     | // to get current value call it without parameters
     | console.log(el.transform());
     > Parameters
     - tstr (string) #optional transformation string
     * If tstr isn’t specified
     = (string) current transformation string
     * else
     = (object) @Element
    \*/
    elproto.transform = function (tstr) {
        var _ = this._;
        if (tstr == null) {
            return _.transform;
        }
        R._extractTransform(this, tstr);

        this.clip && $(this.clip, {transform: this.matrix.invert()});
        this.pattern && updatePosition(this);
        this.node && $(this.node, {transform: this.matrix});

        if (_.sx != 1 || _.sy != 1) {
            var sw = this.attrs[has]("stroke-width") ? this.attrs["stroke-width"] : 1;
            this.attr({"stroke-width": sw});
        }

        //Reduce transform string
        _.transform = this.matrix.toTransformString();

        return this;
    };
    /*\
     * Element.hide
     [ method ]
     **
     * Makes element invisible. See @Element.show.
     = (object) @Element
    \*/
    elproto.hide = function () {
        if(!this.removed) this.node.style.display = "none";
        return this;
    };
    /*\
     * Element.show
     [ method ]
     **
     * Makes element visible. See @Element.hide.
     = (object) @Element
    \*/
    elproto.show = function () {
        if(!this.removed) this.node.style.display = "";
        return this;
    };
    /*\
     * Element.remove
     [ method ]
     **
     * Removes element from the paper.
    \*/
    elproto.remove = function () {
        var node = getRealNode(this.node);
        if (this.removed || !node.parentNode) {
            return;
        }
        var paper = this.paper;
        paper.__set__ && paper.__set__.exclude(this);
        eve.unbind("raphael.*.*." + this.id);
        if (this.gradient) {
            paper.defs.removeChild(this.gradient);
        }
        R._tear(this, paper);

        node.parentNode.removeChild(node);

        // Remove custom data for element
        this.removeData();

        for (var i in this) {
            this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
        }
        this.removed = true;
    };
    elproto._getBBox = function () {
        if (this.node.style.display == "none") {
            this.show();
            var hide = true;
        }
        var canvasHidden = false,
            containerStyle;
        if (this.paper.canvas.parentElement) {
          containerStyle = this.paper.canvas.parentElement.style;
        } //IE10+ can't find parentElement
        else if (this.paper.canvas.parentNode) {
          containerStyle = this.paper.canvas.parentNode.style;
        }

        if(containerStyle && containerStyle.display == "none") {
          canvasHidden = true;
          containerStyle.display = "";
        }
        var bbox = {};
        try {
            bbox = this.node.getBBox();
        } catch(e) {
            // Firefox 3.0.x, 25.0.1 (probably more versions affected) play badly here - possible fix
            bbox = {
                x: this.node.clientLeft,
                y: this.node.clientTop,
                width: this.node.clientWidth,
                height: this.node.clientHeight
            }
        } finally {
            bbox = bbox || {};
            if(canvasHidden){
              containerStyle.display = "none";
            }
        }
        hide && this.hide();
        return bbox;
    };
    /*\
     * Element.attr
     [ method ]
     **
     * Sets the attributes of the element.
     > Parameters
     - attrName (string) attribute’s name
     - value (string) value
     * or
     - params (object) object of name/value pairs
     * or
     - attrName (string) attribute’s name
     * or
     - attrNames (array) in this case method returns array of current values for given attribute names
     = (object) @Element if attrsName & value or params are passed in.
     = (...) value of the attribute if only attrsName is passed in.
     = (array) array of values of the attribute if attrsNames is passed in.
     = (object) object of attributes if nothing is passed in.
     > Possible parameters
     # <p>Please refer to the <a href="http://www.w3.org/TR/SVG/" title="The W3C Recommendation for the SVG language describes these properties in detail.">SVG specification</a> for an explanation of these parameters.</p>
     o arrow-end (string) arrowhead on the end of the path. The format for string is `<type>[-<width>[-<length>]]`. Possible types: `classic`, `block`, `open`, `oval`, `diamond`, `none`, width: `wide`, `narrow`, `medium`, length: `long`, `short`, `midium`.
     o clip-rect (string) comma or space separated values: x, y, width and height
     o cursor (string) CSS type of the cursor
     o cx (number) the x-axis coordinate of the center of the circle, or ellipse
     o cy (number) the y-axis coordinate of the center of the circle, or ellipse
     o fill (string) colour, gradient or image
     o fill-opacity (number)
     o font (string)
     o font-family (string)
     o font-size (number) font size in pixels
     o font-weight (string)
     o height (number)
     o href (string) URL, if specified element behaves as hyperlink
     o opacity (number)
     o path (string) SVG path string format
     o r (number) radius of the circle, ellipse or rounded corner on the rect
     o rx (number) horisontal radius of the ellipse
     o ry (number) vertical radius of the ellipse
     o src (string) image URL, only works for @Element.image element
     o stroke (string) stroke colour
     o stroke-dasharray (string) [“”, “none”, “`-`”, “`.`”, “`-.`”, “`-..`”, “`. `”, “`- `”, “`--`”, “`- .`”, “`--.`”, “`--..`”]
     o stroke-linecap (string) [“`butt`”, “`square`”, “`round`”]
     o stroke-linejoin (string) [“`bevel`”, “`round`”, “`miter`”]
     o stroke-miterlimit (number)
     o stroke-opacity (number)
     o stroke-width (number) stroke width in pixels, default is '1'
     o target (string) used with href
     o text (string) contents of the text element. Use `\n` for multiline text
     o text-anchor (string) [“`start`”, “`middle`”, “`end`”], default is “`middle`”
     o title (string) will create tooltip with a given text
     o transform (string) see @Element.transform
     o width (number)
     o x (number)
     o y (number)
     > Gradients
     * Linear gradient format: “`‹angle›-‹colour›[-‹colour›[:‹offset›]]*-‹colour›`”, example: “`90-#fff-#000`” – 90°
     * gradient from white to black or “`0-#fff-#f00:20-#000`” – 0° gradient from white via red (at 20%) to black.
     *
     * radial gradient: “`r[(‹fx›, ‹fy›)]‹colour›[-‹colour›[:‹offset›]]*-‹colour›`”, example: “`r#fff-#000`” –
     * gradient from white to black or “`r(0.25, 0.75)#fff-#000`” – gradient from white to black with focus point
     * at 0.25, 0.75. Focus point coordinates are in 0..1 range. Radial gradients can only be applied to circles and ellipses.
     > Path String
     # <p>Please refer to <a href="http://www.w3.org/TR/SVG/paths.html#PathData" title="Details of a path’s data attribute’s format are described in the SVG specification.">SVG documentation regarding path string</a>. Raphaël fully supports it.</p>
     > Colour Parsing
     # <ul>
     #     <li>Colour name (“<code>red</code>”, “<code>green</code>”, “<code>cornflowerblue</code>”, etc)</li>
     #     <li>#••• — shortened HTML colour: (“<code>#000</code>”, “<code>#fc0</code>”, etc)</li>
     #     <li>#•••••• — full length HTML colour: (“<code>#000000</code>”, “<code>#bd2300</code>”)</li>
     #     <li>rgb(•••, •••, •••) — red, green and blue channels’ values: (“<code>rgb(200,&nbsp;100,&nbsp;0)</code>”)</li>
     #     <li>rgb(•••%, •••%, •••%) — same as above, but in %: (“<code>rgb(100%,&nbsp;175%,&nbsp;0%)</code>”)</li>
     #     <li>rgba(•••, •••, •••, •••) — red, green and blue channels’ values: (“<code>rgba(200,&nbsp;100,&nbsp;0, .5)</code>”)</li>
     #     <li>rgba(•••%, •••%, •••%, •••%) — same as above, but in %: (“<code>rgba(100%,&nbsp;175%,&nbsp;0%, 50%)</code>”)</li>
     #     <li>hsb(•••, •••, •••) — hue, saturation and brightness values: (“<code>hsb(0.5,&nbsp;0.25,&nbsp;1)</code>”)</li>
     #     <li>hsb(•••%, •••%, •••%) — same as above, but in %</li>
     #     <li>hsba(•••, •••, •••, •••) — same as above, but with opacity</li>
     #     <li>hsl(•••, •••, •••) — almost the same as hsb, see <a href="http://en.wikipedia.org/wiki/HSL_and_HSV" title="HSL and HSV - Wikipedia, the free encyclopedia">Wikipedia page</a></li>
     #     <li>hsl(•••%, •••%, •••%) — same as above, but in %</li>
     #     <li>hsla(•••, •••, •••, •••) — same as above, but with opacity</li>
     #     <li>Optionally for hsb and hsl you could specify hue as a degree: “<code>hsl(240deg,&nbsp;1,&nbsp;.5)</code>” or, if you want to go fancy, “<code>hsl(240°,&nbsp;1,&nbsp;.5)</code>”</li>
     # </ul>
    \*/
    elproto.attr = function (name, value) {
        if (this.removed) {
            return this;
        }
        if (name == null) {
            var res = {};
            for (var a in this.attrs) if (this.attrs[has](a)) {
                res[a] = this.attrs[a];
            }
            res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
            res.transform = this._.transform;
            return res;
        }
        if (value == null && R.is(name, "string")) {
            if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) {
                return this.attrs.gradient;
            }
            if (name == "transform") {
                return this._.transform;
            }
            var names = name.split(separator),
                out = {};
            for (var i = 0, ii = names.length; i < ii; i++) {
                name = names[i];
                if (name in this.attrs) {
                    out[name] = this.attrs[name];
                } else if (R.is(this.paper.customAttributes[name], "function")) {
                    out[name] = this.paper.customAttributes[name].def;
                } else {
                    out[name] = R._availableAttrs[name];
                }
            }
            return ii - 1 ? out : out[names[0]];
        }
        if (value == null && R.is(name, "array")) {
            out = {};
            for (i = 0, ii = name.length; i < ii; i++) {
                out[name[i]] = this.attr(name[i]);
            }
            return out;
        }
        if (value != null) {
            var params = {};
            params[name] = value;
        } else if (name != null && R.is(name, "object")) {
            params = name;
        }
        for (var key in params) {
            eve("raphael.attr." + key + "." + this.id, this, params[key]);
        }
        for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
            var par = this.paper.customAttributes[key].apply(this, [].concat(params[key]));
            this.attrs[key] = params[key];
            for (var subkey in par) if (par[has](subkey)) {
                params[subkey] = par[subkey];
            }
        }
        setFillAndStroke(this, params);
        return this;
    };
    /*\
     * Element.toFront
     [ method ]
     **
     * Moves the element so it is the closest to the viewer’s eyes, on top of other elements.
     = (object) @Element
    \*/
    elproto.toFront = function () {
        if (this.removed) {
            return this;
        }
        var node = getRealNode(this.node);
        node.parentNode.appendChild(node);
        var svg = this.paper;
        svg.top != this && R._tofront(this, svg);
        return this;
    };
    /*\
     * Element.toBack
     [ method ]
     **
     * Moves the element so it is the furthest from the viewer’s eyes, behind other elements.
     = (object) @Element
    \*/
    elproto.toBack = function () {
        if (this.removed) {
            return this;
        }
        var node = getRealNode(this.node);
        var parentNode = node.parentNode;
        parentNode.insertBefore(node, parentNode.firstChild);
        R._toback(this, this.paper);
        var svg = this.paper;
        return this;
    };
    /*\
     * Element.insertAfter
     [ method ]
     **
     * Inserts current object after the given one.
     = (object) @Element
    \*/
    elproto.insertAfter = function (element) {
        if (this.removed || !element) {
            return this;
        }

        var node = getRealNode(this.node);
        var afterNode = getRealNode(element.node || element[element.length - 1].node);
        if (afterNode.nextSibling) {
            afterNode.parentNode.insertBefore(node, afterNode.nextSibling);
        } else {
            afterNode.parentNode.appendChild(node);
        }
        R._insertafter(this, element, this.paper);
        return this;
    };
    /*\
     * Element.insertBefore
     [ method ]
     **
     * Inserts current object before the given one.
     = (object) @Element
    \*/
    elproto.insertBefore = function (element) {
        if (this.removed || !element) {
            return this;
        }

        var node = getRealNode(this.node);
        var beforeNode = getRealNode(element.node || element[0].node);
        beforeNode.parentNode.insertBefore(node, beforeNode);
        R._insertbefore(this, element, this.paper);
        return this;
    };
    elproto.blur = function (size) {
        // Experimental. No Safari support. Use it on your own risk.
        var t = this;
        if (+size !== 0) {
            var fltr = $("filter"),
                blur = $("feGaussianBlur");
            t.attrs.blur = size;
            fltr.id = R.createUUID();
            $(blur, {stdDeviation: +size || 1.5});
            fltr.appendChild(blur);
            t.paper.defs.appendChild(fltr);
            t._blur = fltr;
            $(t.node, {filter: "url(#" + fltr.id + ")"});
        } else {
            if (t._blur) {
                t._blur.parentNode.removeChild(t._blur);
                delete t._blur;
                delete t.attrs.blur;
            }
            t.node.removeAttribute("filter");
        }
        return t;
    };
    R._engine.circle = function (svg, x, y, r) {
        var el = $("circle");
        svg.canvas && svg.canvas.appendChild(el);
        var res = new Element(el, svg);
        res.attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"};
        res.type = "circle";
        $(el, res.attrs);
        return res;
    };
    R._engine.rect = function (svg, x, y, w, h, r) {
        var el = $("rect");
        svg.canvas && svg.canvas.appendChild(el);
        var res = new Element(el, svg);
        res.attrs = {x: x, y: y, width: w, height: h, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000"};
        res.type = "rect";
        $(el, res.attrs);
        return res;
    };
    R._engine.ellipse = function (svg, x, y, rx, ry) {
        var el = $("ellipse");
        svg.canvas && svg.canvas.appendChild(el);
        var res = new Element(el, svg);
        res.attrs = {cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000"};
        res.type = "ellipse";
        $(el, res.attrs);
        return res;
    };
    R._engine.image = function (svg, src, x, y, w, h) {
        var el = $("image");
        $(el, {x: x, y: y, width: w, height: h, preserveAspectRatio: "none"});
        el.setAttributeNS(xlink, "href", src);
        svg.canvas && svg.canvas.appendChild(el);
        var res = new Element(el, svg);
        res.attrs = {x: x, y: y, width: w, height: h, src: src};
        res.type = "image";
        return res;
    };
    R._engine.text = function (svg, x, y, text) {
        var el = $("text");
        svg.canvas && svg.canvas.appendChild(el);
        var res = new Element(el, svg);
        res.attrs = {
            x: x,
            y: y,
            "text-anchor": "middle",
            text: text,
            "font-family": R._availableAttrs["font-family"],
            "font-size": R._availableAttrs["font-size"],
            stroke: "none",
            fill: "#000"
        };
        res.type = "text";
        setFillAndStroke(res, res.attrs);
        return res;
    };
    R._engine.setSize = function (width, height) {
        this.width = width || this.width;
        this.height = height || this.height;
        this.canvas.setAttribute("width", this.width);
        this.canvas.setAttribute("height", this.height);
        if (this._viewBox) {
            this.setViewBox.apply(this, this._viewBox);
        }
        return this;
    };
    R._engine.create = function () {
        var con = R._getContainer.apply(0, arguments),
            container = con && con.container,
            x = con.x,
            y = con.y,
            width = con.width,
            height = con.height;
        if (!container) {
            throw new Error("SVG container not found.");
        }
        var cnvs = $("svg"),
            css = "overflow:hidden;",
            isFloating;
        x = x || 0;
        y = y || 0;
        width = width || 512;
        height = height || 342;
        $(cnvs, {
            height: height,
            version: 1.1,
            width: width,
            xmlns: "http://www.w3.org/2000/svg",
            "xmlns:xlink": "http://www.w3.org/1999/xlink"
        });
        if (container == 1) {
            cnvs.style.cssText = css + "position:absolute;left:" + x + "px;top:" + y + "px";
            R._g.doc.body.appendChild(cnvs);
            isFloating = 1;
        } else {
            cnvs.style.cssText = css + "position:relative";
            if (container.firstChild) {
                container.insertBefore(cnvs, container.firstChild);
            } else {
                container.appendChild(cnvs);
            }
        }
        container = new R._Paper;
        container.width = width;
        container.height = height;
        container.canvas = cnvs;
        container.clear();
        container._left = container._top = 0;
        isFloating && (container.renderfix = function () {});
        container.renderfix();
        return container;
    };
    R._engine.setViewBox = function (x, y, w, h, fit) {
        eve("raphael.setViewBox", this, this._viewBox, [x, y, w, h, fit]);
        var paperSize = this.getSize(),
            size = mmax(w / paperSize.width, h / paperSize.height),
            top = this.top,
            aspectRatio = fit ? "xMidYMid meet" : "xMinYMin",
            vb,
            sw;
        if (x == null) {
            if (this._vbSize) {
                size = 1;
            }
            delete this._vbSize;
            vb = "0 0 " + this.width + S + this.height;
        } else {
            this._vbSize = size;
            vb = x + S + y + S + w + S + h;
        }
        $(this.canvas, {
            viewBox: vb,
            preserveAspectRatio: aspectRatio
        });
        while (size && top) {
            sw = "stroke-width" in top.attrs ? top.attrs["stroke-width"] : 1;
            top.attr({"stroke-width": sw});
            top._.dirty = 1;
            top._.dirtyT = 1;
            top = top.prev;
        }
        this._viewBox = [x, y, w, h, !!fit];
        return this;
    };
    /*\
     * Paper.renderfix
     [ method ]
     **
     * Fixes the issue of Firefox and IE9 regarding subpixel rendering. If paper is dependant
     * on other elements after reflow it could shift half pixel which cause for lines to lost their crispness.
     * This method fixes the issue.
     **
       Special thanks to Mariusz Nowak (http://www.medikoo.com/) for this method.
    \*/
    R.prototype.renderfix = function () {
        var cnvs = this.canvas,
            s = cnvs.style,
            pos;
        try {
            pos = cnvs.getScreenCTM() || cnvs.createSVGMatrix();
        } catch (e) {
            pos = cnvs.createSVGMatrix();
        }
        var left = -pos.e % 1,
            top = -pos.f % 1;
        if (left || top) {
            if (left) {
                this._left = (this._left + left) % 1;
                s.left = this._left + "px";
            }
            if (top) {
                this._top = (this._top + top) % 1;
                s.top = this._top + "px";
            }
        }
    };
    /*\
     * Paper.clear
     [ method ]
     **
     * Clears the paper, i.e. removes all the elements.
    \*/
    R.prototype.clear = function () {
        R.eve("raphael.clear", this);
        var c = this.canvas;
        while (c.firstChild) {
            c.removeChild(c.firstChild);
        }
        this.bottom = this.top = null;
        (this.desc = $("desc")).appendChild(R._g.doc.createTextNode("Created with Rapha\xebl " + R.version));
        c.appendChild(this.desc);
        c.appendChild(this.defs = $("defs"));
    };
    /*\
     * Paper.remove
     [ method ]
     **
     * Removes the paper from the DOM.
    \*/
    R.prototype.remove = function () {
        eve("raphael.remove", this);
        this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
        for (var i in this) {
            this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
        }
    };
    var setproto = R.st;
    for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) {
        setproto[method] = (function (methodname) {
            return function () {
                var arg = arguments;
                return this.forEach(function (el) {
                    el[methodname].apply(el, arg);
                });
            };
        })(method);
    }
}));

// ┌─────────────────────────────────────────────────────────────────────┐ \\
// │ Raphaël 2.1.4 - JavaScript Vector Library                       │ \\
// ├─────────────────────────────────────────────────────────────────────┤ \\
// │ VML Module                                                          │ \\
// ├─────────────────────────────────────────────────────────────────────┤ \\
// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
// └─────────────────────────────────────────────────────────────────────┘ \\

(function (glob, factory) {
    if (typeof define === "function" && define.amd) {
        define("raphael.vml", ["raphael.core"], function(raphael) {
            return factory(raphael);
        });
    } else if (typeof exports === "object") {
        factory(require("./raphael.core"));
    } else {
        factory(glob.Raphael);
    }
}(this, function(R) {
    if (R && !R.vml) {
        return;
    }

    var has = "hasOwnProperty",
        Str = String,
        toFloat = parseFloat,
        math = Math,
        round = math.round,
        mmax = math.max,
        mmin = math.min,
        abs = math.abs,
        fillString = "fill",
        separator = /[, ]+/,
        eve = R.eve,
        ms = " progid:DXImageTransform.Microsoft",
        S = " ",
        E = "",
        map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"},
        bites = /([clmz]),?([^clmz]*)/gi,
        blurregexp = / progid:\S+Blur\([^\)]+\)/g,
        val = /-?[^,\s-]+/g,
        cssDot = "position:absolute;left:0;top:0;width:1px;height:1px;behavior:url(#default#VML)",
        zoom = 21600,
        pathTypes = {path: 1, rect: 1, image: 1},
        ovalTypes = {circle: 1, ellipse: 1},
        path2vml = function (path) {
            var total =  /[ahqstv]/ig,
                command = R._pathToAbsolute;
            Str(path).match(total) && (command = R._path2curve);
            total = /[clmz]/g;
            if (command == R._pathToAbsolute && !Str(path).match(total)) {
                var res = Str(path).replace(bites, function (all, command, args) {
                    var vals = [],
                        isMove = command.toLowerCase() == "m",
                        res = map[command];
                    args.replace(val, function (value) {
                        if (isMove && vals.length == 2) {
                            res += vals + map[command == "m" ? "l" : "L"];
                            vals = [];
                        }
                        vals.push(round(value * zoom));
                    });
                    return res + vals;
                });
                return res;
            }
            var pa = command(path), p, r;
            res = [];
            for (var i = 0, ii = pa.length; i < ii; i++) {
                p = pa[i];
                r = pa[i][0].toLowerCase();
                r == "z" && (r = "x");
                for (var j = 1, jj = p.length; j < jj; j++) {
                    r += round(p[j] * zoom) + (j != jj - 1 ? "," : E);
                }
                res.push(r);
            }
            return res.join(S);
        },
        compensation = function (deg, dx, dy) {
            var m = R.matrix();
            m.rotate(-deg, .5, .5);
            return {
                dx: m.x(dx, dy),
                dy: m.y(dx, dy)
            };
        },
        setCoords = function (p, sx, sy, dx, dy, deg) {
            var _ = p._,
                m = p.matrix,
                fillpos = _.fillpos,
                o = p.node,
                s = o.style,
                y = 1,
                flip = "",
                dxdy,
                kx = zoom / sx,
                ky = zoom / sy;
            s.visibility = "hidden";
            if (!sx || !sy) {
                return;
            }
            o.coordsize = abs(kx) + S + abs(ky);
            s.rotation = deg * (sx * sy < 0 ? -1 : 1);
            if (deg) {
                var c = compensation(deg, dx, dy);
                dx = c.dx;
                dy = c.dy;
            }
            sx < 0 && (flip += "x");
            sy < 0 && (flip += " y") && (y = -1);
            s.flip = flip;
            o.coordorigin = (dx * -kx) + S + (dy * -ky);
            if (fillpos || _.fillsize) {
                var fill = o.getElementsByTagName(fillString);
                fill = fill && fill[0];
                o.removeChild(fill);
                if (fillpos) {
                    c = compensation(deg, m.x(fillpos[0], fillpos[1]), m.y(fillpos[0], fillpos[1]));
                    fill.position = c.dx * y + S + c.dy * y;
                }
                if (_.fillsize) {
                    fill.size = _.fillsize[0] * abs(sx) + S + _.fillsize[1] * abs(sy);
                }
                o.appendChild(fill);
            }
            s.visibility = "visible";
        };
    R.toString = function () {
        return  "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl " + this.version;
    };
    var addArrow = function (o, value, isEnd) {
        var values = Str(value).toLowerCase().split("-"),
            se = isEnd ? "end" : "start",
            i = values.length,
            type = "classic",
            w = "medium",
            h = "medium";
        while (i--) {
            switch (values[i]) {
                case "block":
                case "classic":
                case "oval":
                case "diamond":
                case "open":
                case "none":
                    type = values[i];
                    break;
                case "wide":
                case "narrow": h = values[i]; break;
                case "long":
                case "short": w = values[i]; break;
            }
        }
        var stroke = o.node.getElementsByTagName("stroke")[0];
        stroke[se + "arrow"] = type;
        stroke[se + "arrowlength"] = w;
        stroke[se + "arrowwidth"] = h;
    },
    setFillAndStroke = function (o, params) {
        // o.paper.canvas.style.display = "none";
        o.attrs = o.attrs || {};
        var node = o.node,
            a = o.attrs,
            s = node.style,
            xy,
            newpath = pathTypes[o.type] && (params.x != a.x || params.y != a.y || params.width != a.width || params.height != a.height || params.cx != a.cx || params.cy != a.cy || params.rx != a.rx || params.ry != a.ry || params.r != a.r),
            isOval = ovalTypes[o.type] && (a.cx != params.cx || a.cy != params.cy || a.r != params.r || a.rx != params.rx || a.ry != params.ry),
            res = o;


        for (var par in params) if (params[has](par)) {
            a[par] = params[par];
        }
        if (newpath) {
            a.path = R._getPath[o.type](o);
            o._.dirty = 1;
        }
        params.href && (node.href = params.href);
        params.title && (node.title = params.title);
        params.target && (node.target = params.target);
        params.cursor && (s.cursor = params.cursor);
        "blur" in params && o.blur(params.blur);
        if (params.path && o.type == "path" || newpath) {
            node.path = path2vml(~Str(a.path).toLowerCase().indexOf("r") ? R._pathToAbsolute(a.path) : a.path);
            o._.dirty = 1;
            if (o.type == "image") {
                o._.fillpos = [a.x, a.y];
                o._.fillsize = [a.width, a.height];
                setCoords(o, 1, 1, 0, 0, 0);
            }
        }
        "transform" in params && o.transform(params.transform);
        if (isOval) {
            var cx = +a.cx,
                cy = +a.cy,
                rx = +a.rx || +a.r || 0,
                ry = +a.ry || +a.r || 0;
            node.path = R.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x", round((cx - rx) * zoom), round((cy - ry) * zoom), round((cx + rx) * zoom), round((cy + ry) * zoom), round(cx * zoom));
            o._.dirty = 1;
        }
        if ("clip-rect" in params) {
            var rect = Str(params["clip-rect"]).split(separator);
            if (rect.length == 4) {
                rect[2] = +rect[2] + (+rect[0]);
                rect[3] = +rect[3] + (+rect[1]);
                var div = node.clipRect || R._g.doc.createElement("div"),
                    dstyle = div.style;
                dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect);
                if (!node.clipRect) {
                    dstyle.position = "absolute";
                    dstyle.top = 0;
                    dstyle.left = 0;
                    dstyle.width = o.paper.width + "px";
                    dstyle.height = o.paper.height + "px";
                    node.parentNode.insertBefore(div, node);
                    div.appendChild(node);
                    node.clipRect = div;
                }
            }
            if (!params["clip-rect"]) {
                node.clipRect && (node.clipRect.style.clip = "auto");
            }
        }
        if (o.textpath) {
            var textpathStyle = o.textpath.style;
            params.font && (textpathStyle.font = params.font);
            params["font-family"] && (textpathStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g, E) + '"');
            params["font-size"] && (textpathStyle.fontSize = params["font-size"]);
            params["font-weight"] && (textpathStyle.fontWeight = params["font-weight"]);
            params["font-style"] && (textpathStyle.fontStyle = params["font-style"]);
        }
        if ("arrow-start" in params) {
            addArrow(res, params["arrow-start"]);
        }
        if ("arrow-end" in params) {
            addArrow(res, params["arrow-end"], 1);
        }
        if (params.opacity != null ||
            params["stroke-width"] != null ||
            params.fill != null ||
            params.src != null ||
            params.stroke != null ||
            params["stroke-width"] != null ||
            params["stroke-opacity"] != null ||
            params["fill-opacity"] != null ||
            params["stroke-dasharray"] != null ||
            params["stroke-miterlimit"] != null ||
            params["stroke-linejoin"] != null ||
            params["stroke-linecap"] != null) {
            var fill = node.getElementsByTagName(fillString),
                newfill = false;
            fill = fill && fill[0];
            !fill && (newfill = fill = createNode(fillString));
            if (o.type == "image" && params.src) {
                fill.src = params.src;
            }
            params.fill && (fill.on = true);
            if (fill.on == null || params.fill == "none" || params.fill === null) {
                fill.on = false;
            }
            if (fill.on && params.fill) {
                var isURL = Str(params.fill).match(R._ISURL);
                if (isURL) {
                    fill.parentNode == node && node.removeChild(fill);
                    fill.rotate = true;
                    fill.src = isURL[1];
                    fill.type = "tile";
                    var bbox = o.getBBox(1);
                    fill.position = bbox.x + S + bbox.y;
                    o._.fillpos = [bbox.x, bbox.y];

                    R._preload(isURL[1], function () {
                        o._.fillsize = [this.offsetWidth, this.offsetHeight];
                    });
                } else {
                    fill.color = R.getRGB(params.fill).hex;
                    fill.src = E;
                    fill.type = "solid";
                    if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || Str(params.fill).charAt() != "r") && addGradientFill(res, params.fill, fill)) {
                        a.fill = "none";
                        a.gradient = params.fill;
                        fill.rotate = false;
                    }
                }
            }
            if ("fill-opacity" in params || "opacity" in params) {
                var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+R.getRGB(params.fill).o + 1 || 2) - 1);
                opacity = mmin(mmax(opacity, 0), 1);
                fill.opacity = opacity;
                if (fill.src) {
                    fill.color = "none";
                }
            }
            node.appendChild(fill);
            var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]),
            newstroke = false;
            !stroke && (newstroke = stroke = createNode("stroke"));
            if ((params.stroke && params.stroke != "none") ||
                params["stroke-width"] ||
                params["stroke-opacity"] != null ||
                params["stroke-dasharray"] ||
                params["stroke-miterlimit"] ||
                params["stroke-linejoin"] ||
                params["stroke-linecap"]) {
                stroke.on = true;
            }
            (params.stroke == "none" || params.stroke === null || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false);
            var strokeColor = R.getRGB(params.stroke);
            stroke.on && params.stroke && (stroke.color = strokeColor.hex);
            opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+strokeColor.o + 1 || 2) - 1);
            var width = (toFloat(params["stroke-width"]) || 1) * .75;
            opacity = mmin(mmax(opacity, 0), 1);
            params["stroke-width"] == null && (width = a["stroke-width"]);
            params["stroke-width"] && (stroke.weight = width);
            width && width < 1 && (opacity *= width) && (stroke.weight = 1);
            stroke.opacity = opacity;

            params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter");
            stroke.miterlimit = params["stroke-miterlimit"] || 8;
            params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round");
            if ("stroke-dasharray" in params) {
                var dasharray = {
                    "-": "shortdash",
                    ".": "shortdot",
                    "-.": "shortdashdot",
                    "-..": "shortdashdotdot",
                    ". ": "dot",
                    "- ": "dash",
                    "--": "longdash",
                    "- .": "dashdot",
                    "--.": "longdashdot",
                    "--..": "longdashdotdot"
                };
                stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E;
            }
            newstroke && node.appendChild(stroke);
        }
        if (res.type == "text") {
            res.paper.canvas.style.display = E;
            var span = res.paper.span,
                m = 100,
                fontSize = a.font && a.font.match(/\d+(?:\.\d*)?(?=px)/);
            s = span.style;
            a.font && (s.font = a.font);
            a["font-family"] && (s.fontFamily = a["font-family"]);
            a["font-weight"] && (s.fontWeight = a["font-weight"]);
            a["font-style"] && (s.fontStyle = a["font-style"]);
            fontSize = toFloat(a["font-size"] || fontSize && fontSize[0]) || 10;
            s.fontSize = fontSize * m + "px";
            res.textpath.string && (span.innerHTML = Str(res.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br>"));
            var brect = span.getBoundingClientRect();
            res.W = a.w = (brect.right - brect.left) / m;
            res.H = a.h = (brect.bottom - brect.top) / m;
            // res.paper.canvas.style.display = "none";
            res.X = a.x;
            res.Y = a.y + res.H / 2;

            ("x" in params || "y" in params) && (res.path.v = R.format("m{0},{1}l{2},{1}", round(a.x * zoom), round(a.y * zoom), round(a.x * zoom) + 1));
            var dirtyattrs = ["x", "y", "text", "font", "font-family", "font-weight", "font-style", "font-size"];
            for (var d = 0, dd = dirtyattrs.length; d < dd; d++) if (dirtyattrs[d] in params) {
                res._.dirty = 1;
                break;
            }

            // text-anchor emulation
            switch (a["text-anchor"]) {
                case "start":
                    res.textpath.style["v-text-align"] = "left";
                    res.bbx = res.W / 2;
                break;
                case "end":
                    res.textpath.style["v-text-align"] = "right";
                    res.bbx = -res.W / 2;
                break;
                default:
                    res.textpath.style["v-text-align"] = "center";
                    res.bbx = 0;
                break;
            }
            res.textpath.style["v-text-kern"] = true;
        }
        // res.paper.canvas.style.display = E;
    },
    addGradientFill = function (o, gradient, fill) {
        o.attrs = o.attrs || {};
        var attrs = o.attrs,
            pow = Math.pow,
            opacity,
            oindex,
            type = "linear",
            fxfy = ".5 .5";
        o.attrs.gradient = gradient;
        gradient = Str(gradient).replace(R._radial_gradient, function (all, fx, fy) {
            type = "radial";
            if (fx && fy) {
                fx = toFloat(fx);
                fy = toFloat(fy);
                pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5);
                fxfy = fx + S + fy;
            }
            return E;
        });
        gradient = gradient.split(/\s*\-\s*/);
        if (type == "linear") {
            var angle = gradient.shift();
            angle = -toFloat(angle);
            if (isNaN(angle)) {
                return null;
            }
        }
        var dots = R._parseDots(gradient);
        if (!dots) {
            return null;
        }
        o = o.shape || o.node;
        if (dots.length) {
            o.removeChild(fill);
            fill.on = true;
            fill.method = "none";
            fill.color = dots[0].color;
            fill.color2 = dots[dots.length - 1].color;
            var clrs = [];
            for (var i = 0, ii = dots.length; i < ii; i++) {
                dots[i].offset && clrs.push(dots[i].offset + S + dots[i].color);
            }
            fill.colors = clrs.length ? clrs.join() : "0% " + fill.color;
            if (type == "radial") {
                fill.type = "gradientTitle";
                fill.focus = "100%";
                fill.focussize = "0 0";
                fill.focusposition = fxfy;
                fill.angle = 0;
            } else {
                // fill.rotate= true;
                fill.type = "gradient";
                fill.angle = (270 - angle) % 360;
            }
            o.appendChild(fill);
        }
        return 1;
    },
    Element = function (node, vml) {
        this[0] = this.node = node;
        node.raphael = true;
        this.id = R._oid++;
        node.raphaelid = this.id;
        this.X = 0;
        this.Y = 0;
        this.attrs = {};
        this.paper = vml;
        this.matrix = R.matrix();
        this._ = {
            transform: [],
            sx: 1,
            sy: 1,
            dx: 0,
            dy: 0,
            deg: 0,
            dirty: 1,
            dirtyT: 1
        };
        !vml.bottom && (vml.bottom = this);
        this.prev = vml.top;
        vml.top && (vml.top.next = this);
        vml.top = this;
        this.next = null;
    };
    var elproto = R.el;

    Element.prototype = elproto;
    elproto.constructor = Element;
    elproto.transform = function (tstr) {
        if (tstr == null) {
            return this._.transform;
        }
        var vbs = this.paper._viewBoxShift,
            vbt = vbs ? "s" + [vbs.scale, vbs.scale] + "-1-1t" + [vbs.dx, vbs.dy] : E,
            oldt;
        if (vbs) {
            oldt = tstr = Str(tstr).replace(/\.{3}|\u2026/g, this._.transform || E);
        }
        R._extractTransform(this, vbt + tstr);
        var matrix = this.matrix.clone(),
            skew = this.skew,
            o = this.node,
            split,
            isGrad = ~Str(this.attrs.fill).indexOf("-"),
            isPatt = !Str(this.attrs.fill).indexOf("url(");
        matrix.translate(1, 1);
        if (isPatt || isGrad || this.type == "image") {
            skew.matrix = "1 0 0 1";
            skew.offset = "0 0";
            split = matrix.split();
            if ((isGrad && split.noRotation) || !split.isSimple) {
                o.style.filter = matrix.toFilter();
                var bb = this.getBBox(),
                    bbt = this.getBBox(1),
                    dx = bb.x - bbt.x,
                    dy = bb.y - bbt.y;
                o.coordorigin = (dx * -zoom) + S + (dy * -zoom);
                setCoords(this, 1, 1, dx, dy, 0);
            } else {
                o.style.filter = E;
                setCoords(this, split.scalex, split.scaley, split.dx, split.dy, split.rotate);
            }
        } else {
            o.style.filter = E;
            skew.matrix = Str(matrix);
            skew.offset = matrix.offset();
        }
        if (oldt !== null) { // empty string value is true as well
            this._.transform = oldt;
            R._extractTransform(this, oldt);
        }
        return this;
    };
    elproto.rotate = function (deg, cx, cy) {
        if (this.removed) {
            return this;
        }
        if (deg == null) {
            return;
        }
        deg = Str(deg).split(separator);
        if (deg.length - 1) {
            cx = toFloat(deg[1]);
            cy = toFloat(deg[2]);
        }
        deg = toFloat(deg[0]);
        (cy == null) && (cx = cy);
        if (cx == null || cy == null) {
            var bbox = this.getBBox(1);
            cx = bbox.x + bbox.width / 2;
            cy = bbox.y + bbox.height / 2;
        }
        this._.dirtyT = 1;
        this.transform(this._.transform.concat([["r", deg, cx, cy]]));
        return this;
    };
    elproto.translate = function (dx, dy) {
        if (this.removed) {
            return this;
        }
        dx = Str(dx).split(separator);
        if (dx.length - 1) {
            dy = toFloat(dx[1]);
        }
        dx = toFloat(dx[0]) || 0;
        dy = +dy || 0;
        if (this._.bbox) {
            this._.bbox.x += dx;
            this._.bbox.y += dy;
        }
        this.transform(this._.transform.concat([["t", dx, dy]]));
        return this;
    };
    elproto.scale = function (sx, sy, cx, cy) {
        if (this.removed) {
            return this;
        }
        sx = Str(sx).split(separator);
        if (sx.length - 1) {
            sy = toFloat(sx[1]);
            cx = toFloat(sx[2]);
            cy = toFloat(sx[3]);
            isNaN(cx) && (cx = null);
            isNaN(cy) && (cy = null);
        }
        sx = toFloat(sx[0]);
        (sy == null) && (sy = sx);
        (cy == null) && (cx = cy);
        if (cx == null || cy == null) {
            var bbox = this.getBBox(1);
        }
        cx = cx == null ? bbox.x + bbox.width / 2 : cx;
        cy = cy == null ? bbox.y + bbox.height / 2 : cy;

        this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
        this._.dirtyT = 1;
        return this;
    };
    elproto.hide = function () {
        !this.removed && (this.node.style.display = "none");
        return this;
    };
    elproto.show = function () {
        !this.removed && (this.node.style.display = E);
        return this;
    };
    // Needed to fix the vml setViewBox issues
    elproto.auxGetBBox = R.el.getBBox;
    elproto.getBBox = function(){
      var b = this.auxGetBBox();
      if (this.paper && this.paper._viewBoxShift)
      {
        var c = {};
        var z = 1/this.paper._viewBoxShift.scale;
        c.x = b.x - this.paper._viewBoxShift.dx;
        c.x *= z;
        c.y = b.y - this.paper._viewBoxShift.dy;
        c.y *= z;
        c.width  = b.width  * z;
        c.height = b.height * z;
        c.x2 = c.x + c.width;
        c.y2 = c.y + c.height;
        return c;
      }
      return b;
    };
    elproto._getBBox = function () {
        if (this.removed) {
            return {};
        }
        return {
            x: this.X + (this.bbx || 0) - this.W / 2,
            y: this.Y - this.H,
            width: this.W,
            height: this.H
        };
    };
    elproto.remove = function () {
        if (this.removed || !this.node.parentNode) {
            return;
        }
        this.paper.__set__ && this.paper.__set__.exclude(this);
        R.eve.unbind("raphael.*.*." + this.id);
        R._tear(this, this.paper);
        this.node.parentNode.removeChild(this.node);
        this.shape && this.shape.parentNode.removeChild(this.shape);
        for (var i in this) {
            this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
        }
        this.removed = true;
    };
    elproto.attr = function (name, value) {
        if (this.removed) {
            return this;
        }
        if (name == null) {
            var res = {};
            for (var a in this.attrs) if (this.attrs[has](a)) {
                res[a] = this.attrs[a];
            }
            res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
            res.transform = this._.transform;
            return res;
        }
        if (value == null && R.is(name, "string")) {
            if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) {
                return this.attrs.gradient;
            }
            var names = name.split(separator),
                out = {};
            for (var i = 0, ii = names.length; i < ii; i++) {
                name = names[i];
                if (name in this.attrs) {
                    out[name] = this.attrs[name];
                } else if (R.is(this.paper.customAttributes[name], "function")) {
                    out[name] = this.paper.customAttributes[name].def;
                } else {
                    out[name] = R._availableAttrs[name];
                }
            }
            return ii - 1 ? out : out[names[0]];
        }
        if (this.attrs && value == null && R.is(name, "array")) {
            out = {};
            for (i = 0, ii = name.length; i < ii; i++) {
                out[name[i]] = this.attr(name[i]);
            }
            return out;
        }
        var params;
        if (value != null) {
            params = {};
            params[name] = value;
        }
        value == null && R.is(name, "object") && (params = name);
        for (var key in params) {
            eve("raphael.attr." + key + "." + this.id, this, params[key]);
        }
        if (params) {
            for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
                var par = this.paper.customAttributes[key].apply(this, [].concat(params[key]));
                this.attrs[key] = params[key];
                for (var subkey in par) if (par[has](subkey)) {
                    params[subkey] = par[subkey];
                }
            }
            // this.paper.canvas.style.display = "none";
            if (params.text && this.type == "text") {
                this.textpath.string = params.text;
            }
            setFillAndStroke(this, params);
            // this.paper.canvas.style.display = E;
        }
        return this;
    };
    elproto.toFront = function () {
        !this.removed && this.node.parentNode.appendChild(this.node);
        this.paper && this.paper.top != this && R._tofront(this, this.paper);
        return this;
    };
    elproto.toBack = function () {
        if (this.removed) {
            return this;
        }
        if (this.node.parentNode.firstChild != this.node) {
            this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
            R._toback(this, this.paper);
        }
        return this;
    };
    elproto.insertAfter = function (element) {
        if (this.removed) {
            return this;
        }
        if (element.constructor == R.st.constructor) {
            element = element[element.length - 1];
        }
        if (element.node.nextSibling) {
            element.node.parentNode.insertBefore(this.node, element.node.nextSibling);
        } else {
            element.node.parentNode.appendChild(this.node);
        }
        R._insertafter(this, element, this.paper);
        return this;
    };
    elproto.insertBefore = function (element) {
        if (this.removed) {
            return this;
        }
        if (element.constructor == R.st.constructor) {
            element = element[0];
        }
        element.node.parentNode.insertBefore(this.node, element.node);
        R._insertbefore(this, element, this.paper);
        return this;
    };
    elproto.blur = function (size) {
        var s = this.node.runtimeStyle,
            f = s.filter;
        f = f.replace(blurregexp, E);
        if (+size !== 0) {
            this.attrs.blur = size;
            s.filter = f + S + ms + ".Blur(pixelradius=" + (+size || 1.5) + ")";
            s.margin = R.format("-{0}px 0 0 -{0}px", round(+size || 1.5));
        } else {
            s.filter = f;
            s.margin = 0;
            delete this.attrs.blur;
        }
        return this;
    };

    R._engine.path = function (pathString, vml) {
        var el = createNode("shape");
        el.style.cssText = cssDot;
        el.coordsize = zoom + S + zoom;
        el.coordorigin = vml.coordorigin;
        var p = new Element(el, vml),
            attr = {fill: "none", stroke: "#000"};
        pathString && (attr.path = pathString);
        p.type = "path";
        p.path = [];
        p.Path = E;
        setFillAndStroke(p, attr);
        vml.canvas.appendChild(el);
        var skew = createNode("skew");
        skew.on = true;
        el.appendChild(skew);
        p.skew = skew;
        p.transform(E);
        return p;
    };
    R._engine.rect = function (vml, x, y, w, h, r) {
        var path = R._rectPath(x, y, w, h, r),
            res = vml.path(path),
            a = res.attrs;
        res.X = a.x = x;
        res.Y = a.y = y;
        res.W = a.width = w;
        res.H = a.height = h;
        a.r = r;
        a.path = path;
        res.type = "rect";
        return res;
    };
    R._engine.ellipse = function (vml, x, y, rx, ry) {
        var res = vml.path(),
            a = res.attrs;
        res.X = x - rx;
        res.Y = y - ry;
        res.W = rx * 2;
        res.H = ry * 2;
        res.type = "ellipse";
        setFillAndStroke(res, {
            cx: x,
            cy: y,
            rx: rx,
            ry: ry
        });
        return res;
    };
    R._engine.circle = function (vml, x, y, r) {
        var res = vml.path(),
            a = res.attrs;
        res.X = x - r;
        res.Y = y - r;
        res.W = res.H = r * 2;
        res.type = "circle";
        setFillAndStroke(res, {
            cx: x,
            cy: y,
            r: r
        });
        return res;
    };
    R._engine.image = function (vml, src, x, y, w, h) {
        var path = R._rectPath(x, y, w, h),
            res = vml.path(path).attr({stroke: "none"}),
            a = res.attrs,
            node = res.node,
            fill = node.getElementsByTagName(fillString)[0];
        a.src = src;
        res.X = a.x = x;
        res.Y = a.y = y;
        res.W = a.width = w;
        res.H = a.height = h;
        a.path = path;
        res.type = "image";
        fill.parentNode == node && node.removeChild(fill);
        fill.rotate = true;
        fill.src = src;
        fill.type = "tile";
        res._.fillpos = [x, y];
        res._.fillsize = [w, h];
        node.appendChild(fill);
        setCoords(res, 1, 1, 0, 0, 0);
        return res;
    };
    R._engine.text = function (vml, x, y, text) {
        var el = createNode("shape"),
            path = createNode("path"),
            o = createNode("textpath");
        x = x || 0;
        y = y || 0;
        text = text || "";
        path.v = R.format("m{0},{1}l{2},{1}", round(x * zoom), round(y * zoom), round(x * zoom) + 1);
        path.textpathok = true;
        o.string = Str(text);
        o.on = true;
        el.style.cssText = cssDot;
        el.coordsize = zoom + S + zoom;
        el.coordorigin = "0 0";
        var p = new Element(el, vml),
            attr = {
                fill: "#000",
                stroke: "none",
                font: R._availableAttrs.font,
                text: text
            };
        p.shape = el;
        p.path = path;
        p.textpath = o;
        p.type = "text";
        p.attrs.text = Str(text);
        p.attrs.x = x;
        p.attrs.y = y;
        p.attrs.w = 1;
        p.attrs.h = 1;
        setFillAndStroke(p, attr);
        el.appendChild(o);
        el.appendChild(path);
        vml.canvas.appendChild(el);
        var skew = createNode("skew");
        skew.on = true;
        el.appendChild(skew);
        p.skew = skew;
        p.transform(E);
        return p;
    };
    R._engine.setSize = function (width, height) {
        var cs = this.canvas.style;
        this.width = width;
        this.height = height;
        width == +width && (width += "px");
        height == +height && (height += "px");
        cs.width = width;
        cs.height = height;
        cs.clip = "rect(0 " + width + " " + height + " 0)";
        if (this._viewBox) {
            R._engine.setViewBox.apply(this, this._viewBox);
        }
        return this;
    };
    R._engine.setViewBox = function (x, y, w, h, fit) {
        R.eve("raphael.setViewBox", this, this._viewBox, [x, y, w, h, fit]);
        var paperSize = this.getSize(),
            width = paperSize.width,
            height = paperSize.height,
            H, W;
        if (fit) {
            H = height / h;
            W = width / w;
            if (w * H < width) {
                x -= (width - w * H) / 2 / H;
            }
            if (h * W < height) {
                y -= (height - h * W) / 2 / W;
            }
        }
        this._viewBox = [x, y, w, h, !!fit];
        this._viewBoxShift = {
            dx: -x,
            dy: -y,
            scale: paperSize
        };
        this.forEach(function (el) {
            el.transform("...");
        });
        return this;
    };
    var createNode;
    R._engine.initWin = function (win) {
            var doc = win.document;
            if (doc.styleSheets.length < 31) {
                doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
            } else {
                // no more room, add to the existing one
                // http://msdn.microsoft.com/en-us/library/ms531194%28VS.85%29.aspx
                doc.styleSheets[0].addRule(".rvml", "behavior:url(#default#VML)");
            }
            try {
                !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
                createNode = function (tagName) {
                    return doc.createElement('<rvml:' + tagName + ' class="rvml">');
                };
            } catch (e) {
                createNode = function (tagName) {
                    return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
                };
            }
        };
    R._engine.initWin(R._g.win);
    R._engine.create = function () {
        var con = R._getContainer.apply(0, arguments),
            container = con.container,
            height = con.height,
            s,
            width = con.width,
            x = con.x,
            y = con.y;
        if (!container) {
            throw new Error("VML container not found.");
        }
        var res = new R._Paper,
            c = res.canvas = R._g.doc.createElement("div"),
            cs = c.style;
        x = x || 0;
        y = y || 0;
        width = width || 512;
        height = height || 342;
        res.width = width;
        res.height = height;
        width == +width && (width += "px");
        height == +height && (height += "px");
        res.coordsize = zoom * 1e3 + S + zoom * 1e3;
        res.coordorigin = "0 0";
        res.span = R._g.doc.createElement("span");
        res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;";
        c.appendChild(res.span);
        cs.cssText = R.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden", width, height);
        if (container == 1) {
            R._g.doc.body.appendChild(c);
            cs.left = x + "px";
            cs.top = y + "px";
            cs.position = "absolute";
        } else {
            if (container.firstChild) {
                container.insertBefore(c, container.firstChild);
            } else {
                container.appendChild(c);
            }
        }
        res.renderfix = function () {};
        return res;
    };
    R.prototype.clear = function () {
        R.eve("raphael.clear", this);
        this.canvas.innerHTML = E;
        this.span = R._g.doc.createElement("span");
        this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
        this.canvas.appendChild(this.span);
        this.bottom = this.top = null;
    };
    R.prototype.remove = function () {
        R.eve("raphael.remove", this);
        this.canvas.parentNode.removeChild(this.canvas);
        for (var i in this) {
            this[i] = typeof this[i] == "function" ? R._removedFactory(i) : null;
        }
        return true;
    };

    var setproto = R.st;
    for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) {
        setproto[method] = (function (methodname) {
            return function () {
                var arg = arguments;
                return this.forEach(function (el) {
                    el[methodname].apply(el, arg);
                });
            };
        })(method);
    }
}));

// ┌────────────────────────────────────────────────────────────────────┐ \\
// │ Raphaël @VERSION - JavaScript Vector Library                       │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Copyright © 2008-2012 Dmitry Baranovskiy (http://raphaeljs.com)    │ \\
// │ Copyright © 2008-2012 Sencha Labs (http://sencha.com)              │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Licensed under the MIT (http://raphaeljs.com/license.html) license.│ \\
// └────────────────────────────────────────────────────────────────────┘ \\

(function (glob, factory) {
    if (typeof define === "function" && define.amd) {
        define("raphael", ["raphael.core", "raphael.svg", "raphael.vml"], function(Raphael) {
            return (glob.Raphael = factory(Raphael));
        });
    } else if (typeof exports === "object") {
        var raphael = require("raphael.core");

        require("raphael.svg");
        require("raphael.vml");

        module.exports = factory(raphael);
    } else {
        glob.Raphael = factory(glob.Raphael);
    }
}(this, function (Raphael) {
    return Raphael.ninja();
}));;
$(function () {

  var complaintsCharts = window.complaintsCharts || {};
  var donutNS = complaintsCharts.donut;
  //if no donutNS, this library has loaded without the data embedded in the donut HTML module, so exit
  if (!donutNS) return;

  var donutXPos = debounce(function() {

    if (chart) {
      removeChart();
      setTimeout(function(){
        buildChart(paper, (raphaelElement.offsetWidth/2), cy, or, values, classes, 0, false);
      }, 500);
    }
  }, 500);


  var rolloverMs = 500;
  var timeMs = 250;
  var verticalPos = 60;
  var chart;
  var centre;
  var paper;  
  var lastClassHighlight;
  var ELEMENT_SEL = ".donut-chart";
  var raphaelElement;

  if (Raphael.type && !Raphael.isDisabled()) {
    raphaelElement = $(ELEMENT_SEL).get(0);
    paper = Raphael(raphaelElement, "100%", "100%");
  }

  $(window).on('resize', donutXPos);

  function addCss() {
    var css = donutNS.css;
    /*var html = '<style type="text/css">\n' + cssTxt + '\n</style>';

    $("head").append($(html));*/

    var head = document.head || document.getElementsByTagName('head')[0];
    var style = document.createElement('style');
    style.type = 'text/css';
    if (style.styleSheet){
      style.styleSheet.cssText = css;
    } else {
      style.appendChild(document.createTextNode(css));
    }
    head.appendChild(style);
  }

  // Returns a function, that, as long as it continues to be invoked, will not
  // be triggered. The function will be called after it stops being called for
  // N milliseconds.
  function debounce(func, wait) {
    var timeout;
    return function() {
      var context = this;
      var args = arguments;
      var later = function() {
        timeout = null;
        func.apply(context, args);
      };

      clearTimeout(timeout);
      timeout = setTimeout(later, wait);

    };
  };

  function addLegendTitle(paper){
    var title = paper.text(10, 150, locale.title);
    title.addClass("donut-title");
  }

  function addLegend(paper, labels, values, classes) {
    labelLookup = {};

    var dy = 17;
    var y = 170;

    var touch = isTouch();

    for (var i = 0; i < labels.length; i++) {
      var text = paper.text(40, y, values[i] + "% " + labels[i]);
      text.addClass("donut-legend-label");//.attr({"text-anchor": "start"});

      var tH = Math.max(dy, $(text.node).outerHeight());

      text.transform(["t", 0, tH / 2]);

      var className = classes[i];
      var keyBlock = paper.rect(10, y, 12, 12).addClass(className);

      labelLookup[className] = text;

      if (!touch) {
        var overF = showHighlight.bind(null, className);
        text.mouseover(overF).mouseout(clearHighlight);
      };

      y += tH + 5;
    }
  }

  function addCentre(cx, cy, centreR) {
    centre = paper.circle(cx, cy, centreR);
    $(centre.node).attr({
      "class" : "donut-centre"
    });
  }

  function segment(x, y, r, a1, a2) {
      var flag = (a2 - a1) > 180,
          clr = (a2 - a1) / 360;
      a1 = (a1 % 360) * Math.PI / 180;
      a2 = (a2 % 360) * Math.PI / 180;
      return {
          path: [["M", x, y], ["l", r * Math.cos(a1), r * Math.sin(a1)], ["A", r, r, 0, +flag, 1, x + r * Math.cos(a2), y + r * Math.sin(a2)], ["z"]]
      };
  };


  function clearHighlight() {
      if (lastClassHighlight == null) return;

      sectorLookup[lastClassHighlight].stop().animate({transform: ""}, rolloverMs, "elastic");
      labelLookup[lastClassHighlight].removeClass("donut-legend-label-highlit");

      lastClassHighlight = null;
  };

  function showHighlight(clazz) {
      clearHighlight();

      sectorLookup[clazz].stop().animate({transform: ["s",1.05,1.05]}, rolloverMs, "elastic");
      labelLookup[clazz].addClass("donut-legend-label-highlit");

      lastClassHighlight = clazz;
  };

  function pieChart(paper, cx, cy, r, values, classes, startDeg, ANIMATE) {
      var rad = Math.PI / 180;
      chart = paper.set();

      sectorLookup = {};
      var touch = isTouch();

      var angle = startDeg,
          total = 0,
          process = function (j) {
              var value = values[j],
                  angleplus = 360 * value / total,
                  delta = 10,
                  clazz = classes[j];

              var p;
              if (ANIMATE)
              {
                p = paper.path().toBack().attr({
                  segment: [cx, cy, r, angle, angle]
                }).animate({
                  segment: [cx, cy, r, angle, angle + angleplus],
                }, timeMs, "bounce").toBack();
              } else {
                p = paper.path().toBack().attr({
                  segment: [cx, cy, r, angle, angle + angleplus]
                })
              }

              p.setClass(clazz);
              
              sectorLookup[clazz] = p;

              if (!touch) {
                var overF = showHighlight.bind(null, clazz);
                p.mouseover(overF).mouseout(clearHighlight);
              }

              angle += angleplus;
              chart.push(p);
          };
      for (var i = 0, ii = values.length; i < ii; i++) {
        total += values[i];
      }
      for (i = 0; i < ii; i++) {

        if (ANIMATE)
        {
          (function(j){
            setTimeout(function(){
              requestAnimationFrame(process.bind(null, j));
            }, i * timeMs);
          })(i)
        } else {
          process(i);
        }
      }
      return chart;
  };

  function populateFigCaption() {
    var cEl = $(ELEMENT_SEL + " figcaption");
    $("h4", cEl).text(locale.title);

    //<tr><th role="row">Successful</th><td>62%</td></tr>
    var tEl = $("table", cEl);
    for (var i = 0; i < values.length; i++) {
      tEl.append('<tr><th role="row">' + locale[ids[i]] + '</th><td>' + values[i] + '%</td></tr>');
    }
  }

  function createPattern(paper) {
    //don't think we can use raphael to define a striped pattern
    //so we are manipulating the SVG directly

    var pattern = paper.make("pattern", {
      id          :"pattern-stripe",
      width       :"2",
      height      :"4",
      patternUnits:"userSpaceOnUse",
      patternTransform:"rotate(45)"
    });

    var patternRect = paper.make("rect", {
      width       :"1",
      height      :"4",
      transform   :"translate(0,0)",
      fill        :"white"
    });
    $(pattern.node).append(patternRect);

    var mask = paper.make("mask", {
      id          :"mask-stripe"
    });

    var maskRect = paper.make("rect", {
      x           :0,
      y           :0,
      width       :"100%",
      height      :"100%",
      fill        :"url(#pattern-stripe)"
    });
    $(mask.node).append(maskRect);

    $("defs", paper.canvas).append([pattern, mask]);
  }

  function isTouch() {
    return $("html").hasClass("touch");
  }

  function listenForTouchEvents() {
    if (isTouch())
    {
      $(ELEMENT_SEL).click( function(){
        clearHighlight();
      });
    }
  }

  var locale = donutNS.locale;
  var data = donutNS.data;

  var values = [], ids = [], labels = [], classes = [];
  var labelLookup, sectorLookup;

  if (!donutNS.noData) {
    for (var prop in data) {
      values.push(data[prop]);
      ids.push(prop);
      labels.push(locale[prop]);
      classes.push("donut-" + prop);
    }
  }


  var ELEMENT_SEL = ".donut-chart";

  var ANIMATE = true;
  var cx = (raphaelElement.offsetWidth/2);
  var cy = verticalPos;
  var or = 55;
  var ir = 25;

  function drawFullCircle(className) {
    paper.circle(cx, cy, or).toBack().addClass(className);
  }

  function removeChart() {
    chart.remove();
    centre.remove();
  }

  function buildChart(paper, cx, cy, r, values, classes, startDeg, ANIMATE) {
    if (fullIndex != -1) {
      var fullClass = classes[fullIndex];
      drawFullCircle(fullClass);
    } 
    else {
      var createPieChart = pieChart.bind(null, paper, cx, cy, or, values, classes, 0);
      if (ANIMATE) {
        setTimeout(requestAnimationFrame.bind(null, createPieChart), 750);
      } 
      else {
        createPieChart();
      }
      listenForTouchEvents();
    }
    addCentre(cx, cy, ir);
  }

  populateFigCaption();

  if (Raphael.type && !Raphael.isDisabled())
  {
    paper.customAttributes.segment = segment;

    if (!donutNS.noData)
    {
      addCss();
      createPattern(paper);

      var fullIndex = values.indexOf(100);
      buildChart(paper, cx, cy, or, values, classes, 0, true);

      addLegendTitle(paper);
      addLegend(paper, labels, values, classes);
    } 
    else {
      drawFullCircle("donut-nodata");
      addLegendTitle(paper);
      var text = paper.text(10, 172, locale.nodata);
      text.addClass("donut-nodata-label");
      addCentre(cx, cy, ir);
    }
    

  } else {
    $(ELEMENT_SEL).addClass("chart-fallback");
  }

});
;
$(function(){

  var complaintsCharts = window.complaintsCharts || {};
  var historyNS = complaintsCharts.history;
  //if no historyNS, this library has loaded without the data embedded in the history HTML module, so exit
  if (!historyNS) return;

  var locale = historyNS.locale;
  var data = historyNS.data;
  var iconHex = historyNS.smileyIconCharHex;
  var iconOffset = historyNS.smileyIconYOffset;
  // create an array to collect the plotted lines - they will be reused for rollovers
  var rolloverArray = [];

  var X_AXIS_LABEL_1_BASELINE = 270;
  var X_AXIS_LABEL_2_BASELINE = 283;
  var X_AXIS_LABEL_3_BASELINE = 296;
  var LAST_X_STEP_TOP = 5;
  var LAST_X_STEP_BOTTOM = 255;
  var fill;

  data.sort(function(a, b) {
    //sort data into ascending order just to be sure
    var aNums = getDateNums(a.date);
    var bNums = getDateNums(b.date);

    return new Date(aNums.year, aNums.month) - new Date(bNums.year, bNums.month);
  });

  function getDateNums(date) {
    var dateArr = date.split("/");
    var month = parseInt(dateArr[0]) - 1;
    var year = dateArr[1];
    return {
      month: month,
      year: year
    }
  }

  function rolloverFill(index) {
    
    if (fill) {
      fill.remove();
    }

    // if index is 0 then bail out as there is no fill area
    if (index < 1) {
      return;
    }

    index = index + 1;

    // slice the array to get the correct area underneath
    var chunk = rolloverArray.slice(0, index);

    var path = 'M ' + chunk[0][0] + ' ' + Y_AXIS_START + ' L ' + chunk[0][0] + ' ' + chunk[0][1] + ' ';
    for (var i = 0, len = chunk.length; i < len; i++) {
      var theY = chunk[i][2] === -1 ? Y_AXIS_START : chunk[i][1];
      path += ' L ';
      path += chunk[i][0] + ' ' + theY;
    }
    path += ' L ' + chunk[chunk.length - 1][0] + ' ' + Y_AXIS_START + ',' + X_AXIS_START + ' ' + Y_AXIS_START + ' Z';
    fill = paper.path('[' + path + ']').attr({'fill': fillColour, 'fill-opacity': fillOpacity,'stroke-width': 0});
    fill.toBack();
  }

  function makeRolloverTip() {
    triangle = paper.path(["M", 0, 0, "L", 11, 11, "L", -11, 11, "L", 0, 0, "z"]);
    triangle.setClass("history-rollover-tip");
  }

  function populateTableHeader() {
    var hEl = $(ELEMENT_SEL + " thead tr");
    hEl.append(
      "<th>" + locale.tableDate + "</th>" +
      "<th>" + locale.tableScore + "</th>" +
      "<th>" + locale.tableTotal + "</th>");
  }


  function populateHTML() {
    monthMenu.change(function(){
      //option value is the index of the relevant data in the list
      selectedDatumIndex = parseInt(monthMenu.val());
      var datum = data[selectedDatumIndex];
      populatePanel(datum);

      removePointHighlight();

      //we are plotting the last 12 data points
      //there may be more than 12 data items
      //the x axis extends to 13 incremenets but there is no plot at the last increment

      //we need the x inc index for a given data point
      //inc 12 is the last point => data.length - 1

      var i = (X_STEP_COUNT - 1) - ((data.length - 1) - selectedDatumIndex);

      var x = X_AXIS_START + (i * xStep);
      var score = parseFloat(datum.score) / TOP_SCORE;
      var scoreY = Y_AXIS_START - (score * yRange);
      addPointHighlight(x, scoreY);
      rolloverFill(selectedDatumIndex - 4);
    });

    populateTableHeader();

    $(".history-rollover-complaints-label").text(locale.count);

    var tEl = $(ELEMENT_SEL + " tbody");

    var firstYear = - 1;
    var firstIndex = data.length - 1;
    var lastIndex = data.length - X_STEP_COUNT + 1;

    for (var i = firstIndex; i >= lastIndex; i--) {
      var datum = data[i];
      if (!datum || isNaN(datum.score)) continue;

      var dateNums = getDateNums(datum.date);
      var year = dateNums.year;
      if (i == firstIndex) firstYear = year;

      var text = i == firstIndex ? locale.today + " - " : "";
      text += locale.months[dateNums.month];
      if (year != firstYear) text += " '" + year.substr(2);

      monthMenu
         .append($("<option></option>")
         .attr("value",i)
         .text(text));

      tEl.append("<tr>" +
        "<td>" + text + "</td>" +
        "<td>" + datum.score + "</td>" +
        "<td>" + datum.total + "</td>" +
      "</tr>");
    }
    if ($.uniform) $.uniform.update(monthMenu);
  }

  function addPointHighlight(x, y) {
    var rW = 5;
    paper.circle(x, y, rW).setClass(POINT_HIGHLIGHT_CLASS);
  }

  function removePointHighlight() {
    $("." + POINT_HIGHLIGHT_CLASS).remove();
  }

  function clearTimeouts() {
    while(timeouts.length > 0)
    {
      clearTimeout(timeouts.pop());
    }
  }

  function doResize() {
    clearTimeouts();
    activeIconIndex = -1;

    var w = $(paper.canvas).width();
    xAxisEnd = Math.min(MAX_WIDTH, w - 10);
    xStep = (xAxisEnd - X_AXIS_START) / X_STEP_COUNT;

    isWide = w > COMPACT_MAX_WIDTH;
    if (!isWide)
    {
      //populate panel in case it was changed on rollover
      populatePanel(data[selectedDatumIndex]);
    }
    dataEl.toggleClass("wide", isWide);
    dataEl.css("opacity", isWide ? 0 : 1);

    paper.clear();

    //set up layer groups
    iconsBgGroup = paper.group().setClass("history-icon-bgs");
    yStepGroup = paper.group().setClass("history-y-steps");
    scoreLabelGroup = paper.group().setClass("history-score-labels");
    xStepGroup = paper.group().setClass("history-x-steps");
    monthGroup = paper.group().setClass("history-month-labels");
    yearGroup = paper.group().setClass( "history-year-labels");
    linesGroup = paper.group().setClass("history-score-lines");
    iconsGroup = paper.group().setClass("history-icons");
    rolloverFillGroup = paper.group().setClass("history-subfill");
    makeRolloverTip();
    drawAlongX();
    drawAlongY();

    $("#canvas-width").text("width:" + w);
  }

  function drawAlongX() {

    var currentYear = 0;
    var lastScoreX = -1, lastScoreY = -1;
    var imgWHalf = ICON_WIDTH / 2;
    var wasEmpty = true;
    rolloverArray = [];

    var isTouch = $("html").hasClass("touch");

    if (fill) {
      fill.remove();
    }

    function plotLine(x1, y1, x2, y2, lineClass) {
      var line = paper.path(["M", x1, y1, "L", x2, y2, "z"]);
      if (lineClass) {
        line.setClass(lineClass);
      };
      line.addToGroup(linesGroup);
    }

    function plotMarker(datum, x, y, num) {
      //draw icon bg (since icons are transparent)
      var bg = paper.circle(x, y, imgWHalf + 10).attr('opacity', 0);
      bg.addToGroup(iconsBgGroup);

      var over = iconBgMouseOver.bind(0,bg, datum, num - 1);

      bg.mouseover(over).mouseout(iconBgMouseOut);

      if(isTouch) bg.touchstart( function(e){
        over();
        e.stopPropagation();
      })

      paper.circle(x, y, imgWHalf).addToGroup(iconsGroup);
      var aEl = paper.make("a", {"xlink:href": "#"});
      aEl.addToGroup(iconsGroup);

      var iconNum = getIconNum(datum.score);
      var iconClass = "smiley-rate-" + iconNum;
      var iconEl = paper.text(x, y + iconOffset, iconHex[iconNum - 1]).addClass(iconClass);
      iconEl.removeAttr("font-family");
      iconEl.removeAttr("font-size");
      iconEl.removeAttr("fill");
      iconEl.removeAttr("stroke");
      $(iconEl.node.firstChild).removeAttr("dy");

      $(aEl.node)
        .attr({tabindex: num })
        .focus(over)
        .blur(iconBgMouseOut);

      aEl.node.appendChild(iconEl.node);
    }

    //e854

    //miss the first and last verticals lines
    for (i = 1; i < X_STEP_COUNT; i++) {

      var x = X_AXIS_START + (i * xStep);
      if (isWide)
      {
        var l;
        if (i == X_STEP_COUNT - 1)
        {
          //draw last bold x step
          l = paper.path([
            "M", x, LAST_X_STEP_TOP,
            "L", x, LAST_X_STEP_BOTTOM,
            "z"
          ]);
          l.setClass("history-x-last-line");
        } else {
          //draw vertical x steps
          l = paper.path([
            "M", x, Y_AXIS_START,
            "L", x, Y_AXIS_END,
            "z"
          ]);
        }
        l.addToGroup(xStepGroup);
      }

      //get the date data from the embedded JSON
      var datumIndex = i + (data.length - X_STEP_COUNT);
      var datum = data[datumIndex];
      var dateIndicies = getDateNums(datum.date);
      var year = dateIndicies.year;
      var newYear = year != currentYear;
      var lastStep = (i == X_STEP_COUNT - 1);

      if (isWide || newYear || lastStep)
      {
        //write the month if it is wide format, or if compact format and year has changed, or if last entry
        paper.text(x, X_AXIS_LABEL_1_BASELINE, locale.monthsAbbr[dateIndicies.month]).clearStyle().addToGroup(monthGroup);
      }

      if (newYear)
      {
        currentYear = year;
        //write the year if it has changed
        paper.text(x, X_AXIS_LABEL_2_BASELINE, year).clearStyle().addToGroup(yearGroup);
      }

      //write the TODAY label if the last datum in the list
      if (lastStep)
      {
        yLabel = locale.today;
        //if we have already written the year in this iteration, then write the TODAY label further down
        var todayLabel = paper.text(x, newYear ? X_AXIS_LABEL_3_BASELINE : X_AXIS_LABEL_2_BASELINE, locale.today).setClass("history-today-label").clearStyle();
        todayLabel.addToGroup(yearGroup);
      }

      var nowEmpty = datum.score == undefined;
      var lineClass = null;
      if (i > 1 && !nowEmpty && wasEmpty)
      {
        //part of our graph is missing data. draw a dotted line from y = 0
        lastScoreY = Y_AXIS_START;
        lineClass = "history-score-line-dashed";

        //draw background and its label for missing data
        var rectW = (i - 1) * xStep;
        var mBg = paper.rect(X_AXIS_START, Y_AXIS_END, rectW, yRange).toBack().setClass("history-missing-data-bg");
        var mLabel = paper.text(X_AXIS_START + rectW/2, Y_AXIS_END + yRange/2, locale.missing).setClass("history-missing-label");
        if ($(mLabel.node).width() > rectW)
        {
          mLabel.remove();
        }

      }

      var score = !nowEmpty ? parseFloat(datum.score) / TOP_SCORE : -1;
      var scoreY = Y_AXIS_START - (score * yRange);

      // stuff the plotted lines into an array
      rolloverArray.push([x, scoreY, score]);

      if (lastScoreY > -1)
      {
        //get a bound function which plots the line
        var f = plotLine.bind(null, lastScoreX, lastScoreY, x, scoreY, lineClass);

        if (animate)
        {
          //pass bound function to an anonymous function which will execute it after a timeout
          (function(g) {
            timeouts.push(setTimeout(requestAnimationFrame.bind(null, g), i * 100));
          })(f);
        } else {
          //execute bound function now
          f();
        }
      }

      if (isWide)
      {
        if (datum.score != undefined)
        {
          //get a bound function which plots the marker
          var f = plotMarker.bind(null, datum, x, scoreY, i);

          if (animate)
          {
            (function(g) {
              timeouts.push(setTimeout(requestAnimationFrame.bind(null,g), i * 100));
            })(f);
          } else {
            //execute bound function now
            f();
          }
        }

      } else if (selectedDatumIndex == datumIndex) {
        //if compact
        addPointHighlight(x, scoreY);
      }

      lastScoreX = x;
      lastScoreY = scoreY;
      wasEmpty = nowEmpty;
    }

    triangle.toFront();

    animate = false;
  }

  function drawAlongY() {
    var Y_AXIS_LABEL_CENTER = 10;//-50

    var yStep = yRange / Y_STEP_COUNT;

    //draw horizontal y steps
    for (var i = 0; i <= Y_STEP_COUNT; i++) {
      var y = Y_AXIS_START - (i * yStep);
      var l = paper.path([
        "M", X_AXIS_START, y,
        "L", xAxisEnd, y,
        "z"
      ]);
      l.addToGroup(yStepGroup);
      paper.text(Y_AXIS_LABEL_CENTER, y, (TOP_SCORE / Y_STEP_COUNT) * i).clearStyle().addToGroup(scoreLabelGroup);
    }
  }

  function listenForTabPresses() {
    $("body").keypress(function(e){
      if (e.key == "Tab")
      {
        var current = Math.max(activeIconIndex, 0);
        current += ((e.shiftKey) ? - 1 : 1);
        var links = $(".smiley-rates a");
        var len = links.length;
        if (current < 0) current = len - 1;
        if (current >= len) current = 0;

        links.eq(current).focus();
        e.preventDefault();
      }
    });
  }

  function listenForTouchEvents() {
    if ($("html").hasClass("touch")) $(ELEMENT_SEL).click( function(){
      if (isWide) iconBgMouseOver();
    });
  }

  function populatePanel(datum) {
    var dateIndicies = getDateNums(datum.date);
    $("h2", dataEl).text(locale.monthsAbbr[dateIndicies.month] + " " + dateIndicies.year);
    $(".history-rollover-score span").text(datum.score);
    $(".history-rollover-score i")
      .removeClass(ALL_ICON_CLASSES)
      .addClass("smiley-rate-" + getIconNum(datum.score));
    $(".history-rollover-complaints-count").text(datum.total);
  }

  function getIconNum(score) {
    return Math.max(1,Math.ceil(score / 2));
  }

  function iconBgMouseOver(bg, datum, index, e, x, y) {

      activeIconIndex = index;

      populatePanel(datum);

      var paperEl = $(paper.canvas);
      var offset = paperEl.offset();

      var rw = dataEl.outerWidth();

      var elTop;
      var invert = (datum.score < 5);

      var att = bg.attrs;
      if (invert)
      {
        elTop = att.cy - (60 + dataEl.height());
      } else {
        elTop = att.cy + 30;
      }

      dataEl.offset({
        left: Math.min(offset.left + paperEl.width() - rw, Math.max(offset.left, offset.left + (att.cx - rw / 2))),
        top: offset.top + elTop
      });

      if (invert)
      {
        triangle.transform(["t", att.cx, att.cy - 31, "s", 1, -1]);
      } else {
        triangle.transform(["t", att.cx, att.cy + 20]);
      }

      $([dataEl[0], triangle.node]).stop().animate({opacity: 1}, 500);

      rolloverFill(index);
  }

  function iconBgMouseOut() {
    $([dataEl[0], triangle.node]).stop().animate({opacity: 0}, 100);
    
    if (fill) {
      fill.remove();
    }    
  }

  var xStepGroup, monthGroup, yearGroup, linesGroup, iconsGroup, iconsBgGroup, yStepGroup, scoreLabelGroup;

  var timeouts = [];

  var activeIconIndex;

  var TOP_SCORE = 10;
  var X_AXIS_START = 30
  var X_STEP_COUNT = 13;
  var Y_AXIS_START = 245, Y_AXIS_END = 35, Y_STEP_COUNT = 5;
  var ICON_WIDTH = 24;

  var POINT_HIGHLIGHT_CLASS = "history-point-highlight";
  var ALL_ICON_CLASSES = "smiley-rate-1 smiley-rate-2 smiley-rate-3 smiley-rate-4 smiley-rate-5";

  var MAX_WIDTH = 1000;
  var COMPACT_MAX_WIDTH = 450;

  var ELEMENT_SEL = ".history-graph";
  var fillColour = $(ELEMENT_SEL).data('chart-fill') || 'red';
  var fillOpacity = $(ELEMENT_SEL).data('chart-opacity') || 0.2;  
  var animate = true;

  var yRange = Y_AXIS_START - Y_AXIS_END;
  var xAxisEnd, xStep;
  var xOffset, yOffset;
  var triangle;
  var isWide;
  var selectedDatumIndex = data.length - 1;

  var monthMenu = $(".history-date-list select");
  var dataEl = $(".history-rollover");

  populateHTML();

  if (Raphael.type && !Raphael.isDisabled())
  {
    var paper = Raphael($(ELEMENT_SEL).get(0), 100, 100);
    $(window).resize(function(){
      doResize();
    });
    paper.canvas.setAttribute("focusable", "true");
    doResize();

    listenForTabPresses();
    listenForTouchEvents();
  } else {
    $(ELEMENT_SEL).addClass("chart-fallback");
  }


});
;
(function(){

  function deleteValue(list, value) {
    while(true) {
      var idx = list.indexOf(value);
      if (idx == -1) return;
      list.splice(idx, 1);
    }
  }

  window.requestAnimationFrame = (function(){
    //we are calling this after a setTimeout, so need for a setTimeout in here too
    return  window.requestAnimationFrame       ||
            function( callback ){
              callback();
            };
  })();

  Raphael.isDisabled = function() {
    var bodyClass = $("html")[0].getAttribute("class");
    return bodyClass.indexOf("no-svg") != -1;
  }

  Raphael.fn.make = function(tagName, attr) {
    //Create an SVG element. We need to specify the namespace for this to work
    var el = document.createElementNS("http://www.w3.org/2000/svg", tagName);
    if (attr)
    {
      for (prop in attr) el.setAttribute(prop, attr[prop]);
    }
    return new Raphael.el.constructor(el, this);
  }

  Raphael.fn.group = function() {
    var g = this.make("g");
    this.canvas.appendChild(g.node);
    return g
  }

  Raphael.el.setClass = function(className) {
    this.clearStyle();
    this.node.setAttribute("class",className );
    return this;
  }

  Raphael.el.clearStyle = function() {
    this.removeAttr("style");
    return this;
  }

  Raphael.el.removeAttr = function(name) {
    this.node.removeAttribute(name);
    return this;
  }

  Raphael.el.addToGroup = function(g) {
    g.node.appendChild(this.node);
    return this;
  }

  Raphael.el.getClassList = function() {
    var current = this.node.getAttribute("class") || "";
    return current.split(" ");
  }

  Raphael.el.addClass = function(className) {
    var current = this.getClassList();
    if (current.indexOf(className) == -1) current.push(className);

    return this.setClass(current.join(" "));
  }

  Raphael.el.removeClass = function(className) {
    var current = this.getClassList();
    deleteValue(current, className);

    return this.setClass(current.join(" "));
  }

})();
;
/*

Tooltipster 2.1 | 2/12/13
A rockin' custom tooltip jQuery plugin

Developed by: Caleb Jacob - calebjacob.com
Copyright (C) 2013 Caleb Jacob

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 ($, window, document, undefined) {

    var pluginName = "tooltipster",
		defaults = {
		    animation: 'fade',
		    arrow: true,
		    arrowColor: '',
		    content: '',
		    delay: 200,
		    fixedWidth: 0,
		    maxWidth: 0,
		    functionBefore: function (origin, continueTooltip) {
		        continueTooltip();
		    },
		    functionReady: function (origin, tooltip) { },
		    functionAfter: function (origin) { },
		    icon: '(?)',
		    iconDesktop: false,
		    iconTouch: false,
		    iconTheme: '.tooltipster-icon',
		    interactive: false,
		    interactiveTolerance: 350,
		    offsetX: 0,
		    offsetY: 0,
		    onlyOne: true,
		    position: 'top',
		    speed: 350,
		    timer: 0,
		    theme: '.tooltipster-default',
		    touchDevices: true,
		    trigger: 'hover',
		    updateAnimation: true
		};

    function Plugin(element, options) {
        this.element = element;

        this.options = $.extend({}, defaults, options);

        this._defaults = defaults;
        this._name = pluginName;

        this.init();
    }

    // we'll use this to detect for mobile devices
    function is_touch_device() {
        return !!('ontouchstart' in window);
    }

    // detecting support for CSS transitions
    function supportsTransitions() {
        var b = document.body || document.documentElement;
        var s = b.style;
        var p = 'transition';
        if (typeof s[p] == 'string') { return true; }

        v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms'],
	    p = p.charAt(0).toUpperCase() + p.substr(1);
        for (var i = 0; i < v.length; i++) {
            if (typeof s[v[i] + p] == 'string') { return true; }
        }
        return false;
    }
    var transitionSupport = true;
    if (!supportsTransitions()) {
        transitionSupport = false;
    }

    Plugin.prototype = {

        init: function () {
            var $this = $(this.element);
            var object = this;
            var run = true;

            // if this is a touch device and touch devices are disabled, disable the plugin
            if ((object.options.touchDevices == false) && (is_touch_device())) {
                run = false;
            }

            // if IE7 or lower, disable the plugin
            if (document.all && !document.querySelector) {
                run = false;
            }

            if (run == true) {

                // detect if we're changing the tooltip origin to an icon
                if ((this.options.iconDesktop == true) && (!is_touch_device()) || ((this.options.iconTouch == true) && (is_touch_device()))) {
                    var transferContent = $this.attr('title');
                    $this.removeAttr('title');
                    var theme = object.options.iconTheme;
                    var icon = $('<span class="' + theme.replace('.', '') + '" title="' + transferContent + '">' + this.options.icon + '</span>');
                    icon.insertAfter($this);
                    $this.data('tooltipsterIcon', icon);
                    $this = icon;
                }

                // first, strip the title off of the element and set it as a data attribute to prevent the default tooltips from popping up
                var tooltipsterContent = $.trim(object.options.content).length > 0 ? object.options.content : $this.attr('title');
                $this.data('tooltipsterContent', tooltipsterContent);
                $this.removeAttr('title');

                // if this is a touch device, add some touch events to launch the tooltip
                if ((this.options.touchDevices == true) && (is_touch_device())) {
                    $this.bind('touchstart', function (element, options) {
                        object.showTooltip();
                    });
                }

                    // if this is a desktop, deal with adding regular mouse events
                else {

                    // if hover events are set to show and hide the tooltip, attach those events respectively
                    if (this.options.trigger == 'hover') {
                        $this.mouseenter(function () {
                            object.showTooltip();
                        });

                        // if this is an interactive tooltip, delay getting rid of the tooltip right away so you have a chance to hover on the tooltip
                        if (this.options.interactive == true) {
                            $this.mouseleave(function () {
                                var tooltipster = $this.data('tooltipster');
                                var keepAlive = false;

                                if ((tooltipster !== undefined) && (tooltipster !== '')) {
                                    tooltipster.mouseenter(function () {
                                        keepAlive = true;
                                    });
                                    tooltipster.mouseleave(function () {
                                        keepAlive = false;
                                    });

                                    var tolerance = setTimeout(function () {
                                        if (keepAlive == true) {
                                            tooltipster.mouseleave(function () {
                                                object.hideTooltip();
                                            });
                                        }
                                        else {
                                            object.hideTooltip();
                                        }
                                    }, object.options.interactiveTolerance);
                                }
                                else {
                                    object.hideTooltip();
                                }
                            });
                        }

                            // if this is a dumb tooltip, just get rid of it on mouseleave
                        else {
                            $this.mouseleave(function () {
                                object.hideTooltip();
                            });
                        }
                    }

                    // if click events are set to show and hide the tooltip, attach those events respectively
                    if (this.options.trigger == 'click') {
                        $this.click.tooltipster(function () {
                            if (($this.data('tooltipster') == '') || ($this.data('tooltipster') == undefined)) {
                                object.showTooltip();
                            }
                            else {
                                object.hideTooltip();
                            }
                        });
                    }
                }
            }
        },

        showTooltip: function (options) {

            var $this = $(this.element);
            var object = this;

            // detect if we're actually dealing with an icon or the origin itself
            if ($this.data('tooltipsterIcon') !== undefined) {
                $this = $this.data('tooltipsterIcon');
            }

            // continue if this tooltip is enabled
            if (!$this.hasClass('tooltipster-disable')) {

                // if we only want one tooltip open at a time, close all tooltips currently open
                if (($('.tooltipster-base').not('.tooltipster-dying').length > 0) && (object.options.onlyOne == true)) {
                    $('.tooltipster-base').not('.tooltipster-dying').not($this.data('tooltipster')).each(function () {
                        $(this).addClass('tooltipster-kill');
                        var origin = $(this).data('origin');
                        origin.data('plugin_tooltipster').hideTooltip();
                    });
                }

                // delay the showing of the tooltip according to the delay time
                $this.clearQueue().delay(object.options.delay).queue(function () {

                    // call our custom function before continuing
                    object.options.functionBefore($this, function () {

                        // if this origin already has its tooltip open, keep it open and do nothing else
                        if (($this.data('tooltipster') !== undefined) && ($this.data('tooltipster') !== '')) {
                            var tooltipster = $this.data('tooltipster');

                            if (!tooltipster.hasClass('tooltipster-kill')) {

                                var animation = 'tooltipster-' + object.options.animation;

                                tooltipster.removeClass('tooltipster-dying');

                                if (transitionSupport == true) {
                                    tooltipster.clearQueue().addClass(animation + '-show');
                                }

                                // if we have a timer set, we need to reset it
                                if (object.options.timer > 0) {
                                    var timer = tooltipster.data('tooltipsterTimer');
                                    clearTimeout(timer);

                                    timer = setTimeout(function () {
                                        tooltipster.data('tooltipsterTimer', undefined);
                                        object.hideTooltip();
                                    }, object.options.timer);

                                    tooltipster.data('tooltipsterTimer', timer);
                                }

                                // if this is a touch device, hide the tooltip on body touch
                                if ((object.options.touchDevices == true) && (is_touch_device())) {
                                    $('body').bind('touchstart', function (event) {
                                        if (object.options.interactive == true) {
                                            var touchTarget = $(event.target);
                                            var closeTooltip = true;

                                            touchTarget.parents().each(function () {
                                                if ($(this).hasClass('tooltipster-base')) {
                                                    closeTooltip = false;
                                                }
                                            });

                                            if (closeTooltip == true) {
                                                object.hideTooltip();
                                                $('body').unbind('touchstart');
                                            }
                                        }
                                        else {
                                            object.hideTooltip();
                                            $('body').unbind('touchstart');
                                        }
                                    });
                                }
                            }
                        }

                            // if the tooltip isn't already open, open that sucker up!
                        else {
                            // disable horizontal scrollbar to keep overflowing tooltips from jacking with it
                            $('body').css('overflow-x', 'hidden');

                            // get the content for the tooltip
                            var content = $this.data('tooltipsterContent');

                            // get some other settings related to building the tooltip
                            var theme = object.options.theme;
                            var themeClass = theme.replace('.', '');
                            var animation = 'tooltipster-' + object.options.animation;
                            var animationSpeed = '-webkit-transition-duration: ' + object.options.speed + 'ms; -webkit-animation-duration: ' + object.options.speed + 'ms; -moz-transition-duration: ' + object.options.speed + 'ms; -moz-animation-duration: ' + object.options.speed + 'ms; -o-transition-duration: ' + object.options.speed + 'ms; -o-animation-duration: ' + object.options.speed + 'ms; -ms-transition-duration: ' + object.options.speed + 'ms; -ms-animation-duration: ' + object.options.speed + 'ms; transition-duration: ' + object.options.speed + 'ms; animation-duration: ' + object.options.speed + 'ms;';
                            var fixedWidth = object.options.fixedWidth > 0 ? 'width:' + object.options.fixedWidth + 'px;' : '';
                            var maxWidth = object.options.maxWidth > 0 ? 'max-width:' + object.options.maxWidth + 'px;' : '';
                            var pointerEvents = object.options.interactive == true ? 'pointer-events: auto;' : '';

                            // build the base of our tooltip
                            var tooltipster = $('<div class="tooltipster-base ' + themeClass + ' ' + animation + '" style="' + fixedWidth + ' ' + maxWidth + ' ' + pointerEvents + ' ' + animationSpeed + '"><div class="tooltipster-content">' + content + '</div></div>');
                            tooltipster.appendTo('body');

                            // attach the tooltip to its origin
                            $this.data('tooltipster', tooltipster);
                            tooltipster.data('origin', $this);

                            // do all the crazy calculations and positioning
                            object.positionTooltip();

                            // call our custom callback since the content of the tooltip is now part of the DOM
                            object.options.functionReady($this, tooltipster);

                            // animate in the tooltip
                            if (transitionSupport == true) {
                                tooltipster.addClass(animation + '-show');
                            }
                            else {
                                tooltipster.css('display', 'none').removeClass(animation).fadeIn(object.options.speed);
                            }

                            // check to see if our tooltip content changes or its origin is removed while the tooltip is alive
                            var currentTooltipContent = content;
                            var contentUpdateChecker = setInterval(function () {
                                var newTooltipContent = $this.data('tooltipsterContent');

                                // if this tooltip's origin is removed, remove the tooltip
                                if ($('body').find($this).length == 0) {
                                    tooltipster.addClass('tooltipster-dying');
                                    object.hideTooltip();
                                }

                                    // if the content changed for the tooltip, update it											
                                else if ((currentTooltipContent !== newTooltipContent) && (newTooltipContent !== '')) {
                                    currentTooltipContent = newTooltipContent;

                                    // set the new content in the tooltip
                                    tooltipster.find('.tooltipster-content').html(newTooltipContent);

                                    // if we want to play a little animation showing the content changed
                                    if (object.options.updateAnimation == true) {
                                        if (supportsTransitions()) {
                                            tooltipster.css({
                                                'width': '',
                                                '-webkit-transition': 'all ' + object.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms',
                                                '-moz-transition': 'all ' + object.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms',
                                                '-o-transition': 'all ' + object.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms',
                                                '-ms-transition': 'all ' + object.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms',
                                                'transition': 'all ' + object.options.speed + 'ms, width 0ms, height 0ms, left 0ms, top 0ms'
                                            }).addClass('tooltipster-content-changing');

                                            // reset the CSS transitions and finish the change animation
                                            setTimeout(function () {
                                                tooltipster.removeClass('tooltipster-content-changing');
                                                // after the changing animation has completed, reset the CSS transitions
                                                setTimeout(function () {
                                                    tooltipster.css({
                                                        '-webkit-transition': object.options.speed + 'ms',
                                                        '-moz-transition': object.options.speed + 'ms',
                                                        '-o-transition': object.options.speed + 'ms',
                                                        '-ms-transition': object.options.speed + 'ms',
                                                        'transition': object.options.speed + 'ms'
                                                    });
                                                }, object.options.speed);
                                            }, object.options.speed);
                                        }
                                        else {
                                            tooltipster.fadeTo(object.options.speed, 0.5, function () {
                                                tooltipster.fadeTo(object.options.speed, 1);
                                            });
                                        }
                                    }

                                    // reposition and resize the tooltip
                                    object.positionTooltip();
                                }

                                // if the tooltip is closed or origin is removed, clear this interval
                                if (($('body').find(tooltipster).length == 0) || ($('body').find($this).length == 0)) {
                                    clearInterval(contentUpdateChecker);
                                }
                            }, 200);

                            // if we have a timer set, let the countdown begin!
                            if (object.options.timer > 0) {
                                var timer = setTimeout(function () {
                                    tooltipster.data('tooltipsterTimer', undefined);
                                    object.hideTooltip();
                                }, object.options.timer + object.options.speed);

                                tooltipster.data('tooltipsterTimer', timer);
                            }

                            // if this is a touch device, hide the tooltip on body touch
                            if ((object.options.touchDevices == true) && (is_touch_device())) {
                                $('body').bind('touchstart', function (event) {
                                    if (object.options.interactive == true) {

                                        var touchTarget = $(event.target);
                                        var closeTooltip = true;

                                        touchTarget.parents().each(function () {
                                            if ($(this).hasClass('tooltipster-base')) {
                                                closeTooltip = false;
                                            }
                                        });

                                        if (closeTooltip == true) {
                                            object.hideTooltip();
                                            $('body').unbind('touchstart');
                                        }
                                    }
                                    else {
                                        object.hideTooltip();
                                        $('body').unbind('touchstart');
                                    }
                                });
                            }

                            // if this is an interactive tooltip activated by a click, close the tooltip when you hover off the tooltip
                            tooltipster.mouseleave(function () {
                                object.hideTooltip();
                            });
                        }
                    });

                    $this.dequeue();
                });
            }
        },

        hideTooltip: function (options) {

            var $this = $(this.element);
            var object = this;

            // detect if we're actually dealing with an icon or the origin itself
            if ($this.data('tooltipsterIcon') !== undefined) {
                $this = $this.data('tooltipsterIcon');
            }

            var tooltipster = $this.data('tooltipster');

            // if the origin has been removed, find all tooltips assigned to death
            if (tooltipster == undefined) {
                tooltipster = $('.tooltipster-dying');
            }

            // clear any possible queues handling delays and such
            $this.clearQueue();

            if ((tooltipster !== undefined) && (tooltipster !== '')) {

                // detect if we need to clear a timer
                var timer = tooltipster.data('tooltipsterTimer');
                if (timer !== undefined) {
                    clearTimeout(timer);
                }

                var animation = 'tooltipster-' + object.options.animation;

                if (transitionSupport == true) {
                    tooltipster.clearQueue().removeClass(animation + '-show').addClass('tooltipster-dying').delay(object.options.speed).queue(function () {
                        tooltipster.remove();
                        $this.data('tooltipster', '');
                        $('body').css('verflow-x', '');

                        // finally, call our custom callback function
                        object.options.functionAfter($this);
                    });
                }
                else {
                    tooltipster.clearQueue().addClass('tooltipster-dying').fadeOut(object.options.speed, function () {
                        tooltipster.remove();
                        $this.data('tooltipster', '');
                        $('body').css('verflow-x', '');

                        // finally, call our custom callback function
                        object.options.functionAfter($this);
                    });
                }
            }
        },

        positionTooltip: function (options) {

            var $this = $(this.element);
            var object = this;

            // detect if we're actually dealing with an icon or the origin itself
            if ($this.data('tooltipsterIcon') !== undefined) {
                $this = $this.data('tooltipsterIcon');
            }

            if (($this.data('tooltipster') !== undefined) && ($this.data('tooltipster') !== '')) {

                // find tooltipster and reset its width
                var tooltipster = $this.data('tooltipster');
                tooltipster.css('width', '');

                // find variables to determine placement
                var windowWidth = $(window).width();
                var containerWidth = $this.outerWidth(false);
                var containerHeight = $this.outerHeight(false);
                var tooltipWidth = tooltipster.outerWidth(false);
                var tooltipInnerWidth = tooltipster.innerWidth() + 1; // this +1 stops FireFox from sometimes forcing an additional text line
                var tooltipHeight = tooltipster.outerHeight(false);
                var offset = $this.offset();
                var offsetTop = offset.top;
                var offsetLeft = offset.left;
                var resetPosition = undefined;

                // if this is an <area> tag inside a <map>, all hell breaks loose. Recaclulate all the measurements based on coordinates
                if ($this.is('area')) {
                    var areaShape = $this.attr('shape');
                    var mapName = $this.parent().attr('name');
                    var map = $('img[usemap="#' + mapName + '"]');
                    var mapOffsetLeft = map.offset().left;
                    var mapOffsetTop = map.offset().top;
                    var areaMeasurements = $this.attr('coords') !== undefined ? $this.attr('coords').split(',') : undefined;

                    if (areaShape == 'circle') {
                        var areaLeft = parseInt(areaMeasurements[0]);
                        var areaTop = parseInt(areaMeasurements[1]);
                        var areaWidth = parseInt(areaMeasurements[2]);
                        containerHeight = areaWidth * 2;
                        containerWidth = areaWidth * 2;
                        offsetTop = mapOffsetTop + areaTop - areaWidth;
                        offsetLeft = mapOffsetLeft + areaLeft - areaWidth;
                    }
                    else if (areaShape == 'rect') {
                        var areaLeft = parseInt(areaMeasurements[0]);
                        var areaTop = parseInt(areaMeasurements[1]);
                        var areaRight = parseInt(areaMeasurements[2]);
                        var areaBottom = parseInt(areaMeasurements[3]);
                        containerHeight = areaBottom - areaTop;
                        containerWidth = areaRight - areaLeft;
                        offsetTop = mapOffsetTop + areaTop;
                        offsetLeft = mapOffsetLeft + areaLeft;
                    }
                    else if (areaShape == 'poly') {
                        var areaXs = [];
                        var areaYs = [];
                        var areaSmallestX = 0,
							areaSmallestY = 0,
							areaGreatestX = 0,
							areaGreatestY = 0;
                        var arrayAlternate = 'even';

                        for (i = 0; i < areaMeasurements.length; i++) {
                            var areaNumber = parseInt(areaMeasurements[i]);

                            if (arrayAlternate == 'even') {
                                if (areaNumber > areaGreatestX) {
                                    areaGreatestX = areaNumber;
                                    if (i == 0) {
                                        areaSmallestX = areaGreatestX;
                                    }
                                }

                                if (areaNumber < areaSmallestX) {
                                    areaSmallestX = areaNumber;
                                }

                                arrayAlternate = 'odd';
                            }
                            else {
                                if (areaNumber > areaGreatestY) {
                                    areaGreatestY = areaNumber;
                                    if (i == 1) {
                                        areaSmallestY = areaGreatestY;
                                    }
                                }

                                if (areaNumber < areaSmallestY) {
                                    areaSmallestY = areaNumber;
                                }

                                arrayAlternate = 'even';
                            }
                        }

                        containerHeight = areaGreatestY - areaSmallestY;
                        containerWidth = areaGreatestX - areaSmallestX;
                        offsetTop = mapOffsetTop + areaSmallestY;
                        offsetLeft = mapOffsetLeft + areaSmallestX;
                    }
                    else {
                        containerHeight = map.outerHeight(false);
                        containerWidth = map.outerWidth(false);
                        offsetTop = mapOffsetTop;
                        offsetLeft = mapOffsetLeft;
                    }
                }

                // hardcoding the width and removing the padding fixed an issue with the tooltip width collapsing when the window size is small
                if (object.options.fixedWidth == 0) {
                    tooltipster.css({
                        'width': tooltipInnerWidth + 'px',
                        'padding-left': '0px',
                        'padding-right': '0px'
                    });
                }

                // our function and global vars for positioning our tooltip
                var myLeft = 0,
					myTop = 0;
                var offsetY = parseInt(object.options.offsetY);
                var offsetX = parseInt(object.options.offsetX);
                var arrowConstruct = '';

                // A function to detect if the tooltip is going off the screen horizontally. If so, reposition the crap out of it!
                function dontGoOffScreenX() {

                    var windowLeft = $(window).scrollLeft();

                    // If the tooltip goes off the left side of the screen, line it up with the left side of the window
                    if ((myLeft - windowLeft) < 0) {
                        var arrowReposition = myLeft - windowLeft;
                        myLeft = windowLeft;

                        tooltipster.data('arrow-reposition', arrowReposition);
                    }

                    // If the tooltip goes off the right of the screen, line it up with the right side of the window
                    if (((myLeft + tooltipWidth) - windowLeft) > windowWidth) {
                        var arrowReposition = myLeft - ((windowWidth + windowLeft) - tooltipWidth);
                        myLeft = (windowWidth + windowLeft) - tooltipWidth;

                        tooltipster.data('arrow-reposition', arrowReposition);
                    }
                }

                // A function to detect if the tooltip is going off the screen vertically. If so, switch to the opposite!
                function dontGoOffScreenY(switchTo, resetTo) {
                    // if it goes off the top off the page
                    if (((offsetTop - $(window).scrollTop() - tooltipHeight - offsetY - 12) < 0) && (resetTo.indexOf('top') > -1)) {
                        object.options.position = switchTo;
                        resetPosition = resetTo;
                    }

                    // if it goes off the bottom of the page
                    if (((offsetTop + containerHeight + tooltipHeight + 12 + offsetY) > ($(window).scrollTop() + $(window).height())) && (resetTo.indexOf('bottom') > -1)) {
                        object.options.position = switchTo;
                        resetPosition = resetTo;
                        myTop = (offsetTop - tooltipHeight) - offsetY - 12;
                    }
                }

                if (object.options.position == 'top') {
                    var leftDifference = (offsetLeft + tooltipWidth) - (offsetLeft + containerWidth);
                    myLeft = (offsetLeft + offsetX) - (leftDifference / 2);
                    myTop = (offsetTop - tooltipHeight) - offsetY - 12;
                    dontGoOffScreenX();
                    dontGoOffScreenY('bottom', 'top');
                }

                if (object.options.position == 'top-left') {
                    myLeft = offsetLeft + offsetX;
                    myTop = (offsetTop - tooltipHeight) - offsetY - 12;
                    dontGoOffScreenX();
                    dontGoOffScreenY('bottom-left', 'top-left');
                }

                if (object.options.position == 'top-right') {
                    myLeft = (offsetLeft + containerWidth + offsetX) - tooltipWidth;
                    myTop = (offsetTop - tooltipHeight) - offsetY - 12;
                    dontGoOffScreenX();
                    dontGoOffScreenY('bottom-right', 'top-right');
                }

                if (object.options.position == 'bottom') {
                    var leftDifference = (offsetLeft + tooltipWidth) - (offsetLeft + containerWidth);
                    myLeft = offsetLeft - (leftDifference / 2) + offsetX;
                    myTop = (offsetTop + containerHeight) + offsetY + 12;
                    dontGoOffScreenX();
                    dontGoOffScreenY('top', 'bottom');
                }

                if (object.options.position == 'bottom-left') {
                    myLeft = offsetLeft + offsetX;
                    myTop = (offsetTop + containerHeight) + offsetY + 12;
                    dontGoOffScreenX();
                    dontGoOffScreenY('top-left', 'bottom-left');
                }

                if (object.options.position == 'bottom-right') {
                    myLeft = (offsetLeft + containerWidth + offsetX) - tooltipWidth;
                    myTop = (offsetTop + containerHeight) + offsetY + 12;
                    dontGoOffScreenX();
                    dontGoOffScreenY('top-right', 'bottom-right');
                }

                if (object.options.position == 'left') {
                    myLeft = offsetLeft - offsetX - tooltipWidth - 12;
                    myLeftMirror = offsetLeft + offsetX + containerWidth + 12;
                    var topDifference = (offsetTop + tooltipHeight) - (offsetTop + $this.outerHeight(false));
                    myTop = offsetTop - (topDifference / 2) - offsetY;

                    // If the tooltip goes off boths sides of the page
                    if ((myLeft < 0) && ((myLeftMirror + tooltipWidth) > windowWidth)) {
                        var borderWidth = parseFloat(tooltipster.css('border-width')) * 2;
                        var newWidth = (tooltipWidth + myLeft) - borderWidth;
                        tooltipster.css('width', newWidth + 'px');

                        tooltipHeight = tooltipster.outerHeight(false);
                        myLeft = offsetLeft - offsetX - newWidth - 12 - borderWidth;
                        topDifference = (offsetTop + tooltipHeight) - (offsetTop + $this.outerHeight(false));
                        myTop = offsetTop - (topDifference / 2) - offsetY;
                    }

                        // If it only goes off one side, flip it to the other side
                    else if (myLeft < 0) {
                        myLeft = offsetLeft + offsetX + containerWidth + 12;
                        tooltipster.data('arrow-reposition', 'left');
                    }
                }

                if (object.options.position == 'right') {
                    myLeft = offsetLeft + offsetX + containerWidth + 12;
                    myLeftMirror = offsetLeft - offsetX - tooltipWidth - 12;
                    var topDifference = (offsetTop + tooltipHeight) - (offsetTop + $this.outerHeight(false));
                    myTop = offsetTop - (topDifference / 2) - offsetY;

                    // If the tooltip goes off boths sides of the page
                    if (((myLeft + tooltipWidth) > windowWidth) && (myLeftMirror < 0)) {
                        var borderWidth = parseFloat(tooltipster.css('border-width')) * 2;
                        var newWidth = (windowWidth - myLeft) - borderWidth;
                        tooltipster.css('width', newWidth + 'px');

                        tooltipHeight = tooltipster.outerHeight(false);
                        topDifference = (offsetTop + tooltipHeight) - (offsetTop + $this.outerHeight(false));
                        myTop = offsetTop - (topDifference / 2) - offsetY;

                    }

                        // If it only goes off one side, flip it to the other side
                    else if ((myLeft + tooltipWidth) > windowWidth) {
                        myLeft = offsetLeft - offsetX - tooltipWidth - 12;
                        tooltipster.data('arrow-reposition', 'right');
                    }
                }

                // if arrow is set true, style it and append it
                if (object.options.arrow == true) {

                    var arrowClass = 'tooltipster-arrow-' + object.options.position;

                    // set color of the arrow
                    if (object.options.arrowColor.length < 1) {
                        var arrowColor = tooltipster.css('background-color');
                    }
                    else {
                        var arrowColor = object.options.arrowColor;
                    }

                    // if the tooltip was going off the page and had to re-adjust, we need to update the arrow's position
                    var arrowReposition = tooltipster.data('arrow-reposition');
                    if (!arrowReposition) {
                        arrowReposition = '';
                    }
                    else if (arrowReposition == 'left') {
                        arrowClass = 'tooltipster-arrow-right';
                        arrowReposition = '';
                    }
                    else if (arrowReposition == 'right') {
                        arrowClass = 'tooltipster-arrow-left';
                        arrowReposition = '';
                    }
                    else {
                        arrowReposition = 'left:' + arrowReposition + 'px;';
                    }

                    // building the logic to create the border around the arrow of the tooltip
                    if ((object.options.position == 'top') || (object.options.position == 'top-left') || (object.options.position == 'top-right')) {
                        var tooltipBorderWidth = parseFloat(tooltipster.css('border-bottom-width'));
                        var tooltipBorderColor = tooltipster.css('border-bottom-color');
                    }
                    else if ((object.options.position == 'bottom') || (object.options.position == 'bottom-left') || (object.options.position == 'bottom-right')) {
                        var tooltipBorderWidth = parseFloat(tooltipster.css('border-top-width'));
                        var tooltipBorderColor = tooltipster.css('border-top-color');
                    }
                    else if (object.options.position == 'left') {
                        var tooltipBorderWidth = parseFloat(tooltipster.css('border-right-width'));
                        var tooltipBorderColor = tooltipster.css('border-right-color');
                    }
                    else if (object.options.position == 'right') {
                        var tooltipBorderWidth = parseFloat(tooltipster.css('border-left-width'));
                        var tooltipBorderColor = tooltipster.css('border-left-color');
                    }
                    else {
                        var tooltipBorderWidth = parseFloat(tooltipster.css('border-bottom-width'));
                        var tooltipBorderColor = tooltipster.css('border-bottom-color');
                    }

                    if (tooltipBorderWidth > 1) {
                        tooltipBorderWidth++;
                    }

                    var arrowBorder = '';
                    if (tooltipBorderWidth !== 0) {
                        var arrowBorderSize = '';
                        var arrowBorderColor = 'border-color: ' + tooltipBorderColor + ';';
                        if (arrowClass.indexOf('bottom') !== -1) {
                            arrowBorderSize = 'margin-top: -' + tooltipBorderWidth + 'px;';
                        }
                        else if (arrowClass.indexOf('top') !== -1) {
                            arrowBorderSize = 'margin-bottom: -' + tooltipBorderWidth + 'px;';
                        }
                        else if (arrowClass.indexOf('left') !== -1) {
                            arrowBorderSize = 'margin-right: -' + tooltipBorderWidth + 'px;';
                        }
                        else if (arrowClass.indexOf('right') !== -1) {
                            arrowBorderSize = 'margin-left: -' + tooltipBorderWidth + 'px;';
                        }
                        arrowBorder = '<span class="tooltipster-arrow-border" style="' + arrowBorderSize + ' ' + arrowBorderColor + ';"></span>';
                    }

                    // if the arrow already exists, remove and replace it
                    tooltipster.find('.tooltipster-arrow').remove();

                    // build out the arrow and append it		
                    arrowConstruct = '<div class="' + arrowClass + ' tooltipster-arrow" style="' + arrowReposition + '">' + arrowBorder + '<span style="border-color:' + arrowColor + ';"></span></div>';
                    tooltipster.append(arrowConstruct);
                }

                // position the tooltip
                tooltipster.css({ 'top': myTop + 'px', 'left': myLeft + 'px' });

                // if we had to change the position of the tooltip so it wouldn't go off screen, reset it
                if (resetPosition !== undefined) {
                    object.options.position = resetPosition;
                }
            }
        }
    };

    $.fn[pluginName] = function (options) {
        // better API name spacing by glebtv
        if (typeof options === 'string') {
            var $t = this;
            var newContent = arguments[1];

            // if we're calling a container to interact with API's of tooltips inside it - select all those tooltip origins first
            if ($t.data('plugin_tooltipster') == undefined) {
                var query = $t.find('*');
                $t = $();
                query.each(function () {
                    if ($(this).data('plugin_tooltipster') !== undefined) {
                        $t.push($(this));
                    }
                });
            }

            $t.each(function () {
                switch (options.toLowerCase()) {
                    case 'show':
                        $(this).data('plugin_tooltipster').showTooltip();
                        break;

                    case 'hide':
                        $(this).data('plugin_tooltipster').hideTooltip();
                        break;

                    case 'disable':
                        $(this).addClass('tooltipster-disable');
                        break;

                    case 'enable':
                        $(this).removeClass('tooltipster-disable');
                        break;

                    case 'destroy':
                        $(this).data('plugin_tooltipster').hideTooltip();
                        $(this).data('plugin_tooltipster', '').attr('title', $t.data('tooltipsterContent')).data('tooltipsterContent', '').data('plugin_tooltipster', '').off('mouseenter.tooltipster mouseleave.tooltipster click.tooltipster');
                        break;

                    case 'update':
                        $(this).data('tooltipsterContent', newContent);
                        break;

                    case 'reposition':
                        $(this).data('plugin_tooltipster').positionTooltip();
                        break;
                }
            });

            return this;
        }

        // attach a tooltipster object to each element if it doesn't already have one
        return this.each(function () {
            if (!$.data(this, "plugin_" + pluginName)) {
                $.data(this, "plugin_" + pluginName, new Plugin(this, options));
            }

            var thisOptions = $(this).data('plugin_tooltipster').options;

            if ((thisOptions.iconDesktop == true) && (!is_touch_device()) || ((thisOptions.iconTouch == true) && (is_touch_device()))) {
                var transferObject = $(this).data('plugin_tooltipster');
                $(this).next().data('plugin_tooltipster', transferObject);
            }
        });
    };

    // hide tooltips on orientation change
    if (is_touch_device()) {
        window.addEventListener("orientationchange", function () {
            if ($('.tooltipster-base').length > 0) {
                $('.tooltipster-base').each(function () {
                    var origin = $(this).data('origin');
                    origin.data('plugin_tooltipster').hideTooltip();
                });
            }
        }, false);
    }

    // on window resize, reposition and open tooltips
    $(window).resize(function () {
        var origin = $('.tooltipster-base').data('origin');

        if ((origin !== null) && (origin !== undefined)) {
            origin.tooltipster('reposition');
        }
    });

})(jQuery, window, document);;

(function ($) {

    //===JQUERY EXTENSIONS===
    $.extend({
        PrettyUrl: {
            // internal use only
            Subscribers: {},

            // internal use only
            PrettyUrlSeparator: "&!",

            // internal use only
            Submitter: null,

            // public use
            AutomaticScroll: false,

            // internal use only
            TimeOutDuration: 30000,

            // public use
            Setup: function (widgetTag, anchorTagSelector, widgetSelector, beforeSendCallback, successCallback, errorCallBack) {
                $.PrettyUrl.RegisterWidgetLinks(
                    widgetTag,
                    anchorTagSelector,
                    widgetSelector,
                    beforeSendCallback,
                    successCallback,
                    errorCallBack,
                    false);

                $(anchorTagSelector).click(
                    function (e) {
                        e.preventDefault();
                        $.PrettyUrl.ClickEvent($(this));
                    }
                );
            },

            // public use
            PageLinkSetup: function (anchorTagSelector, widgetSelector) {
                var pageUrl = $.param.fragment($(anchorTagSelector).attr('href').replace('!', ''));
                var parts = pageUrl.split('=');

                if (parts[1] == null) {
                    return;
                }

                pageListStorage = {
                    Url: parts[1],
                    HttpMethod: 'GET',
                    WidgetSelector: widgetSelector
                };

                localStorage.setItem('pageList', JSON.stringify(pageListStorage));
            },

            // public use
            Get: function (url, parameters, targetTagSelector, beforeSendCallback, successCallback, errorCallback, dataType, timeout) {
                $.PrettyUrl.Request(url, parameters, "GET", targetTagSelector, beforeSendCallback, successCallback, errorCallback, dataType, timeout);
            },

            // public use
            Post: function (url, parameters, targetTagSelector, beforeSendCallback, successCallback, errorCallback, dataType, timeout) {
                $.PrettyUrl.Request(url, parameters, "POST", targetTagSelector, beforeSendCallback, successCallback, errorCallback, dataType, timeout);
            },

            // internal use only
            Request: function (url, args, httpMethod, targetTagSelector, beforeSendCallback, successCallback, errorCallback, dataType, timeout) {
                var sep = url.indexOf('?') == -1 ? "?" : "&";
                var parameters = $.param({ nocache: new Date().getTime() });
                var rewrittenUrl = url + sep + parameters;

                jQuery.ajax({
                    url: rewrittenUrl,
                    type: httpMethod,
                    data: args,
                    beforeSend: beforeSendCallback == null ?
                        (function () {
                            $(targetTagSelector).Loading(true);
                        }) :
                        beforeSendCallback,
                    success: successCallback == null ?
                        (function (result) {
                            $(targetTagSelector).Loading(false);
                            $(targetTagSelector).replaceWith(result);
                            $.PrettyUrl.UpdateCache(url, result);

                            if ($.PrettyUrl.AutomaticScroll) {
                                $('html, body').animate({
                                    scrollTop: $(targetTagSelector).offset().top
                                },
                                    'normal');
                                $.PrettyUrl.AutomaticScroll = false;
                            }
                        }) :
                        successCallback,
                    error: errorCallback == null ?
                        (function () {
                            $(targetTagSelector).Loading(false);
                            alert('error');
                        }) :
                        errorCallback,
                    dataType: dataType == null ? "html" : dataType,
                    timeout: timeout == null ? $.PrettyUrl.TimeOutDuration : timeout
                });
            },

            // internal use only
            ClickEvent: function (obj) {
                var url = $.param.fragment(obj.attr('href').replace('!', ''));
                var meta = $.PrettyUrl.Subscribers[url];

                if (meta != null) {
                    var state = {};

                    $.PrettyUrl.Submitter = url;
                    state['!' + meta.Tag] = url;
                    $.bbq.pushState(state);
                }
            },

            // internal use
            UpdateCache: function (url, content) {
                url = $.param.fragment(url.replace('!', ''));
                var meta = $.PrettyUrl.Subscribers[url];

                if (meta != null) {
                    meta.Cache = content;
                }
            },

            // internal use
            GetCache: function (anchorSelector) {
                url = $.param.fragment(anchorSelector.attr("href").replace('!', ''));

                var meta = $.PrettyUrl.Subscribers[url];
                return meta == null ? null : meta.Cache;
            },

            // internal use
            RegisterLink: function (url, parameters, widgetTag, httpMethod, beforeSendCallback, successCallback, errorCallBack) {
                if (url) {
                    url = $.param.fragment(url.replace('!', ''));

                    $.PrettyUrl.Subscribers[url] = {
                        Url: url,
                        Parameters: parameters,
                        Tag: widgetTag,
                        HttpMethod: httpMethod,
                        BeforeSendCallback: beforeSendCallback,
                        SuccessCallback: successCallback,
                        ErrorCallback: errorCallBack,
                        Cache: $(this).find($(widgetTag))
                    };
                }
            },

            // internal use
            RegisterWidgetLinks: function (widgetTag, anchorsTagSelector, widgetSelector, beforeSendCallback, successCallback, errorCallBack, persist) {
                $(anchorsTagSelector).each(function (index, elem) {
                    $.PrettyUrl.RegisterLink(
                        $(this).attr('href'),
                        null,
                        widgetTag,
                        'GET',
                        beforeSendCallback == null ?
                            (function () {
                                $(widgetSelector).Loading(true);
                            }) :
                            beforeSendCallback,
                        successCallback == null ?
                            (function (result) {
                                $(widgetSelector).Loading(false);
                                $(widgetSelector).replaceWith(result);
                                $.PrettyUrl.UpdateCache(widgetSelector, result);

                                if ($.PrettyUrl.AutomaticScroll) {
                                    $('html, body').animate({
                                        scrollTop: $(widgetSelector).offset().top
                                    },
                                        'normal');
                                    $.PrettyUrl.AutomaticScroll = false;
                                }
                            }) :
                            successCallback,
                        errorCallBack == null ?
                            (function () {
                                $(widgetSelector).Loading(false);
                                alert(Globalization.GetLocalizedString('Error'));
                            }) :
                            errorCallBack
                    );
                });
            },

            // internal use
            Fire: function (hash) {
                var doAjaxRequest = function (url, tag) {
                    var request = null;
                    var item = $.PrettyUrl.Subscribers[url];
                    var sep = url.indexOf('?') == -1 ? "?" : "&";
                    var parameters = $.param({ nocache: new Date().getTime() });
                    var rewrittenUrl = url + sep + parameters;

                    if (item == null) {
                        item = JSON.parse(localStorage.getItem('pageList'));
                        localStorage.removeItem('pageList'); //clean the localStorage entry

                        if (item == null) {
                            var selector = '#' + tag + ", " + "." + tag;
                            if (tag != null && $(selector).size() == 1) {
                                request = {
                                    url: url,
                                    type: 'GET',
                                    beforeSend: function () {
                                        $(selector).Loading(true);
                                    },
                                    success: function (result) {
                                        $(selector).Loading(false);
                                        $(selector).replaceWith(result);
                                    },
                                    error: function () {
                                        $(selector).Loading(false);
                                        alert(Globalization.GetLocalizedString('Error'));
                                    },
                                    dataType: 'html'
                                };
                            } else {
                                request = null;
                            }
                        } else {
                            request = {
                                url: rewrittenUrl,
                                type: item.HttpMethod,
                                beforeSend: function () {
                                    $(item.WidgetSelector).Loading(true);
                                },
                                success: function (result) {
                                    $(item.WidgetSelector).Loading(false);
                                    $(item.WidgetSelector).replaceWith(result);
                                },
                                error: function () {
                                    $(item.WidgetSelector).Loading(false);
                                    alert(Globalization.GetLocalizedString('Error'));
                                },
                                dataType: "html"
                            };
                        }
                    } else {
                        request = {
                            url: rewrittenUrl,
                            type: item.HttpMethod,
                            data: item.Parameters,
                            beforeSend: item.BeforeSendCallback,
                            success: item.SuccessCallback,
                            error: item.ErrorCallback,
                            dataType: "html"
                        };
                    }

                    if (request != null) {
                        jQuery.ajax(request);
                    }
                };

                var hashParts = hash.split($.PrettyUrl.PrettyUrlSeparator);

                if ($.PrettyUrl.Submitter != null) {
                    var url = $.PrettyUrl.Submitter;
                    $.PrettyUrl.Submitter = null;

                    doAjaxRequest(url);

                    return;
                }

                for (urlCount = 0; urlCount < hashParts.length; urlCount++) {
                    var operators = hashParts[urlCount].split('=');
                    if (operators[1] == null) {
                        continue;
                    }
                    var url = unescape(operators[1]);
                    var tag = operators[0];

                    doAjaxRequest(url, tag);
                }
            },

            // internal use
            InitTrigger: (function () {
                $(document).ready(function () {
                    $(window).bind('hashchange', function (event) {
                        if (event.fragment != null) {
                            $.PrettyUrl.Fire(event.fragment.replace('!', ''));
                        }
                    })

                    $(window).trigger('hashchange');
                });
            })()
        }
    });


    //===JQUERY PLUGINS===
    $.fn.extend({
        /* --- pager v1.0 NOT DEPRECATED --- */
        pager: function (options) {
            var defaults = {
                resultSet: undefined,
                onclick: function (page) {
                    alert(Globalization.GetFormattedLocalizedString('ImplementAjaxMethod', page));
                    return false;
                },
                separator: ' - ',
                firstText: '<<',
                prevText: '<',
                nextText: '>',
                lastText: '>>',
                linksVisible: 5,
                url: 'javascript:void(0)'
            };

            var options = $.extend(defaults, options);

            return this.each(function () {
                var rnd = Math.floor(Math.random() * 9999)
                var result = '';

                if (options.firstText.length > 0 && options.resultSet.CurrentPage != 1)
                    result += '<a href="' + options.url + '" id="' + rnd + '_pagerPage_first">' + options.firstText + '</a> ';
                //        else if (options.firstText.length > 0)
                //            result += options.firstText + ' ';

                if (options.prevText.length > 0 && options.resultSet.CurrentPage != 1)
                    result += ' ' + '<a href="' + options.url + '" id="' + rnd + '_pagerPage_prev">' + options.prevText + '</a> ';
                //        else if (options.prevText.length > 0) 
                //            result += options.prevText + ' '; //why?


                var startPageToDisplay = 1;
                var endPageToDisplay = options.resultSet.PageCount;
                if (options.resultSet.PageCount > options.linksVisible) {
                    var tempCounterStart = options.resultSet.CurrentPage - Math.ceil((options.linksVisible / 2)) + 1;
                    var tempCounterEnd = options.resultSet.CurrentPage + Math.floor((options.linksVisible / 2));

                    if (tempCounterStart > 1 && tempCounterEnd < options.resultSet.PageCount + 1) {
                        startPageToDisplay = tempCounterStart;
                        endPageToDisplay = tempCounterEnd;
                    } else if (tempCounterStart <= 1) {
                        endPageToDisplay = startPageToDisplay + options.linksVisible - 1;
                    } else if (tempCounterEnd >= options.resultSet.PageCount) {
                        endPageToDisplay = options.resultSet.PageCount;
                        startPageToDisplay = endPageToDisplay - options.linksVisible + 1;
                    }
                    ;
                }
                ;

                if (options.resultSet != undefined) {
                    for (var i = startPageToDisplay; i <= endPageToDisplay; i++) {
                        if (i == options.resultSet.CurrentPage)
                            result += i;
                        else
                            result += '<a href="' + options.url + '" id="' + rnd + '_pagerPage_' + i + '">' + i + '</a>';

                        if (i != endPageToDisplay)
                            result += options.separator;
                    }
                }
                if (options.nextText.length > 0 && options.resultSet.CurrentPage != options.resultSet.PageCount)
                    result += ' <a href="' + options.url + '" id="' + rnd + '_pagerPage_next">' + options.nextText + '</a>';
                //else
                //    result += ' ' + options.nextText;

                if (options.lastText.length > 0 && options.resultSet.CurrentPage != options.resultSet.PageCount)
                    result += ' ' + '<a href="' + options.url + '" id="' + rnd + '_pagerPage_last">' + options.lastText + '</a>';
                //else
                //    result += ' ' + options.lastText;

                $(this).html(result);

                var createClickHandler = function (n) { return function () { options.onclick(n); } };
                $('#' + rnd + '_pagerPage_first').click(function () { options.onclick(1) });
                $('#' + rnd + '_pagerPage_prev').click(function () { options.onclick(options.resultSet.CurrentPage - 1) });
                $('#' + rnd + '_pagerPage_next').click(function () { options.onclick(options.resultSet.CurrentPage + 1) });
                $('#' + rnd + '_pagerPage_last').click(function () { options.onclick(options.resultSet.PageCount) });

                for (var i = startPageToDisplay; i <= endPageToDisplay; i++) {
                    $('#' + rnd + '_pagerPage_' + i).click(createClickHandler(i));
                }
            });
        },

        /* --- AjaxPager v1.0 --- */
        AjaxPager: function (currentPage, settings) {
            settings = this.extend({
                separatorHtml: ' - ',
                firstHtml: '<< ',
                lastHtml: ' >>',
                prevHtml: Globalization.GetLocalizedString('Previous'),
                nextHtml: Globalization.GetLocalizedString('Next'),
                linksVisible: 5,
                currentPageClass: null,
                onClickEvent: null,
                showLastLink: true
            }, settings);
            // Settings overrides
            settings.linksVisible = settings.linksVisible % 2 == 0 ? settings.linksVisible + 1 : settings.linksVisible;
            settings.currentPageClass = $.type(settings.currentPageClass) != "string" ? "" : settings.currentPageClass;

            // Classes

            function Pager(obj, currentPage) {
                this.$Object = $(obj);
                this.PageCount = this.$Object.children('a').size();
                this.CurrentPageIndex = currentPage == null ? 1 : currentPage - 1;
                this.CurrentPageNumber = currentPage;

                this.GetPageLinks = function () {
                    var _this = this;
                    var _pageLinks = new Array();
                    _this.$Object.children('a').each(function (i) {

                        var _pagelink = new PageLink(this, i);


                        if (_pagelink.Index == 0)
                            _pagelink.IsFirstPage = true;
                        if (_pagelink.Index == _this.PageCount - 1)
                            _pagelink.IsLastPage = true;
                        if (_pagelink.Index == _this.CurrentPageIndex)
                            _pagelink.IsCurrentPage = true;

                        _pageLinks.push(_pagelink);
                    });
                    return _pageLinks;
                };
                this.PageLinks = this.GetPageLinks();

                // re-fill pager with pager custom display
                this.Render = function () {
                    var _this = this;
                    var _lowerUpperLinksCount = Math.floor(settings.linksVisible / 2)

                    // !!! CONFLICT: ALL HANDLERS DISAPPEAR WHEN CONTAINER IS SET TO EMPTY ---
                    _this.$Object.empty();

                    // display current page link
                    if (this.PageCount && settings.linksVisible > 1)
                        _this.$Object.append($('<span />').html(_this.CurrentPageNumber).addClass(settings.currentPageClass));

                    var counter = _lowerUpperLinksCount;
                    if (_this.CurrentPageIndex < _lowerUpperLinksCount)
                        counter = settings.linksVisible - _this.CurrentPageNumber;
                    if (_this.CurrentPageIndex > ((_this.PageCount - 1) - _lowerUpperLinksCount))
                        counter = _this.CurrentPageNumber - (_this.PageCount - (settings.linksVisible - 1));

                    for (var i = 0; i < counter; i++) {

                        if ((_this.CurrentPageIndex - i) > 0) {
                            _this.$Object.prepend(_this.PageLinks[_this.CurrentPageIndex - (i + 1)].$Object.html(_this.CurrentPageNumber - (i + 1)));
                        }

                        if ((_this.CurrentPageIndex + i) < (_this.PageCount - 1)) {
                            _this.$Object.append(_this.PageLinks[_this.CurrentPageIndex + (i + 1)].$Object.html(_this.CurrentPageNumber + (i + 1)));
                        }
                    }

                    _this.$Object.children(':gt(0)').filter('a, span').before(settings.separatorHtml);

                    //display beginning with 'first' and 'prev'
                    if (_this.CurrentPageIndex > 0) {
                        _this.$Object
                            .prepend(_this.PageLinks[_this.CurrentPageIndex - 1].$Object.clone(true).html(settings.prevHtml))       //'previous' button
                            .prepend(_this.PageLinks[0].$Object.clone(true).html(settings.firstHtml)); //'first' button

                    }

                    // display end with next and prev
                    if (_this.CurrentPageIndex < (_this.PageCount - 1)) {
                        _this.$Object
                            .append(_this.PageLinks[_this.CurrentPageIndex + 1].$Object.clone(true).html(settings.nextHtml)); //'next' button
                        if (settings.showLastLink) {
                            _this.$Object
                                .append(_this.PageLinks[_this.PageCount - 1].$Object.clone(true).html(settings.lastHtml)); //'last' button
                        }
                    }

                    return _this;
                };

                return this.Render();
            }

            ;


            function PageLink(obj, pageIndex) {
                this.$Object = $(obj).clone(true);

                this.Index = pageIndex == null ? 0 : pageIndex;

                this.PrettyUrl = this.$Object.attr('href'); //TODO: modify when implementing the pretty url concept

                this.AjaxUrl = this.$Object.attr('href'); //TODO: modify when implementing the pretty url concept

                this.IsCurrentPage = false;
                this.IsFirstPage = false;
                this.IsLastPage = false;

                switch ($.type(settings.onClickEvent)) {
                    case "function":
                        this.$Object.click(function (event) {
                            event.preventDefault();

                            settings.onClickEvent(this);
                        });
                        break;
                    case "string":
                        this.$Object.click(function (event) {
                            event.preventDefault();

                            eval(settings.onClickEvent);
                        });
                        break;
                }

                return this;
            }

            ;

            // Methods
            $AjaxPager = this.extend({
                _init: function ($matchedObject) {
                    return pager = new Pager($matchedObject, currentPage);
                }
            });

            // Main code
            return this.each(function () {
                $AjaxPager._init(this);
            });
        },

        /* --- Loading v3.0 --- */
        Loading: function (loading, settings) {
            settings = this.extend({
                animationDelay: 333,
                fadeDuration: 250,
                overlayColor: '#fff',
                opacityPercentage: 80,
                spinnerTopPosition: null,
                spinnerColor: null,
                spinnerStartRadius: null,
                spinnerEndRadius: null,
                spinnerBarCount: null,
                spinnerBarWidth: null
            }, settings);

            (ValidateSettings = function () {
                settings.spinnerColor = settings.spinnerColor || '#000',
                settings.spinnerStartRadius = settings.spinnerStartRadius || 14,
                settings.spinnerEndRadius = settings.spinnerEndRadius || 7,
                settings.spinnerBarCount = settings.spinnerBarCount || 12,
                settings.spinnerBarWidth = settings.spinnerBarWidth || 2
            })();

            var $Object = this

            function LoadingDom(obj) {
                this.$Object = $(obj);

                this.NodeName = obj.nodeName != null ? obj.nodeName.toUpperCase() : this.$Object[0].nodeName.toUpperCase();

                this.Height = this.$Object.outerHeight(true);
                this.Width = this.$Object.outerWidth(true);
                this.Position = this.$Object.position();
                this.Zindex = isNaN(parseInt(this.$Object.css('z-index'))) ? 1 : parseInt(this.$Object.css('z-index'));

                this.StartLoading = function () {
                    if (this.$Object.data('Loading') == null) {
                        new LoadingOverlay(
                            this.Height,
                            this.Width,
                            this.Position.top,
                            this.Position.left)
                            .$Object
                            .css('z-index', this.Zindex + 1)
                            //insert loadingOverlay
                            .insertBefore(this.$Object)
                            //show children after delay
                            .children()
                            .delay(settings.animationDelay)
                            .fadeIn(settings.fadeDuration); //use of "fadeIn" instead of "show" to run the previous delay (delay is not working followed with "show")

                        this.$Object.data('Loading', { loading: true });
                    }
                    return this;
                };

                this.StopLoading = function () {
                    if (this.$Object.data('Loading') != null) {
                        this.$Object.prev('.jq_LoadingOverlay').remove();
                        this.$Object.removeData('Loading');
                    }
                    return this;
                };

                return this;
            }

            ;

            function LoadingOverlay(height, width, top, left) {
                this.$Object = $('<div />')
                    .css({
                        'position': 'absolute',
                        'top': top,
                        'left': left
                    })
                    .height(height)
                    .width(width)
                    .addClass('jq_LoadingOverlay')
                    .append(// add a transparent ovelayer to block user clicks
                        new OverLayer(height, width, '#fff', 0).$Object
                    )
                    .append(// add overlayer object
                        new OverLayer(height, width, settings.overlayColor, settings.opacityPercentage).$Object.hide()
                    )
                    .append(// add Raphael "Paper" to hold a spinner
                        new Spinner(
                            settings.spinnerStartRadius,
                            settings.spinnerEndRadius,
                            settings.spinnerBarCount,
                            settings.spinnerBarWidth,
                            settings.spinnerColor)
                            .$Object
                            .css({
                                // will make a spinner of 32px diameter
                                'position': 'absolute',
                                'top': settings.spinnerTopPosition == null ? Math.floor((height / 2) - (14 + 2)) + 'px' : settings.spinnerTopPosition + 'px',
                                'left': Math.floor((width / 2) - (14 + 2)) + 'px',
                                'height': 32,
                                'width': 32
                            })
                            .hide()
                    );

                return this;
            }

            ;

            function OverLayer(height, width, color, opacitypercentage) {
                this.$Object = $('<div />')
                    .css({
                        'position': 'absolute',
                        /* for IE */
                        'filter': 'alpha(opacity=' + opacitypercentage + ')',
                        /* CSS3 standard */
                        'opacity': opacitypercentage / 100,
                        'background-color': color
                    })
                    .height(height)
                    .width(width);

                return this;
            }

            ;

            function Spinner(R1, R2, count, stroke_width, colour) {

                this.$Object = $('<div>')
                    .addClass('LoadingSpinner');


                /*--- Create Spinner ---*/
                var sectorsCount = count || 12,
                    color = colour || "#fff",
                    width = stroke_width || 15,
                    r1 = Math.min(R1, R2) || 35,
                    r2 = Math.max(R1, R2) || 60,
                    cx = r2 + width,
                    cy = r2 + width,
                    r = Raphael(this.$Object[0], r2 * 2 + width * 2, r2 * 2 + width * 2),
                    sectors = [],
                    opacity = [],
                    beta = 2 * Math.PI / sectorsCount,
                    pathParams = { stroke: color, "stroke-width": width, "stroke-linecap": "round" };

                Raphael.getColor.reset();

                for (var i = 0; i < sectorsCount; i++) {
                    var alpha = beta * i - Math.PI / 2,
                        cos = Math.cos(alpha),
                        sin = Math.sin(alpha);
                    opacity[i] = 1 / sectorsCount * i;
                    sectors[i] = r.path([["M", cx + r1 * cos, cy + r1 * sin], ["L", cx + r2 * cos, cy + r2 * sin]]).attr(pathParams);
                    if (color == "rainbow") {
                        sectors[i].attr("stroke", Raphael.getColor());
                    }
                }

                var tick;
                (function ticker() {
                    opacity.unshift(opacity.pop());
                    for (var i = 0; i < sectorsCount; i++) {
                        sectors[i].attr("opacity", opacity[i]);
                    }
                    r.safari();
                    tick = setTimeout(ticker, 1000 / sectorsCount);
                })();

                return this;
            }

            ;


            $.Loading = this.extend({
                _init: function () {
                }
            });

            return this.each(function (i) {
                if (this.nodeName.toUpperCase() == "#WINDOW" && this.nodeName.toUpperCase() == "#DOCUMENT" && this.nodeName.toUpperCase() == "BODY") {
                    throw new Error(Globalization.GetLocalizedString('LoadingPluginError'));
                } else {
                    if (loading)
                        new LoadingDom($(this)).StartLoading();
                    else
                        new LoadingDom($(this)).StopLoading();
                }
            });
        },

        /* --- NavigationMenuTA v1.0 --- */
        NavigationMenuTA: function (settings) {
            settings = jQuery.extend({
                subMenuWidth: 0,
                verticalMenu: false,
                checkForWindowSize: false
            }, settings);

            //--OpeningDirection ENUM----------------------------------------
            var OpeningDirection = { Right: "right", Left: "left" };

            //--Navigation CLASS---------------------------------------------

            function Navigation(obj, level, parentNavigationItem) {
                this.Type = "Navigation";
                this.ID = parentNavigationItem == null ? level.toString() : parentNavigationItem.ID + "_" + level;
                this.baseObject = obj;
                this.level = level;
                this.items = _getNavigationItems(this, this.level);
                this.parentNavigationItem = parentNavigationItem == null ? null : parentNavigationItem;
                this.parentNavigation = parentNavigationItem == null ? null : parentNavigationItem.parentNavigation;
                this.openingDirection = null;

                this.addHtmlData = function (obj) {
                    $(obj.baseObject).attr('navigationLevel', obj.level);
                };

                this.setInitialState = function (obj) {
                    $(obj.baseObject).css({ 'list-style-type': 'none', 'z-index': '1' });
                    if (obj.level > 0) {
                        $(obj.baseObject)
                            .css({
                                'position': 'absolute',
                                'width': settings.subMenuWidth + 'px'
                            })
                            .hide();
                    }
                };

                this.open = function () {
                    if (this.level == 1 && !settings.verticalMenu) {
                        $(this.baseObject).show();
                    }

                    if (this.level > 1 || settings.verticalMenu) {

                        //set in which direction the menu need to be open
                        if (settings.checkForWindowSize) {
                            if (this.parentNavigation.openingDirection == OpeningDirection.Right || this.parentNavigation.openingDirection == null) {

                                var windowWidth = $(window).width();
                                var maxOffset = windowWidth - (2 * settings.subMenuWidth);

                                this.openingDirection = $(this.parentNavigationItem.baseObject).offset().left > maxOffset ?
                                    OpeningDirection.Left : OpeningDirection.Right;
                            }
                            if (this.parentNavigation.openingDirection == OpeningDirection.Left) {
                                this.openingDirection = $(this.parentNavigationItem.baseObject).offset().left < settings.subMenuWidth ?
                                    OpeningDirection.Right : OpeningDirection.Left;
                            }
                        } else {
                            this.openingDirection = OpeningDirection.Right;
                        }

                        $(this.baseObject)
                            .css({
                                'left': this.openingDirection == OpeningDirection.Right ?
                                    (settings.verticalMenu && this.parentNavigation.level == 0 ?
                                        $(this.parentNavigationItem.baseObject).width() + 'px'
                                        : settings.subMenuWidth + 'px')
                                    : -settings.subMenuWidth + 'px',
                                'top': '0px'
                                /*'z-index': $(this.parentNavigation.baseObject).css('z-index') == "NaN" || $(this.parentNavigation.baseObject).css('z-index') == "auto" ?
                                1 : parseInt($(this.parentNavigation.baseObject).css('z-index')) + 1*/
                            })
                            .show();
                    }
                };

                this.close = function () {
                    this.openingDirection = null;
                    $(this.baseObject).hide();
                };

                this.addHtmlData(this);
                this.setInitialState(this);
            }

            //--NavigationItem CLASS---------------------------------------------

            function NavigationItem(obj, level, index, parentNavigation) {
                this.Type = "NavigationItem";
                this.ID = parentNavigation.ID + "-" + index;
                this.baseObject = obj;
                this.level = level;
                this.parentNavigation = parentNavigation;
                this.subNavigation = $(obj).children('ul:first').length > 0 ? new Navigation($(obj).children('ul:first'), level + 1, this) : null;
                this.hasSubNavigation = this.subNavigation != null;

                this.setInitialState = function (obj) {
                    //$(obj.baseObject).css({ 'position': 'relative'});
                    if (obj.level == 0 && !settings.verticalMenu) {
                        $(obj.baseObject).css({ 'float': 'left' });
                    }
                };

                this.setHoverBehavior = function (obj) {
                    $(obj.baseObject).hover(
                        function () {
                            if (obj.subNavigation != null && obj.subNavigation.baseObject != null) {
                                obj.subNavigation.open();
                            }

                        },
                        function () {
                            if (obj.subNavigation != null && obj.subNavigation.baseObject != null) {
                                obj.subNavigation.close();
                            }
                        });
                };

                this.setInitialState(this);
                this.setHoverBehavior(this);
            }

            //--global methods---------------------------------------------------

            function _getNavigationItems(navigationObj, level) {
                var items = new Array();
                $(navigationObj.baseObject).children('li').each(function () {
                    var item = new NavigationItem($(this), level, $(this).index(), navigationObj);
                    items.push(item);
                });

                return items;
            }

            function _getsubLevelWidth(itemCollection) {
                var itemMaxWidth = 0;

                $(itemCollection)
                    .each(function () {
                        var itemWidth = $(this).outerWidth(true);

                        if (itemWidth > itemMaxWidth)
                            itemMaxWidth = itemWidth;
                    });

                return itemMaxWidth;
            }

            //xxx--------------MAIN CODE--------------xxx
            var jQueryMatchedObj = null;
            var navigation = null;

            function _initNavigationMenu() {
                jQueryMatchedObj = this;

                settings.subMenuWidth = settings.subMenuWidth == null || settings.subMenuWidth == 0 ?
                    _getsubLevelWidth($(jQueryMatchedObj).find("ul").find("a"))
                    : settings.subMenuWidth;

                navigation = new Navigation(jQueryMatchedObj, 0, null);
            }

            return this.each(_initNavigationMenu);
        },


        /* --- RatingTA v2.0 --- */
        RatingTA: function (pictureNonRated, pictureRated, settings) {
            settings = jQuery.extend({
                ratingSteps: 5,
                readOnly: false,
                onRatingElementClick: false,
                onFinish: false
            }, settings);

            var jQueryMatchedObj = null;

            function _setInitialState() {
                //TODO: need to build rating images dynamically
                $(jQueryMatchedObj)
                    .children(".ratingElement")
                    .each(function () {
                        var elementValue = parseInt($(this).attr("alt"));
                        var ratingValue = parseInt($(jQueryMatchedObj).find(".ratingValue").val());

                        if (elementValue <= ratingValue) {
                            $(this).attr("src", pictureRated);
                        } else {
                            $(this).attr("src", pictureNonRated);
                        }
                    });
            }

            function _initRatingTA() {
                jQueryMatchedObj = this;

                //building HTML rating images            
                $(jQueryMatchedObj).find(".ratingValue:input")
                    .each(function () {

                        //create 'ratingElements'
                        for (var i = 0; i < settings.ratingSteps; i++) {
                            var ratingPicture = $('<img />')
                                .insertBefore($(this))
                                .attr('src', pictureNonRated)
                                .attr('alt', i + 1)
                                .addClass('ratingElement');

                            //Assign Hover/Click behavior on each element
                            if (!settings.readOnly) {
                                $(ratingPicture)
                                    .css({ 'cursor': 'pointer' })
                                    .hover(function () {
                                        var realtedRatingElements = $(this).parent().children(".ratingElement");
                                        var elementHoverValue = parseInt($(this).attr("alt"));

                                        $(realtedRatingElements).each(function () {
                                            var elementValue = parseInt($(this).attr("alt"));

                                            if (elementValue <= elementHoverValue) {
                                                $(this).attr("src", pictureRated);
                                            } else {
                                                $(this).attr("src", pictureNonRated);
                                            }
                                        });
                                    },
                                        function () {
                                            _setInitialState($(this).parent());
                                        })
                                    .click(function () {
                                        var ratingInput = $(this).parent().children(".ratingValue");

                                        $(ratingInput).val($(this).attr("alt"));
                                        _setInitialState();

                                        eval(settings.onRatingElementClick);
                                    });
                            }
                        }

                        eval(settings.onFinish);
                    });

                //Change the picture element depending of the rating value (=Get)
                _setInitialState();
            }

            return this.each(_initRatingTA);
        },


        /* --- ItemSliderTA v1.0 --- */
        ItemSliderTA: function (itemWidth, itemHeight, settings) {
            settings = jQuery.extend({
                padding: 0, //=> padding for each item
                numberOfLines: 1, //=> number of item per column
                itemsPerLine: 2,  //=> number of colums
                itemsPerPage: 10,
                nextButtonSelector: '',
                previewButtonSelector: '',
                slideValue: 1,  //=> number of items to slide at once
                slideLoop: false,
                frameClass: null
            }, settings);


            var _$this = this;
            var _numberOfItems = _$this.children().length;

            //--reusable methods--
            var setWidthFunction = function (width) {
                this.$Object.width(width);
            };
            var setHeightFunction = function (height) {
                this.$Object.height(height);
            };
            //-/reusable methods--

            //--------------------------------------------------------------------------------------------

            function Frame() {
                this.$Object = $('<div />');

                if (settings.frameClass != null && $.type(settings.frameClass) == "string") {
                    this.$Object.addClass(settings.frameClass);
                }

                this.Width = (itemWidth + settings.padding) * settings.itemsPerLine - settings.padding;
                this.Height = itemHeight * settings.numberOfLines;
                this.Slider = new Slider();

                this.SetWidth = setWidthFunction;
                this.SetHeight = setHeightFunction;

                //direct code
                this.Slider.$Object.appendTo(this.$Object);
                this.SetWidth(this.Width);
                this.SetHeight(this.Height);
                this.$Object.css('overflow', 'hidden');
            }

            ;

            function SliderButton($obj, slider, slideDirectionRight, slideSteps) {
                this.$Object = $obj;
                this.SlideDirection = slideDirectionRight;
                this.SlideSteps = slideSteps;


                //direct code
                this.$Object.click(function () {
                    slider.Slide(slideDirectionRight, slideSteps);
                });

                //If the items are less than the items per line, we hide the sliding buttons: infact there's no sliding behaviour in such a case.
                if (_numberOfItems <= settings.itemsPerPage) {
                    this.$Object.hide();
                }
            }

            ;

            function Slider() {

                //--private methods
                var dummies = Math.floor(settings.itemsPerLine / 2); //number of dummies to put at the beginning and at the end => total 2 * dummies

                var fillSliderColumnList = function (slider) {
                    var columnList = new Array();

                    numberofcolumns = Math.floor(_numberOfItems / settings.numberOfLines) + (_numberOfItems % settings.numberOfLines > 0 ? 1 : 0);

                    //build an array of Slider columns (<li>) and add them to the Slider (<ul>)
                    for (var c = 0; c < numberofcolumns; c++) {
                        var slidercolumn = new SliderColumn(c);
                        columnList.push(slidercolumn);
                        //slidercolumn.$Object.appendTo(slider.$Object);
                    }
                    ;

                    return columnList;
                };

                var fillSliderPageList = function (slider) {
                    var pageList = new Array();

                    numberofpages = Math.floor(_numberOfItems / settings.itemsPerPage) + (_numberOfItems % settings.itemsPerPage > 0 ? 1 : 0);

                    //build an array of Slider columns (<li>) and add them to the Slider (<ul>)
                    for (var p = 0; p < numberofpages; p++) {
                        var sliderpage = new SliderPage(p);
                        pageList.push(sliderpage);
                        sliderpage.$Object.appendTo(slider.$Object);
                    }
                    ;

                    return pageList;
                };

                var fillSliderItemList = function (slider) {

                    if (settings.itemsPerPage == null) {
                        var columnIndex = 0; //counter
                        for (var i = 0; i < _numberOfItems; i++) {

                            //create the Item
                            var sliderItem = new SliderColumnItem(i);
                            sliderItem.$Object.appendTo(slider.SliderColumnList[columnIndex].$Object);

                            if ((i + 1) % settings.numberOfLines == 0)
                                columnIndex++;
                        }
                    } else {
                        var pageIndex = 0; //counter
                        var itemsAdded = 0; //counter

                        for (var i = 0; i < _numberOfItems; i++) {
                            //create the Item
                            var sliderItem = new SliderPageItem(i);
                            sliderItem.$Object.appendTo(slider.SliderPageList[pageIndex].$Object);
                            itemsAdded++;
                            if (itemsAdded >= settings.itemsPerPage) {
                                pageIndex++;
                                itemsAdded = 0;
                            }
                        }
                    }
                };
                //-/private methods

                this.$Object = $('<div></div>');
                this.SliderColumnList = fillSliderColumnList(this);
                this.SliderPageList = fillSliderPageList(this);
                this.SliderItemList = fillSliderItemList(this);
                //                this.Width = (this.SliderColumnList.length * (itemWidth + settings.padding)) + 20;
                this.Width = (this.SliderPageList.length * (itemWidth + settings.padding) * settings.itemsPerLine) + 20;
                this.Height = settings.numberOfLines * itemHeight;

                //alert(this.Width);

                this.PageWidth = settings.slideValue * (itemWidth + settings.padding); // each item width plus padding
                this.Pages = Math.floor(this.SliderColumnList.length / settings.slideValue) + (this.SliderColumnList.length % settings.slideValue != 0 ? 1 : 0);
                this.CurrentPage = 1;
                this.Position = 0;
                //alert(this.SliderColumnList.length);
                this.MaxPosition = settings.slideValue > 1 ?
                    ((this.Pages - 1) * this.PageWidth) * -1 :
                    ((this.SliderColumnList.length - settings.itemsPerLine) * (itemWidth + settings.padding)) * -1;


                this.SliderButtonNext = settings.nextButtonSelector != null && settings.nextButtonSelector != '' ?
                    new SliderButton($(settings.nextButtonSelector), this, true, settings.slideValue) :
                    null;
                this.SliderButtonPrevious = settings.previewButtonSelector != null && settings.previewButtonSelector != '' ?
                    new SliderButton($(settings.previewButtonSelector), this, false, settings.slideValue) :
                    null;

                this.SetWidth = setWidthFunction;
                this.SetHeight = setHeightFunction;

                this.Slide = function (slideDirectionRight, slideSteps) {
                    this.Position = slideDirectionRight ? this.Position - this.PageWidth : this.Position + this.PageWidth;

                    //loop behavior
                    if (settings.slideLoop) {
                        this.Position = this.Position > 0 ? this.MaxPosition :
                            this.Position < this.MaxPosition ? 0 : this.Position;
                    }
                        //no loop behavior
                    else {
                        this.Position = this.Position > 0 ? 0 :
                            this.Position < this.MaxPosition ? this.MaxPosition : this.Position;
                    }

                    this.$Object.animate({ marginLeft: this.Position + 'px' }, 'slow');
                };

                // TODO: build paging behavior
                this.BuildPaging = null;

                //direct code
                this.SetHeight(this.Height);
                this.SetWidth(this.Width);
                this.$Object.css({ 'list-style-type': 'none', 'margin': '0', 'padding': '0' });
            }

            ;


            function SliderColumn(index) {
                this.$Object = $('<li></li>').attr('sliderColumnIndex', index);
                this.Index = index;
                this.Width = itemWidth;
                this.Height = itemHeight * settings.numberOfLines;

                this.SetWidth = setWidthFunction;
                this.SetHeight = setHeightFunction;

                //direct code
                this.SetHeight(this.Height);
                this.SetWidth(this.Width);
                this.$Object.css({ 'display': 'block', 'float': 'left' });
            }

            ;

            function SliderPage(index) {
                this.$Object = $('<ul></ul>')
                    .attr({
                        'class': 'paging',
                        'sliderPageIndex': index
                    })
                    .css(
                        {
                            'width': itemWidth * settings.itemsPerLine,
                            'margin': '0',
                            'padding': '0'
                        });
                this.Index = index;

            }

            ;

            function SliderColumnItem(index) {
                //members
                this.$Object = $('<div></div>').attr('sliderColumnItem', index);
                this.Width = itemWidth + settings.padding;
                this.Height = itemHeight;

                this.SetWidth = setWidthFunction;
                this.SetHeight = setHeightFunction;

                //direct code
                this.SetWidth(this.Width);
                this.SetHeight(this.Height);
                this.$Object.css({
                    'overflow': 'hidden'
                });
                //direct code
                if (index != -1) // -1 if dummy
                    this.$Object.html(_$this.children('li:eq(' + index + ')').html());
            }

            ;

            function SliderPageItem(index) {
                //members
                this.$Object = $('<li></li>').attr('sliderPageItem', index);
                this.Width = itemWidth + settings.padding;
                this.Height = itemHeight;

                this.SetWidth = setWidthFunction;
                this.SetHeight = setHeightFunction;

                //direct code
                this.SetWidth(this.Width);
                this.SetHeight(this.Height);
                this.$Object.css({
                    'overflow': 'hidden'
                });
                //direct code
                if (index != -1) // -1 if dummy
                    this.$Object.html(_$this.children('li:eq(' + index + ')').html());
            }

            ;

            //--------------------------------------------------------------------------------------------

            var $ItemSliderTA = $.ItemSliderTA = jQuery.extend({
                _init: function () {
                    var itemSlider = new Frame();

                    itemSlider.$Object.insertAfter(_$this);
                    _$this.remove();
                }
            });

            return _$this.each($ItemSliderTA._init);
        },

        /* --- CharLimit v2.0 --- */
        CharLimit: function (settings) {
            settings = this.extend({
                charsCount: 0,
                height: 0,
                ellipsis: " ...",
                ellipsisCssClass: "",
                wordWrap: false,
                onEllipsisClick: null,
                expandCollapse: ""       // fn or string("expand") or string("expand-collapse|collapseEllisis)")
            }, settings);

            var _betweenTagSpacesRegEx = new RegExp(">\\s+<", "g");
            var _multipleSpacesRegEx = new RegExp("\\s+", "g");

            // height on which content needs to be limited
            var level0ContentDom = null;

            // CharLimitEllipsis added to the level 0 ContentDom 
            var level0Ellipsis = null;

            var $variableHeightObj = null;

            var HeightLimit = 0;

            //--- CLASS ContentDom ---
            // obj: the dom object
            // domLevel: level of the DOM in the parent/child hierarchy

            function ContentDom(obj, domLevel, parentContentDom) {

                // Jquery DOM object
                this.$Object = $(obj);
                // Node Name of the DOM 
                this.NodeName = obj.nodeName.toLowerCase();
                // level of the DOM in the parent/child hierarchy
                this.Level = domLevel == null ? 0 : domLevel;
                // Since IE9 need to remove all whitespaces from dom content
                this.GetTrimmedHtml = function () {
                    if (this.NodeName == '#text')
                        return $.trim(this.$Object[0].nodeValue).replace(_multipleSpacesRegEx, ' ');
                    else
                        return $.trim(this.$Object.html()).replace(_multipleSpacesRegEx, ' ').replace(_betweenTagSpacesRegEx, '><');
                };
                this.HTMLText = this.GetTrimmedHtml();
                // Html content of the DOM + remove any space between DOMs
                this.HTML = this.$Object.html(this.HTMLText).html();
                // Text content of the DOM
                this.Text = this.NodeName == '#text' ? this.$Object.text().replace(_multipleSpacesRegEx, ' ') : $.trim(this.$Object.text().replace(_multipleSpacesRegEx, ' '));
                // Total length of Text contained in DOM + children DOM + children text nodes 
                this.TotalTextLength = this.Text.length;
                // Gets the colletion of ContentDom representing the full content of the current parent ContentDom
                this.GetSubDomList = function () {
                    var subdomlist = new Array();

                    var contents = this.$Object.contents();
                    for (var i = 0; i < contents.length; i++) {
                        subdomlist.push(new ContentDom(contents[i], this.Level - 1, this));
                    }

                    return subdomlist;
                };
                // colletion of ContentDom representing the full content of the current parent ContentDom
                this.SubDomList = this.GetSubDomList();
                // Maximum height of the DOM (if DOM is level 0 => set the height on which content needs to be limited)
                this.MaxHeight = this.SubDomList.length != 0 ? this.$Object.height() : 0;
                // Tells if the DOM is limited
                this.IsLimited = false;
                // Attach data on the DOM
                this.SetData = function () {
                    var _this = this;
                    _this.$Object.data('CharLimit', {
                        isLimited: _this.IsLimited,
                        initialHTML: _this.HTML
                    });
                };


                // Limits the content of the DOM based on a char count
                this.LimitByCharCount = function (charCount) {

                    if (charCount >= this.TotalTextLength) {
                        return;
                    }

                    //empty parent object
                    this.$Object.empty();

                    var availableCharCount = charCount;

                    for (var i = 0; i < this.SubDomList.length; i++) {
                        //check if child text is lower than charcount
                        var child = this.SubDomList[i];
                        if (child.TotalTextLength < availableCharCount) {
                            //append child and reduce contentLength from remaining available char count
                            this.$Object.append(child.$Object);
                            availableCharCount = availableCharCount - child.TotalTextLength;
                        } else {
                            // if only text node is remaining
                            if (child.SubDomList.length == 0) {
                                child.$Object[0].nodeValue = child.Text.substring(0, availableCharCount); //REMARK: wonder if it's the way to do this
                                this.$Object.append(child.$Object);
                            } else {
                                child.LimitByCharCount(availableCharCount);
                                this.$Object.append(child.$Object);
                            }
                            break;
                        }
                    }

                    if (this.Level == 0) {
                        // set IsLimited and Data
                        level0ContentDom.IsLimited = true;
                        level0ContentDom.SetData();

                        // add the ellipsis to the first level object
                        this.$Object.append(new CharLimitEllipsis().$Object)
                    }

                    return this;
                };


                // Limits the content of the DOM based on its height
                this.LimitByHeight = function (forcedHeight) {

                    // code to apply only on the level 0 container
                    if (this.Level == 0) {

                        // add the ellipsis to the first level object
                        level0Ellipsis = new CharLimitEllipsis();

                        if (forcedHeight != 0) {
                            HeightLimit = forcedHeight;
                        } else {
                            HeightLimit = level0ContentDom.$Object.empty().height();
                            level0ContentDom.$Object.html(level0ContentDom.HTML);
                        }

                        // test content is shorter than HeightLimit
                        if (HeightLimit == 0 || HeightLimit > level0ContentDom.$Object.css('height', 'auto').height()/*level0ContentDom.MaxHeight*/) {
                            level0ContentDom.$Object.css('height', level0ContentDom.MaxHeight);
                            return;
                        }

                        $variableHeightObj = level0ContentDom.$Object.css('height', 'auto');
                    }

                    this.$Object.empty();

                    var parent = this.$Object;

                    for (var i = 0; i < this.SubDomList.length; i++) {
                        //check if child text is lower than charcount
                        var child = this.SubDomList[i];

                        // insert child                        
                        //child.$Object.appendTo($variableHeightObj);
                        child.$Object.appendTo(parent);

                        // check is max height is not reached
                        if ($variableHeightObj.append(level0Ellipsis.$Object).height() < HeightLimit) {
                            level0Ellipsis.$Object.remove();
                            $.noop() //=> continue with next child 
                            //child.LimitByHeight();
                        } else {
                            level0Ellipsis.$Object.remove();
                            //child.$Object.remove();

                            //if child has no children => node == <br /> || nodeName == '#text'
                            if (child.SubDomList.length == 0) {
                                if (child.NodeName == '#text') {
                                    child.$Object[0].nodeValue = "";
                                    //child.$Object.appendTo($variableHeightObj);
                                    child.$Object.appendTo(parent);

                                    // add text content and limit using dichotomic way
                                    var fill = function (content) {
                                        if (content.length > 0) {

                                            var currentNodeValue = child.$Object[0].nodeValue;
                                            var cutIndex = Math.round(content.length / 2);
                                            var firstHalf = content.substring(0, cutIndex);
                                            var secondHalf = content.substring(cutIndex);

                                            child.$Object[0].nodeValue += firstHalf;
                                            if ($variableHeightObj.append(level0Ellipsis.$Object).height() > HeightLimit) {
                                                level0Ellipsis.$Object.remove();

                                                child.$Object[0].nodeValue = currentNodeValue;
                                                if (firstHalf.length > 1)
                                                    fill(firstHalf);
                                            } else {
                                                currentNodeValue = child.$Object[0].nodeValue;

                                                child.$Object[0].nodeValue += secondHalf;
                                                if ($variableHeightObj.append(level0Ellipsis.$Object).height() > HeightLimit) {
                                                    level0Ellipsis.$Object.remove();

                                                    child.$Object[0].nodeValue = currentNodeValue;
                                                    if (secondHalf.length > 1)
                                                        fill(secondHalf);
                                                }
                                                level0Ellipsis.$Object.remove();
                                            }
                                            level0Ellipsis.$Object.remove();
                                        }
                                    };
                                    fill(child.Text);

                                    if (settings.wordWrap == true)
                                        child.$Object[0].nodeValue = child.$Object[0].nodeValue.substring(0, child.$Object[0].nodeValue.lastIndexOf(" "));

                                }
                            }
                                //if child has children
                            else {
                                child.$Object.empty();
                                child.LimitByHeight();
                            }
                            break;
                        }
                    }

                    if (this.Level == 0) {
                        var lastchild = level0ContentDom.$Object.children().last();
                        if (lastchild.html() == "") {
                            lastchild.remove();
                        }

                        level0ContentDom.$Object.css('height', forcedHeight != 0 ? forcedHeight : level0ContentDom.MaxHeight);

                        // set IsLimited and Data
                        level0ContentDom.IsLimited = true;
                        level0ContentDom.SetData();

                        level0ContentDom.$Object.append(new CharLimitEllipsis().$Object);
                    }

                    return this;
                };

                // Limits the content of the DOM
                this.Limit = function () {
                    //check if Dom has been limited before
                    var data = this.$Object.data("CharLimit");
                    if (data == null || data.isLimited == false) {
                        this.$Object.removeData("CharLimit");
                        if (settings.charsCount > 0)
                            return this.LimitByCharCount(settings.charsCount);
                        else
                            return this.LimitByHeight(settings.height);
                    }
                };


                //--CONTENTDOM MAIN CODE--
                if (this.Level == 0)
                    level0ContentDom = this;

                return this;
            }

            ;


            //--- CLASS CharLimitEllipsis ---

            function CharLimitEllipsis() {

                var expandCollapse = function () {
                    if (settings.expandCollapse == "expand") {
                        level0ContentDom
                            .$Object
                            .css('height', 'auto')
                            .DisableCharLimit();
                    }
                    if (settings.expandCollapse.indexOf('expand-collapse') == 0 && settings.expandCollapse.indexOf('|') != -1) {
                        //var collapseEllipsis = settings.onEllipsisClick.split('|')[1];
                        var collapseEllipsis = settings.expandCollapse.substring(settings.expandCollapse.indexOf('|') + 1);
                        if (settings.charsCount > 0) {
                            level0ContentDom
                                .$Object
                                .DisableCharLimit({
                                    ellipsis: collapseEllipsis,
                                    ellipsisCssClass: settings.ellipsisCssClass,
                                    onEllipsisClick: function () {
                                        level0ContentDom
                                            .$Object
                                            .CharLimit(settings);
                                    }
                                });
                        } else {
                            level0ContentDom
                                .$Object
                                .css('height', 'auto')
                                .DisableCharLimit({
                                    ellipsis: collapseEllipsis,
                                    ellipsisCssClass: settings.ellipsisCssClass,
                                    onEllipsisClick: function () {
                                        level0ContentDom
                                            .$Object
                                            .height(level0ContentDom.MaxHeight)
                                            .CharLimit(settings);
                                    }
                                });
                        }
                    }
                };

                this.$Object = $('<span />')
                    .addClass('charLimitEllipsis')
                    .addClass(settings.ellipsisCssClass)
                    .html(settings.ellipsis)
                    .click(function (event) {
                        if (settings.onEllipsisClick != null || settings.expandCollapse != "") {
                            event.preventDefault();

                            expandCollapse();
                            switch ($.type(settings.onEllipsisClick)) {
                                case "function":
                                    settings.onEllipsisClick();
                                    break;
                                case "string":
                                    eval(settings.onEllipsisClick);
                                    break;
                            }
                        }
                    });

                // Html content of the ellepsis
                this.HTML = settings.ellipsis;

                //--Main Code--
                return this;
            }

            ;

            return this.each(function () {
                //var test = $(this).contents();
                var contentDom = new ContentDom(this);
                contentDom.Limit();
            });
        },

        DisableCharLimit: function (settings) {

            settings = this.extend({
                ellipsis: "",
                ellipsisCssClass: "",
                onEllipsisClick: null
            }, settings);

            function UnLimitedContent(obj) {
                this.$Object = $(obj);

                this.Data = this.$Object.data("CharLimit");

                this.ResetText = function () {
                    var initialhtml = this.Data.initialHTML;
                    this.$Object
                        .empty()
                        .html(initialhtml);

                    if (settings.ellipsis != null && settings.ellipsis != "") {
                        this.$Object.append(new DisableCharLimitEllipsis().$Object);
                    }

                    return this;
                };

                this.UpdateData = function () {
                    var data = this.Data
                    $.extend(data, {
                        isLimited: false
                    });

                    return this;
                };

                return this.ResetText().UpdateData().$Object;
            }

            ;

            //--- CLASS DisableCharLimitEllipsis ---

            function DisableCharLimitEllipsis() {
                this.$Object = $('<span />')
                    .addClass('disableCharLimitEllipsis')
                    .addClass(settings.ellipsisCssClass)
                    .html(settings.ellipsis)
                    .click(function (event) {
                        event.preventDefault();

                        $(this).remove();

                        switch ($.type(settings.onEllipsisClick)) {
                            case "function":
                                settings.onEllipsisClick();
                                break;
                            case "string":
                                eval(settings.onEllipsisClick);
                                break;
                        }
                    });
                // Html content of the ellepsis
                this.HTML = settings.ellipsis;

                //--Main Code--
                return this;
            }

            ;

            return this.each(function () {
                new UnLimitedContent(this);
            });
        },

        /* --- DefaultTextTA v1.0 --- */
        DefaultTextTA: function (settings) {
            settings = jQuery.extend({
                CssClassWhenDefault: null,
                SubmitElementClass: null,
                onInputFill: false
            }, settings)

            var jQueryMatchedObject = this;

            function _initDefaultTextInputTA() {
                $(jQueryMatchedObject).find("*[defaulttextvalue]").each(function (i) {

                    //record the initial value in an attribute "initialvalue"        
                    if ($(this).is("[alwaysdefault=true]")) {
                        $(this)
                            .val($(this).attr("defaulttextvalue"))
                            .addClass(settings.CssClassWhenDefault);
                    }
                    $(this).attr("initialvalue", $(this).attr("value"));


                    //if value is empty, replace by the default value
                    if ($(this).val() == "") {
                        $(this)
                            .val($(this).attr("defaulttextvalue"))
                            .addClass(settings.CssClassWhenDefault);
                    }

                    //set the onfocus event: "select or remove input content"
                    $(this).focus(function () {
                        if ($(this).val() == $(this).attr("defaulttextvalue")) {
                            $(this)
                                .val("")
                                .removeClass(settings.CssClassWhenDefault);
                        } else {
                            $(this).select();
                        }
                    });

                    //set the onblur event: "reset the input initial or default value" 
                    $(this).blur(function () {
                        if ($(this).val() == "") {
                            if (($(this).attr("initialvalue") == "") || ($(this).attr("initialvalue") == $(this).attr("defaulttextvalue"))) {
                                $(this)
                                    .val($(this).attr("defaulttextvalue"))
                                    .addClass(settings.CssClassWhenDefault);
                            } else
                                $(this).val($(this).attr("initialvalue"));
                        }
                    });

                    //set the onkeyup event:"enable validate widget related submit button"
                    $(this).keyup(function () {
                        eval(settings.onInputFill);
                    });
                });

                //add on the click event the function that clears the input values when containing default value
                $(jQueryMatchedObject).find(":submit ").each(function (i) {
                    $(this).click(function () {
                        ClearValue();
                    });
                });
                if (settings.SubmitElementClass != null) {
                    $(jQueryMatchedObject).find("." + settings.SubmitElementClass).each(function (i) {
                        $(this).click(function () {
                            ClearValue();
                        });
                    });
                }
            }

            ;

            function ClearValue(widgetId) {
                $(jQueryMatchedObject).find("*[defaulttextvalue]").each(function (i) {
                    //when value is the default one, just replace it by empty
                    if ($(this).val() == $(this).attr("defaulttextvalue"))
                        $(this).val("");
                });
            }

            ;

            return this.each(_initDefaultTextInputTA);
        },

        /* --- PrintWidgetTA v1.0 --- */
        PrintWidget: function (settings) {
            settings = jQuery.extend({
                cssPrintFile: "",
                height: "600",
                width: "1000",
                autoPrint: false
            }, settings);

            var $objets = this;
            var domainURL = window.location.protocol + "//" + document.domain + (window.location.port != null ? ":" + window.location.port : "");

            Methods = {
                BuildPrintWindowHead: function () {
                    var printWindowHead = $('<head />');

                    $(window.document.documentElement)
                        .children("head")
                        .children("link[rel='stylesheet']")
                        .clone()
                        .each(function () {
                            var hrefValue = $(this).attr('href');
                            if (hrefValue.indexOf(domainURL, 0) == -1)
                                $(this).attr('href', domainURL + hrefValue);
                        })
                        .appendTo(printWindowHead);

                    if (settings.cssPrintFile != "")
                        $('<link />')
                            .attr({
                                'type': 'text/css',
                                'rel': 'stylesheet',
                                'href': domainURL + settings.cssPrintFile
                            })
                            .appendTo(printWindowHead);

                    return printWindowHead;
                },
                BuildPrintWindowBody: function () {
                    var printWindowBody = $('<body />');

                    $objets.each(function (i) {
                        $(this).clone().appendTo(printWindowBody);
                    });

                    printWindowBody
                        .find("img")
                        .each(function () {
                            var srcValue = $(this).attr('src');
                            if (srcValue.indexOf('http', 0) == -1 && srcValue.indexOf(domainURL, 0) == -1)
                                $(this).attr('src', domainURL + srcValue);
                        });

                    return printWindowBody;
                },

                CreatePrintWindow: function () {
                    var newWindow = window.open("", "Print", "dependent=yes, menubar=yes, toolbar=no, scrollbars=yes, location=no, height=" + settings.height + ", width=" + settings.width + ", resizable=yes");

                    $(newWindow.document)
                        .find('head')
                        .html(Methods.BuildPrintWindowHead().html())
                        .end()
                        .find('body')
                        .html(Methods.BuildPrintWindowBody().html());

                    return newWindow;
                },

                Init_PrintWidget: function () {
                    var newWindow = this.CreatePrintWindow();

                    if (settings.autoPrint) {
                        newWindow.focus();
                        newWindow.print();
                    }
                }
            };
            Methods.Init_PrintWidget();
        },

        /* --- product selector basket v1.0 --- */
        basket: function (method) {

            var defaults = {
                basketMaxSize: 5,
                cocName: null,
                themeName: null,
                publicationUrlTitle: null,
                updateSelectionAction: null,
                loadBasketAction: null,
                removeFromBasketAction: null,
                targetSelector: null,
                disableLoader: false,
                onBasketInitialized: function () { return true; },
                onBasketChanged: function () {
                    alert(Globalization.GetLocalizedString('ImplementOnBasketChangedError'));
                    return false;
                },
                onBasketFull: function () {
                    alert(Globalization.GetLocalizedString('ImplementOnBasketFullError'));
                    return false;
                },
                onItemRemoving: null,
                onBasketChanging: null
            };

            var _options = null;
            var _initialized = false;

            var methods = {
                // Public use
                toggleItem: function (itemId) {
                    if (!_options) {
                        _options = $(this).data('basket-options');
                    }
                    methods.showBasket();

                    var timestamp = new Date().getTime().toString();
                    methods._queueRequest(timestamp);

                    var itemIds = methods.selectedItemIds();
                    if ($.inArray(itemId, itemIds) < 0) {
                        // ADD
                        if (itemIds.length >= _options.basketMaxSize) {
                            methods._dequeueRequest(timestamp);
                            _options.onBasketFull();
                            return;
                        }
                        methods._addToHiddenField(itemId);
                    } else {
                        // DELETE
                        methods._removeFromHiddenField(itemId);
                    }

                    var updateSelectionParams = {
                        cocName: _options.cocName,
                        themeName: _options.themeName,
                        publicationUrlTitle: _options.publicationUrlTitle,
                        productId: itemId,
                        timestamp: timestamp
                    };

                    methods._updateSelection(updateSelectionParams);
                },

                // Public use
                showBasket: function () {
                    if (!_options) {
                        _options = $(this).data('basket-options');
                    }
                    $(_options.targetSelector).find('.jq_Content').show(); // slideDown('fast', function () { });
                },

                // Public use
                hideBasket: function () {
                    if (!_options) {
                        _options = $(this).data('basket-options');
                    }
                    $(_options.targetSelector).find('.jq_Content').slideUp('fast', function () {
                    });
                },

                // Public use
                selectedItemIds: function (useBasketAsSource) {
                    if (!_options) {
                        _options = $(this).data('basket-options');
                    }
                    var itemIds = [];
                    if (useBasketAsSource == true) {
                        $(_options.targetSelector).find('.productList > li').each(function (index, item) {
                            var $item = $(item);
                            if (!$item.hasClass('empty')) {
                                if ($item.attr('id')) {
                                    itemIds.push($item.attr('id').replace('basket', ''));
                                }
                            }
                        });
                    } else {
                        var $PsBasketItemIds = $('#PsBasketItemIds');
                        if ($PsBasketItemIds.length > 0) {
                            itemIds = $PsBasketItemIds.val().split("|");
                        }
                    }
                    return itemIds;
                },

                // public use
                currentBasketSize: function () {
                    if (!_options) {
                        _options = $(this).data('basket-options');
                    }
                    return methods.selectedItemIds().length;
                },

                _queueRequest: function (timestamp) {
                    var $PsBasketRequests = $("#PsBasketRequests");
                    var tmp = [];
                    if ($PsBasketRequests.val()) {
                        tmp = $PsBasketRequests.val().split("|");
                    }
                    tmp.push(timestamp);
                    $PsBasketRequests.val(tmp.join("|"));
                    //console.log("Queue: ", tmp);
                },

                _dequeueRequest: function (timestamp) {
                    var $PsBasketRequests = $("#PsBasketRequests");
                    var tmp = [];
                    if ($PsBasketRequests.val()) {
                        tmp = $PsBasketRequests.val().split("|");
                    }
                    var idx = $.inArray(timestamp, tmp);
                    if (idx != -1) {
                        tmp.splice(idx, 1);
                    }
                    $PsBasketRequests.val(tmp.join("|"));
                    //console.log("Dequeue: ", tmp);
                },

                // Private use                                
                _loadBasket: function (loadBasketParams) {
                    $.ajax({
                        url: _options.loadBasketAction,
                        type: 'GET',
                        data: loadBasketParams,
                        beforeSend: function () {
                            if (!_options.disableLoader) {
                                $(_options.targetSelector).Loading(true);
                            }
                        },
                        success: function (result) {
                            if (!_options.disableLoader) {
                                $(_options.targetSelector).Loading(false);
                            }
                            $(_options.targetSelector).replaceWith(result);

                            var itemIds = methods.selectedItemIds();
                            $.each(itemIds, function (index, item) {
                                $("#" + "jq_remove_" + item).unbind('click').bind('click', methods._removeItem);
                            });
                            _options.onBasketChanged(itemIds);
                        },
                        error: function () {
                            if (!_options.disableLoader) {
                                $(_options.targetSelector).Loading(false);
                            }
                            $.error(Globalization.GetLocalizedString('LoadBasketError'));
                        }
                    });

                },

                _addToHiddenField: function (itemId) {
                    var $PsBasketItemIds = $('#PsBasketItemIds');
                    if ($PsBasketItemIds.length > 0) {
                        var tmp = [];
                        if ($PsBasketItemIds.val()) {
                            tmp = $PsBasketItemIds.val().split("|");
                        }
                        tmp.push(itemId);
                        $PsBasketItemIds.val(tmp.join("|"));
                    }
                },

                _removeFromHiddenField: function (itemId) {
                    var $PsBasketItemIds = $('#PsBasketItemIds');
                    if ($PsBasketItemIds.length > 0) {
                        var tmp = [];
                        if ($PsBasketItemIds.val()) {
                            tmp = $PsBasketItemIds.val().split("|");
                        }
                        var idx = $.inArray(itemId, tmp);
                        if (idx != -1) {
                            tmp.splice(idx, 1);
                        }
                        $PsBasketItemIds.val(tmp.join("|"));
                    }
                },

                // private use
                _updateSelection: function (updateSelectionParams) {
                    $.ajax({
                        url: _options.updateSelectionAction,
                        type: 'POST',
                        data: updateSelectionParams,
                        beforeSend: function () {
                            if (!_options.disableLoader) {
                                $(_options.targetSelector).Loading(true);
                            }
                        },
                        success: function (newBasketSize) {

                            methods._dequeueRequest(updateSelectionParams.timestamp);

                            if (_options.onBasketChanging == null || (_options.onBasketChanging != null && _options.onBasketChanging() == true)) {

                                var loadBasketParams = {
                                    themeName: _options.themeName,
                                    cocName: _options.cocName,
                                    publicationUrlTitle: _options.publicationUrlTitle,
                                    nocache: new Date().getTime(),
                                    alwaysShowBasket: true
                                };

                                if ($("#PsBasketRequests").val().length <= 0) {
                                    //console.log("Basket load for: ", updateSelectionParams.timestamp);
                                    methods._loadBasket(loadBasketParams);
                                } else {
                                    //console.log("Skip basket load for: ", updateSelectionParams.timestamp);
                                }
                            } else {
                                if (!_options.disableLoader) {
                                    $(_options.targetSelector).Loading(false);
                                }
                            }
                        },
                        error: function () {

                            methods._dequeueRequest(updateSelectionParams.timestamp);

                            if (!_options.disableLoader) {
                                $(_options.targetSelector).Loading(false);
                            }
                            $.error(Globalization.GetLocalizedString('UpdateSelectionError'));
                        }
                    });
                },

                _removeItem: function () {

                    var id = $(this).attr("id").replace('jq_remove_', '');
                    if (id) {

                        var oldIds = methods.selectedItemIds();
                        methods._removeFromHiddenField(id);

                        if (_options.onItemRemoving == null || (_options.onItemRemoving != null && _options.onItemRemoving(id) == true)) {

                            var removeFromBasketParams = {
                                cocName: _options.cocName,
                                themeName: _options.themeName,
                                publicationUrlTitle: _options.publicationUrlTitle,
                                productId: id
                            };

                            $.ajax({
                                url: _options.removeFromBasketAction,
                                type: 'POST',
                                data: removeFromBasketParams,
                                beforeSend: function () {
                                    if (!_options.disableLoader) {
                                        $(_options.targetSelector).Loading(true);
                                    }
                                },
                                success: function (refreshedBasket) {

                                    if (_options.onBasketChanging == null || (_options.onBasketChanging != null && _options.onBasketChanging() == true)) {

                                        if (refreshedBasket) {
                                            $.each(oldIds, function (index, item) {
                                                $("#" + "jq_remove_" + item).unbind('click');
                                            });

                                            if (!_options.disableLoader) {
                                                $(_options.targetSelector).Loading(false);
                                            }
                                            $(_options.targetSelector).replaceWith(refreshedBasket);

                                            var itemIds = methods.selectedItemIds();
                                            $.each(itemIds, function (index, item) {
                                                $("#" + "jq_remove_" + item).bind('click', methods._removeItem);
                                            });

                                            $('#PsBasketItemIds').val(itemIds.join("|"));

                                            _options.onBasketChanged(itemIds);

                                        } else {
                                            if (!_options.disableLoader) {
                                                $(_options.targetSelector).Loading(false);
                                            }
                                        }
                                    } else {
                                        if (!_options.disableLoader) {
                                            $(_options.targetSelector).Loading(false);
                                        }
                                    }

                                },
                                error: function () {
                                    if (!_options.disableLoader) {
                                        $(_options.targetSelector).Loading(false);
                                    }
                                    $.error(Globalization.GetLocalizedString('UpdateSelectionError'));
                                }
                            });
                        }
                    } else {
                        $.error(Globalization.GetLocalizedString('BasketRemoveError'));
                    }
                },

                init: function (options) {

                    _options = $.extend(defaults, options);

                    if (_options.updateSelectionAction && _options.loadBasketAction && _options.removeFromBasketAction && _options.publicationUrlTitle && _options.targetSelector) {
                        _initialized = true;
                    } else {
                        $.error(Globalization.GetLocalizedString('InvalidOptionsError'));
                    }

                    return this.each(function () {
                        if (_initialized) {
                            var $this = $(this);
                            var data = $this.data('basket');

                            if ($(this).find('#PsBasketItemIds').length <= 0) {
                                $(this).append("<input id='PsBasketItemIds' type='hidden'>");
                            }
                            if ($(this).find('#PsBasketRequests').length <= 0) {
                                $(this).append("<input id='PsBasketRequests' type='hidden'>");
                            }

                            if (!data) {
                                $(this).data('basket', '_initialized');
                                $(this).data('basket-options', _options);

                                var itemIds = methods.selectedItemIds(true);

                                $.each(itemIds, function (index, item) {
                                    $("#" + "jq_remove_" + item).bind('click', methods._removeItem);
                                });

                                $('#PsBasketItemIds').val(itemIds.join("|"));

                                // Trigger initial update
                                _options.onBasketInitialized(itemIds);

                            }
                        }
                    });
                }
            };

            if (methods[method]) {
                return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
            } else if (typeof method === 'object' || !method) {
                return methods.init.apply(this, arguments);
            } else {
                $.error(Globalization.GetFormattedLocalizedString('InvalidMethodError', method));
            }
        },

        //--- PictureGallery v1.0.0 ---
        PictureGallery: function (settings) {
            settings = jQuery.extend({
                resizePictureToFitCanvas: true,

                setMaxWidth: false,//force the max-width to not be dynamically calculated

                dialogCssClass: null, //css class especially for the jQuery Dialog plugin (string)
                dialogDraggable: true, //Sets the dialog to be draggable (boolean)

                pictureCanvasWidth: null, //width of the picturegallery (int, null and other value will be turned into '100%')
                pictureCanvasHeight: null, //height of the picturegallery (int, null and other value will be turned into '100%')
                tileSize: 50, //Size of the tiles (int)

                pagingStringPattern: 'Picture {0} of {1}', // where '{0}' will be replace by currentpicture and '{1}' will be replaced by total picture count  (string)

                enableNavigationButtons: true, //Show or hide the navigationbuttons (boolean)
                previousButtonHtml: null, //Html for the previous button (if enableNavigation is unset in option or true)  (string)
                nextButtonHtml: null, //Html for the next button (if enableNavigation is unset in option or true)  (string)

                pictureCanvasCssClass: null, //Css class for the Picture container (string)
                titleCssClass: null, //Css class for the Tile container (string)
                descriptionCssClass: null, //Css class for the Description container (string)
                tilesCssClass: null, //Css class for the Tiles container (string)
                tileCssClass: null, //Css class for the Tile container (string)
                activeTileCssClass: null, //Css class for the Active Tile (string)
                pagerCssClass: null //Css class for the Pager container (string)
            }, settings);
            //--------------------------------------------------------------------------------------------

            (ValidateSettings = function () {
                settings.pictureCanvasWidth = $.type(settings.pictureCanvasWidth) != 'number' ? '100%' : Math.floor(settings.pictureCanvasWidth);
                settings.pictureCanvasHeight = $.type(settings.pictureCanvasHeight) != 'number' ? '100%' : Math.floor(settings.pictureCanvasHeight);
                settings.enableNavigationButtons = $.type(settings.enableNavigationButtons) == 'boolean' ? settings.enableNavigationButtons : false;
            })();

            var $SelectedObject = this;

            $.PictureGallery = {
                Classes: {
                    Base: function () {
                    },

                    Containers: {
                        PopUpWindow: function () {
                            var _this = this;

                            _this.$Object = $('<div />')
                                .addClass('jQueryPictureGalleryPopUpWindow')
                                .css('overflow', 'hidden');

                            _this.Dialog = null;

                            _this.SetDialog = function () {
                                _this.Dialog = _this.$Object
                                    .appendTo('body')
                                    .dialog({
                                        autoOpen: false,
                                        modal: true,
                                        resizable: false,
                                        dialogClass: settings.dialogCssClass,
                                        draggable: settings.dialogDraggable,
                                        width: 'auto',
                                        height: 'auto',
                                        close: function () {
                                            $(_this.Selector).unbind('keydown');
                                        }
                                    });

                                return _this;
                            };

                            _this.Build = function () {
                                $.map(arguments, function (argument) {
                                    _this.$Object.append(argument);
                                });
                                return _this;
                            };

                            _this.Open = function () {
                                _this.Dialog.dialog('open');
                                return _this;
                            };

                            _this.Close = function () {
                                _this.Dialog.dialog('close');
                                return _this;
                            };

                            _this.IsOpen = function () {
                                _this.Dialog.dialog("isOpen");
                                return _this;
                            };

                            _this.Center = function () {
                                _this.Dialog.dialog("option", 'position', 'center');
                                return _this;
                            };

                            _this.Clear = function () {
                                _this.$Object.children().detach();
                                return _this;
                            };

                            _this.Selector = function () {
                                _this.Dialog.dialog('widget');
                                return _this;
                            };
                        },
                        Picture: function () {
                            var _this = this;

                            _this.$Object = $('<div />').addClass('jQueryPictureGalleryPicture').addClass(settings.pictureCanvasCssClass);

                            if (!settings.setMaxWidth) {
                                _this.$Object.css({
                                    'width': settings.pictureCanvasWidth,
                                    'height': settings.pictureCanvasHeight,
                                    'max-width': settings.pictureCanvasWidth,
                                    'max-height': settings.pictureCanvasHeight
                                });
                            }

                            _this.$Object.css({
                                'width': settings.pictureCanvasWidth,
                                'height': settings.pictureCanvasHeight,
                                'max-width': settings.pictureCanvasWidth,
                                'max-height': settings.pictureCanvasHeight
                            });

                            _this.$InnerImage = null;

                            _this.ResizeInnerImage = function () {
                                _this.$InnerImage.css({ 'min-width': 'auto', 'min-height': 'auto' });
                                if (settings.resizePictureToFitCanvas) {
                                    _this.$InnerImage.css({ 'max-width': $(window).width() - 200 + 'px', 'max-height': $(window).height() - 200 + 'px' });
                                }
                            };

                            _this.Update = function ($img, clickEvent) {

                                _this.$InnerImage = $img
                                    .clone()
                                    .click(clickEvent)
                                    .attr({
                                        'src': $img.data('FullSizeUrl'),
                                        'title': $img.data('Title'),
                                        'alt': $img.data('Description')
                                    });

                                if (!settings.setMaxWidth) {
                                    _this.$InnerImage.css({
                                        'max-width': '100%',
                                        'max-height': '100%'
                                    });
                                }

                                _this.$InnerImage.appendTo(_this.$Object.empty());
                            };

                            _this.Load = function (loadedCallback) {
                                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                                // Hack... test if browser is chrome
                                if (!/chrome/.test(navigator.userAgent.toLowerCase()))
                                    _this.ResizeInnerImage();

                                _this.$InnerImage.load(function (event) {
                                    _this.ResizeInnerImage();
                                    if (loadedCallback != null)
                                        loadedCallback();
                                });
                            };
                        },
                        Title: function () {
                            var _this = this;

                            _this.$Object = $('<div />')
                                .addClass('jQueryPictureGalleryTitle')
                                .addClass(settings.titleCssClass);

                            if (!settings.setMaxWidth) {
                                _this.$Object.css({
                                    'width': $.type(settings.pictureCanvasWidth) != 'number' ? 900 : settings.pictureCanvasWidth,
                                    'max-width': $.type(settings.pictureCanvasWidth) != 'number' ? 900 : settings.pictureCanvasWidth
                                });
                            }


                            _this.Update = function (titleText) {
                                _this.$Object
                                    .empty()
                                    .html(titleText);
                            };
                        },
                        Description: function () {
                            var _this = this;

                            _this.$Object = $('<div />')
                                .addClass('jQueryPictureGalleryDescription')
                                .addClass(settings.descriptionCssClass);

                            if (!settings.setMaxWidth) {
                                _this.$Object.css({
                                    'width': $.type(settings.pictureCanvasWidth) != 'number' ? 900 : settings.pictureCanvasWidth,
                                    'max-width': $.type(settings.pictureCanvasWidth) != 'number' ? 900 : settings.pictureCanvasWidth
                                });
                            }


                            _this.Update = function (descriptionText) {
                                _this.$Object
                                    .empty()
                                    .html(descriptionText);
                            };
                        },
                        Tile: function () {
                            var _this = this;

                            _this.$Object = $('<div />')
                                .addClass('jQueryPictureGalleryTile')
                                .width(settings.tileSize)
                                .height(settings.tileSize)
                                .css({
                                    'max-width': settings.tileSize,
                                    'max-height': settings.tileSize
                                })
                                .addClass(settings.tileCssClass);

                            _this.Update = function ($image, clickEvent) {
                                _this.$Object
                                    .empty()
                                    .append(
                                        $image
                                            .clone(false)
                                            .attr({
                                                'src': $image.data('ThumbnailUrl'),
                                                'title': $image.data('Title'),
                                                'alt': $image.data('Description')
                                            })
                                            .css({
                                                'max-width': '100%',
                                                'max-height': '100%'
                                            })
                                    ).click(clickEvent);


                                _this.$Object
                                    .empty()
                                    .append(
                                        $image
                                            .clone(false)
                                            .click(clickEvent)
                                            .attr({
                                                'src': $image.data('ThumbnailUrl'),
                                                'title': $image.data('Title'),
                                                'alt': $image.data('Description')
                                            })
                                            .css({
                                                'max-width': '100%',
                                                'max-height': '100%'
                                            })
                                    );

                                return _this;
                            };

                            _this.Activate = function () {
                                _this.$Object.addClass(settings.activeTileCssClass);
                            };

                            _this.DeActivate = function () {
                                _this.$Object.removeClass(settings.activeTileCssClass);
                            };

                            return _this;
                        },
                        Tiles: function () {
                            var _this = this;

                            _this.$Object = $('<div />')
                                .addClass('jQueryPictureGalleryTiles')
                                .addClass(settings.tilesCssClass);
                            if (!settings.setMaxWidth) {
                                _this.$Object.css({
                                    'width': $.type(settings.pictureCanvasWidth) != 'number' ? 900 : settings.pictureCanvasWidth,
                                    'max-width': $.type(settings.pictureCanvasWidth) != 'number' ? 900 : settings.pictureCanvasWidth
                                });
                            }


                            _this.Update = function (tileCollection) {
                                _this.$Object.children().detach();
                                $.map(tileCollection, function (tile) {
                                    _this.$Object.append(tile.$Object);
                                });

                                return _this;
                            };

                            return _this;
                        },
                        Pager: function () {
                            var _this = this;

                            _this.$Object = $('<div />')
                                .addClass('jQueryPictureGalleryPager')
                                .addClass(settings.pagerCssClass);
                            if (!settings.setMaxWidth) {
                                _this.$Object.css({
                                    'width': $.type(settings.pictureCanvasWidth) != 'number' ? 900 : settings.pictureCanvasWidth,
                                    'max-width': $.type(settings.pictureCanvasWidth) != 'number' ? 900 : settings.pictureCanvasWidth
                                });
                            }


                            _this.Update = function (pagingStringPattern, page, pageCount) {
                                var pagingText = $.PictureGallery.Methods.StringFormat(pagingStringPattern, page.toString(), pageCount.toString());

                                _this.$Object
                                    .html(pagingText);
                            };
                        },
                        NavigationButton: function () {
                            var _this = this;

                            _this.$Object = settings.enableNavigationButtons ? $('<div />').addClass('jQueryPictureGalleryNavigationButton') : null;

                            _this.Update = function (directionClassName, clickEvent, html) {
                                if (settings.enableNavigationButtons) {
                                    _this.$Object
                                        .addClass(directionClassName)
                                        .unbind('click.picturegallery')
                                        .bind('click.picturegallery', function (event) {
                                            event.preventDefault();
                                            clickEvent();
                                        })
                                        .empty()
                                        .html(html);
                                }
                            };
                        }
                    },

                    Gallery: function () {
                        var _this = this;

                        _this.CurrentPictureIndex = 0;

                        _this.ImageCollection = null;
                        (_this.SetImageCollection = function () {
                            _this.ImageCollection = new Array();
                            $SelectedObject.each(function (i) {
                                var $imgOriginal = $(this);

                                var $img = $imgOriginal.clone(true);

                                var urls = $imgOriginal.attr('src').split("#");

                                var imgData = {
                                    Index: i,
                                    ThumbnailUrl: urls[0],
                                    FullSizeUrl: urls.length > 1 ? urls[1] : urls[0],
                                    Title: $imgOriginal.attr('title'),
                                    Description: $imgOriginal.attr('alt')
                                };

                                _this.ImageCollection.push($img.data(imgData));
                            });

                        })();

                        _this.TileCollection = null;
                        (_this.SetTileCollection = function () {
                            _this.TileCollection = new Array();
                            $.map(_this.ImageCollection, function ($image) {
                                _this.TileCollection.push(
                                    new $.PictureGallery.Classes.Containers.Tile()
                                        .Update(
                                            $image,
                                            function () {
                                                _this.GoToPicture($image.data('Index'));
                                            }
                                        )
                                );
                            });
                        })();

                        _this.Picture = new $.PictureGallery.Classes.Containers.Picture();
                        _this.Title = new $.PictureGallery.Classes.Containers.Title();
                        _this.Description = new $.PictureGallery.Classes.Containers.Description();
                        _this.Pager = new $.PictureGallery.Classes.Containers.Pager();
                        _this.Tiles = new $.PictureGallery.Classes.Containers.Tiles().Update(_this.TileCollection);
                        _this.PopUpWindow = new $.PictureGallery.Classes.Containers.PopUpWindow();
                        _this.PreviousButton = new $.PictureGallery.Classes.Containers.NavigationButton();
                        _this.NextButton = new $.PictureGallery.Classes.Containers.NavigationButton();

                        _this.ActivateTile = function (pictureIndex) {
                            $.map(_this.TileCollection, function ($tile, i) {
                                if (i != pictureIndex) {
                                    $tile.DeActivate();
                                } else {
                                    $tile.Activate();
                                }
                            });
                        };

                        _this.BindKeys = function () {
                            $(_this.PopUpWindow.Selector).keydown(function (e) {
                                if (e.which == 37) {
                                    e.preventDefault();
                                    _this.GoToPicture(_this.CurrentPictureIndex - 1);
                                }

                                if (e.which == 39) {
                                    e.preventDefault();
                                    _this.GoToPicture(_this.CurrentPictureIndex + 1);
                                }
                            });
                        };

                        _this.PictureLoadCallback = function () {
                            _this.PopUpWindow.Center();
                        };

                        _this.GoToPicture = function (pictureIndex) {
                            //check validity of passed index
                            if (pictureIndex >= _this.ImageCollection.length)
                                pictureIndex = 0;
                            if (pictureIndex < 0)
                                pictureIndex = _this.ImageCollection.length - 1;

                            _this.CurrentPictureIndex = pictureIndex;

                            _this.PopUpWindow.Clear().Open();

                            _this.ActivateTile(pictureIndex);

                            _this.Picture.Update(
                                _this.ImageCollection[pictureIndex],
                                function () {
                                    _this.GoToPicture(pictureIndex + 1);
                                }
                            );

                            // build the Dom
                            _this.Title.Update(_this.ImageCollection[pictureIndex].data('Title'));
                            _this.Description.Update(_this.ImageCollection[pictureIndex].data('Description'));
                            _this.Pager.Update(settings.pagingStringPattern, pictureIndex + 1, _this.ImageCollection.length);
                            _this.PreviousButton.Update('previous', function () {
                                _this.GoToPicture(pictureIndex - 1);
                            }, settings.previousButtonHtml);

                            _this.NextButton.Update('next', function () {
                                _this.GoToPicture(pictureIndex + 1);
                            }, settings.nextButtonHtml);

                            _this
                                .PopUpWindow
                                .Build(
                                    _this.NextButton.$Object,
                                    _this.PreviousButton.$Object,
                                    _this.Picture.$Object,
                                    _this.Title.$Object,
                                    _this.Description.$Object,
                                    _this.Pager.$Object,
                                    _this.Tiles.$Object
                                );

                            _this.Picture.Load(_this.PictureLoadCallback);
                        };


                        _this.Init = function () {
                            // apply click event on original pictures
                            $SelectedObject.each(function (i) {
                                if (settings.clickOnContainer) {
                                    $(this)
                                        .parent()
                                        .click(function (event) {
                                            event.preventDefault();
                                            _this.GoToPicture(i);
                                            _this.BindKeys();
                                        });
                                } else {
                                    $(this)
                                    .click(function (event) {
                                        event.preventDefault();
                                        _this.GoToPicture(i);
                                        _this.BindKeys();
                                    });
                                }
                            });

                            _this.PopUpWindow
                                .Build(
                                    _this.Picture.$Object,
                                    _this.Title.$Object,
                                    _this.Description.$Object,
                                    _this.Pager.$Object,
                                    _this.Tiles.$Object
                                )
                                .SetDialog();

                            return _this;
                        };

                        return _this;
                    }
                },

                Methods: {
                    StringFormat: function (pattern) {
                        var formatRegexp = new RegExp("{-?[0-9]+}", "g");
                        var formattedString = pattern;
                        var args = arguments;

                        return formattedString.replace(
                            formatRegexp,
                            function (item) {
                                var intVal = parseInt(item.substring(1, item.length - 1));

                                var replace;
                                if (intVal >= 0) {
                                    replace = args[intVal + 1];
                                } else if (intVal === -1) {
                                    replace = "{";
                                } else if (intVal === -2) {
                                    replace = "}";
                                } else {
                                    replace = "";
                                }

                                return replace;
                            }
                        );
                    }
                }
            };

            var gallery = new $.PictureGallery.Classes.Gallery().Init();
            return gallery;
        }
    });
})(jQuery);
;

function initAutoScalingNav(C) { if (!C.menuId) { C.menuId = "main-nav" } if (!C.tag) { C.tag = "a" } if (!C.spacing) { C.spacing = 0 } if (!C.constant) { C.constant = 0 } if (!C.minPaddings) { C.minPaddings = 0 } if (!C.liHovering) { C.liHovering = false } if (!C.sideClasses) { C.sideClasses = false } var A = document.getElementById(C.menuId); if (A) { var J = A.getElementsByTagName("li"); var H = []; var D = []; for (var F = 0, E = 0; F < J.length; F++) { if (J[F].parentNode == A) { var I = J[F].getElementsByTagName(C.tag).item(0); H.push(I); H[E++].width = I.offsetWidth; D.push(J[F]) } if (C.liHovering) { J[F].onmouseover = function () { this.className += " hover" }; J[F].onmouseout = function () { this.className = this.className.replace("hover", "") } } } var G = A.clientWidth - H.length * C.spacing - C.constant; if (B(H) < G) { for (var F = 0; B(H) < G; F++) { H[F].width++; if (F >= H.length - 1) { F = -1 } } for (var F = 0; F < H.length; F++) { H[F].style.width = H[F].width + "px" } } else { if (C.minPaddings > 0) { for (var F = 0; F < H.length; F++) { H[F].style.width = H[F].width + C.minPaddings * 2 + "px" } } } } function B(L) { var K = 0; for (var M = 0; M < L.length; M++) { K += L[M].width } return K } if (C.sideClasses) { D[0].className += " first-child"; D[D.length - 1].className += " last-child" } };
;
(function($) {

    'use strict';
   
    //$.widget("aline.wffmExtensions", {
    //
    //    // defaults
    //    options: {
    //        enableToolTip: true,
    //        formSelector: ".scfForm",
    //        toolTipFieldSelector: "[title]",
    //        tooltipTemplateSelector: "[data-selector='tooltipTemplate']",
    //        enableQuestionDependency: true,
    //        questionDependencySelector: "[class^='selectorDependsOn']",
    //        enablePrefillFields: true,
    //        prefillFieldsSelector: "[data-selector='prefill-field']"
    //    },
    //
    //    // widget constructor
    //    _create: function () {
    //
    //        this._forms = $(this.options.formSelector);
    //
    //        if (this.options.enableToolTip)
    //            this._initToolTips();
    //
    //        if (this.options.enableQuestionDependency)
    //            this._initQuestionDependency();
    //
    //        if (this.options.enablePrefillFields)
    //            this._initPrefillFields();
    //    },
    //    // ToolTips
    //    _initToolTips: function () {
    //        var toolTipTemplate = this.element.find(this.options.tooltipTemplateSelector);
    //        var self = this;
    //        $.each(this._forms.find(this.options.toolTipFieldSelector), function () {
    //            self._initToolTip($(this), toolTipTemplate);
    //        });
    //    },
    //    // Question Dependency
    //    _initQuestionDependency: function () {
    //        var self = this;
    //
    //        $.each(this._forms.find(this.options.questionDependencySelector), function () {
    //            self._applyDependency($(this));
    //        });
    //    },
    //
    //    _showOrHideFields: function (inputField, e) {
    //
    //        var dependantFields = inputField.data("dependantFields");
    //        if (!dependantFields)
    //            return;
    //
    //        var checked = false;
    //        var type = inputField.attr("type");
    //        if (type == "checkbox") {
    //            checked = inputField.is(":checked");
    //        }
    //        else if (type == "radio") {
    //
    //            var selectorDependsOnData = inputField.data("selectorDependsOnData");
    //            var name = inputField.attr('name');
    //            var value = selectorDependsOnData.value;
    //
    //            checked = this._forms.find("[name='" + name + "'][value='" + value + "']").is(":checked");
    //        }
    //
    //        var self = this;
    //
    //        $.each(dependantFields, function () {
    //            if (checked) {
    //                $(this).show();
    //            } else {
    //                $(this).hide();
    //                self._resetField($(this));
    //            }
    //        });
    //    },
    //    _resetField: function(resetField) {
    //        if (!resetField)
    //            return;
    //
    //        var inputFields = resetField.find("input");
    //        $.each(inputFields, function() {
    //            var inputField = $(this);
    //            var type = inputField.attr("type");
    //
    //            if (type == "text") {
    //                inputField.val("");
    //            } else {
    //                inputField.prop('checked', false);
    //            }
    //
    //        });
    //    },
    //    _applyDependency: function (element) {
    //        var data = this._parseDependencyData(element);
    //        if (!data)
    //            return;
    //
    //        var field = this._forms.find("[class~='" + data.field + "']").not(this.options.questionDependencySelector);
    //        var inputField = this._findInputField(field, data);
    //
    //        if (!inputField)
    //            return;
    //
    //        var eventBound = inputField.data("eventBound");
    //        var dependantFields = inputField.data("dependantFields");
    //
    //        if (!dependantFields)
    //            dependantFields = [];
    //
    //        dependantFields.push(element);
    //        inputField.data("dependantFields", dependantFields);
    //        inputField.data("selectorDependsOnData", data);
    //
    //        if (!eventBound) {
    //            inputField.on("change", $.proxy(this._showOrHideFields, this, inputField));
    //            inputField.data("eventBound", true);
    //        }
    //
    //        this._showOrHideFields(inputField, null);
    //    },
    //
    //    // Prefill fields
    //    _initPrefillFields: function () {
    //        var self = this;
    //        $.each(this.element.find(this.options.prefillFieldsSelector), function () {
    //            self._prefillField($(this));
    //        });
    //    },
    //
    //    _prefillField: function (element) {
    //        var fieldId = element.data("field");
    //        var field = this._forms.find("[id$='" + fieldId + "']");
    //        if (!field)
    //            return;
    //
    //        field.val(element.val());
    //    },
    //
    //    // Helpers
    //    _findInputField:function(field, data) {
    //        if (this._isCheckBoxListField(field)) {
    //            return field.find("input[type='checkbox'][id$='list_" + data.value + "']");
    //        }
    //        else if (this._isRadioListField(field)) {
    //            return field.find("input[type='radio']");
    //        }
    //
    //        return null;
    //    },
    //    _parseDependencyData: function (element) {
    //        var classes = element.attr("class").split(" ");
    //
    //        var dependsOnClass = $.grep(classes, function (className) {
    //            return className.indexOf("selectorDependsOn") === 0;
    //        });
    //
    //        if (dependsOnClass.length == 0)
    //            return null;
    //
    //        var data = dependsOnClass[0].split("_");
    //    
    //        return {
    //            field: data[1],
    //            value: data[2]
    //        };
    //    },
    //    _isCheckBoxListField: function(field) {
    //        return field.find("input[type='checkbox']").length > 0;
    //    },
    //    _isRadioListField:function(field) {
    //        return field.find("input[type='radio']").length > 0;
    //    }
    //});

    $.widget("aline.wffmExtensions", {

        // defaults
        options: {
            enableToolTip: true,
            formSelector: ".scfForm",
            toolTipFieldSelector: "[title]",
            tooltipTemplateSelector: "[data-selector='tooltipTemplate']",
            enableQuestionDependency: true,
            questionDependencySelector: "[class^='selectorDependsOn']",
            enablePrefillFields: true,
            prefillFieldsSelector: "[data-selector='prefill-field']"
        },

        // ToolTips
        _initToolTips: function () {
            var toolTipTemplate = this.element.find(this.options.tooltipTemplateSelector);
            var self = this;
            $.each(this._forms.find(this.options.toolTipFieldSelector), function () {
                self._initToolTip($(this), toolTipTemplate);
            });
        },

        // widget constructor
        _create: function () {

            this._forms = $(this.options.formSelector);

            //if (this.options.enableToolTip)
            //    this._initToolTips();

            if (this.options.enableQuestionDependency)
                this._initQuestionDependency();

            if (this.options.enablePrefillFields)
                this._initPrefillFields();
        },
        
        // Set Show/hide dependency
        _initQuestionDependency: function () {
            var self = this;

            //hide form sections
            $('[class^="selectorDependsOnAction"]').hide();

            //get list of checkboxes
            var actionsCheckboxes = $('fieldset').first().find(':checkbox');

            actionsCheckboxes.each(function (index, element) {
                $(this).change(function (event) {
                    var dependentElement = $('.selectorDependsOnAction' + index);

                    if ($(this).is(':checked'))
                        dependentElement.show();
                    else {
                        dependentElement.hide();
                        self._clearInputs(dependentElement);
                    }
                });
            });
        },
        _clearInputs: function (parent) {
            // iterate over all of the inputs for the parent element
            $(parent)
            .find(':input')
            .each(function () {
                var type = $(this).attr('type');
                var tag = $(this).prop("tagName").toLowerCase();
                // password inputs, and textareas
                if (type == 'text' || type == 'password' || tag == 'textarea')
                    $(this).val("");
                    // checkboxes and radios need to have their checked state cleared
                else if (type == 'checkbox' || type == 'radio')
                    $(this).prop('checked', false);
                    // select elements need to have their options unselected
                else if (tag == 'select')
                    $(this).find("option:selected").removeAttr("selected");
            });
        },

        // Prefill fields
        _initPrefillFields: function () {
            var self = this;
            $.each(this.element.find(this.options.prefillFieldsSelector), function () {
                self._prefillField($(this));
            });
        },

        _prefillField: function (element) {
            var fieldId = element.data("field");
            var field = this._forms.find("[id$='" + fieldId + "']");
            if (!field)
                return;

            field.val(element.val());
        }
    });

})(jQuery);;
(function ($) {

    'use strict';

    jQuery.widget("aline.actionLanding", {

        // defaults
        options: {
            tooltipSelector: "[data-selector='tooltip']"
        },

        // widget constructor
        _create: function () {

            this.element.find(this.options.tooltipSelector).tooltipster({ maxWidth: 300 });
        }
    });

})(window.jQuery);
;
var EC;
(function (EC) {
    var ALine;
    (function (ALine) {
        var TypeScript;
        (function (TypeScript) {
            'use strict';
            var ActionEnergyAuditForm = (function () {
                function ActionEnergyAuditForm() {
                    this.bindEvents();
                }
                ActionEnergyAuditForm.prototype.bindEvents = function () {
                    $("[data-selector='energyAuditForm']").on("submit", "form", $.proxy(this.submitData, this));
                };
                ActionEnergyAuditForm.prototype.submitData = function (e) {
                    e.preventDefault();
                    var formElement = e.currentTarget;
                    var form = $(e.currentTarget);
                    var successPanel = $("[data-selector='success-Panel']");
                    var errorPanel = $("[data-selector='error-Panel']");
                    var validityErrorPanel = $("[data-selector='validity-error-Panel']");
                    var submitButton = $("[data-selector='btn-formSubmit']");
                    //init
                    successPanel.hide();
                    errorPanel.hide();
                    validityErrorPanel.hide();
                    //handle loading on click
                    submitButton
                        .addClass("is-disabled")
                        .addClass("btn--waiting")
                        .prop("disabled", true);
                    //ajax submit
                    this.submitFormAndGetData(formElement, {
                        success: function (data) {
                            if (data.Data.Valid) {
                                $(form).hide();
                                successPanel.show();
                            }
                            else {
                                submitButton
                                    .removeClass("is-disabled")
                                    .removeClass("btn--waiting")
                                    .prop("disabled", false);
                                if (data.Data.ServiceError) {
                                    errorPanel.show();
                                }
                                else {
                                    validityErrorPanel.show();
                                }
                            }
                        },
                        useWaitPanel: true
                    });
                };
                /** Submit the form by doing an AJAX request. */
                ActionEnergyAuditForm.prototype.submitFormAndGetData = function (form, options) {
                    var defaults = {
                        url: form.action,
                        type: form.method,
                        data: $(form).serialize(),
                        traditional: true
                    };
                    var settings = $.extend(defaults, options);
                    $.ajax(settings);
                };
                return ActionEnergyAuditForm;
            })();
            TypeScript.ActionEnergyAuditForm = ActionEnergyAuditForm;
        })(TypeScript = ALine.TypeScript || (ALine.TypeScript = {}));
    })(ALine = EC.ALine || (EC.ALine = {}));
})(EC || (EC = {}));
;
