/* Minification failed. Returning unminified contents.
(15523,627-633): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: height
 */
/* Minification failed. Returning unminified contents.
(15520,627-633): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: height
 */
!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});;
/*!
 * 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);
;
/* == jquery mousewheel plugin == Version: 3.1.13, License: MIT License (MIT) */
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});
/* == malihu jquery custom scrollbar plugin == Version: 3.1.5, License: MIT License (MIT) */
!function(e){"function"==typeof define&&define.amd?define(["jquery"],e):"undefined"!=typeof module&&module.exports?module.exports=e:e(jQuery,window,document)}(function(e){!function(t){var o="function"==typeof define&&define.amd,a="undefined"!=typeof module&&module.exports,n="https:"==document.location.protocol?"https:":"http:",i="cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.13/jquery.mousewheel.min.js";o||(a?require("jquery-mousewheel")(e):e.event.special.mousewheel||e("head").append(decodeURI("%3Cscript src="+n+"//"+i+"%3E%3C/script%3E"))),t()}(function(){var t,o="mCustomScrollbar",a="mCS",n=".mCustomScrollbar",i={setTop:0,setLeft:0,axis:"y",scrollbarPosition:"inside",scrollInertia:950,autoDraggerLength:!0,alwaysShowScrollbar:0,snapOffset:0,mouseWheel:{enable:!0,scrollAmount:"auto",axis:"y",deltaFactor:"auto",disableOver:["select","option","keygen","datalist","textarea"]},scrollButtons:{scrollType:"stepless",scrollAmount:"auto"},keyboard:{enable:!0,scrollType:"stepless",scrollAmount:"auto"},contentTouchScroll:25,documentTouchScroll:!0,advanced:{autoScrollOnFocus:"input,textarea,select,button,datalist,keygen,a[tabindex],area,object,[contenteditable='true']",updateOnContentResize:!0,updateOnImageLoad:"auto",autoUpdateTimeout:60},theme:"light",callbacks:{onTotalScrollOffset:0,onTotalScrollBackOffset:0,alwaysTriggerOffsets:!0}},r=0,l={},s=window.attachEvent&&!window.addEventListener?1:0,c=!1,d=["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"],u={init:function(t){var t=e.extend(!0,{},i,t),o=f.call(this);if(t.live){var s=t.liveSelector||this.selector||n,c=e(s);if("off"===t.live)return void m(s);l[s]=setTimeout(function(){c.mCustomScrollbar(t),"once"===t.live&&c.length&&m(s)},500)}else m(s);return t.setWidth=t.set_width?t.set_width:t.setWidth,t.setHeight=t.set_height?t.set_height:t.setHeight,t.axis=t.horizontalScroll?"x":p(t.axis),t.scrollInertia=t.scrollInertia>0&&t.scrollInertia<17?17:t.scrollInertia,"object"!=typeof t.mouseWheel&&1==t.mouseWheel&&(t.mouseWheel={enable:!0,scrollAmount:"auto",axis:"y",preventDefault:!1,deltaFactor:"auto",normalizeDelta:!1,invert:!1}),t.mouseWheel.scrollAmount=t.mouseWheelPixels?t.mouseWheelPixels:t.mouseWheel.scrollAmount,t.mouseWheel.normalizeDelta=t.advanced.normalizeMouseWheelDelta?t.advanced.normalizeMouseWheelDelta:t.mouseWheel.normalizeDelta,t.scrollButtons.scrollType=g(t.scrollButtons.scrollType),h(t),e(o).each(function(){var o=e(this);if(!o.data(a)){o.data(a,{idx:++r,opt:t,scrollRatio:{y:null,x:null},overflowed:null,contentReset:{y:null,x:null},bindEvents:!1,tweenRunning:!1,sequential:{},langDir:o.css("direction"),cbOffsets:null,trigger:null,poll:{size:{o:0,n:0},img:{o:0,n:0},change:{o:0,n:0}}});var n=o.data(a),i=n.opt,l=o.data("mcs-axis"),s=o.data("mcs-scrollbar-position"),c=o.data("mcs-theme");l&&(i.axis=l),s&&(i.scrollbarPosition=s),c&&(i.theme=c,h(i)),v.call(this),n&&i.callbacks.onCreate&&"function"==typeof i.callbacks.onCreate&&i.callbacks.onCreate.call(this),e("#mCSB_"+n.idx+"_container img:not(."+d[2]+")").addClass(d[2]),u.update.call(null,o)}})},update:function(t,o){var n=t||f.call(this);return e(n).each(function(){var t=e(this);if(t.data(a)){var n=t.data(a),i=n.opt,r=e("#mCSB_"+n.idx+"_container"),l=e("#mCSB_"+n.idx),s=[e("#mCSB_"+n.idx+"_dragger_vertical"),e("#mCSB_"+n.idx+"_dragger_horizontal")];if(!r.length)return;n.tweenRunning&&Q(t),o&&n&&i.callbacks.onBeforeUpdate&&"function"==typeof i.callbacks.onBeforeUpdate&&i.callbacks.onBeforeUpdate.call(this),t.hasClass(d[3])&&t.removeClass(d[3]),t.hasClass(d[4])&&t.removeClass(d[4]),l.css("max-height","none"),l.height()!==t.height()&&l.css("max-height",t.height()),_.call(this),"y"===i.axis||i.advanced.autoExpandHorizontalScroll||r.css("width",x(r)),n.overflowed=y.call(this),M.call(this),i.autoDraggerLength&&S.call(this),b.call(this),T.call(this);var c=[Math.abs(r[0].offsetTop),Math.abs(r[0].offsetLeft)];"x"!==i.axis&&(n.overflowed[0]?s[0].height()>s[0].parent().height()?B.call(this):(G(t,c[0].toString(),{dir:"y",dur:0,overwrite:"none"}),n.contentReset.y=null):(B.call(this),"y"===i.axis?k.call(this):"yx"===i.axis&&n.overflowed[1]&&G(t,c[1].toString(),{dir:"x",dur:0,overwrite:"none"}))),"y"!==i.axis&&(n.overflowed[1]?s[1].width()>s[1].parent().width()?B.call(this):(G(t,c[1].toString(),{dir:"x",dur:0,overwrite:"none"}),n.contentReset.x=null):(B.call(this),"x"===i.axis?k.call(this):"yx"===i.axis&&n.overflowed[0]&&G(t,c[0].toString(),{dir:"y",dur:0,overwrite:"none"}))),o&&n&&(2===o&&i.callbacks.onImageLoad&&"function"==typeof i.callbacks.onImageLoad?i.callbacks.onImageLoad.call(this):3===o&&i.callbacks.onSelectorChange&&"function"==typeof i.callbacks.onSelectorChange?i.callbacks.onSelectorChange.call(this):i.callbacks.onUpdate&&"function"==typeof i.callbacks.onUpdate&&i.callbacks.onUpdate.call(this)),N.call(this)}})},scrollTo:function(t,o){if("undefined"!=typeof t&&null!=t){var n=f.call(this);return e(n).each(function(){var n=e(this);if(n.data(a)){var i=n.data(a),r=i.opt,l={trigger:"external",scrollInertia:r.scrollInertia,scrollEasing:"mcsEaseInOut",moveDragger:!1,timeout:60,callbacks:!0,onStart:!0,onUpdate:!0,onComplete:!0},s=e.extend(!0,{},l,o),c=Y.call(this,t),d=s.scrollInertia>0&&s.scrollInertia<17?17:s.scrollInertia;c[0]=X.call(this,c[0],"y"),c[1]=X.call(this,c[1],"x"),s.moveDragger&&(c[0]*=i.scrollRatio.y,c[1]*=i.scrollRatio.x),s.dur=ne()?0:d,setTimeout(function(){null!==c[0]&&"undefined"!=typeof c[0]&&"x"!==r.axis&&i.overflowed[0]&&(s.dir="y",s.overwrite="all",G(n,c[0].toString(),s)),null!==c[1]&&"undefined"!=typeof c[1]&&"y"!==r.axis&&i.overflowed[1]&&(s.dir="x",s.overwrite="none",G(n,c[1].toString(),s))},s.timeout)}})}},stop:function(){var t=f.call(this);return e(t).each(function(){var t=e(this);t.data(a)&&Q(t)})},disable:function(t){var o=f.call(this);return e(o).each(function(){var o=e(this);if(o.data(a)){o.data(a);N.call(this,"remove"),k.call(this),t&&B.call(this),M.call(this,!0),o.addClass(d[3])}})},destroy:function(){var t=f.call(this);return e(t).each(function(){var n=e(this);if(n.data(a)){var i=n.data(a),r=i.opt,l=e("#mCSB_"+i.idx),s=e("#mCSB_"+i.idx+"_container"),c=e(".mCSB_"+i.idx+"_scrollbar");r.live&&m(r.liveSelector||e(t).selector),N.call(this,"remove"),k.call(this),B.call(this),n.removeData(a),$(this,"mcs"),c.remove(),s.find("img."+d[2]).removeClass(d[2]),l.replaceWith(s.contents()),n.removeClass(o+" _"+a+"_"+i.idx+" "+d[6]+" "+d[7]+" "+d[5]+" "+d[3]).addClass(d[4])}})}},f=function(){return"object"!=typeof e(this)||e(this).length<1?n:this},h=function(t){var o=["rounded","rounded-dark","rounded-dots","rounded-dots-dark"],a=["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"],n=["minimal","minimal-dark"],i=["minimal","minimal-dark"],r=["minimal","minimal-dark"];t.autoDraggerLength=e.inArray(t.theme,o)>-1?!1:t.autoDraggerLength,t.autoExpandScrollbar=e.inArray(t.theme,a)>-1?!1:t.autoExpandScrollbar,t.scrollButtons.enable=e.inArray(t.theme,n)>-1?!1:t.scrollButtons.enable,t.autoHideScrollbar=e.inArray(t.theme,i)>-1?!0:t.autoHideScrollbar,t.scrollbarPosition=e.inArray(t.theme,r)>-1?"outside":t.scrollbarPosition},m=function(e){l[e]&&(clearTimeout(l[e]),$(l,e))},p=function(e){return"yx"===e||"xy"===e||"auto"===e?"yx":"x"===e||"horizontal"===e?"x":"y"},g=function(e){return"stepped"===e||"pixels"===e||"step"===e||"click"===e?"stepped":"stepless"},v=function(){var t=e(this),n=t.data(a),i=n.opt,r=i.autoExpandScrollbar?" "+d[1]+"_expand":"",l=["<div id='mCSB_"+n.idx+"_scrollbar_vertical' class='mCSB_scrollTools mCSB_"+n.idx+"_scrollbar mCS-"+i.theme+" mCSB_scrollTools_vertical"+r+"'><div class='"+d[12]+"'><div id='mCSB_"+n.idx+"_dragger_vertical' class='mCSB_dragger' style='position:absolute;'><div class='mCSB_dragger_bar' /></div><div class='mCSB_draggerRail' /></div></div>","<div id='mCSB_"+n.idx+"_scrollbar_horizontal' class='mCSB_scrollTools mCSB_"+n.idx+"_scrollbar mCS-"+i.theme+" mCSB_scrollTools_horizontal"+r+"'><div class='"+d[12]+"'><div id='mCSB_"+n.idx+"_dragger_horizontal' class='mCSB_dragger' style='position:absolute;'><div class='mCSB_dragger_bar' /></div><div class='mCSB_draggerRail' /></div></div>"],s="yx"===i.axis?"mCSB_vertical_horizontal":"x"===i.axis?"mCSB_horizontal":"mCSB_vertical",c="yx"===i.axis?l[0]+l[1]:"x"===i.axis?l[1]:l[0],u="yx"===i.axis?"<div id='mCSB_"+n.idx+"_container_wrapper' class='mCSB_container_wrapper' />":"",f=i.autoHideScrollbar?" "+d[6]:"",h="x"!==i.axis&&"rtl"===n.langDir?" "+d[7]:"";i.setWidth&&t.css("width",i.setWidth),i.setHeight&&t.css("height",i.setHeight),i.setLeft="y"!==i.axis&&"rtl"===n.langDir?"989999px":i.setLeft,t.addClass(o+" _"+a+"_"+n.idx+f+h).wrapInner("<div id='mCSB_"+n.idx+"' class='mCustomScrollBox mCS-"+i.theme+" "+s+"'><div id='mCSB_"+n.idx+"_container' class='mCSB_container' style='position:relative; top:"+i.setTop+"; left:"+i.setLeft+";' dir='"+n.langDir+"' /></div>");var m=e("#mCSB_"+n.idx),p=e("#mCSB_"+n.idx+"_container");"y"===i.axis||i.advanced.autoExpandHorizontalScroll||p.css("width",x(p)),"outside"===i.scrollbarPosition?("static"===t.css("position")&&t.css("position","relative"),t.css("overflow","visible"),m.addClass("mCSB_outside").after(c)):(m.addClass("mCSB_inside").append(c),p.wrap(u)),w.call(this);var g=[e("#mCSB_"+n.idx+"_dragger_vertical"),e("#mCSB_"+n.idx+"_dragger_horizontal")];g[0].css("min-height",g[0].height()),g[1].css("min-width",g[1].width())},x=function(t){var o=[t[0].scrollWidth,Math.max.apply(Math,t.children().map(function(){return e(this).outerWidth(!0)}).get())],a=t.parent().width();return o[0]>a?o[0]:o[1]>a?o[1]:"100%"},_=function(){var t=e(this),o=t.data(a),n=o.opt,i=e("#mCSB_"+o.idx+"_container");if(n.advanced.autoExpandHorizontalScroll&&"y"!==n.axis){i.css({width:"auto","min-width":0,"overflow-x":"scroll"});var r=Math.ceil(i[0].scrollWidth);3===n.advanced.autoExpandHorizontalScroll||2!==n.advanced.autoExpandHorizontalScroll&&r>i.parent().width()?i.css({width:r,"min-width":"100%","overflow-x":"inherit"}):i.css({"overflow-x":"inherit",position:"absolute"}).wrap("<div class='mCSB_h_wrapper' style='position:relative; left:0; width:999999px;' />").css({width:Math.ceil(i[0].getBoundingClientRect().right+.4)-Math.floor(i[0].getBoundingClientRect().left),"min-width":"100%",position:"relative"}).unwrap()}},w=function(){var t=e(this),o=t.data(a),n=o.opt,i=e(".mCSB_"+o.idx+"_scrollbar:first"),r=oe(n.scrollButtons.tabindex)?"tabindex='"+n.scrollButtons.tabindex+"'":"",l=["<a href='#' class='"+d[13]+"' "+r+" />","<a href='#' class='"+d[14]+"' "+r+" />","<a href='#' class='"+d[15]+"' "+r+" />","<a href='#' class='"+d[16]+"' "+r+" />"],s=["x"===n.axis?l[2]:l[0],"x"===n.axis?l[3]:l[1],l[2],l[3]];n.scrollButtons.enable&&i.prepend(s[0]).append(s[1]).next(".mCSB_scrollTools").prepend(s[2]).append(s[3])},S=function(){var t=e(this),o=t.data(a),n=e("#mCSB_"+o.idx),i=e("#mCSB_"+o.idx+"_container"),r=[e("#mCSB_"+o.idx+"_dragger_vertical"),e("#mCSB_"+o.idx+"_dragger_horizontal")],l=[n.height()/i.outerHeight(!1),n.width()/i.outerWidth(!1)],c=[parseInt(r[0].css("min-height")),Math.round(l[0]*r[0].parent().height()),parseInt(r[1].css("min-width")),Math.round(l[1]*r[1].parent().width())],d=s&&c[1]<c[0]?c[0]:c[1],u=s&&c[3]<c[2]?c[2]:c[3];r[0].css({height:d,"max-height":r[0].parent().height()-10}).find(".mCSB_dragger_bar").css({"line-height":c[0]+"px"}),r[1].css({width:u,"max-width":r[1].parent().width()-10})},b=function(){var t=e(this),o=t.data(a),n=e("#mCSB_"+o.idx),i=e("#mCSB_"+o.idx+"_container"),r=[e("#mCSB_"+o.idx+"_dragger_vertical"),e("#mCSB_"+o.idx+"_dragger_horizontal")],l=[i.outerHeight(!1)-n.height(),i.outerWidth(!1)-n.width()],s=[l[0]/(r[0].parent().height()-r[0].height()),l[1]/(r[1].parent().width()-r[1].width())];o.scrollRatio={y:s[0],x:s[1]}},C=function(e,t,o){var a=o?d[0]+"_expanded":"",n=e.closest(".mCSB_scrollTools");"active"===t?(e.toggleClass(d[0]+" "+a),n.toggleClass(d[1]),e[0]._draggable=e[0]._draggable?0:1):e[0]._draggable||("hide"===t?(e.removeClass(d[0]),n.removeClass(d[1])):(e.addClass(d[0]),n.addClass(d[1])))},y=function(){var t=e(this),o=t.data(a),n=e("#mCSB_"+o.idx),i=e("#mCSB_"+o.idx+"_container"),r=null==o.overflowed?i.height():i.outerHeight(!1),l=null==o.overflowed?i.width():i.outerWidth(!1),s=i[0].scrollHeight,c=i[0].scrollWidth;return s>r&&(r=s),c>l&&(l=c),[r>n.height(),l>n.width()]},B=function(){var t=e(this),o=t.data(a),n=o.opt,i=e("#mCSB_"+o.idx),r=e("#mCSB_"+o.idx+"_container"),l=[e("#mCSB_"+o.idx+"_dragger_vertical"),e("#mCSB_"+o.idx+"_dragger_horizontal")];if(Q(t),("x"!==n.axis&&!o.overflowed[0]||"y"===n.axis&&o.overflowed[0])&&(l[0].add(r).css("top",0),G(t,"_resetY")),"y"!==n.axis&&!o.overflowed[1]||"x"===n.axis&&o.overflowed[1]){var s=dx=0;"rtl"===o.langDir&&(s=i.width()-r.outerWidth(!1),dx=Math.abs(s/o.scrollRatio.x)),r.css("left",s),l[1].css("left",dx),G(t,"_resetX")}},T=function(){function t(){r=setTimeout(function(){e.event.special.mousewheel?(clearTimeout(r),W.call(o[0])):t()},100)}var o=e(this),n=o.data(a),i=n.opt;if(!n.bindEvents){if(I.call(this),i.contentTouchScroll&&D.call(this),E.call(this),i.mouseWheel.enable){var r;t()}P.call(this),U.call(this),i.advanced.autoScrollOnFocus&&H.call(this),i.scrollButtons.enable&&F.call(this),i.keyboard.enable&&q.call(this),n.bindEvents=!0}},k=function(){var t=e(this),o=t.data(a),n=o.opt,i=a+"_"+o.idx,r=".mCSB_"+o.idx+"_scrollbar",l=e("#mCSB_"+o.idx+",#mCSB_"+o.idx+"_container,#mCSB_"+o.idx+"_container_wrapper,"+r+" ."+d[12]+",#mCSB_"+o.idx+"_dragger_vertical,#mCSB_"+o.idx+"_dragger_horizontal,"+r+">a"),s=e("#mCSB_"+o.idx+"_container");n.advanced.releaseDraggableSelectors&&l.add(e(n.advanced.releaseDraggableSelectors)),n.advanced.extraDraggableSelectors&&l.add(e(n.advanced.extraDraggableSelectors)),o.bindEvents&&(e(document).add(e(!A()||top.document)).unbind("."+i),l.each(function(){e(this).unbind("."+i)}),clearTimeout(t[0]._focusTimeout),$(t[0],"_focusTimeout"),clearTimeout(o.sequential.step),$(o.sequential,"step"),clearTimeout(s[0].onCompleteTimeout),$(s[0],"onCompleteTimeout"),o.bindEvents=!1)},M=function(t){var o=e(this),n=o.data(a),i=n.opt,r=e("#mCSB_"+n.idx+"_container_wrapper"),l=r.length?r:e("#mCSB_"+n.idx+"_container"),s=[e("#mCSB_"+n.idx+"_scrollbar_vertical"),e("#mCSB_"+n.idx+"_scrollbar_horizontal")],c=[s[0].find(".mCSB_dragger"),s[1].find(".mCSB_dragger")];"x"!==i.axis&&(n.overflowed[0]&&!t?(s[0].add(c[0]).add(s[0].children("a")).css("display","block"),l.removeClass(d[8]+" "+d[10])):(i.alwaysShowScrollbar?(2!==i.alwaysShowScrollbar&&c[0].css("display","none"),l.removeClass(d[10])):(s[0].css("display","none"),l.addClass(d[10])),l.addClass(d[8]))),"y"!==i.axis&&(n.overflowed[1]&&!t?(s[1].add(c[1]).add(s[1].children("a")).css("display","block"),l.removeClass(d[9]+" "+d[11])):(i.alwaysShowScrollbar?(2!==i.alwaysShowScrollbar&&c[1].css("display","none"),l.removeClass(d[11])):(s[1].css("display","none"),l.addClass(d[11])),l.addClass(d[9]))),n.overflowed[0]||n.overflowed[1]?o.removeClass(d[5]):o.addClass(d[5])},O=function(t){var o=t.type,a=t.target.ownerDocument!==document&&null!==frameElement?[e(frameElement).offset().top,e(frameElement).offset().left]:null,n=A()&&t.target.ownerDocument!==top.document&&null!==frameElement?[e(t.view.frameElement).offset().top,e(t.view.frameElement).offset().left]:[0,0];switch(o){case"pointerdown":case"MSPointerDown":case"pointermove":case"MSPointerMove":case"pointerup":case"MSPointerUp":return a?[t.originalEvent.pageY-a[0]+n[0],t.originalEvent.pageX-a[1]+n[1],!1]:[t.originalEvent.pageY,t.originalEvent.pageX,!1];case"touchstart":case"touchmove":case"touchend":var i=t.originalEvent.touches[0]||t.originalEvent.changedTouches[0],r=t.originalEvent.touches.length||t.originalEvent.changedTouches.length;return t.target.ownerDocument!==document?[i.screenY,i.screenX,r>1]:[i.pageY,i.pageX,r>1];default:return a?[t.pageY-a[0]+n[0],t.pageX-a[1]+n[1],!1]:[t.pageY,t.pageX,!1]}},I=function(){function t(e,t,a,n){if(h[0].idleTimer=d.scrollInertia<233?250:0,o.attr("id")===f[1])var i="x",s=(o[0].offsetLeft-t+n)*l.scrollRatio.x;else var i="y",s=(o[0].offsetTop-e+a)*l.scrollRatio.y;G(r,s.toString(),{dir:i,drag:!0})}var o,n,i,r=e(this),l=r.data(a),d=l.opt,u=a+"_"+l.idx,f=["mCSB_"+l.idx+"_dragger_vertical","mCSB_"+l.idx+"_dragger_horizontal"],h=e("#mCSB_"+l.idx+"_container"),m=e("#"+f[0]+",#"+f[1]),p=d.advanced.releaseDraggableSelectors?m.add(e(d.advanced.releaseDraggableSelectors)):m,g=d.advanced.extraDraggableSelectors?e(!A()||top.document).add(e(d.advanced.extraDraggableSelectors)):e(!A()||top.document);m.bind("contextmenu."+u,function(e){e.preventDefault()}).bind("mousedown."+u+" touchstart."+u+" pointerdown."+u+" MSPointerDown."+u,function(t){if(t.stopImmediatePropagation(),t.preventDefault(),ee(t)){c=!0,s&&(document.onselectstart=function(){return!1}),L.call(h,!1),Q(r),o=e(this);var a=o.offset(),l=O(t)[0]-a.top,u=O(t)[1]-a.left,f=o.height()+a.top,m=o.width()+a.left;f>l&&l>0&&m>u&&u>0&&(n=l,i=u),C(o,"active",d.autoExpandScrollbar)}}).bind("touchmove."+u,function(e){e.stopImmediatePropagation(),e.preventDefault();var a=o.offset(),r=O(e)[0]-a.top,l=O(e)[1]-a.left;t(n,i,r,l)}),e(document).add(g).bind("mousemove."+u+" pointermove."+u+" MSPointerMove."+u,function(e){if(o){var a=o.offset(),r=O(e)[0]-a.top,l=O(e)[1]-a.left;if(n===r&&i===l)return;t(n,i,r,l)}}).add(p).bind("mouseup."+u+" touchend."+u+" pointerup."+u+" MSPointerUp."+u,function(){o&&(C(o,"active",d.autoExpandScrollbar),o=null),c=!1,s&&(document.onselectstart=null),L.call(h,!0)})},D=function(){function o(e){if(!te(e)||c||O(e)[2])return void(t=0);t=1,b=0,C=0,d=1,y.removeClass("mCS_touch_action");var o=I.offset();u=O(e)[0]-o.top,f=O(e)[1]-o.left,z=[O(e)[0],O(e)[1]]}function n(e){if(te(e)&&!c&&!O(e)[2]&&(T.documentTouchScroll||e.preventDefault(),e.stopImmediatePropagation(),(!C||b)&&d)){g=K();var t=M.offset(),o=O(e)[0]-t.top,a=O(e)[1]-t.left,n="mcsLinearOut";if(E.push(o),W.push(a),z[2]=Math.abs(O(e)[0]-z[0]),z[3]=Math.abs(O(e)[1]-z[1]),B.overflowed[0])var i=D[0].parent().height()-D[0].height(),r=u-o>0&&o-u>-(i*B.scrollRatio.y)&&(2*z[3]<z[2]||"yx"===T.axis);if(B.overflowed[1])var l=D[1].parent().width()-D[1].width(),h=f-a>0&&a-f>-(l*B.scrollRatio.x)&&(2*z[2]<z[3]||"yx"===T.axis);r||h?(U||e.preventDefault(),b=1):(C=1,y.addClass("mCS_touch_action")),U&&e.preventDefault(),w="yx"===T.axis?[u-o,f-a]:"x"===T.axis?[null,f-a]:[u-o,null],I[0].idleTimer=250,B.overflowed[0]&&s(w[0],R,n,"y","all",!0),B.overflowed[1]&&s(w[1],R,n,"x",L,!0)}}function i(e){if(!te(e)||c||O(e)[2])return void(t=0);t=1,e.stopImmediatePropagation(),Q(y),p=K();var o=M.offset();h=O(e)[0]-o.top,m=O(e)[1]-o.left,E=[],W=[]}function r(e){if(te(e)&&!c&&!O(e)[2]){d=0,e.stopImmediatePropagation(),b=0,C=0,v=K();var t=M.offset(),o=O(e)[0]-t.top,a=O(e)[1]-t.left;if(!(v-g>30)){_=1e3/(v-p);var n="mcsEaseOut",i=2.5>_,r=i?[E[E.length-2],W[W.length-2]]:[0,0];x=i?[o-r[0],a-r[1]]:[o-h,a-m];var u=[Math.abs(x[0]),Math.abs(x[1])];_=i?[Math.abs(x[0]/4),Math.abs(x[1]/4)]:[_,_];var f=[Math.abs(I[0].offsetTop)-x[0]*l(u[0]/_[0],_[0]),Math.abs(I[0].offsetLeft)-x[1]*l(u[1]/_[1],_[1])];w="yx"===T.axis?[f[0],f[1]]:"x"===T.axis?[null,f[1]]:[f[0],null],S=[4*u[0]+T.scrollInertia,4*u[1]+T.scrollInertia];var y=parseInt(T.contentTouchScroll)||0;w[0]=u[0]>y?w[0]:0,w[1]=u[1]>y?w[1]:0,B.overflowed[0]&&s(w[0],S[0],n,"y",L,!1),B.overflowed[1]&&s(w[1],S[1],n,"x",L,!1)}}}function l(e,t){var o=[1.5*t,2*t,t/1.5,t/2];return e>90?t>4?o[0]:o[3]:e>60?t>3?o[3]:o[2]:e>30?t>8?o[1]:t>6?o[0]:t>4?t:o[2]:t>8?t:o[3]}function s(e,t,o,a,n,i){e&&G(y,e.toString(),{dur:t,scrollEasing:o,dir:a,overwrite:n,drag:i})}var d,u,f,h,m,p,g,v,x,_,w,S,b,C,y=e(this),B=y.data(a),T=B.opt,k=a+"_"+B.idx,M=e("#mCSB_"+B.idx),I=e("#mCSB_"+B.idx+"_container"),D=[e("#mCSB_"+B.idx+"_dragger_vertical"),e("#mCSB_"+B.idx+"_dragger_horizontal")],E=[],W=[],R=0,L="yx"===T.axis?"none":"all",z=[],P=I.find("iframe"),H=["touchstart."+k+" pointerdown."+k+" MSPointerDown."+k,"touchmove."+k+" pointermove."+k+" MSPointerMove."+k,"touchend."+k+" pointerup."+k+" MSPointerUp."+k],U=void 0!==document.body.style.touchAction&&""!==document.body.style.touchAction;I.bind(H[0],function(e){o(e)}).bind(H[1],function(e){n(e)}),M.bind(H[0],function(e){i(e)}).bind(H[2],function(e){r(e)}),P.length&&P.each(function(){e(this).bind("load",function(){A(this)&&e(this.contentDocument||this.contentWindow.document).bind(H[0],function(e){o(e),i(e)}).bind(H[1],function(e){n(e)}).bind(H[2],function(e){r(e)})})})},E=function(){function o(){return window.getSelection?window.getSelection().toString():document.selection&&"Control"!=document.selection.type?document.selection.createRange().text:0}function n(e,t,o){d.type=o&&i?"stepped":"stepless",d.scrollAmount=10,j(r,e,t,"mcsLinearOut",o?60:null)}var i,r=e(this),l=r.data(a),s=l.opt,d=l.sequential,u=a+"_"+l.idx,f=e("#mCSB_"+l.idx+"_container"),h=f.parent();f.bind("mousedown."+u,function(){t||i||(i=1,c=!0)}).add(document).bind("mousemove."+u,function(e){if(!t&&i&&o()){var a=f.offset(),r=O(e)[0]-a.top+f[0].offsetTop,c=O(e)[1]-a.left+f[0].offsetLeft;r>0&&r<h.height()&&c>0&&c<h.width()?d.step&&n("off",null,"stepped"):("x"!==s.axis&&l.overflowed[0]&&(0>r?n("on",38):r>h.height()&&n("on",40)),"y"!==s.axis&&l.overflowed[1]&&(0>c?n("on",37):c>h.width()&&n("on",39)))}}).bind("mouseup."+u+" dragend."+u,function(){t||(i&&(i=0,n("off",null)),c=!1)})},W=function(){function t(t,a){if(Q(o),!z(o,t.target)){var r="auto"!==i.mouseWheel.deltaFactor?parseInt(i.mouseWheel.deltaFactor):s&&t.deltaFactor<100?100:t.deltaFactor||100,d=i.scrollInertia;if("x"===i.axis||"x"===i.mouseWheel.axis)var u="x",f=[Math.round(r*n.scrollRatio.x),parseInt(i.mouseWheel.scrollAmount)],h="auto"!==i.mouseWheel.scrollAmount?f[1]:f[0]>=l.width()?.9*l.width():f[0],m=Math.abs(e("#mCSB_"+n.idx+"_container")[0].offsetLeft),p=c[1][0].offsetLeft,g=c[1].parent().width()-c[1].width(),v="y"===i.mouseWheel.axis?t.deltaY||a:t.deltaX;else var u="y",f=[Math.round(r*n.scrollRatio.y),parseInt(i.mouseWheel.scrollAmount)],h="auto"!==i.mouseWheel.scrollAmount?f[1]:f[0]>=l.height()?.9*l.height():f[0],m=Math.abs(e("#mCSB_"+n.idx+"_container")[0].offsetTop),p=c[0][0].offsetTop,g=c[0].parent().height()-c[0].height(),v=t.deltaY||a;"y"===u&&!n.overflowed[0]||"x"===u&&!n.overflowed[1]||((i.mouseWheel.invert||t.webkitDirectionInvertedFromDevice)&&(v=-v),i.mouseWheel.normalizeDelta&&(v=0>v?-1:1),(v>0&&0!==p||0>v&&p!==g||i.mouseWheel.preventDefault)&&(t.stopImmediatePropagation(),t.preventDefault()),t.deltaFactor<5&&!i.mouseWheel.normalizeDelta&&(h=t.deltaFactor,d=17),G(o,(m-v*h).toString(),{dir:u,dur:d}))}}if(e(this).data(a)){var o=e(this),n=o.data(a),i=n.opt,r=a+"_"+n.idx,l=e("#mCSB_"+n.idx),c=[e("#mCSB_"+n.idx+"_dragger_vertical"),e("#mCSB_"+n.idx+"_dragger_horizontal")],d=e("#mCSB_"+n.idx+"_container").find("iframe");d.length&&d.each(function(){e(this).bind("load",function(){A(this)&&e(this.contentDocument||this.contentWindow.document).bind("mousewheel."+r,function(e,o){t(e,o)})})}),l.bind("mousewheel."+r,function(e,o){t(e,o)})}},R=new Object,A=function(t){var o=!1,a=!1,n=null;if(void 0===t?a="#empty":void 0!==e(t).attr("id")&&(a=e(t).attr("id")),a!==!1&&void 0!==R[a])return R[a];if(t){try{var i=t.contentDocument||t.contentWindow.document;n=i.body.innerHTML}catch(r){}o=null!==n}else{try{var i=top.document;n=i.body.innerHTML}catch(r){}o=null!==n}return a!==!1&&(R[a]=o),o},L=function(e){var t=this.find("iframe");if(t.length){var o=e?"auto":"none";t.css("pointer-events",o)}},z=function(t,o){var n=o.nodeName.toLowerCase(),i=t.data(a).opt.mouseWheel.disableOver,r=["select","textarea"];return e.inArray(n,i)>-1&&!(e.inArray(n,r)>-1&&!e(o).is(":focus"))},P=function(){var t,o=e(this),n=o.data(a),i=a+"_"+n.idx,r=e("#mCSB_"+n.idx+"_container"),l=r.parent(),s=e(".mCSB_"+n.idx+"_scrollbar ."+d[12]);s.bind("mousedown."+i+" touchstart."+i+" pointerdown."+i+" MSPointerDown."+i,function(o){c=!0,e(o.target).hasClass("mCSB_dragger")||(t=1)}).bind("touchend."+i+" pointerup."+i+" MSPointerUp."+i,function(){c=!1}).bind("click."+i,function(a){if(t&&(t=0,e(a.target).hasClass(d[12])||e(a.target).hasClass("mCSB_draggerRail"))){Q(o);var i=e(this),s=i.find(".mCSB_dragger");if(i.parent(".mCSB_scrollTools_horizontal").length>0){if(!n.overflowed[1])return;var c="x",u=a.pageX>s.offset().left?-1:1,f=Math.abs(r[0].offsetLeft)-u*(.9*l.width())}else{if(!n.overflowed[0])return;var c="y",u=a.pageY>s.offset().top?-1:1,f=Math.abs(r[0].offsetTop)-u*(.9*l.height())}G(o,f.toString(),{dir:c,scrollEasing:"mcsEaseInOut"})}})},H=function(){var t=e(this),o=t.data(a),n=o.opt,i=a+"_"+o.idx,r=e("#mCSB_"+o.idx+"_container"),l=r.parent();r.bind("focusin."+i,function(){var o=e(document.activeElement),a=r.find(".mCustomScrollBox").length,i=0;o.is(n.advanced.autoScrollOnFocus)&&(Q(t),clearTimeout(t[0]._focusTimeout),t[0]._focusTimer=a?(i+17)*a:0,t[0]._focusTimeout=setTimeout(function(){var e=[ae(o)[0],ae(o)[1]],a=[r[0].offsetTop,r[0].offsetLeft],s=[a[0]+e[0]>=0&&a[0]+e[0]<l.height()-o.outerHeight(!1),a[1]+e[1]>=0&&a[0]+e[1]<l.width()-o.outerWidth(!1)],c="yx"!==n.axis||s[0]||s[1]?"all":"none";"x"===n.axis||s[0]||G(t,e[0].toString(),{dir:"y",scrollEasing:"mcsEaseInOut",overwrite:c,dur:i}),"y"===n.axis||s[1]||G(t,e[1].toString(),{dir:"x",scrollEasing:"mcsEaseInOut",overwrite:c,dur:i})},t[0]._focusTimer))})},U=function(){var t=e(this),o=t.data(a),n=a+"_"+o.idx,i=e("#mCSB_"+o.idx+"_container").parent();i.bind("scroll."+n,function(){0===i.scrollTop()&&0===i.scrollLeft()||e(".mCSB_"+o.idx+"_scrollbar").css("visibility","hidden")})},F=function(){var t=e(this),o=t.data(a),n=o.opt,i=o.sequential,r=a+"_"+o.idx,l=".mCSB_"+o.idx+"_scrollbar",s=e(l+">a");s.bind("contextmenu."+r,function(e){e.preventDefault()}).bind("mousedown."+r+" touchstart."+r+" pointerdown."+r+" MSPointerDown."+r+" mouseup."+r+" touchend."+r+" pointerup."+r+" MSPointerUp."+r+" mouseout."+r+" pointerout."+r+" MSPointerOut."+r+" click."+r,function(a){function r(e,o){i.scrollAmount=n.scrollButtons.scrollAmount,j(t,e,o)}if(a.preventDefault(),ee(a)){var l=e(this).attr("class");switch(i.type=n.scrollButtons.scrollType,a.type){case"mousedown":case"touchstart":case"pointerdown":case"MSPointerDown":if("stepped"===i.type)return;c=!0,o.tweenRunning=!1,r("on",l);break;case"mouseup":case"touchend":case"pointerup":case"MSPointerUp":case"mouseout":case"pointerout":case"MSPointerOut":if("stepped"===i.type)return;c=!1,i.dir&&r("off",l);break;case"click":if("stepped"!==i.type||o.tweenRunning)return;r("on",l)}}})},q=function(){function t(t){function a(e,t){r.type=i.keyboard.scrollType,r.scrollAmount=i.keyboard.scrollAmount,"stepped"===r.type&&n.tweenRunning||j(o,e,t)}switch(t.type){case"blur":n.tweenRunning&&r.dir&&a("off",null);break;case"keydown":case"keyup":var l=t.keyCode?t.keyCode:t.which,s="on";if("x"!==i.axis&&(38===l||40===l)||"y"!==i.axis&&(37===l||39===l)){if((38===l||40===l)&&!n.overflowed[0]||(37===l||39===l)&&!n.overflowed[1])return;"keyup"===t.type&&(s="off"),e(document.activeElement).is(u)||(t.preventDefault(),t.stopImmediatePropagation(),a(s,l))}else if(33===l||34===l){if((n.overflowed[0]||n.overflowed[1])&&(t.preventDefault(),t.stopImmediatePropagation()),"keyup"===t.type){Q(o);var f=34===l?-1:1;if("x"===i.axis||"yx"===i.axis&&n.overflowed[1]&&!n.overflowed[0])var h="x",m=Math.abs(c[0].offsetLeft)-f*(.9*d.width());else var h="y",m=Math.abs(c[0].offsetTop)-f*(.9*d.height());G(o,m.toString(),{dir:h,scrollEasing:"mcsEaseInOut"})}}else if((35===l||36===l)&&!e(document.activeElement).is(u)&&((n.overflowed[0]||n.overflowed[1])&&(t.preventDefault(),t.stopImmediatePropagation()),"keyup"===t.type)){if("x"===i.axis||"yx"===i.axis&&n.overflowed[1]&&!n.overflowed[0])var h="x",m=35===l?Math.abs(d.width()-c.outerWidth(!1)):0;else var h="y",m=35===l?Math.abs(d.height()-c.outerHeight(!1)):0;G(o,m.toString(),{dir:h,scrollEasing:"mcsEaseInOut"})}}}var o=e(this),n=o.data(a),i=n.opt,r=n.sequential,l=a+"_"+n.idx,s=e("#mCSB_"+n.idx),c=e("#mCSB_"+n.idx+"_container"),d=c.parent(),u="input,textarea,select,datalist,keygen,[contenteditable='true']",f=c.find("iframe"),h=["blur."+l+" keydown."+l+" keyup."+l];f.length&&f.each(function(){e(this).bind("load",function(){A(this)&&e(this.contentDocument||this.contentWindow.document).bind(h[0],function(e){t(e)})})}),s.attr("tabindex","0").bind(h[0],function(e){t(e)})},j=function(t,o,n,i,r){function l(e){u.snapAmount&&(f.scrollAmount=u.snapAmount instanceof Array?"x"===f.dir[0]?u.snapAmount[1]:u.snapAmount[0]:u.snapAmount);var o="stepped"!==f.type,a=r?r:e?o?p/1.5:g:1e3/60,n=e?o?7.5:40:2.5,s=[Math.abs(h[0].offsetTop),Math.abs(h[0].offsetLeft)],d=[c.scrollRatio.y>10?10:c.scrollRatio.y,c.scrollRatio.x>10?10:c.scrollRatio.x],m="x"===f.dir[0]?s[1]+f.dir[1]*(d[1]*n):s[0]+f.dir[1]*(d[0]*n),v="x"===f.dir[0]?s[1]+f.dir[1]*parseInt(f.scrollAmount):s[0]+f.dir[1]*parseInt(f.scrollAmount),x="auto"!==f.scrollAmount?v:m,_=i?i:e?o?"mcsLinearOut":"mcsEaseInOut":"mcsLinear",w=!!e;return e&&17>a&&(x="x"===f.dir[0]?s[1]:s[0]),G(t,x.toString(),{dir:f.dir[0],scrollEasing:_,dur:a,onComplete:w}),e?void(f.dir=!1):(clearTimeout(f.step),void(f.step=setTimeout(function(){l()},a)))}function s(){clearTimeout(f.step),$(f,"step"),Q(t)}var c=t.data(a),u=c.opt,f=c.sequential,h=e("#mCSB_"+c.idx+"_container"),m="stepped"===f.type,p=u.scrollInertia<26?26:u.scrollInertia,g=u.scrollInertia<1?17:u.scrollInertia;switch(o){case"on":if(f.dir=[n===d[16]||n===d[15]||39===n||37===n?"x":"y",n===d[13]||n===d[15]||38===n||37===n?-1:1],Q(t),oe(n)&&"stepped"===f.type)return;l(m);break;case"off":s(),(m||c.tweenRunning&&f.dir)&&l(!0)}},Y=function(t){var o=e(this).data(a).opt,n=[];return"function"==typeof t&&(t=t()),t instanceof Array?n=t.length>1?[t[0],t[1]]:"x"===o.axis?[null,t[0]]:[t[0],null]:(n[0]=t.y?t.y:t.x||"x"===o.axis?null:t,n[1]=t.x?t.x:t.y||"y"===o.axis?null:t),"function"==typeof n[0]&&(n[0]=n[0]()),"function"==typeof n[1]&&(n[1]=n[1]()),n},X=function(t,o){if(null!=t&&"undefined"!=typeof t){var n=e(this),i=n.data(a),r=i.opt,l=e("#mCSB_"+i.idx+"_container"),s=l.parent(),c=typeof t;o||(o="x"===r.axis?"x":"y");var d="x"===o?l.outerWidth(!1)-s.width():l.outerHeight(!1)-s.height(),f="x"===o?l[0].offsetLeft:l[0].offsetTop,h="x"===o?"left":"top";switch(c){case"function":return t();case"object":var m=t.jquery?t:e(t);if(!m.length)return;return"x"===o?ae(m)[1]:ae(m)[0];case"string":case"number":if(oe(t))return Math.abs(t);if(-1!==t.indexOf("%"))return Math.abs(d*parseInt(t)/100);if(-1!==t.indexOf("-="))return Math.abs(f-parseInt(t.split("-=")[1]));if(-1!==t.indexOf("+=")){var p=f+parseInt(t.split("+=")[1]);return p>=0?0:Math.abs(p)}if(-1!==t.indexOf("px")&&oe(t.split("px")[0]))return Math.abs(t.split("px")[0]);if("top"===t||"left"===t)return 0;if("bottom"===t)return Math.abs(s.height()-l.outerHeight(!1));if("right"===t)return Math.abs(s.width()-l.outerWidth(!1));if("first"===t||"last"===t){var m=l.find(":"+t);return"x"===o?ae(m)[1]:ae(m)[0]}return e(t).length?"x"===o?ae(e(t))[1]:ae(e(t))[0]:(l.css(h,t),void u.update.call(null,n[0]))}}},N=function(t){function o(){return clearTimeout(f[0].autoUpdate),0===l.parents("html").length?void(l=null):void(f[0].autoUpdate=setTimeout(function(){return c.advanced.updateOnSelectorChange&&(s.poll.change.n=i(),s.poll.change.n!==s.poll.change.o)?(s.poll.change.o=s.poll.change.n,void r(3)):c.advanced.updateOnContentResize&&(s.poll.size.n=l[0].scrollHeight+l[0].scrollWidth+f[0].offsetHeight+l[0].offsetHeight+l[0].offsetWidth,s.poll.size.n!==s.poll.size.o)?(s.poll.size.o=s.poll.size.n,void r(1)):!c.advanced.updateOnImageLoad||"auto"===c.advanced.updateOnImageLoad&&"y"===c.axis||(s.poll.img.n=f.find("img").length,s.poll.img.n===s.poll.img.o)?void((c.advanced.updateOnSelectorChange||c.advanced.updateOnContentResize||c.advanced.updateOnImageLoad)&&o()):(s.poll.img.o=s.poll.img.n,void f.find("img").each(function(){n(this)}))},c.advanced.autoUpdateTimeout))}function n(t){function o(e,t){return function(){
return t.apply(e,arguments)}}function a(){this.onload=null,e(t).addClass(d[2]),r(2)}if(e(t).hasClass(d[2]))return void r();var n=new Image;n.onload=o(n,a),n.src=t.src}function i(){c.advanced.updateOnSelectorChange===!0&&(c.advanced.updateOnSelectorChange="*");var e=0,t=f.find(c.advanced.updateOnSelectorChange);return c.advanced.updateOnSelectorChange&&t.length>0&&t.each(function(){e+=this.offsetHeight+this.offsetWidth}),e}function r(e){clearTimeout(f[0].autoUpdate),u.update.call(null,l[0],e)}var l=e(this),s=l.data(a),c=s.opt,f=e("#mCSB_"+s.idx+"_container");return t?(clearTimeout(f[0].autoUpdate),void $(f[0],"autoUpdate")):void o()},V=function(e,t,o){return Math.round(e/t)*t-o},Q=function(t){var o=t.data(a),n=e("#mCSB_"+o.idx+"_container,#mCSB_"+o.idx+"_container_wrapper,#mCSB_"+o.idx+"_dragger_vertical,#mCSB_"+o.idx+"_dragger_horizontal");n.each(function(){Z.call(this)})},G=function(t,o,n){function i(e){return s&&c.callbacks[e]&&"function"==typeof c.callbacks[e]}function r(){return[c.callbacks.alwaysTriggerOffsets||w>=S[0]+y,c.callbacks.alwaysTriggerOffsets||-B>=w]}function l(){var e=[h[0].offsetTop,h[0].offsetLeft],o=[x[0].offsetTop,x[0].offsetLeft],a=[h.outerHeight(!1),h.outerWidth(!1)],i=[f.height(),f.width()];t[0].mcs={content:h,top:e[0],left:e[1],draggerTop:o[0],draggerLeft:o[1],topPct:Math.round(100*Math.abs(e[0])/(Math.abs(a[0])-i[0])),leftPct:Math.round(100*Math.abs(e[1])/(Math.abs(a[1])-i[1])),direction:n.dir}}var s=t.data(a),c=s.opt,d={trigger:"internal",dir:"y",scrollEasing:"mcsEaseOut",drag:!1,dur:c.scrollInertia,overwrite:"all",callbacks:!0,onStart:!0,onUpdate:!0,onComplete:!0},n=e.extend(d,n),u=[n.dur,n.drag?0:n.dur],f=e("#mCSB_"+s.idx),h=e("#mCSB_"+s.idx+"_container"),m=h.parent(),p=c.callbacks.onTotalScrollOffset?Y.call(t,c.callbacks.onTotalScrollOffset):[0,0],g=c.callbacks.onTotalScrollBackOffset?Y.call(t,c.callbacks.onTotalScrollBackOffset):[0,0];if(s.trigger=n.trigger,0===m.scrollTop()&&0===m.scrollLeft()||(e(".mCSB_"+s.idx+"_scrollbar").css("visibility","visible"),m.scrollTop(0).scrollLeft(0)),"_resetY"!==o||s.contentReset.y||(i("onOverflowYNone")&&c.callbacks.onOverflowYNone.call(t[0]),s.contentReset.y=1),"_resetX"!==o||s.contentReset.x||(i("onOverflowXNone")&&c.callbacks.onOverflowXNone.call(t[0]),s.contentReset.x=1),"_resetY"!==o&&"_resetX"!==o){if(!s.contentReset.y&&t[0].mcs||!s.overflowed[0]||(i("onOverflowY")&&c.callbacks.onOverflowY.call(t[0]),s.contentReset.x=null),!s.contentReset.x&&t[0].mcs||!s.overflowed[1]||(i("onOverflowX")&&c.callbacks.onOverflowX.call(t[0]),s.contentReset.x=null),c.snapAmount){var v=c.snapAmount instanceof Array?"x"===n.dir?c.snapAmount[1]:c.snapAmount[0]:c.snapAmount;o=V(o,v,c.snapOffset)}switch(n.dir){case"x":var x=e("#mCSB_"+s.idx+"_dragger_horizontal"),_="left",w=h[0].offsetLeft,S=[f.width()-h.outerWidth(!1),x.parent().width()-x.width()],b=[o,0===o?0:o/s.scrollRatio.x],y=p[1],B=g[1],T=y>0?y/s.scrollRatio.x:0,k=B>0?B/s.scrollRatio.x:0;break;case"y":var x=e("#mCSB_"+s.idx+"_dragger_vertical"),_="top",w=h[0].offsetTop,S=[f.height()-h.outerHeight(!1),x.parent().height()-x.height()],b=[o,0===o?0:o/s.scrollRatio.y],y=p[0],B=g[0],T=y>0?y/s.scrollRatio.y:0,k=B>0?B/s.scrollRatio.y:0}b[1]<0||0===b[0]&&0===b[1]?b=[0,0]:b[1]>=S[1]?b=[S[0],S[1]]:b[0]=-b[0],t[0].mcs||(l(),i("onInit")&&c.callbacks.onInit.call(t[0])),clearTimeout(h[0].onCompleteTimeout),J(x[0],_,Math.round(b[1]),u[1],n.scrollEasing),!s.tweenRunning&&(0===w&&b[0]>=0||w===S[0]&&b[0]<=S[0])||J(h[0],_,Math.round(b[0]),u[0],n.scrollEasing,n.overwrite,{onStart:function(){n.callbacks&&n.onStart&&!s.tweenRunning&&(i("onScrollStart")&&(l(),c.callbacks.onScrollStart.call(t[0])),s.tweenRunning=!0,C(x),s.cbOffsets=r())},onUpdate:function(){n.callbacks&&n.onUpdate&&i("whileScrolling")&&(l(),c.callbacks.whileScrolling.call(t[0]))},onComplete:function(){if(n.callbacks&&n.onComplete){"yx"===c.axis&&clearTimeout(h[0].onCompleteTimeout);var e=h[0].idleTimer||0;h[0].onCompleteTimeout=setTimeout(function(){i("onScroll")&&(l(),c.callbacks.onScroll.call(t[0])),i("onTotalScroll")&&b[1]>=S[1]-T&&s.cbOffsets[0]&&(l(),c.callbacks.onTotalScroll.call(t[0])),i("onTotalScrollBack")&&b[1]<=k&&s.cbOffsets[1]&&(l(),c.callbacks.onTotalScrollBack.call(t[0])),s.tweenRunning=!1,h[0].idleTimer=0,C(x,"hide")},e)}}})}},J=function(e,t,o,a,n,i,r){function l(){S.stop||(x||m.call(),x=K()-v,s(),x>=S.time&&(S.time=x>S.time?x+f-(x-S.time):x+f-1,S.time<x+1&&(S.time=x+1)),S.time<a?S.id=h(l):g.call())}function s(){a>0?(S.currVal=u(S.time,_,b,a,n),w[t]=Math.round(S.currVal)+"px"):w[t]=o+"px",p.call()}function c(){f=1e3/60,S.time=x+f,h=window.requestAnimationFrame?window.requestAnimationFrame:function(e){return s(),setTimeout(e,.01)},S.id=h(l)}function d(){null!=S.id&&(window.requestAnimationFrame?window.cancelAnimationFrame(S.id):clearTimeout(S.id),S.id=null)}function u(e,t,o,a,n){switch(n){case"linear":case"mcsLinear":return o*e/a+t;case"mcsLinearOut":return e/=a,e--,o*Math.sqrt(1-e*e)+t;case"easeInOutSmooth":return e/=a/2,1>e?o/2*e*e+t:(e--,-o/2*(e*(e-2)-1)+t);case"easeInOutStrong":return e/=a/2,1>e?o/2*Math.pow(2,10*(e-1))+t:(e--,o/2*(-Math.pow(2,-10*e)+2)+t);case"easeInOut":case"mcsEaseInOut":return e/=a/2,1>e?o/2*e*e*e+t:(e-=2,o/2*(e*e*e+2)+t);case"easeOutSmooth":return e/=a,e--,-o*(e*e*e*e-1)+t;case"easeOutStrong":return o*(-Math.pow(2,-10*e/a)+1)+t;case"easeOut":case"mcsEaseOut":default:var i=(e/=a)*e,r=i*e;return t+o*(.499999999999997*r*i+-2.5*i*i+5.5*r+-6.5*i+4*e)}}e._mTween||(e._mTween={top:{},left:{}});var f,h,r=r||{},m=r.onStart||function(){},p=r.onUpdate||function(){},g=r.onComplete||function(){},v=K(),x=0,_=e.offsetTop,w=e.style,S=e._mTween[t];"left"===t&&(_=e.offsetLeft);var b=o-_;S.stop=0,"none"!==i&&d(),c()},K=function(){return window.performance&&window.performance.now?window.performance.now():window.performance&&window.performance.webkitNow?window.performance.webkitNow():Date.now?Date.now():(new Date).getTime()},Z=function(){var e=this;e._mTween||(e._mTween={top:{},left:{}});for(var t=["top","left"],o=0;o<t.length;o++){var a=t[o];e._mTween[a].id&&(window.requestAnimationFrame?window.cancelAnimationFrame(e._mTween[a].id):clearTimeout(e._mTween[a].id),e._mTween[a].id=null,e._mTween[a].stop=1)}},$=function(e,t){try{delete e[t]}catch(o){e[t]=null}},ee=function(e){return!(e.which&&1!==e.which)},te=function(e){var t=e.originalEvent.pointerType;return!(t&&"touch"!==t&&2!==t)},oe=function(e){return!isNaN(parseFloat(e))&&isFinite(e)},ae=function(e){var t=e.parents(".mCSB_container");return[e.offset().top-t.offset().top,e.offset().left-t.offset().left]},ne=function(){function e(){var e=["webkit","moz","ms","o"];if("hidden"in document)return"hidden";for(var t=0;t<e.length;t++)if(e[t]+"Hidden"in document)return e[t]+"Hidden";return null}var t=e();return t?document[t]:!1};e.fn[o]=function(t){return u[t]?u[t].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof t&&t?void e.error("Method "+t+" does not exist"):u.init.apply(this,arguments)},e[o]=function(t){return u[t]?u[t].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof t&&t?void e.error("Method "+t+" does not exist"):u.init.apply(this,arguments)},e[o].defaults=i,window[o]=!0,e(window).bind("load",function(){e(n)[o](),e.extend(e.expr[":"],{mcsInView:e.expr[":"].mcsInView||function(t){var o,a,n=e(t),i=n.parents(".mCSB_container");if(i.length)return o=i.parent(),a=[i[0].offsetTop,i[0].offsetLeft],a[0]+ae(n)[0]>=0&&a[0]+ae(n)[0]<o.height()-n.outerHeight(!1)&&a[1]+ae(n)[1]>=0&&a[1]+ae(n)[1]<o.width()-n.outerWidth(!1)},mcsInSight:e.expr[":"].mcsInSight||function(t,o,a){var n,i,r,l,s=e(t),c=s.parents(".mCSB_container"),d="exact"===a[3]?[[1,0],[1,0]]:[[.9,.1],[.6,.4]];if(c.length)return n=[s.outerHeight(!1),s.outerWidth(!1)],r=[c[0].offsetTop+ae(s)[0],c[0].offsetLeft+ae(s)[1]],i=[c.parent()[0].offsetHeight,c.parent()[0].offsetWidth],l=[n[0]<i[0]?d[0]:d[1],n[1]<i[1]?d[0]:d[1]],r[0]-i[0]*l[0][0]<0&&r[0]+n[0]-i[0]*l[0][1]>=0&&r[1]-i[1]*l[1][0]<0&&r[1]+n[1]-i[1]*l[1][1]>=0},mcsOverflow:e.expr[":"].mcsOverflow||function(t){var o=e(t).data(a);if(o)return o.overflowed[0]||o.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));;
/*
 * jQuery throttle / debounce - v1.1 - 3/7/2010
 * http://benalman.com/projects/jquery-throttle-debounce-plugin/
 * 
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */
(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);;
/**
 * @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);
;
/**
 * @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;

                events['click'+'.primary-nav__link'] = self._trackMenuClick;
                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);
        },

        _trackMenuClick: function(event){
            window.dataLayer = window.dataLayer || [];
            window.dataLayer.push({
                'event':'menuItemClicked',
                'itemClicked':$(event.target).text().trim(),
                'categoryClicked':$(event.target).closest('.primary-nav__list').prev('h3').text().trim()
            });
        },

        /**
         * 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() {
				if(this.length > 0)
                {
					// After JQuery upgrade to v3.3.1, the "#" throws an exception so we need to cover this scenario.
					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.cxcollapsible', {

        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': ['.js-compare-checkbox','.listing__item__compare'],
            'compareBarItem': 'compare-bar__item',
            'hasCompareBarClass': 'has-compare-bar',
            'hasCollapsedCompareBarClass': 'has-collapsed-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');
					
						//check for retrocompatibility just in case (transform compareItem val in correctly formatted array) --Riccardo
					if (!$.isArray(self.options.compareItem)) {
						if (self.options.compareItem.match("^.") != ".") {
							self.options.compareItem = ["." + self.options.compareItem];
						}
						else {
							self.options.compareItem = [self.options.compareItem];
						}
					}

            // 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 = {};
								$.each(self.options.compareItem, function () {
									events['click ' + this] = 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.toString());
            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; });
						var self = this;						
            for (var i = 0; i < compareBoxes.length; i++) {
                var serviceId = compareBoxes[i].getAttribute("data-compare-id");
							if ($.inArray(serviceId, compareIds) > -1) {
									$(compareBoxes[i]).addClass(self.options.checkedClass);
									$(compareBoxes[i]).prop("checked", true);
                }
            }
            },

        /**
         * 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).prop('checked', false);
            }

        },

        // 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).prop('checked', false);

                            // 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).prop('checked', true).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).prop('checked', true);

            //  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).prop('checked', false);

                // 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).prop('checked', true).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);

            if(isCollapsed) {
                this._html.addClass(this.options.hasCollapsedCompareBarClass);
            } else {
                this._html.removeClass(this.options.hasCollapsedCompareBarClass);
            }

            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).prop('checked', false);
                    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.toString());
        }

    });

})(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',
          'removeActionSelector': '.js-cx-duplicate-remove-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.options.cloneActionSelector).on('click', function(e) {
            e.preventDefault();
            self._cloneElement();
            self._btnRemoveVisibility();
          });

          $(self.options.removeActionSelector).on('click', function(e) {
            e.preventDefault();
            this.closest(".js-cx-duplicate-container").remove();
            self._btnRemoveVisibility();
          });

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

          // After loading, hide the only remove button on the page
          $('.js-cx-duplicate-remove-trigger').hide();

        },

        _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.clone(true).insertBefore(self.options.cloneActionSelector);
          var template = self._template.clone(true, true);
          var newSubForm = 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({selectClass:"has-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);
        },

        /**
        * Show/Hide the "Remove" buton depending of the number of instances of the duplicated container
        * It should be hidden if there is only one container
        */
        _btnRemoveVisibility: function(element) {
          var self = this;
          // first we need to be sure eventual hidden "remove" buttons are displayed again
          // this is needed if we have one container with a display:none remove button and we add a second container (clone)
          $('.js-cx-duplicate-remove-trigger').show();

          // then we check if we only have one container (no clones)
          if ($('.js-cx-duplicate-remove-trigger').length < 2) {
            $('.js-cx-duplicate-remove-trigger').hide();
          }
        },

        /**
         * 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
				.html(label[0])
				.removeClass(this.options.openClass);
		},

		_viewFewer: function(label) {
			this._target.addClass(this.options.openClass);
			this._link
				.html(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--tiny">{{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
			});
			self._removeSaveButton();
            self.close(this._target, this._fields);

		},

		/**
		 *  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}}}{{#address}}<br><span class="autosuggest__title zeta">{{address}}</span>{{/address}}<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.title;
                    // 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,
                    'title': 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.title,
                        '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) {
										setTimeout(function(){
											$(event.target).closest(self._selector).remove();
									}, 100);
								}
            });
        }
    });

})(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));
;

// OBSOLETE: Do not longer use or modify, use ec-blueImpFileUpload instead !

(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');
                    }
                }
            });

            /*
             * 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 () {
            $.extend(this.options, this.element.data("options"));
	 		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);





;
/**
 * @fileOverview A jQuery UI Widget to make tables headers fixed
 * @author tympanus.net/codrops + Riccardo Moschetti, Riccardo Farina
 * @name $.cx.stickyheader
 * @dependencies: jQuery, jQuery UI widget factory, jQuery throttle-debounce
 */


(function ($) {

	$('table').each(function() {
		if($(this).find('thead').length > 0 && $(this).find('.js-stickyheader').length > 0) {
			// Clone <thead>
			var $w	   			= $(window),
				$t	   				= $(this),
				$thead 				= $t.find('.js-stickyheader').clone(), 
				$col   				= $t.find('thead, tbody').clone(),
				$tableclass		= $t.attr('class'),
				$tableHeadClass = $t.children("thead:first").attr('class');
				
			// WEB4 and MOBILE HEADER height
			var web4Height = 41,
				mobileHeaderHeight = 46;
			
			// If desktop and WEB4
			if($(window).width() > 851) {
				reservedTopPixels = web4Height;
			} else {
				reservedTopPixels =mobileHeaderHeight;
			}
			
			
			// get array of classes, separated by space
			var $totalListOfTableClasses =$tableclass.split(/\s+/);
			var $stringOfTableClassesToKeep="";
			$.each($totalListOfTableClasses, function(index, item){
			if (!item.match("^js-")) {
				$stringOfTableClassesToKeep+=item+" ";
				}
			});
			$t
			.addClass('sticky-is-enabled')
			.css({
				margin: 0,
				width: '100%'
			}).wrap('<div class="stickyheader__wrap" />');

			if($t.hasClass('overflow-y')) $t.removeClass('overflow-y').parent().addClass('overflow-y');

			// Create new sticky table head (basic)
			$t.after('<table class="stickyheader__thead '+$stringOfTableClassesToKeep+'" />');

			//If <tbody> contains <th>, then we create sticky column and intersect (advanced)
			//if($t.find('tbody th').length > 0) {
			//$t.after('<table class="stickyheader__col" /><table class="stickyheader__intersect" />');
			//}

			// Create shorthand for things
			var $stickyHead  = $(this).siblings('.stickyheader__thead'),
				$stickyCol   = $(this).siblings('.stickyheader__col'),
				$stickyInsct = $(this).siblings('.stickyheader__intersect'),
				$stickyWrap  = $(this).parent('.stickyheader__wrap'),
                 $stickyFixContainer = $('.js-sticky-container'),
                $compareWrap =$('.stickyheader__wrap');

			$stickyHead.append($thead);
            if($('.js-sticky-container').length){
            $stickyFixContainer.append($stickyHead);
            }

			//$stickyCol
			//.append($col)
			//	.find('thead th:gt(0)').remove()
			//	.end()
			//	.find('tbody td').remove();

			//$stickyInsct.html('<thead><tr><th>'+$t.find('thead th:first-child').html()+'</th></tr></thead>');
			//$firstTRSticky=$t.find("tr[class*='stickyheader']:first-child th");// td:first-child");
			
			
			 			// Set widths
			var setWidths = function () {
					$t
					.find('thead:not(.compare-table__head) th').each(function (i) {
						$stickyHead.find('th').eq(i).width($(this).width());
                        //console.log($(this).width());
					})
					.end()
					//.find('tr').each(function (i) {
					//	$stickyCol.find('tr').eq(i).height($(this).height());
					//});

					// Set width of sticky table head
					$stickyHead.width($t.width());

					// Set width of sticky table col
					//$stickyCol.find('th').add($stickyInsct.find('th')).width($t.find('thead th').width())
				},
                
				repositionStickyHead = function () {
					// Return value of calculated allowance
					var allowance = calcAllowance();

					// Added to take WEB4 into account
					var scrollTop     = $(window).scrollTop(),
						elementOffset = $stickyWrap.offset().top,
						distanceFromTop = (elementOffset - scrollTop);
				
					// Check if wrapper parent is overflowing along the y-axis
					if($t.height() > $stickyWrap.height()) {

						// If it is overflowing (advanced layout)

						// Position sticky header based on wrapper scrollTop()
						 if($stickyWrap.scrollTop() > 0) {
						// Position sticky header with WEB4 taken into account (kept by CGY in case it's needed)
						// if(distanceFromTop < reservedTopPixels) {

							// When top of wrapping parent is out of view
							$stickyHead.add($stickyInsct).css({
								visibility: "visible",
								opacity: 1,
								"transition-delay":"0s",
								top: $stickyWrap.scrollTop() + reservedTopPixels
							});
                           $stickyFixContainer.removeClass('js-hide');
						} else {
							// When top of wrapping parent is in view
							$stickyHead.add($stickyInsct).css({
								visibility: "hidden",
								opacity: 0,
								top: 0
							});
                            $stickyFixContainer.addClass('js-hide');
						}
					} else {

						// If it is not overflowing (basic layout)
						// Position sticky header based on viewport scrollTop
						var $firstTRStickyOffsetTop=0;
						$firstTRSticky=$t.find("tr[class*='sticky']").eq(0);
						
						var $firstTRStickyOffset='';
			
						if($firstTRSticky.length)
						{
							$firstTRStickyOffset=$firstTRSticky.offset();
							$firstTRStickyOffsetTop=$firstTRStickyOffset.top - reservedTopPixels;
						}
									
						if($w.scrollTop() > $firstTRStickyOffsetTop && $firstTRStickyOffsetTop!=0 && $w.scrollTop() < $t.offset().top + $t.outerHeight() - allowance) {
						//40                > 225                     && 225	                  !=0 && 40             < ??              + 400              - 101
						//alert ("entered");
							$stickyHead.add($stickyInsct).css({
								visibility: "visible",
								opacity: 1,
								"transition-delay":"0s",
								top: $w.scrollTop() - $t.offset().top + reservedTopPixels
							});
							
                        $stickyFixContainer.removeClass('js-hide');
						} else {
							// When top of viewport is above or below table
							$stickyHead.add($stickyInsct).css({
								visibility: "hidden",
								opacity: 0,
								top: 0
							});
                             $stickyFixContainer.addClass('js-hide');
						}
					}
				},
				/*
				repositionStickyCol = function () {
					if($stickyWrap.scrollLeft() > 0) {
						// When left of wrapping parent is out of view
						$stickyCol.add($stickyInsct).css({
							visibility: "visible",
							opacity: 1,
							"transition-delay":"0s",
							left: $stickyWrap.scrollLeft()
						});
					} else {
						// When left of wrapping parent is in view
						$stickyCol
						.css({ visibility: "hidden", opacity: 0 })
						.add($stickyInsct).css({ left: 0 });
					}
				},*/
				calcAllowance = function () {
					var a = 0;
					// Calculate allowance
					$t.find('tbody tr:lt(3)').each(function () {
						a += $(this).height();
					});
					
					// Set fail safe limit (last three row might be too tall)
					// Set arbitrary limit at 0.25 of viewport height, or you can use an arbitrary pixel value
					if(a > $w.height()*0.25) {
						a = $w.height()*0.25;
					}
					
					// Add the height of sticky header
					a += $stickyHead.height();
					return a;
				};

			setWidths();

			$t.parent('.stickyheader__wrap').scroll($.throttle(250, function() {
				repositionStickyHead();
				//repositionStickyCol();
			}));

			$w
			.on("load", setWidths)

			.resize($.debounce(250, function () {
				setWidths();
				repositionStickyHead();
				//repositionStickyCol();
			}))
			.scroll($.throttle(250, repositionStickyHead));
            
            //sync horizontal postion of stickyhead on mobile with table
            $compareWrap.scroll(function() {
                //repositionStickyHeadHorizontal();
                var horzoffset = $compareWrap.scrollLeft();
                $stickyFixContainer.css("margin-left", -horzoffset);
                //console.log(horzoffset);
            });
		}
        
			//let us get the tbodies that are created by the script and add the tableHeader's class
				$tBodyToModify=$( "table[class*='sticky']" ).children("tbody");
				$tBodyToModify.addClass($tableHeadClass);
	});
			
})(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",autoFormat:!1},_create:function(){this.element.addClass("has-plugin");var a=this.element;return!a.hasClass("has-datepicker")&&(a.addClass("has-datepicker"),this.settings=a.datepicker(this.options),void this._subscribeToEvents())},_init:function(){var a=this.element;void 0==a.attr("value")&&void 0==a.val()&&a.attr("value",""),"from-to"==a.data("rel-pos")?(this.settings=a.datepicker("option","numberOfMonths",2),this._setOptionsFromTo(a),a.trigger("initialised")):(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.extend(d.settings.datepicker("option"),d.options),d.element[0]&&void 0!==d.element.attr("value")&&d.settings.datepicker("setDate",d.element.attr("value"))):a.ajax({url:"https://cdn.euroconsumers.org/vendor/jquery/jquery-ui/1.12.1/ui/i18n/datepicker-"+c+".js",dataType:"script",success:function(b,e,f){d.options=a.extend(a.datepicker.regional[c],d.options),d.settings.datepicker("option",d.options),d.options.autoFormat&&d._autoFormat(),d.element[0]&&void 0!==d.element.attr("value")&&d.settings.datepicker("setDate",d.element.attr("value"))},error:function(a,b,c){console.error(b,c,a)},async:!1})},_setOptions:function(a){var b=this;if(a[0])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)},onClose:function(c){b._onSelect(a,c)}}),this.settings.datepicker("option",{yearRange:a.attr("year-range")||this.options.yearRange});else for(var c in a)this.settings.datepicker("option",c,a[c])},_setOptionsFromTo:function(b){this.settings.datepicker("option",{onSelect:function(b){a(this).data().datepicker.first?(a(this).datepicker("getDate")>a(this).data().datepicker.firstunformat?(a(this).val(a(this).data().datepicker.first+" - "+b),a(this).attr("value",a(this).val())):(a(this).val(b+" - "+a(this).data().datepicker.first),a(this).attr("value",a(this).val())),a(this).data().datepicker.inline=!1):(a(this).data().datepicker.inline=!0,a(this).data().datepicker.first=b,a(this).data().datepicker.firstunformat=a(this).datepicker("getDate"))},onClose:function(){delete a(this).data().datepicker.first,a(this).data().datepicker.inline=!1,a(this).trigger("dateChanged")}})},_autoFormat:function(){if(this.options.dateFormat){var b=this.options.dateFormat,c=this.element;if(b.match(/[A-Z]/))return;b.match("y").length<3&&(b=b.replace(/y/g,"yy"));for(var d=b.replace(/[a-zA-Z 0-9]+/g,"").charAt(0),e=[],f=0;f<b.length;f++)b.charAt(f)===d&&e.push(f);c.attr("placeholder")||c.attr("placeholder",b),c.keydown(function(f){f.keyCode>=48&&f.keyCode<=57&&(c.val().length>=b.length?f.preventDefault():setTimeout(function(){a.inArray(c.val().length,e)>-1&&c.val(c.val()+d)},0))})}},_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){if(a(b).attr("value")!==c){a(b).trigger("dateChanged",{el:b,date:c}),a(b).attr("value",c);var d=b.data("rel");if(d){var e=b.data("rel-pos");"from"==e?a(d).datepicker("option","minDate",c):"to"==e&&a(d).datepicker("option","maxDate",c)}}}})}(jQuery);var EC;!function(a){var b;!function(a){a.parseDate=function(a,b,c){return $.datepicker.parseDate(a,b,c)},a.regional=$.datepicker.regional}(b=a.DateUtilities||(a.DateUtilities={}))}(EC||(EC={}));;
!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,forceDesktop:!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.options.forceDesktop===!0||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++){if(a(b.$owlItems[d].firstChild).data("title"))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 b=this,c=b._closestItem();return c>b.maximumItem?(b.currentItem=b.maximumItem,c=b.maximumItem):b.newPosX>=0&&(c=0,b.currentItem=0),"tabs"==b.options.pagination&&(a(b.paginationWrapper).find("li.is-active").removeClass("is-active"),a(b.paginationWrapper).find("li:eq("+c+")").addClass("is-active")),c},_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(a){"use strict";a.widget("ec.fileUpload",{options:{settings:{uploadMethod:"blueimp",url:"//jquery-file-upload.appspot.com/",azureData:"/api/v1/echo/jsonfrombase64?value=eyJ1cGxvYWRVcmwiOiAiLy9ldXJvY29uc3VtZXJzZG9jc3RnYWNjLmJsb2IuY29yZS53aW5kb3dzLm5ldC90ZW1wL2tlZXBpdC8yYzM2ZjJmOC0wYmI0LTQ5MTAtYmFlYS05ODYzN2QxNDA4MTYvNWI5MDFjYjYtNTRjOC00ZThlLTk2MmMtZTAzNWU3ZDk2MjVmP3N2PTIwMTctMDQtMTcmc3I9YiZzaWc9UnoxQUhScTJGZGFkTTlHMFE3QlNFRTJWRnVmaDAzMGhwQXJaMTFSckRXayUzRCZzdD0yMDE4LTAxLTA5VDEwOjAwOjAyWiZzZT0yMDI4LTAxLTA3VDExOjAwOjAyWiZzcD1hdyIsImRvY3VtZW50SUQiOiAiZnloZHVpc2ZoaXNkZmhpc2RvZmhzZGlvZmhzZGkifQ==",controllerStatus:"/api/v1/echo/jsonfrombase64?value=eyJkZWxldGVVcmwiOiAiL2FwaS92MS9lY2hvL2pzb24vc3RhdHVzL3N1Y2Nlc3MvIn0",debug:!1,controlInput:"",listInput:"",maxChunkSize:1e5,maxFileSize:1e6,maxNumberOfFiles:10,fileTypes:["gif","jpeg","png","jpg","pdf"],timeout:3e4,filesBeforeCompact:3,messages:{complete:"Complete",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."}},classes:{disabled:"is-disabled",fileWrapper:"fileupload__wrapper",fileTemplate:"fileupload__item",fileTemplateLoading:"fileupload__item--loading",fileTemplateSuccess:"fileupload__item--success",fileTemplateError:"fileupload__item--error",fileTemplateProgress:"progress__bar",fileTemplateCancel:["btn--cancel","icon-delete"],fileTemplateInfo:"fileupload__item__info"},elements:{fileButton:".js-fileupload-button",fileField:".js-fileupload-field",filesContainer:".js-fileupload-container",continueButton:".js-fileupload-continue",dropzone:".js-fileupload-dropzone"},templates:{upload:function(b,c){var d="";b.size&&(d=(parseInt(b.size)/1048576).toFixed(2)+" MB");var e=a('<li class="template-upload '+c.options.classes.fileWrapper+'"><div class="'+c.options.classes.fileTemplate+" "+c.options.classes.fileTemplateLoading+'"><div class="template-message">'+b.name+'</div><div class="'+c.options.classes.fileTemplateProgress+'"></div><div class="'+c.options.classes.fileTemplateInfo+'"><span class="percent"></span><span class="filesize">'+d+'</span></div><button class="cancel '+c.options.classes.fileTemplateCancel.join(" ")+'"><span class="visuallyhidden">Cancel</span></button></div></li>');return a(e)},download:function(b,c){var d="";if(b.size&&(d=(parseInt(b.size)/1048576).toFixed(2)+" MB"),b.name&&(b.fileId=b.name.toLowerCase().replace(/ /g,"-").replace(/[^\w-]+/g,"")+"-"+Math.floor(1e7*Math.random())),b.error)switch(b.error){case"timeout":b.error=data.options.messages.timeout;break;case"Internal Server Error":b.error=data.options.messages.internalServerError;break;case"unknownError":b.error=data.options.messages.unknownError}var e,f,g;f=b.deleteType?'<button title="Delete '+b.name+'" class="delete '+c.options.classes.fileTemplateCancel.join(" ")+'" data-type="'+b.deleteType+'" data-url="'+b.deleteUrl+'"><span class="visuallyhidden">Delete '+b.name+'</span></button><label class="visuallyhidden" for="'+b.fileId+'">Delete '+b.name+'</label><input tabindex="-1" id="'+b.fileId+'" type="checkbox" checked name="delete" value="'+b.name+'" class="visuallyhidden toggle">':'<button class="cancel '+c.options.classes.fileTemplateCancel.join(" ")+'"><span class="visuallyhidden">Cancel</span></button>',b.error?(e=b.error,g=c.options.classes.fileTemplateError):(e=b.name,g=c.options.classes.fileTemplateSuccess);var h=a('<li class="template-download '+c.options.classes.fileWrapper+'"><div class="'+c.options.classes.fileTemplate+" "+g+'"><div class="template-message">'+e+'</div><div class="'+c.options.classes.fileTemplateProgress+'" style="width: 100%"></div><div class="'+c.options.classes.fileTemplateInfo+'"><span class="percent">'+c.options.settings.messages.complete+'</span><span class="filesize">'+d+"</span></div>"+f+"</div></li>");return a(h)}}},widgetEventPrefix:"file_upload_",_create:function(){function b(a){var b=null;return a.size>f.options.settings.maxFileSize?b=f.options.settings.messages.maxFileSize:f.options.settings.acceptFileTypes.test(a.type)||f.options.settings.acceptFileTypes.test(a.name)||(b=f.options.settings.messages.acceptFileTypes),b}function c(c){function d(a){return new Promise(function(b){setTimeout(b,a)})}i=f.options.templates.upload(c,f),j=a(i),f._disableContinueButton(h),f._testFinished(h),a(g.filesContainer).prepend(j);var e=b(c);if(f.options.settings.prefilled&&c.isPrefilled&&(j.find("."+f.options.classes.fileTemplateProgress).addClass("complete"),j.find("."+f.options.classes.fileTemplate).addClass(f.options.classes.fileTemplateSuccess).removeClass(f.options.classes.fileTemplateLoading),"debug"!=f.options.settings.uploadMethod&&(j.data("delete-url",c.deleteUrl),j.data("file-name",c.name),j.data("file-id",c.documentID),j.data("file-size",c.size))),"debug"==f.options.settings.uploadMethod&&!e){console.log("debug mode active. no actual ajax calls are being made");var k=j.find("."+f.options.classes.fileTemplateProgress),l=j.find("."+f.options.classes.fileTemplateInfo+" .percent");Promise.delay=function(a,b){return d(b).then(a)},Promise.prototype.delay=function(a,b){return this.then(function(){return Promise.delay(a,b)})},Promise.delay(function(){k.css("width","1%"),l.text("1%")},500).delay(function(){k.css("width","30%"),l.text("30%")},500).delay(function(){k.css("width","66%"),l.text("66%")},500).delay(function(){k.css("width","98%"),l.text("98%")},500).delay(function(){var a=j.find("."+f.options.classes.fileTemplate);a.addClass(f.options.classes.fileTemplateSuccess).removeClass(f.options.classes.fileTemplateLoading),l.text(f.options.settings.messages.complete)},700)}}function d(b){var c=null;return a.ajax({type:"GET",url:b,dataType:"json",async:!1,success:function(a){c=a},error:function(){f.element.trigger("onSitecoreGetError"),c=!1}}),c}function e(b,c,d){var e=" ";return a.ajax({type:"POST",url:b,dataType:"json",data:c,async:!1,success:function(a){e=a;var b=j.find("."+f.options.classes.fileTemplate);b.addClass(f.options.classes.fileTemplateSuccess).removeClass(f.options.classes.fileTemplateLoading),j.find("."+f.options.classes.fileTemplateInfo+" .percent").text(" "+f.options.settings.messages.complete),f._testFinished(h)},error:function(a){var b=f.options.settings.messages.unknownError;a.responseJSON&&a.responseJSON.errorMessage&&(b=a.responseJSON.errorMessage),f.element.trigger("onSitecorePostError"),e=!1,f._testFinished(h,b)}}),e}var f=this;this.options.disabled=!1;var g=a.extend(this.options.settings,{dropZone:this.element.find(this.options.elements.dropzone),filesContainer:this.element.find(this.options.elements.filesContainer),singleFileUploads:!0,previewSourceFileTypes:/^disabled$/,acceptFileTypes:new RegExp("(.|)("+f.options.settings.fileTypes.join("|")+")$"),autoUpload:!0,uploadTemplateId:null,downloadTemplateId:null,uploadTemplate:function(a){return f.options.templates.upload(a.files[0],f)},downloadTemplate:function(a){return f.options.templates.download(a.files[0],f)},prependFiles:!0}),h={_wrapper:this.element,_fileInput:this.element.find(this.options.elements.fileField).hide(),_fileButton:this.element.find(this.options.elements.fileButton),_continueButton:this.element.find(this.options.elements.continueButton),_filesContainer:this.options.settings.filesContainer,_dropzone:this.options.settings.dropZone};if(this.options.settings.controlInput&&(h._controlInput=a('<input type="hidden" id="id-'+this.options.settings.controlInput+'" name="'+this.options.settings.controlInput+'" value=""/>'),h._fileInput.after(h._controlInput)),this.options.settings.listInput&&(h._listInput=a('<input type="hidden" id="id-'+this.options.settings.listInput+'" name="'+this.options.settings.listInput+'" value=""/>'),h._fileInput.after(h._listInput)),this.options.classes.fileButton=h._fileButton.attr("class"),h._continueButton.length&&(h._continueButtonText=h._continueButton.html(),h._continueButtonClass=h._continueButton.attr("class")),h._fileButtonText=h._fileButton.html(),h._id=h._fileInput.prop("id"),h._fileButton.prop("for",h._id),this._uploading=!1,this._buttonsState(h),this._testFinished(h),this._trigger("files_added"),g=a.extend({},g,{filesChanged:function(a){0===a?f._trigger("no_files_uploaded"):f._trigger("files_added")}}),"azure"==f.options.settings.uploadMethod||"debug"==f.options.settings.uploadMethod){var i,j,k,l=new FileReader,m={};if(f.options.settings.prefilled){var n;"string"==a.type(f.options.settings.prefilled)?a.ajax({type:"GET",url:f.options.settings.prefilled,dataType:"json",success:function(b){n=b,a.each(n,function(a,b){b.isPrefilled=!0,c(b)})},error:function(){console.log("Prefilled json object not found or malformed")}}):"array"!=a.type(f.options.settings.prefilled)&&"object"!=a.type(f.options.settings.prefilled)||(n=f.options.settings.prefilled,a.each(n,function(a,b){b.isPrefilled=!0,c(b)}))}l.onloadend=function(g){if(g.target.readyState==FileReader.DONE){c(m);var i=b(m);if("azure"!=f.options.settings.uploadMethod||i){if(i){var l=j.find("."+f.options.classes.fileTemplate);l.addClass(f.options.classes.fileTemplateError).removeClass(f.options.classes.fileTemplateLoading),l.find(".template-message").text(i)}}else{var n=f.options.settings.azureData,o=f.options.settings.controllerStatus,p=d(n),q=p.uploadUrl,r=p.documentID;if(!q){i=f.options.settings.messages.unknownError;var l=j.find("."+f.options.classes.fileTemplate);console.log("Controller JSON data not found or unexpected format. Expected 'uploadUrl' and 'deleteUrl' values from "+n),l.addClass(f.options.classes.fileTemplateError).removeClass(f.options.classes.fileTemplateLoading),l.find(".template-message").text(i),f._uploading=!1,f._buttonsState(h),f._testFinished(h)}if(!i){var s=new Uint8Array(g.target.result);f._uploading=!0,f.element.trigger("onBeginUpload"),k=a.ajax({url:q,type:"PUT",data:s,processData:!1,xhr:function(){var a=new window.XMLHttpRequest;return a.upload.addEventListener("progress",function(a){if(a.lengthComputable){var b=Math.ceil(a.loaded/a.total*100);j.find("."+f.options.classes.fileTemplateProgress).css({width:parseInt(b,10)+"%"}),j.find("."+f.options.classes.fileTemplateInfo+" .percent").text(" "+b+"%")}},!1),a},beforeSend:function(a,b){a.setRequestHeader("x-ms-blob-type","BlockBlob"),a.setRequestHeader("x-ms-blob-content-disposition",'inline; filename="'+f._normalizeText(m.name)+'"'),a.setRequestHeader("x-ms-blob-content-type",m.type)},success:function(a,b){f.element.trigger("onEndUpload");var c={status:b,documentID:r,filename:m.name},d=e(o,c,b);j.data("delete-url",d.deleteUrl),j.data("file-id",r),j.data("file-name",m.name),j.data("file-size",m.size)},error:function(a,b,c){f.element.trigger("onUploadError"),console.log(b+" "+c),j.find(".template-message").text(f.options.settings.messages.internalServerError)},complete:function(a,b){if("success"==b);else{var c=j.find("."+f.options.classes.fileTemplate);c.addClass(f.options.classes.fileTemplateError).removeClass(f.options.classes.fileTemplateLoading)}f._uploading=!1,f._buttonsState(h),f._testFinished(h)}})}}}},a(h._fileInput).on("change",function(b){if(a(this).val()){if(m=b.target.files[0]){var c=m.slice(0,m.size-1);l.readAsArrayBuffer(c)}a(this).val("")}}),a(g.filesContainer).on("click",".cancel",function(b){function c(){e.remove(),f._buttonsState(h),f._testFinished(h)}var d=a(this),e=d.closest("."+f.options.classes.fileWrapper);if(b.preventDefault(),k&&f._uploading)k.abort(),f._uploading=!1,setTimeout(function(){c()},1);else if(e.data("delete-url")){f.element.trigger("onBeginDelete");var g=e.data("delete-url"),i=d.closest("."+f.options.classes.fileTemplate);d.hide(),i.addClass(f.options.classes.fileTemplateLoading),a.ajax({url:g,type:"POST",success:function(){f.element.trigger("onEndDelete"),c()},error:function(){f.element.trigger("onDeleteError"),i.removeClass(f.options.classes.fileTemplateLoading),d.show(),console.log("delete POST request to controller returned an error.")}})}else c()}),a(h._dropzone).on("dragenter dragstart dragend dragleave dragover drag drop",function(a){a.preventDefault(),a.stopPropagation()}).on("dragenter dragover drag",function(){f.options.disabled||a(this).addClass("active")}).on("dragleave",function(){a(this).removeClass("active")}).on("drop",function(b){if(a(this).removeClass("active"),!f.options.disabled&&(m=b.originalEvent.dataTransfer.files[0])){var c=m.slice(0,m.size-1);l.readAsArrayBuffer(c)}})}else"blueimp"==f.options.settings.uploadMethod?this.element.fileupload(this.options.settings).on("fileuploadprogress",function(a,b){b.context.find("."+f.options.classes.fileTemplateProgress).css({width:parseInt(b.loaded/b.total*100,10)+"%"}),b.context.find("."+f.options.classes.fileTemplateInfo+" .percent").text(parseInt(b.loaded/b.total*100,10)+"%")}).on("fileuploadprogressall",function(a,b){f._uploading=!0,b.loaded===b.total&&(f._buttonsState(h),f._enableContinueButton(h),f._uploading=!1)}).on("fileuploadadd",function(b,c){var d=c.files,e=a(this).data("blueimp-fileupload");a(this).fileupload("process",c).done(function(){c.context=e._renderUpload(d).data("data",c),e._forceReflow(c.context)}),f._trigger("files_added")}).on("fileuploadstart",function(a,b){f._uploading=!0,f._disableContinueButton(h),f._testFinished(h)}).on("fileuploadstop",function(a,b){f._uploading=!1,f._testFinished(h)}).on("fileuploadalways",function(a,b){f._testFinished(h)}).on("fileuploadprocessfail",function(a,b){f._disableContinueButton(h),f._uploading=!1,b.files[0].error&&f._testFinished(h,b.files[0].error)}).on("fileuploaddone",function(a,b){f._buttonsState(h),f._delay(function(){f.element.find(f.options.elements.fileField).focus()},0)}).on("fileuploaddrop",function(a,b){return h._dropzone.removeClass("active"),!f.options.disabled&&(f._disableContinueButton(h),void f._testFinished(h))}).on("fileuploaddragover",function(a,b){f.options.disabled||h._dropzone.addClass("active")}).on("dragleave",function(a){h._dropzone.removeClass("active")}).on("fileuploaddestroy",function(a,b){setTimeout(function(){f._testFinished(h)},500)}):console.log("error: no uploadMethod selected. check widget settings");this.element.trigger("onReady")},_fileCounter:function(a){return a._filesContainer.find("."+this.options.classes.fileTemplate).length<1?a._wrapper.addClass("empty"):(a._wrapper.removeClass("empty"),this.options.settings.filesBeforeCompact&&(a._filesContainer.find("."+this.options.classes.fileTemplate).length>this.options.settings.filesBeforeCompact?a._wrapper.addClass("compact"):a._wrapper.removeClass("compact"))),a._filesContainer.find("."+this.options.classes.fileTemplate).not("."+this.options.classes.fileTemplateError).length},_disableContinueButton:function(a){a._continueButton.length&&a._continueButton.html(this.options.settings.continueButton.uploadingText).addClass(this.options.settings.continueButton.class).addClass(this.options.settings.continueButton.uploadingClass).prop("tabindex",-1).on("click.continue",function(a){a.preventDefault()})},_enableContinueButton:function(a){a._continueButton.length&&(a._continueButton.text(this.options.settings.continueButton.text),a._continueButton.removeClass(this.options.settings.continueButton.uploadingClass).prop("tabindex",0).off("click.continue"))},_revertContinueButton:function(a){a._continueButton.length&&a._continueButton.html(a._continueButtonText).attr("class",a._continueButtonClass).prop("tabindex",0).off("click.continue")},enableUpload:function(a){if(this.options.disabled){if(!a)var a={_wrapper:this.element,_fileInput:this.element.find(this.options.elements.fileField),_fileButton:this.element.find(this.options.elements.fileButton),_continueButton:this.element.find(this.options.elements.continueButton),_filesContainer:this.options.settings.filesContainer,_dropzone:this.options.settings.dropZone};this.options.disabled=!1,a._fileButton.removeClass(this.options.classes.disabled).prop("for",a._fileInput.prop("id"))}},disableUpload:function(a){if(!this.options.disabled){if(!a)var a={_wrapper:this.element,_fileInput:this.element.find(this.options.elements.fileField),_fileButton:this.element.find(this.options.elements.fileButton),_continueButton:this.element.find(this.options.elements.continueButton),_filesContainer:this.options.settings.filesContainer,_dropzone:this.options.settings.dropZone};this.options.disabled=!0,a._fileButton.addClass(this.options.classes.disabled).prop("for","")}},_buttonsState:function(a){this.options.settings.addButton&&(1==this._fileCounter(a)?(this.options.settings.addButton.class&&a._fileButton.attr("class","").addClass(this.options.elements.fileButton.replace(".","")).addClass(this.options.settings.addButton.class),this.options.settings.addButton.text&&a._fileButton.html(this.options.settings.addButton.text)):0==this._fileCounter(a)&&(this.options.settings.addButton.class&&a._fileButton.attr("class",this.options.classes.fileButton),this.options.settings.addButton.text&&a._fileButton.html(a._fileButtonText)))},_testFinished:function(b,c){var d=this;this._delay(function(){var e=this._fileCounter(b);if(this.options.settings.controlInput&&(b._controlInput.val(e),this.element.trigger("onFileNumberUpdate")),this.options.settings.listInput){var f=[],g=b._filesContainer.find("."+d.options.classes.fileWrapper);a.each(g,function(){var b={};b.fileId=a(this).data("file-id"),b.fileName=a(this).data("file-name"),b.fileSize=a(this).data("file-Size"),f.push(b)}),b._listInput.val(JSON.stringify(f)),this.element.data("fileList",f),this.element.trigger("onFileListUpdate")}if(this._buttonsState(b),0===e?(this._revertContinueButton(b),this._trigger("no_files_uploaded")):this._uploading||this._enableContinueButton(b),e>=this.options.settings.maxNumberOfFiles?this.disableUpload(b):this.enableUpload(b),c){var h=b._filesContainer.find("."+this.options.classes.fileTemplate).not("."+this.options.classes.fileTemplateSuccess).not("."+this.options.classes.fileTemplateError);h.addClass(this.options.classes.fileTemplateError).removeClass(this.options.classes.fileTemplateLoading),h.find(".template-message").text(c)}},100)},_normalizeText:function(a){for(var b=[{base:"A",letters:"AⒶＡÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ"},{base:"AA",letters:"Ꜳ"},{base:"AE",letters:"ÆǼǢ"},{base:"AO",letters:"Ꜵ"},{base:"AU",letters:"Ꜷ"},{base:"AV",letters:"ꜸꜺ"},{base:"AY",letters:"Ꜽ"},{base:"B",letters:"BⒷＢḂḄḆɃƂƁ"},{base:"C",letters:"CⒸＣĆĈĊČÇḈƇȻꜾ"},{base:"D",letters:"DⒹＤḊĎḌḐḒḎĐƋƊƉꝹ"},{base:"DZ",letters:"ǱǄ"},{base:"Dz",letters:"ǲǅ"},{base:"E",letters:"EⒺＥÈÉÊỀẾỄỂẼĒḔḖĔĖËẺĚȄȆẸỆȨḜĘḘḚƐƎ"},{base:"F",letters:"FⒻＦḞƑꝻ"},{base:"G",letters:"GⒼＧǴĜḠĞĠǦĢǤƓꞠꝽꝾ"},{base:"H",letters:"HⒽＨĤḢḦȞḤḨḪĦⱧⱵꞍ"},{base:"I",letters:"IⒾＩÌÍÎĨĪĬİÏḮỈǏȈȊỊĮḬƗ"},{base:"J",letters:"JⒿＪĴɈ"},{base:"K",letters:"KⓀＫḰǨḲĶḴƘⱩꝀꝂꝄꞢ"},{base:"L",letters:"LⓁＬĿĹĽḶḸĻḼḺŁȽⱢⱠꝈꝆꞀ"},{base:"LJ",letters:"Ǉ"},{base:"Lj",letters:"ǈ"},{base:"M",letters:"MⓂＭḾṀṂⱮƜ"},{base:"N",letters:"NⓃＮǸŃÑṄŇṆŅṊṈȠƝꞐꞤ"},{base:"NJ",letters:"Ǌ"},{base:"Nj",letters:"ǋ"},{base:"O",letters:"OⓄＯÒÓÔỒỐỖỔÕṌȬṎŌṐṒŎȮȰÖȪỎŐǑȌȎƠỜỚỠỞỢỌỘǪǬØǾƆƟꝊꝌ"},{base:"OI",letters:"Ƣ"},{base:"OO",letters:"Ꝏ"},{base:"OU",letters:"Ȣ"},{base:"OE",letters:"Œ"},{base:"oe",letters:"œ"},{base:"P",letters:"PⓅＰṔṖƤⱣꝐꝒꝔ"},{base:"Q",letters:"QⓆＱꝖꝘɊ"},{base:"R",letters:"RⓇＲŔṘŘȐȒṚṜŖṞɌⱤꝚꞦꞂ"},{base:"S",letters:"SⓈＳẞŚṤŜṠŠṦṢṨȘŞⱾꞨꞄ"},{base:"T",letters:"TⓉＴṪŤṬȚŢṰṮŦƬƮȾꞆ"},{base:"TZ",letters:"Ꜩ"},{base:"U",letters:"UⓊＵÙÚÛŨṸŪṺŬÜǛǗǕǙỦŮŰǓȔȖƯỪỨỮỬỰỤṲŲṶṴɄ"},{base:"V",letters:"VⓋＶṼṾƲꝞɅ"},{base:"VY",letters:"Ꝡ"},{base:"W",letters:"WⓌＷẀẂŴẆẄẈⱲ"},{base:"X",letters:"XⓍＸẊẌ"},{base:"Y",letters:"YⓎＹỲÝŶỸȲẎŸỶỴƳɎỾ"},{base:"Z",letters:"ZⓏＺŹẐŻŽẒẔƵȤⱿⱫꝢ"},{base:"a",letters:"aⓐａẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐ"},{base:"aa",letters:"ꜳ"},{base:"ae",letters:"æǽǣ"},{base:"ao",letters:"ꜵ"},{base:"au",letters:"ꜷ"},{base:"av",letters:"ꜹꜻ"},{base:"ay",letters:"ꜽ"},{base:"b",letters:"bⓑｂḃḅḇƀƃɓ"},{base:"c",letters:"cⓒｃćĉċčçḉƈȼꜿↄ"},{base:"d",letters:"dⓓｄḋďḍḑḓḏđƌɖɗꝺ"},{base:"dz",letters:"ǳǆ"},{base:"e",letters:"eⓔｅèéêềếễểẽēḕḗĕėëẻěȅȇẹệȩḝęḙḛɇɛǝ"},{base:"f",letters:"fⓕｆḟƒꝼ"},{base:"g",letters:"gⓖｇǵĝḡğġǧģǥɠꞡᵹꝿ"},{base:"h",letters:"hⓗｈĥḣḧȟḥḩḫẖħⱨⱶɥ"},{base:"hv",letters:"ƕ"},{base:"i",letters:"iⓘｉìíîĩīĭïḯỉǐȉȋịįḭɨı"},{base:"j",letters:"jⓙｊĵǰɉ"},{base:"k",letters:"kⓚｋḱǩḳķḵƙⱪꝁꝃꝅꞣ"},{base:"l",letters:"lⓛｌŀĺľḷḹļḽḻſłƚɫⱡꝉꞁꝇ"},{base:"lj",letters:"ǉ"},{base:"m",letters:"mⓜｍḿṁṃɱɯ"},{base:"n",letters:"nⓝｎǹńñṅňṇņṋṉƞɲŉꞑꞥ"},{base:"nj",letters:"ǌ"},{base:"o",letters:"oⓞｏòóôồốỗổõṍȭṏōṑṓŏȯȱöȫỏőǒȍȏơờớỡởợọộǫǭøǿɔꝋꝍɵ"},{base:"oi",letters:"ƣ"},{base:"ou",letters:"ȣ"},{base:"oo",letters:"ꝏ"},{base:"p",letters:"pⓟｐṕṗƥᵽꝑꝓꝕ"},{base:"q",letters:"qⓠｑɋꝗꝙ"},{base:"r",letters:"rⓡｒŕṙřȑȓṛṝŗṟɍɽꝛꞧꞃ"},{base:"s",letters:"sⓢｓßśṥŝṡšṧṣṩșşȿꞩꞅẛ"},{base:"t",letters:"tⓣｔṫẗťṭțţṱṯŧƭʈⱦꞇ"},{base:"tz",letters:"ꜩ"},{base:"u",letters:"uⓤｕùúûũṹūṻŭüǜǘǖǚủůűǔȕȗưừứữửựụṳųṷṵʉ"},{base:"v",letters:"vⓥｖṽṿʋꝟʌ"},{base:"vy",letters:"ꝡ"},{base:"w",letters:"wⓦｗẁẃŵẇẅẘẉⱳ"},{base:"x",letters:"xⓧｘẋẍ"},{base:"y",letters:"yⓨｙỳýŷỹȳẏÿỷẙỵƴɏỿ"},{base:"z",letters:"zⓩｚźẑżžẓẕƶȥɀⱬꝣ"}],c={},d=0;d<b.length;d++)for(var e=b[d].letters,f=0;f<e.length;f++)c[e[f]]=b[d].base;return a.replace(/[^\u0000-\u007E]/g,function(a){return c[a]||a}).replace(/[^\x00-\x7F]/g,"_")},destroy:function(){}})}(jQuery);;
"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a};String.prototype.includes||(String.prototype.includes=function(a,b){return"number"!=typeof b&&(b=0),!(b+a.length>this.length)&&this.indexOf(a,b)!==-1});var loadPluginAndDependencies=function(a,b,c,d){return new Promise(function(e,f){getDependenciesForNonExistentPlugins(a+"dependencies.json").then(function(a){loadDependencies(a)}).then(function(){var g=document.createElement("script");g.type="text/javascript",g.src=""+a+c+"-"+b+d,g.async=!1,document.body.appendChild(g),g.onload=function(){e()},g.onerror=function(){console.debug("Script Error"),f()}})})},getPropertiesFromUrl=function(a){var b=document.createElement("a");return b.href=a||window.location.href,b},getAllPluginAttributes=function(a){return a.querySelectorAll("[data-plugin]")},isPluginDisabled=function(a,b){var c={},d=$(a).data("plugin-settings");if(d){var e=$(d).text();if(c=JSON.parse(e),c.disabled){console.info(b+" has been intentionally disabled");var f='<div class="panel panel--red padding-small"><p>This widget had been disabled</p></div>';return c.hidden?$(a).html(f):$(a).prepend(f),!0}return!1}return!1},resourceLocator=function(a,b){var c=!1;return"object"===("undefined"==typeof a?"undefined":_typeof(a))&&(c=b?"localhost"===a.hostname?{baseUrl:"https://cdn-tst.euroconsumers.org/html/guidelines"}:{baseUrl:a.protocol+"//"+a.hostname.replace("design","cdn")+"/html/guidelines"}:"localhost"===a.hostname?{baseUrl:a.protocol+"//"+a.hostname+":"+a.port+"/Common/widgets",src:"src",ext:".js"}:a.hostname.includes("design")?{baseUrl:a.protocol+"//"+a.hostname.replace("design","cdn")+"/vendor/euroconsumers",src:"dist",ext:".min.js"}:{baseUrl:a.protocol+"//"+a.hostname+"/~/resource/javascript/common/vendor/euroconsumers",src:"dist",ext:".min.js"})},sortPlugins=function(a){for(var b={},c=0;c<a.length;c++){var d=a[c];if(!d.classList.contains("has-plugin")){var e=d.getAttribute("data-plugin");"file_upload"!==e&&"socialShare"!==e&&(isPluginDisabled(d,e)||(b.hasOwnProperty(e)?b[e].push(a[c]):b[e]=[a[c]]))}}return b},snakeToCamel=function(a){return a.replace(/(\-\w)/g,function(a){return a[1].toUpperCase()})},sortPluginsByExistence=function(a,b){var c={existent:{},nonexistent:{}},d=!0,e=!1,f=void 0;try{for(var g,h=Object.keys(a)[Symbol.iterator]();!(d=(g=h.next()).done);d=!0){var i=g.value;if(a.hasOwnProperty(i)){var j=snakeToCamel(i);"function"==typeof $[b][j]?c.existent[j]=a[i]:c.nonexistent[j]=a[i]}}}catch(a){e=!0,f=a}finally{try{!d&&h.return&&h.return()}finally{if(e)throw f}}return c},getDependenciesForNonExistentPlugins=function(a){return new Promise(function(b,c){var d=new XMLHttpRequest;d.addEventListener("load",function(){d.status>=200&&d.status<400?b(JSON.parse(d.responseText)):c(d.status)}),d.open("get",a,!0),d.send()})},initPlugins=function(a,b,c){return new Promise(function(d,e){var f={},g=$(a),h=g.data("plugin-settings");if(h){var i=$(h).text();f=JSON.parse(i)}$[c][b](f,g),d()})},checkIfDependencyExists=function(a,b){var c=function(a){var b=a.lastIndexOf("/")+1,c=a.lastIndexOf(".");if(!(b>=c))return a.substring(b,c)},d=$("script[src]").filter(function(){return c($(this).attr("src"))===c(b)}).length;return"function"==typeof window[a]||d>0},loadDependencies=function(a){return new Promise(function(b,c){var d=[];if(!a.js)return void b();var e=!0,f=!1,g=void 0;try{for(var h,i=function(){var b=h.value;if(a.js.hasOwnProperty(b))if("string"==typeof a.js[b])checkIfDependencyExists(b,a.js[b])||d.push(new Promise(function(c,d){var e=document.createElement("script");e.type="text/javascript",e.src=a.js[b],e.async=!1,document.body.appendChild(e),e.onload=function(){c()},e.onerror=function(){console.debug("Script Error"),d()}}));else if("jQuery"===b){var c=!0,e=!1,f=void 0;try{for(var g,i=function(){var b=g.value;if(a.js.jQuery.hasOwnProperty(b)&&"function"!=typeof jQuery[b])if(Array.isArray(a.js.jQuery[b])){var c=!0,e=!1,f=void 0;try{for(var h,i=function(){var a=h.value;d.push(new Promise(function(b,c){var d=document.createElement("script");d.type="text/javascript",d.src=a,d.async=!1,document.body.appendChild(d),d.onload=function(){b()},d.onerror=function(){console.debug("Script Error"),c()}}))},j=a.js.jQuery[b][Symbol.iterator]();!(c=(h=j.next()).done);c=!0)i()}catch(a){e=!0,f=a}finally{try{!c&&j.return&&j.return()}finally{if(e)throw f}}}else d.push(new Promise(function(c,d){var e=document.createElement("script");e.type="text/javascript",e.src=a.js.jQuery[b],e.async=!1,document.body.appendChild(e),e.onload=function(){c()},e.onerror=function(){console.debug("Script Error"),d()}}))},j=Object.keys(a.js.jQuery)[Symbol.iterator]();!(c=(g=j.next()).done);c=!0)i()}catch(a){e=!0,f=a}finally{try{!c&&j.return&&j.return()}finally{if(e)throw f}}}},j=Object.keys(a.js)[Symbol.iterator]();!(e=(h=j.next()).done);e=!0)i()}catch(a){f=!0,g=a}finally{try{!e&&j.return&&j.return()}finally{if(f)throw g}}Promise.all(d).then(function(){b()}).catch(function(a){c(a)})})};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){a.fn.initialiseWidgets=function(){var b=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},c=arguments[1],d=getPropertiesFromUrl();if(!b.baseUrl&&(d.hostname.includes("design")&&d.pathname.includes("Common/widgets")||"localhost"===d.hostname&&d.pathname.includes("Common/widgets")))return!1;var e=b.ns||"ec",f=window.location,g=resourceLocator(f),h=b.baseUrl||g.baseUrl;"/"!==h.slice(-1)&&"\\"!==h.slice(-1)||(h=h.slice(0,-1));var i=b.src||g.src,j=b.ext||g.ext;void 0===a[e]&&(a[e]={});var k=[],l=[],m=getAllPluginAttributes(document.body),n=sortPlugins(m),o=sortPluginsByExistence(n,e),p=!0,q=!1,r=void 0;try{for(var s,t=Object.keys(o.existent)[Symbol.iterator]();!(p=(s=t.next()).done);p=!0){var u=s.value;if(o.existent.hasOwnProperty(u)){var v=!0,w=!1,x=void 0;try{for(var y,z=o.existent[u][Symbol.iterator]();!(v=(y=z.next()).done);v=!0){var A=y.value;k.push(initPlugins(A,u,e))}}catch(a){w=!0,x=a}finally{try{!v&&z.return&&z.return()}finally{if(w)throw x}}}}}catch(a){q=!0,r=a}finally{try{!p&&t.return&&t.return()}finally{if(q)throw r}}var B=!0,C=!1,D=void 0;try{for(var E,F=function(){var a=E.value;if(o.nonexistent.hasOwnProperty(a)){var b=h+"/"+e+"-"+a+"/"+i+"/";l.push(loadPluginAndDependencies(b,a,e,j).then(function(){var b=!0,c=!1,d=void 0;try{for(var f,g=o.nonexistent[a][Symbol.iterator]();!(b=(f=g.next()).done);b=!0){var h=f.value;k.push(initPlugins(h,a,e))}}catch(a){c=!0,d=a}finally{try{!b&&g.return&&g.return()}finally{if(c)throw d}}}))}},G=Object.keys(o.nonexistent)[Symbol.iterator]();!(B=(E=G.next()).done);B=!0)F()}catch(a){C=!0,D=a}finally{try{!B&&G.return&&G.return()}finally{if(C)throw D}}Promise.all(l).then(function(a){Promise.all(k).then(function(a){console.log("All plugins are initialised"),"function"==typeof c&&c(a)})}).catch(function(a){console.error(a)})}}(jQuery);;
!function(a){"use strict";a.widget("ec.sorttable",{options:{DATE_RE:/^(\d\d?)[\/.-](\d\d?)[\/.-]((\d\d)?\d\d)$/,stableSort:!1,lng:!1,sortableRowIndex:0},_create:function(){this.element.addClass("has-plugin");var b=this,c=b.element[0],d=a(c);if(this.options.tableSort=d.data("sorttable-stablesort")||this.options.tableSort,this.options.lng=d.data("sorttable-lng")||this.options.lng,this.options.sortableRowIndex=parseInt(d.data("sorttable-sortablerowindex"))||this.options.sortableRowIndex,"THEAD"!==c.firstElementChild.tagName){var e=document.createElement("thead");e.appendChild(c.rows[0]),c.insertBefore(e,c.firstChild)}if(b.options.lng&&(b.sort_alpha=function(a,c){return a[0].localeCompare(c[0],b.options.lng)}),!(this.options.sortableRowIndex<0||this.options.sortableRowIndex>=c.tHead.rows.length)){a(c.tHead.rows[this.options.sortableRowIndex]).addClass("sortable-row");for(var f=c.tHead.rows[this.options.sortableRowIndex].cells,g=0;g<f.length;g++)if(a(f[g]).data("sorttable-nosort"))a(f[g]).addClass("noSort");else{var h=a(f[g]).data("sorttable-type");h?f[g].sorttable_sortfunction=this["sort_"+h]:f[g].sorttable_sortfunction=this._guessType(c,g),f[g].sorttable_columnindex=g,f[g].sorttable_tbody=c.tBodies[0],a(f[g]).on("click",function(){var c,d,e=a(this);if(e.hasClass("sorttable_sorted"))return b.reverse(e[0].sorttable_tbody),e.removeClass("sorttable_sorted"),e.addClass("sorttable_sorted_reverse"),a(e[0].firstElementChild).remove(),c=a("<span>"),c.attr("id","sorttable_sortrevind"),c.html("&nbsp;&#x25B4;"),void e.append(c);if(e.hasClass("sorttable_sorted_reverse"))return b.reverse(e[0].sorttable_tbody),e.removeClass("sorttable_sorted_reverse"),e.addClass("sorttable_sorted"),a(e[0].firstElementChild).remove(),d=a("<span>"),d.attr("id","sorttable_sortfwdind"),d.html("&nbsp;&#x25BE;"),void e.append(d);for(var f=e[0].parentElement,g=f.children,h=0;h<g.length;h++)a(g[h]).removeClass("sorttable_sorted_reverse sorttable_sorted");d=a("#sorttable_sortfwdind"),d&&d.remove(),c=a("#sorttable_sortrevind"),c&&c.remove(),e.addClass("sorttable_sorted"),d=a("<span>"),d.attr("id","sorttable_sortfwdind"),d.html("&nbsp;&#x25BE;"),e.append(d);for(var i=[],j=e[0].sorttable_columnindex,k=e[0].sorttable_tbody.rows,h=0;h<k.length;h++)i[i.length]=[b.getInnerText(k[h].cells[j]),k[h]];b.options.stableSort?b.shaker_sort(i,e[0].sorttable_sortfunction):i.sort(e[0].sorttable_sortfunction);for(var l=a(e[0].sorttable_tbody),h=0;h<i.length;h++)l.append(i[h][1])})}d.addClass("hasSorttable")}},_init:function(){},getInnerText:function(b){if(!b)return"";var c=a(b),d="function"==typeof b.getElementsByTagName&&b.getElementsByTagName("input").length;if(c.data("sorttable-customkey"))return c.data("sorttable-customkey").toString();if(!d){var e=c.text();return e.replace(/^\s+|\s+$/g,"")}switch(b.nodeType){case 3:if("input"==b.nodeName.toLowerCase())return b.value.replace(/^\s+|\s+$/g,"");break;case 4:return b.nodeValue.replace(/^\s+|\s+$/g,"");case 1:case 11:for(var f="",g=0;g<b.childNodes.length;g++)f+=this.getInnerText(b.childNodes[g]);return f.replace(/^\s+|\s+$/g,"");default:return""}},_guessType:function(a,b,c){for(var d=this.sort_alpha,e=0;e<a.tBodies[0].rows.length;e++){var f=this.getInnerText(a.tBodies[0].rows[e].cells[b]);if(""!==f){if(f.match(/^-?[£$¤]?[\d,.]+%?$/))return this.sort_numeric;var g=f.match(this.options.DATE_RE);if(g){var h=parseInt(g[1]),i=parseInt(g[2]);if(h>12)return this.sort_ddmm;if(i>12)return this.sort_mmdd;d=this.sort_ddmm}}}return d},reverse:function(a){for(var b=[],c=0;c<a.rows.length;c++)b[b.length]=a.rows[c];for(var c=b.length-1;c>=0;c--)a.appendChild(b[c])},sort_numeric:function(a,b){var c=parseFloat(a[0].replace(/[^0-9.-]/g,""));isNaN(c)&&(c=0);var d=parseFloat(b[0].replace(/[^0-9.-]/g,""));return isNaN(d)&&(d=0),c-d},sort_alpha:function(a,b){return a[0]==b[0]?0:a[0]<b[0]?-1:1},sort_ddmm:function(a,b){var c=/^(\d\d?)[\/.-](\d\d?)[\/.-]((\d\d)?\d\d)$/,d=a[0].match(c),e=d[3],f=d[2],g=d[1];1==f.length&&(f="0"+f),1==g.length&&(g="0"+g);var h=""+e+f+g;d=b[0].match(c),e=d[3],f=d[2],g=d[1],1==f.length&&(f="0"+f),1==g.length&&(g="0"+g);var i=""+e+f+g;return h==i?0:h<i?-1:1},sort_mmdd:function(a,b){var c=/^(\d\d?)[\/.-](\d\d?)[\/.-]((\d\d)?\d\d)$/,d=a[0].match(c),e=d[3],f=d[2],g=d[1];1==g.length&&(g="0"+g),1==f.length&&(f="0"+f);var h=""+e+g+f;d=b[0].match(c),e=d[3],f=d[2],g=d[1],1==g.length&&(g="0"+g),1==f.length&&(f="0"+f);var i=""+e+g+f;return h==i?0:h<i?-1:1},shaker_sort:function(a,b){for(var c=0,d=a.length-1,e=!0;e;){e=!1;for(var f=c;f<d;++f)if(b(a[f],a[f+1])>0){var g=a[f];a[f]=a[f+1],a[f+1]=g,e=!0}if(d--,!e)break;for(var f=d;f>c;--f)if(b(a[f],a[f-1])<0){var g=a[f];a[f]=a[f-1],a[f-1]=g,e=!0}c++}}})}(jQuery);;
!function(a){"use strict";a.widget("ec.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(b,c){a.publish("domupdated.popup-form",c.content)},v2:!1},widgetEventPrefix:"modal_",_create:function(){this.element.addClass("has-plugin");var b=this,c=b.element.data("popup-type")||b.options.type,d=b.element.data("popup-modal")||!1,e=d===!0?"popup popup--modal":"popup popup--removeable";b.options.v2&&(e+=" popup-v2");b.element.data("popup-focus-field")||"";b._initialised=!1,b._on({modal_init_super_popup:function(a,c){b._initialiseSuperPopup(a,c)}});var f=function(c,d){a(c.content).hasClass(b.options.popupClass)&&a(c.contentContainer).addClass(b.options.popupClass),d.find(".js-popup").unbind().on("click",function(a){a.preventDefault(),b._trigger("init_super_popup","click",{magnific:c,trigger:a.currentTarget})}).end().find(b.options.checkList).addClass(b.options.noscroll)};b.element.magnificPopup({mainClass:e,type:c,showCloseBtn:!0,closeBtnInside:!0,modal:d,closeMarkup:b.options.button,callbacks:{open:function(){var c=a.magnificPopup.instance;c.container.on("click",".popup-close",function(a){a.preventDefault(),c.close()}),c.container.on("click",".mfp-close",function(a){a.preventDefault(),c.close()}),c.container.on("click","#modal-confirmation-close",function(b){a(".mfp-container").removeClass("dirty"),a.magnificPopup.proto.close.call(this)}),c.container.on("click","#modal-confirmation-cancel",function(b){a("#modal-confirmation").hide()}),c.close=function(){return a(".mfp-container.dirty").length?(a("#modal-confirmation").show(),void a(".popup").scrollTop(0)):void a.magnificPopup.proto.close.call(this)},b._trigger("init_form","update",{content:this.content}),f(c,c.container),b._stackFormLayout(c.container)},beforeClose:function(){var c=a.magnificPopup.instance;c.container.off("click").find(b.options.checkList).removeClass(b.options.noscroll),a.publish("popup_close")},change:function(){},parseAjax:function(b){var c=a.magnificPopup.instance,d=c.ev.prop("href"),e=d.split("#")[1];""!==e&&(b.data=a(b.data).find("#"+e),b.data.hasClass("mfp-hide")&&b.data.removeClass("mfp-hide")),f(c,b.data)},ajaxContentAdded:function(){b._trigger("init_form","update",{content:this.content})}}})},_stackFormLayout:function(a){a.find(".form").removeClass("form--horizontal")},_initialiseSuperPopup:function(b,c){var d=this,e=a(c.trigger).prop("href"),f=e.split("#")[1],g=a("body"),h=a("#"+f),i=h.html(),j=".mfp";h.empty(),d._initialised===!1&&(d._initialised=!0,d._container=a(d.options.secondaryModalContainer),d._container.attr("tabindex",-1).css({overflowY:"auto",overflowX:"hidden"}).prependTo(g).hide(),d._underlay=a(d.options.secondaryUnderlay),d.document.off("focusin"+j,c.magnific._onFocusIn),d.document.off("keyup"+j),d._underlay.hide().prependTo(g).fadeIn(0),d._on(d._container,{"click .mfp-close":function(){d._removeSuperPopup(c,h,i)}}),d._on(d._underlay,{click:function(){d._removeSuperPopup(c,h,i)}}),d._container.fadeIn("fast",function(){var b=d._container.find(".mfp-content");b.attr("tabindex",-1).html(i).prepend(d.options.button).focus().removeAttr("tabindex"),d._stackFormLayout(b),d.document.on("keyup.secondary",function(a){27===a.keyCode?d._removeSuperPopup(c,h,i):9===a.keyCode&&d._onFocusIn(a,b)}),d._trigger("init_form","update",{content:b}),d._on(".popup--super__content",{click:function(e){var f=a(e.target);3===f.parents(b).size()&&d._removeSuperPopup(c,h,i)}})}))},_onFocusIn:function(a,b){var c=b.find(a.target);if(0===c.length)return b.attr("tabindex",-1).focus().removeAttr("tabindex"),!1},_removeSuperPopup:function(b,c,d){var e=this;c.html(d),e.document.off("keyup.secondary"),e._initialised=!1,e._underlay.remove(),e._container.remove(),e.document.on("focusin.mfp",b.magnific._onFocusIn),e.document.on("keyup.mfp",function(a){27===a.keyCode&&b.magnific.close()}),a.publish("popup_close")},_destroy:function(){}})}(jQuery);;
!function($){"use strict";$.widget("ec.form",{options:{method:"GET",validate:!0,error:".error:input",microInteractionsLabels:{},microInteractions:!1,disableMicroInteractions:!1,timeout:500,autoSubmit:!1,requestOnce:!1,submitButton:".form-submit",submitField:".form-submit-field",fieldSubmission:!1,submitAllFields:!0,booleanCheckbox:".form-boolean-checkbox",reload:!1,restart:!1,restartDelay:"2001",cancelButton:".form-cancel",header:"header.masthead"},_create:function(){var a=this;if(a.element.addClass("has-plugin"),$.extend(a.options,{postData:{}}),a.options.guid&&(a.options.postData.guid=a.options.guid),a._updateSavedValues(),0===a.element.find(a.options.submitButton).length&&(a.options.fieldSubmission=!0),void 0!==a.options.microInteractionsLabels.submit&&void 0!==a.options.microInteractionsLabels.loading&&void 0!==a.options.microInteractionsLabels.done&&(a.options.microInteractions=!0),a.options.validate&&!a.options.validateFn&&a._setValidate(),a.options.onCreateFn){var b=a._getFunctionByString(a.options.onCreateFn,window);b(a.element)}a.options.autoSubmit?a._processItems():a.options.fieldSubmission?a._bindSelfEvents():a._bindSubmitEvent()},_bindSubmitEvent:function(){var a=this,b=a.element.find(this.options.submitButton),c=a.element.find(this.options.cancelButton);a._on(c,{click:function(b){b.preventDefault(),a._revertItemsToSavedState()}}),a._on(b,{click:function(b){if(b.preventDefault(),!a.element.data("request-disabled")){var c=$(b.target);a.options.disableMicroInteractions||c.addClass("is-disabled btn--waiting btn--loading"),a._processItems()}}}),a._on({"change input, change select":function(b){b.preventDefault(),a._getDirtyStatus()}})},_bindSelfEvents:function(){var a=this;a._on({"click a":function(b){if($(b.target).hasClass(a.options.submitButton)){b.preventDefault();var c=$(b.target);if(!c.hasClass(a.options.submitField.replace(/(\.)/,""))||a.element.data("request-disabled"))return;a._processItems(c)}}}),a._on({change:function(b){var c=$(b.target);c.hasClass(a.options.submitField.replace(/(\.)/,""))&&!a.element.data("request-disabled")&&a._processItems(c)}})},_processItems:function(a){var b=this,c=b._serializeObjArray(a),d=b.element.find(b.options.submitButton),e={};if(b.options.endpoint){d.addClass("is-disabled");for(var f in b.options.postData)if(b.options.postData.hasOwnProperty(f)){var g={name:f,value:b.options.postData[f]};c.push(g)}var h=b._processFormValues(c);if("post"===b.options.method.toLowerCase()&&void 0!==b.options.postBody&&(e=JSON.parse($(b.options.postBody).text())),b.options.validateFn){var i=b._getFunctionByString(b.options.validateFn,window),j=i(b.element);if(j)b._send(b.options.endpoint,h,e);else{if(b.options.notValidFn){var k=b._getFunctionByString(b.options.notValidFn,window);k(b.element)}else console.log("notValidFn not defined. Carry on.");b.options.disableMicroInteractions||d.removeClass("btn--waiting btn--loading"),d.removeClass("is-disabled"),b._scrollTop()}}else b.options.validate?(b._setValidate(),b.element.valid()?b._send(b.options.endpoint,h,e):(b.options.disableMicroInteractions||d.removeClass("btn--waiting btn--loading"),d.removeClass("is-disabled"),b._scrollTop())):b._send(b.options.endpoint,h,e)}else console.log("No Endpoint set."),d.removeClass("is-disabled")},_send:function(a,b,c){var d,e=this,f=e.element.find(e.options.submitButton);if(e.options.microInteractions&&!e.options.disableMicroInteractions&&f.text(e.options.microInteractionsLabels.loading),e.options.beforeStartFn){var g=e._getFunctionByString(e.options.beforeStartFn,window);g(e.element)}setTimeout(function(){if("post"===e.options.method.toLowerCase()){var g=[];for(var h in c){var i={name:h,value:c[h]};b.push(i)}var j=$("meta[name=antiForgeryToken]");j&&g.push({name:"__RequestVerificationToken",value:j.prop("content")}),d=$.ajax({method:"POST",url:a,headers:g,data:b})}else d=$.ajax({method:"GET",url:a,data:b});d.done(function(a,b,c){if(e.options.microInteractions&&!e.options.disableMicroInteractions&&f.text(e.options.microInteractionsLabels.done).addClass("btn--success"),"string"==typeof a)if(e.options.onSuccessFn){var d=e._getFunctionByString(e.options.onSuccessFn,window);d(a,e.element,c)}else console.log("You may define a onSuccessFn.");else if(a.hasOwnProperty("Valid"))if("false"!==a.Valid&&a.Valid){if(e.options.onSuccessFn){var d=e._getFunctionByString(e.options.onSuccessFn,window);d(a,e.element,c)}e._updateSavedValues()}else{if(e.options.onErrorFn){var g=e._getFunctionByString(e.options.onErrorFn,window);g(a,e.element,c)}else console.log("You may define a onErrorFn.");f.removeClass("is-disabled"),e.options.microInteractions&&!e.options.disableMicroInteractions&&f.removeClass("btn--waiting btn--loading btn--success").text(e.options.microInteractionsLabels.submit)}else console.log("You need a Valid property.");e.options.restart&&!e.options.disableMicroInteractions&&setTimeout(function(){f.text(e.options.microInteractionsLabels.submit).removeClass("is-disabled btn--success")},e.options.restartDelay)}).fail(function(a,b,c){if(f.removeClass("is-disabled"),e.options.microInteractions&&!e.options.disableMicroInteractions&&f.text(e.options.microInteractionsLabels.submit),e.options.onErrorFn){var d=e._getFunctionByString(e.options.onErrorFn,window);d(c,e.element,a)}else console.log("You may define a onErrorFn.");console.log("Fail: ",b,c)}).always(function(){e._getDirtyStatus(),e.options.reload?location.reload():e.options.disableMicroInteractions||f.removeClass("btn--waiting btn--loading"),e.options.requestOnce&&e.element.attr("data-request-disabled","true"),e._updateSavedValues()})},e.options.timeout)},_setValidate:function(){var a=this;a.element.validate({rules:a.options.validationRules,messages:a.options.validationMessages,onfocusout:function(a){this.element(a)}})},_scrollTop:function(){var a=this,b=$(a.options.header),c=a.element.find(a.options.error).first();if(c.length>0&&b.length>0){var d=b.height()+100;$("html, body").animate({scrollTop:c.offset().top-d},500),setTimeout(function(){c.focus()},750)}},_getFunctionByString:function(a,b){b="undefined"==typeof window?b||global:b||window;for(var c=a.split("."),d=c.pop(),e=0;e<c.length;e++)b=b[c[e]];return b?b[d]:void 0},_revertItemsToSavedState:function(){var a=this;a.initialValues.forEach(function(b){var c=a.element.find('[name="'+b.name+'"]');c.length>1?c.val([a.initialValues[b.value]]):1==c.length&&"checkbox"==c[0].type?"on"==b.value&&c.prop("checked","checked"):1==c.length&&"text"==c[0].type?c.val(b.value):1==c.length&&"select-one"==c[0].type?c.val(b.value).change():1==c.length&&"hidden"==c[0].type?c.val(b.value):console.log("Unknown type: ",c)})},_getCurrentValues:function(){var a=this,b=a.element.find("input, select, textarea").serializeArray();return a._processFormValues(b)},_getDirtyStatus:function(){var a=this,b=a._getCurrentValues(),c=a.initialValues,d=!1;if(a.element.removeClass("dirty"),Object.keys(b).length!==Object.keys(c).length)d=!0,a.element.addClass("dirty");else for(var e in b)if(b.hasOwnProperty(e)){var f=b[e],g=c[e];if(f!==g){d=!0,a.element.addClass("dirty");break}}return d},_serializeObjArray:function(a){var b,c=this,d=[];return b=c.options.masterForm?$(c.options.masterForm):c.element,!c.options.fieldSubmission||c.options.fieldSubmission&&c.options.submitAllFields?(d=b.find("input, select, textarea").not(c.options.booleanCheckbox).serializeArray(),b.find(c.options.booleanCheckbox).each(function(){c._evaluateCheckboxes($(this),d)})):(a.type=a.hasClass(c.options.booleanCheckbox.replace(/(\.)/,"")))?c._evaluateCheckboxes(a,d):d=c.element.find(a).serializeArray(),d},_evaluateCheckboxes:function(a,b){a.prop("checked")?b.push({name:a.attr("name"),checked:"on"}):b.push({name:a.attr("name"),checked:"off"})},_updateSavedValues:function(){var a=this,b=a.element.find("input, select, textarea").serializeArray();a.initialValues=a._processFormValues(b)},_processFormValues:function(a){for(var b=0;b<a.length;b++)"on"===a[b].checked?a[b].value="True":"off"===a[b].checked&&(a[b].value="False");return a},_convertStringToObject:function(str){var obj={};if(str||"string"==typeof str){var objStr=str.match(/\{(.)+\}/g);eval("obj ="+objStr)}return obj},_destroy:function(){var a=this;a.options.fieldSubmission?a.element.unbind():a.element.find(a.options.submitField).unbind(),a.element.removeClass("has-plugin")},resetButtonState:function(){var a=this,b=a.element.find(a.options.submitButton);b.length>0?a.options.microInteractions&&!a.options.disableMicroInteractions?b.text(a.options.microInteractionsLabels.submit).removeClass("is-disabled btn--success"):console.log("Undefined submitButton state label."):console.log("No submitButton found within this widget instance scope.")},submit:function(){var a=this;a._processItems()}})}(jQuery);;
!function(a){"use strict";a.widget("ec.readMore",{options:{enabled:!1,mode:"pixel",pixel:300,paragraph:3,backgroundColor:"#FFFFFF",readMoreButtonClasses:"btn btn--secondary-alt",readMoreButtonLabel:"Read more"},_create:function(){var a=this;a.element.addClass("has-plugin"),a.options.enabled?"pixel"===a.options.mode.toLowerCase()?a._byPixel(a.options.pixel):a._byParagraph(a.options.paragraph):console.log("ec-readMore disabled")},_bindEvents:function(){var a=this;a.element.find("a.js-read-more").on("click",function(b){b.preventDefault(),a._destroy()})},_byParagraph:function(a){var b=this,c=b.element.find("p").eq(a-1);c?(height=c.height()+c.offset().top+140-b.element.offset().top,b.element.height()-height>200?(b._setContainer(height),b._setReadmore(),b._bindEvents()):console.log("ec-readMore disabled")):console.log("ec-readMore disabled: not reachable paragraph, check configurations.")},_byPixel:function(a){var b=this;b.element.height()-a>200?(b._setContainer(a),b._setReadmore(),b._bindEvents()):console.log("ec-readMore disabled")},_setReadmore:function(){var a=this,b="position:absolute; text-align: center; padding: 50px 0; bottom: 0; left: 0; position: absolute; width: 100%; ",c=a._setBackgroundGradient(a.options.backgroundColor),d='<a class="js-read-more '+a.options.readMoreButtonClasses+'" href="#" title="'+a.options.readMoreButtonLabel+'">'+a.options.readMoreButtonLabel+"</a>";a.element.append('<div class="js-continue-reading" style="'+b+c+'">'+d+"</div>")},_setContainer:function(a){var b=this;b.element.css({height:a,overflow:"hidden"}).addClass("relative")},_setBackgroundGradient:function(a){var b=parseInt(a.slice(1,3),16),c=parseInt(a.slice(3,5),16),d=parseInt(a.slice(5,7),16),e="rgba("+b+","+c+","+d+",x)",f=e.replace("x","0"),g=e.replace("x","1");return"background: -moz-linear-gradient(top,"+f+" 0,"+g+" 80%,"+g+" 100%);background: -webkit-linear-gradient(top,"+f+" 0,"+g+" 80%,"+g+" 100%); background: linear-gradient(to bottom,"+f+" 0,"+g+" 80%,"+g+" 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='"+a+"',endColorstr='"+a+"',GradientType=0);"},_destroy:function(){var a=this;a.element.removeClass("has-plugin relative").css({height:"auto",overflow:"visible"}),a.element.find(".js-continue-reading").remove()}})}(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 () {
                        document.cookie = "hideCookieWarning=true; expires=Fri, 31 Dec 9999 23:59:59 GMT;path=/";
                        $cookiebanner.remove();
                    });

                    $(document.body).off('click', 'a,button', removeHandler);
                };

                // 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,
            lsv2: $.noop,
            loading: $.noop
        },
        /**
         * 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" OR with the new LSV component
                    if (me.options.usesteps || me.options.lsv2 === "True") {
                        //panes
                        var $oldpane = $allPanes.eq(oldValue);
                        var $newpane = $allPanes.eq(newValue);

                        $oldpane.removeClass('active');

                        //nav
                        $nav.eq(newValue).addClass("visited").animate({
                            "opacity": 1
                        });
                        $nav.eq(oldValue).animate({
                            "opacity": 0.5
                        });

                        if($newpane.hasClass('hidden')) {
                            // Jump to the next pane when Rating given by user isn't in the range interval set to show feedback radios
                            paneController.val(paneController.val() + 1);

                        } else  {

                            $newpane.addClass('active');

                            if(me.options.lsv2 === "True") {
                                $oldpane.css('display', 'none', 'important');
                                $newpane.css('display', 'none', 'important');
                                me.element.find('.waiting').css('display', 'inline-block', 'important'); // Set loading state
                            }

                            if($newpane.data('selector') === 'lsv2-confirmation') {
                                $nav.css('display', 'none');
                                setTimeout(function () {
                                    //call the close button (not the minimize button)
                                    me.element.find(".LSV2_maxify .close").trigger("click");

                                }, me.options.secondsToThankyou * 1000);
                            }
                        }
                       
                    }

                });

                //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: 300,
                            //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: 300,
                                //height: 0,
                                opacity: 0,
                                bottom: -999
                            }, {
                                complete: function() {
                                    me.options.onClosed();
                                }
                            });

                            $tooltip.fadeIn();
                        }
                    }
                });

                // Launch LSV2
                var lsv_v2 = container.attr('data-lsv2');
                if(lsv_v2 !== undefined && lsv_v2 === "True") {
                    $tooltip.trigger('click');
                } else {
                    $tooltip.delay(startdelay).fadeIn(function() {});
                }
                
                /**
                 * setup questions
                 */
                //used in graph Rating10 : LSV2_graph jq_lsv2Next
                $(".LSV2_button.jq_lsv2Next,.jq_graph .LSV2_graph.jq_lsv2Next,.LSV2_ratingselect .jq_lsv2Next, .LSV2_rating .LSV2_selector").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('is-disabled 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();

                if(self.element.hasClass('js-reloadAfterSubmit')){
                    location.reload();
                }else{
                    self.element.find(self.options.submit).removeClass('is-disabled btn--waiting');
                }
                jQuery.publish("spinner.close", [jQuery("body")]);
            });
        }

    });

})(jQuery);;
(function ($, document, undefined) {

	'use strict';

	/**
	 * @function
	 * @private
	 * @description Generates a unique id.
	 */
	function uuid() {
	    // Math.random should be unique because of its seeding algorithm.
	    // Convert it to base 36 (numbers + letters), and grab the first 9 characters
	    // after the decimal.
	    return 'a' + Math.random().toString(36).substr(2, 9);
	};
	

	///////////

	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;

		self.videoUrl = videoUrl;
		self.videoId = videoId;
		self.uniqueId = uuid() + '_' + videoId;

		self._loadApi();

		$(document).on({
			'playerReady.playvideoapi': $.proxy(self._onPlayerReady, self),
			'startPlaying.playvideoapi': $.proxy(self._onStartPlaying, self)
		});

	};

	YouTube.prototype._onPlayerReady = function (e, uniqueId, player) {

		var self = this;

		if (uniqueId !== self.uniqueId) {
			return;
		}

		player.addEventListener('onStateChange', function (event) {
			if (event.data === YT.PlayerState.PLAYING) {
				$(document).triggerHandler('startPlaying.playvideoapi', [self.uniqueId]);
			}
		});

		self.player = player;

	};

	YouTube.prototype._onStartPlaying = function (e, uniqueId) {
		
		var self = this;
		
		if (!self.player || self.uniqueId === uniqueId) {
			return;
		}

		self.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 self = this,
		    videoId = self.videoId;

		var div = document.createElement('div');
		div.id = self.uniqueId;
		div.setAttribute('data-videoid', videoId);

		replaceFunc(div);

		new YT.Player(self.uniqueId, {
			width: '100%',
			height: 'auto',
			videoId: videoId,
			playerVars: {
				'autoplay': 1,
				'controls': 1,
				'rel': 0,
				'origin': window.location.origin
			},
			events: {
				'onReady': function (event) {
					$(document).triggerHandler('playerReady.playvideoapi', [self.uniqueId, event.target]);
				}
			}
		});

	};

	YouTube.prototype.getPlayerElement = function () {
		return document.getElementById(this.uniqueId);
	};
	
	YouTube.prototype.clearPlayer = function() {
		this.player.destroy();
		delete this.player;
	};

	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;

		self.videoUrl = videoUrl;
		self.videoId = videoId;
		self.uniqueId = uuid() + '_' + videoId;

		self._loadApi();

		$(document).on({
			'playerReady.playvideoapi': $.proxy(self._onPlayerReady, self),
			'startPlaying.playvideoapi': $.proxy(self._onStartPlaying, self)
		});

	};

	FaceBook.prototype._onPlayerReady = function (e, uniqueId, player) {

		var self = this;

		if (uniqueId !== self.uniqueId) {
			return;
		}

		player.subscribe('startedPlaying', function () {
			$(document).triggerHandler('startPlaying.playvideoapi', [self.uniqueId]);
		});

		this.player = player;

	};

	FaceBook.prototype._onStartPlaying = function (e, uniqueId) {
		
		var self = this;
		
		if (!self.player) {
			return;
		}
		
		if (self.uniqueId === uniqueId) {
			self.player.unmute();
			return;
		}

		self.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 uniqueId = document.getElementById(msg.id).getAttribute("data-uniqueid");' +
			'       $(document).triggerHandler("playerReady.playvideoapi", [uniqueId, 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 self = this,
		    videoId = self.videoId;

		var div = document.createElement('div');
		div.id = self.uniqueId;
		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-uniqueid', self.uniqueId);

		var script = document.createElement('script');
		script.innerHTML = '(function() { FB.XFBML.parse(); })()';

		replaceFunc([div, script]);

	};

	FaceBook.prototype.getPlayerElement = function () {
		return document.getElementById(this.uniqueId);
	};
	
	FaceBook.prototype.clearPlayer = function() {
		var playerElement = this.getPlayerElement();
		if (playerElement) {
			playerElement.parentNode.removeChild(playerElement);
		}
		delete this.player;
	};

	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,
				$videoMarkup;
			
			// create the markup using the api and display it with MFP using inline type
			// the reason is that, using MFP iframe, it's not possible sometimes to use
			// the javascript player api
			
			self.api.createPlayer(function (elements) {
				$videoMarkup = $('<div />')
					.append(elements)
					.appendTo('body');
			})

			$.magnificPopup.open({
				
				items: [{
					src: $videoMarkup,
					type: 'inline'
				}],
				
				callbacks: {
					open: function () {
						
						// we do this because we use "inline" type, MFP does not add css classes for proper iframe display
						this.container
							.removeClass('mfp-inline-holder')
							.addClass('mfp-iframe-holder');
							
						this.content
							.addClass('mfp-iframe-scaler')
							.children()
							.first()
							.addClass('mfp-iframe');
						
						// add the video footer
						this.contentContainer.addClass("video-popup--has-header")
							.find('.video-popup__footer')
							.html(self.options.popupFooter);
					},
					afterClose: function() {
						self.api.clearPlayer();
						$videoMarkup.remove();
					}
				}

			});

		},

		/**
		 * @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: function(size){
				/*var bgSize = size == 2 ? "icon-bullet-2x" : "";

				return '<div class="zoomicon"><span class="icon-stack"><i class="icon-bullet icon-stack-2x icon--inverse '
				+ bgSize
				+ '"></i><i class="icon-search icon-stack-'
				+ size
				+ 'x"></i></span></div>';*/

				return size == 2 
					? '<div class="zoomicon"><span class="icon-stack"><i class="icon-bullet icon-stack-2x icon--inverse icon-bullet-2x"></i><i class="icon-search icon-stack-2x"></i></span></div>'
					: '<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,
				iconsize = $(this.element).data('zoomimageIconsize') || 1,
				zoomIcon = settings.zoomicontemplate(iconsize),
				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',
			nohovercontainer : '.js-navmenu--main-nohover',
			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),
				nohovercontainer = $(options.nohovercontainer),
				focusClass = options.accessibleoptions.focusClass,
				topmenu_item = '.js-navmenu--main-nohover .js-navmenu-hassubmenu >a',
				submenu_trigger = '.block-list__item--pane-trigger-nohover';

			if (container.length) {
				$(container).accessibleMegaMenu(options.accessibleoptions);
			}

			//$(nohovercontainer).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');
			});

			//no-hover menu
			$(topmenu_item).on('click', function (e) {
				e.preventDefault();
				e.stopPropagation();

				if ($(this).parent().hasClass('active')) {
					$('.js-navmenu--main-nohover .js-navmenu-hassubmenu').removeClass('active');
					
				} else {
					$('.js-navmenu--main-nohover .js-navmenu-hassubmenu').removeClass('active');
					$(this).parent().addClass('active');
				}

				$(submenu_trigger).removeClass('active');
			});

			$(submenu_trigger).on('click', function (e) {
				e.stopPropagation();
				$(submenu_trigger).removeClass('active');
				$(this).addClass('active');

			});

			$(window).on('click', function () {
				$(submenu_trigger).removeClass('active');
				$('.js-navmenu--main-nohover .js-navmenu-hassubmenu').removeClass('active');
			});

			//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-nohover a.navigation-tiles__tile , .navmenu--main-nohover a.block-list__item').on('click', function (e) {
				var linktarget = $(this).attr('target') || "_self",
					linkurl = $(this).attr('href');
				window.dataLayer = window.dataLayer || [];
				window.dataLayer.push({'event':'menuItemClicked',
					'itemClicked':$(this).find('.navigation-tiles__tile__text__inner').text().trim()
						||$(this).children().first().text().trim()
						||$(this).text().trim(),
					'categoryClicked':$(this).closest('.block-list__pane').prev('a').text().trim()
						||$(this).closest('.navmenu__item__submenu ').prev('a.navmenu__item__title').text().trim()
				});
				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);;
!function(a){"use strict";a.widget("ec.autohide",{options:{selectorClass:".js-autohide",timeout:"0",closeButtons:".popup-close",fadeOutOptions:"slow"},_create:function(){if(this.element.hasClass("has-autohide"))return!1;var b=this;b._timeout=b.options.timeout,b._closeButtons=a(b.options.closeButtons),b._fadeOutOptions=b.options.fadeOutOptions,b._timeout>0&&setInterval(function(){b.element.fadeOut(b._fadeOutOptions)},b._timeout),b._on(b._closeButtons,{click:function(a){setTimeout(function(){b.element.remove()},100)}})}})}(jQuery);;
!function(a){"use strict";a.widget("ec.mobileHeader",{options:{lastScrollTop:0,delta:5,navbarHeight:a(".masthead").outerHeight()},_create:function(){this.element.addClass("has-mobile-header");var a=this;a._scrollListenner()},_scrollListenner:function(){var b=this;a(window).scroll(function(a){b._hasScrolled()})},_hasScrolled:function(){var b=this,c=a(window).scrollTop();Math.abs(b.options.lastScrollTop-c)<=b.options.delta||(c>b.options.lastScrollTop&&c>b.options.navbarHeight?a(".masthead").removeClass("nav-down notransition").addClass("nav-up"):c>=0&&c<=b.options.navbarHeight?a(".masthead").addClass("notransition"):a(".masthead").removeClass("nav-up").addClass("nav-down"),b.options.lastScrollTop=c)}})}(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" } };
;
