/* Modernizr 2.5.3 (Custom Build) | MIT & BSD * Build: http://www.modernizr.com/download/#-csstransforms-csstransforms3d-shiv-cssclasses-teststyles-testprop-testallprops-prefixes-domprefixes-load */ ;window.Modernizr=function(a,b,c){function z(a){j.cssText=a}function A(a,b){return z(m.join(a+";")+(b||""))}function B(a,b){return typeof a===b}function C(a,b){return!!~(""+a).indexOf(b)}function D(a,b){for(var d in a)if(j[a[d]]!==c)return b=="pfx"?a[d]:!0;return!1}function E(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:B(f,"function")?f.bind(d||b):f}return!1}function F(a,b,c){var d=a.charAt(0).toUpperCase()+a.substr(1),e=(a+" "+o.join(d+" ")+d).split(" ");return B(b,"string")||B(b,"undefined")?D(e,b):(e=(a+" "+p.join(d+" ")+d).split(" "),E(e,b,c))}var d="2.5.3",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m=" -webkit- -moz- -o- -ms- ".split(" "),n="Webkit Moz O ms",o=n.split(" "),p=n.toLowerCase().split(" "),q={},r={},s={},t=[],u=t.slice,v,w=function(a,c,d,e){var f,i,j,k=b.createElement("div"),l=b.body,m=l?l:b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),k.appendChild(j);return f=["­",""].join(""),k.id=h,(l?k:m).innerHTML+=f,m.appendChild(k),l||(m.style.background="",g.appendChild(m)),i=c(k,a),l?k.parentNode.removeChild(k):m.parentNode.removeChild(m),!!i},x={}.hasOwnProperty,y;!B(x,"undefined")&&!B(x.call,"undefined")?y=function(a,b){return x.call(a,b)}:y=function(a,b){return b in a&&B(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=u.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(u.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(u.call(arguments)))};return e});var G=function(a,c){var d=a.join(""),f=c.length;w(d,function(a,c){var d=b.styleSheets[b.styleSheets.length-1],g=d?d.cssRules&&d.cssRules[0]?d.cssRules[0].cssText:d.cssText||"":"",h=a.childNodes,i={};while(f--)i[h[f].id]=h[f];e.csstransforms3d=(i.csstransforms3d&&i.csstransforms3d.offsetLeft)===9&&i.csstransforms3d.offsetHeight===3},f,c)}([,["@media (",m.join("transform-3d),("),h,")","{#csstransforms3d{left:9px;position:absolute;height:3px;}}"].join("")],[,"csstransforms3d"]);q.csstransforms=function(){return!!F("transform")},q.csstransforms3d=function(){var a=!!F("perspective");return a&&"webkitPerspective"in g.style&&(a=e.csstransforms3d),a};for(var H in q)y(q,H)&&(v=H.toLowerCase(),e[v]=q[H](),t.push((e[v]?"":"no-")+v));return z(""),i=k=null,function(a,b){function g(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function h(){var a=k.elements;return typeof a=="string"?a.split(" "):a}function i(a){var b={},c=a.createElement,e=a.createDocumentFragment,f=e();a.createElement=function(a){var e=(b[a]||(b[a]=c(a))).cloneNode();return k.shivMethods&&e.canHaveChildren&&!d.test(a)?f.appendChild(e):e},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+h().join().replace(/\w+/g,function(a){return b[a]=c(a),f.createElement(a),'c("'+a+'")'})+");return n}")(k,f)}function j(a){var b;return a.documentShived?a:(k.shivCSS&&!e&&(b=!!g(a,"article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio{display:none}canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden]{display:none}audio[controls]{display:inline-block;*display:inline;*zoom:1}mark{background:#FF0;color:#000}")),f||(b=!i(a)),b&&(a.documentShived=b),a)}var c=a.html5||{},d=/^<|^(?:button|form|map|select|textarea)$/i,e,f;(function(){var a=b.createElement("a");a.innerHTML="",e="hidden"in a,f=a.childNodes.length==1||function(){try{b.createElement("a")}catch(a){return!0}var c=b.createDocumentFragment();return typeof c.cloneNode=="undefined"||typeof c.createDocumentFragment=="undefined"||typeof c.createElement=="undefined"}()})();var k={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:j};a.html5=k,j(b)}(this,b),e._version=d,e._prefixes=m,e._domPrefixes=p,e._cssomPrefixes=o,e.testProp=function(a){return D([a])},e.testAllProps=F,e.testStyles=w,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+t.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return o.call(a)=="[object Function]"}function e(a){return typeof a=="string"}function f(){}function g(a){return!a||a=="loaded"||a=="complete"||a=="uninitialized"}function h(){var a=p.shift();q=1,a?a.t?m(function(){(a.t=="c"?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){a!="img"&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l={},o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};y[c]===1&&(r=1,y[c]=[],l=b.createElement(a)),a=="object"?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),a!="img"&&(r||y[c]===2?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i(b=="c"?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),p.length==1&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&o.call(a.opera)=="[object Opera]",l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return o.call(a)=="[object Array]"},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;fopera.version()? new Audio(null):new Audio).canPlayType!==g}catch(xb){Wa=!1}this.hasHTML5=Wa;this.setup=function(b){var d=!c.url;b!==g&&q&&u&&c.ok()&&(b.flashVersion!==g||b.url!==g||b.html5Test!==g)&&K(t("setupLate"));Aa(b);if(!B)if(va){if(!c.setupOptions.ignoreMobileRestrictions||c.setupOptions.forceUseGlobalHTML5Audio)L.push(z.globalHTML5),B=!0}else c.setupOptions.forceUseGlobalHTML5Audio&&(L.push(z.globalHTML5),B=!0);if(!Pa&&va)if(c.setupOptions.ignoreMobileRestrictions)L.push(z.ignoreMobile);else if(c.setupOptions.useHTML5Audio&& !c.setupOptions.preferFlash||c._wD(z.mobileUA),c.setupOptions.useHTML5Audio=!0,c.setupOptions.preferFlash=!1,Qa)c.ignoreFlash=!0;else if(Ra&&!v.match(/android\s2\.3/i)||!Ra)c._wD(z.globalHTML5),B=!0;b&&(d&&Z&&b.url!==g&&c.beginDelayedInit(),Z||b.url===g||"complete"!==m.readyState||setTimeout(P,1));Pa=!0;return c};this.supported=this.ok=function(){return u?q&&!y:c.useHTML5Audio&&c.hasHTML5};this.getMovie=function(c){return E(c)||m[c]||h[c]};this.createSound=function(b,d){function e(){f=oa(f);c.sounds[f.id]= new V(f);c.soundIDs.push(f.id);return c.sounds[f.id]}var a,f;a=null;a="soundManager.createSound(): "+t(q?"notOK":"notReady");if(!q||!c.ok())return K(a),!1;d!==g&&(b={id:b,url:d});f=A(b);f.url=ra(f.url);f.id===g&&(f.id=c.setupOptions.idPrefix+nb++);f.id.toString().charAt(0).match(/^[0-9]$/)&&c._wD("soundManager.createSound(): "+t("badID",f.id),2);c._wD("soundManager.createSound(): "+f.id+(f.url?" ("+f.url+")":""),1);if(w(f.id,!0))return c._wD("soundManager.createSound(): "+f.id+" exists",1),c.sounds[f.id]; if(sa(f))a=e(),c.html5Only||c._wD(f.id+": Using HTML5"),a._setup_html5(f);else{if(c.html5Only)return c._wD(f.id+": No HTML5 support for this sound, and no Flash. Exiting."),e();if(c.html5.usingFlash&&f.url&&f.url.match(/data:/i))return c._wD(f.id+": data: URIs not supported via Flash. Exiting."),e();8a.instanceCount?(m(),e=a._setup_html5(),a.setPosition(a._iO.position),e.play()):(c._wD(a.id+": Cloning Audio() for instance #"+a.instanceCount+"..."),k=new Audio(a._iO.url),F=function(){x.remove(k,"ended",F);a._onfinish(a);ta(k);k=null},h=function(){x.remove(k,"canplay",h);try{k.currentTime=a._iO.position/ 1E3}catch(c){K(a.id+": multiShot play() failed to apply position of "+a._iO.position/1E3)}k.play()},x.add(k,"ended",F),a._iO.volume!==g&&(k.volume=Math.max(0,Math.min(1,a._iO.volume/100))),a.muted&&(k.muted=!0),a._iO.position?x.add(k,"canplay",h):k.play()):(f=l._start(a.id,a._iO.loops||1,9===n?a.position:a.position/1E3,a._iO.multiShot||!1),9!==n||f||(c._wD(e+"No sound hardware, or 32-sound ceiling hit",2),a._iO.onplayerror&&a._iO.onplayerror.apply(a))));return a};this.stop=function(b){var d=a._iO; 1===a.playState&&(c._wD(a.id+": stop()"),a._onbufferchange(0),a._resetOnPosition(0),a.paused=!1,a.isHTML5||(a.playState=0),Xa(),d.to&&a.clearOnPosition(d.to),a.isHTML5?a._a&&(b=a.position,a.setPosition(0),a.position=b,a._a.pause(),a.playState=0,a._onTimer(),F()):(l._stop(a.id,b),d.serverURL&&a.unload()),a.instanceCount=0,a._iO={},d.onstop&&d.onstop.apply(a));return a};this.setAutoPlay=function(b){c._wD(a.id+": Autoplay turned "+(b?"on":"off"));a._iO.autoPlay=b;a.isHTML5||(l._setAutoPlay(a.id,b),b&& !a.instanceCount&&1===a.readyState&&(a.instanceCount++,c._wD(a.id+": Incremented instance count to "+a.instanceCount)))};this.getAutoPlay=function(){return a._iO.autoPlay};this.setPlaybackRate=function(b){var d=Math.max(.5,Math.min(4,b));d!==b&&c._wD(a.id+": setPlaybackRate("+b+"): limiting rate to "+d,2);if(a.isHTML5)try{a._iO.playbackRate=d,a._a.playbackRate=d}catch(e){c._wD(a.id+": setPlaybackRate("+d+") failed: "+e.message,2)}return a};this.setPosition=function(b){b===g&&(b=0);var d=a.isHTML5? Math.max(b,0):Math.min(a.duration||a._iO.duration,Math.max(b,0));a.position=d;b=a.position/1E3;a._resetOnPosition(a.position);a._iO.position=d;if(!a.isHTML5)b=9===n?a.position:b,a.readyState&&2!==a.readyState&&l._setPosition(a.id,b,a.paused||!a.playState,a._iO.multiShot);else if(a._a){if(a._html5_canplay){if(a._a.currentTime.toFixed(3)!==b.toFixed(3)){c._wD(a.id+": setPosition("+b+")");try{a._a.currentTime=b,(0===a.playState||a.paused)&&a._a.pause()}catch(e){c._wD(a.id+": setPosition("+b+") failed: "+ e.message,2)}}}else if(b)return c._wD(a.id+": setPosition("+b+"): Cannot seek yet, sound not ready",2),a;a.paused&&a._onTimer(!0)}return a};this.pause=function(b){if(a.paused||0===a.playState&&1!==a.readyState)return a;c._wD(a.id+": pause()");a.paused=!0;a.isHTML5?(a._setup_html5().pause(),F()):(b||b===g)&&l._pause(a.id,a._iO.multiShot);a._iO.onpause&&a._iO.onpause.apply(a);return a};this.resume=function(){var b=a._iO;if(!a.paused)return a;c._wD(a.id+": resume()");a.paused=!1;a.playState=1;a.isHTML5? (a._setup_html5().play(),m()):(b.isMovieStar&&!b.serverURL&&a.setPosition(a.position),l._pause(a.id,b.multiShot));!r&&b.onplay?(b.onplay.apply(a),r=!0):b.onresume&&b.onresume.apply(a);return a};this.togglePause=function(){c._wD(a.id+": togglePause()");if(0===a.playState)return a.play({position:9!==n||a.isHTML5?a.position/1E3:a.position}),a;a.paused?a.resume():a.pause();return a};this.setPan=function(b,c){b===g&&(b=0);c===g&&(c=!1);a.isHTML5||l._setPan(a.id,b);a._iO.pan=b;c||(a.pan=b,a.options.pan= b);return a};this.setVolume=function(b,d){b===g&&(b=100);d===g&&(d=!1);a.isHTML5?a._a&&(c.muted&&!a.muted&&(a.muted=!0,a._a.muted=!0),a._a.volume=Math.max(0,Math.min(1,b/100))):l._setVolume(a.id,c.muted&&!a.muted||a.muted?0:b);a._iO.volume=b;d||(a.volume=b,a.options.volume=b);return a};this.mute=function(){a.muted=!0;a.isHTML5?a._a&&(a._a.muted=!0):l._setVolume(a.id,0);return a};this.unmute=function(){a.muted=!1;var b=a._iO.volume!==g;a.isHTML5?a._a&&(a._a.muted=!1):l._setVolume(a.id,b?a._iO.volume: a.options.volume);return a};this.toggleMute=function(){return a.muted?a.unmute():a.mute()};this.onposition=this.onPosition=function(b,c,d){D.push({position:parseInt(b,10),method:c,scope:d!==g?d:a,fired:!1});return a};this.clearOnPosition=function(a,b){var c;a=parseInt(a,10);if(!isNaN(a))for(c=0;c=b)return!1;for(--b;0<=b;b--)c=D[b],!c.fired&& a.position>=c.position&&(c.fired=!0,v++,c.method.apply(c.scope,[c.position]));return!0};this._resetOnPosition=function(a){var b,c;b=D.length;if(!b)return!1;for(--b;0<=b;b--)c=D[b],c.fired&&a<=c.position&&(c.fired=!1,v--);return!0};y=function(){var b=a._iO,d=b.from,e=b.to,f,g;g=function(){c._wD(a.id+': "To" time of '+e+" reached.");a.clearOnPosition(e,g);a.stop()};f=function(){c._wD(a.id+': Playing "from" '+d);if(null!==e&&!isNaN(e))a.onPosition(e,g)};null===d||isNaN(d)||(b.position=d,b.multiShot= !1,f());return b};q=function(){var b,c=a._iO.onposition;if(c)for(b in c)if(c.hasOwnProperty(b))a.onPosition(parseInt(b,10),c[b])};Xa=function(){var b,c=a._iO.onposition;if(c)for(b in c)c.hasOwnProperty(b)&&a.clearOnPosition(parseInt(b,10))};m=function(){a.isHTML5&&eb(a)};F=function(){a.isHTML5&&fb(a)};f=function(b){b||(D=[],v=0);r=!1;a._hasTimer=null;a._a=null;a._html5_canplay=!1;a.bytesLoaded=null;a.bytesTotal=null;a.duration=a._iO&&a._iO.duration?a._iO.duration:null;a.durationEstimate=null;a.buffered= [];a.eqData=[];a.eqData.left=[];a.eqData.right=[];a.failures=0;a.isBuffering=!1;a.instanceOptions={};a.instanceCount=0;a.loaded=!1;a.metadata={};a.readyState=0;a.muted=!1;a.paused=!1;a.peakData={left:0,right:0};a.waveformData={left:[],right:[]};a.playState=0;a.position=null;a.id3={}};f();this._onTimer=function(b){var c,f=!1,g={};(a._hasTimer||b)&&a._a&&(b||(0opera.version()?new Audio(null):new Audio,c=a._a,c._called_load=!1,B&&(Ya=c);a.isHTML5=!0;a._a=c;c._s=a;h();a._apply_loop(c,b.loops);b.autoLoad||b.autoPlay?a.load():(c.autobuffer=!1,c.preload="auto");return c};h=function(){if(a._a._added_events)return!1;var b;a._a._added_events= !0;for(b in I)I.hasOwnProperty(b)&&a._a&&a._a.addEventListener(b,I[b],!1);return!0};k=function(){var b;c._wD(a.id+": Removing event listeners");a._a._added_events=!1;for(b in I)I.hasOwnProperty(b)&&a._a&&a._a.removeEventListener(b,I[b],!1)};this._onload=function(b){var d=!!b||!a.isHTML5&&8===n&&a.duration;b=a.id+": ";c._wD(b+(d?"onload()":"Failed to load / invalid sound?"+(a.duration?" -":" Zero-length duration reported.")+" ("+a.url+")"),d?1:2);d||a.isHTML5||(!0===c.sandbox.noRemote&&c._wD(b+t("noNet"), 1),!0===c.sandbox.noLocal&&c._wD(b+t("noLocal"),1));a.loaded=d;a.readyState=d?3:2;a._onbufferchange(0);d||a.isHTML5||a._onerror();a._iO.onload&&fa(a,function(){a._iO.onload.apply(a,[d])});return!0};this._onerror=function(b,c){a._iO.onerror&&fa(a,function(){a._iO.onerror.apply(a,[b,c])})};this._onbufferchange=function(b){if(0===a.playState||b&&a.isBuffering||!b&&!a.isBuffering)return!1;a.isBuffering=1===b;a._iO.onbufferchange&&(c._wD(a.id+": Buffer state change: "+b),a._iO.onbufferchange.apply(a,[b])); return!0};this._onsuspend=function(){a._iO.onsuspend&&(c._wD(a.id+": Playback suspended"),a._iO.onsuspend.apply(a));return!0};this._onfailure=function(b,d,e){a.failures++;c._wD(a.id+": Failure ("+a.failures+"): "+b);if(a._iO.onfailure&&1===a.failures)a._iO.onfailure(b,d,e);else c._wD(a.id+": Ignoring failure")};this._onwarning=function(b,c,d){if(a._iO.onwarning)a._iO.onwarning(b,c,d)};this._onfinish=function(){var b=a._iO.onfinish;a._onbufferchange(0);a._resetOnPosition(0);a.instanceCount&&(a.instanceCount--, a.instanceCount||(Xa(),a.playState=0,a.paused=!1,a.instanceCount=0,a.instanceOptions={},a._iO={},F(),a.isHTML5&&(a.position=0)),a.instanceCount&&!a._iO.multiShotEvents||!b||(c._wD(a.id+": onfinish()"),fa(a,function(){b.apply(a)})))};this._whileloading=function(b,c,d,e){var f=a._iO;a.bytesLoaded=b;a.bytesTotal=c;a.duration=Math.floor(d);a.bufferLength=e;a.durationEstimate=a.isHTML5||f.isMovieStar?a.duration:f.duration?a.duration>f.duration?a.duration:f.duration:parseInt(a.bytesTotal/a.bytesLoaded* a.duration,10);a.isHTML5||(a.buffered=[{start:0,end:a.duration}]);(3!==a.readyState||a.isHTML5)&&f.whileloading&&f.whileloading.apply(a)};this._whileplaying=function(b,c,d,e,f){var k=a._iO;if(isNaN(b)||null===b)return!1;a.position=Math.max(0,b);a._processOnPosition();!a.isHTML5&&8opera.version()? new Audio(null):new Audio:null,e,a,f={},h,k;h=c.audioFormats;for(e in h)if(h.hasOwnProperty(e)&&(a="audio/"+e,f[e]=b(h[e].type),f[a]=f[e],e.match(pb)?(c.flash[e]=!0,c.flash[a]=!0):(c.flash[e]=!1,c.flash[a]=!1),h[e]&&h[e].related))for(k=h[e].related.length-1;0<=k;k--)f["audio/"+h[e].related[k]]=f[e],c.html5[h[e].related[k]]=f[e],c.flash[h[e].related[k]]=f[e];f.canPlayType=d?b:null;c.html5=A(c.html5,f);c.html5.usingFlash=hb();u=c.html5.usingFlash;return!0};z={notReady:"Unavailable - wait until onready() has fired.", notOK:"Audio support is not available.",domError:"soundManagerexception caught while appending SWF to DOM.",spcWmode:"Removing wmode, preventing known SWF loading issue(s)",swf404:"soundManager: Verify that %s is a valid path.",tryDebug:"Try soundManager.debugFlash = true for more security details (output goes to SWF.)",checkSWF:"See SWF output for more debug info.",localFail:"soundManager: Non-HTTP page ("+m.location.protocol+" URL?) Review Flash player security settings for this special case:\nhttp://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html\nMay need to add/allow path, eg. c:/sm2/ or /users/me/sm2/", waitFocus:"soundManager: Special case: Waiting for SWF to load with window focus...",waitForever:"soundManager: Waiting indefinitely for Flash (will recover if unblocked)...",waitSWF:"soundManager: Waiting for 100% SWF load...",needFunction:"soundManager: Function object expected for %s",badID:'Sound ID "%s" should be a string, starting with a non-numeric character',currentObj:"soundManager: _debug(): Current sound objects",waitOnload:"soundManager: Waiting for window.onload()",docLoaded:"soundManager: Document already loaded", onload:"soundManager: initComplete(): calling soundManager.onload()",onloadOK:"soundManager.onload() complete",didInit:"soundManager: init(): Already called?",secNote:"Flash security note: Network/internet URLs will not load due to security restrictions. Access can be configured via Flash Player Global Security Settings Page: http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html",badRemove:"soundManager: Failed to remove Flash node.",shutdown:"soundManager.disable(): Shutting down", queue:"soundManager: Queueing %s handler",smError:"SMSound.load(): Exception: JS-Flash communication failed, or JS error.",fbTimeout:"No flash response, applying .swf_timedout CSS...",fbLoaded:"Flash loaded",fbHandler:"soundManager: flashBlockHandler()",manURL:"SMSound.load(): Using manually-assigned URL",onURL:"soundManager.load(): current URL already assigned.",badFV:'soundManager.flashVersion must be 8 or 9. "%s" is invalid. Reverting to %s.',as2loop:"Note: Setting stream:false so looping can work (flash 8 limitation)", noNSLoop:"Note: Looping not implemented for MovieStar formats",needfl9:"Note: Switching to flash 9, required for MP4 formats.",mfTimeout:"Setting flashLoadTimeout = 0 (infinite) for off-screen, mobile flash case",needFlash:"soundManager: Fatal error: Flash is needed to play some required formats, but is not available.",gotFocus:"soundManager: Got window focus.",policy:"Enabling usePolicyFile for data access",setup:"soundManager.setup(): allowed parameters: %s",setupError:'soundManager.setup(): "%s" cannot be assigned with this method.', setupUndef:'soundManager.setup(): Could not find option "%s"',setupLate:"soundManager.setup(): url, flashVersion and html5Test property changes will not take effect until reboot().",noURL:"soundManager: Flash URL required. Call soundManager.setup({url:...}) to get started.",sm2Loaded:"SoundManager 2: Ready. "+String.fromCharCode(10003),reset:"soundManager.reset(): Removing event callbacks",mobileUA:"Mobile UA detected, preferring HTML5 by default.",globalHTML5:"Using singleton HTML5 Audio() pattern for this device.", ignoreMobile:"Ignoring mobile restrictions for this device."};t=function(){var b,c,e,a;b=kb.call(arguments);c=b.shift();if((a=z&&z[c]?z[c]:"")&&b&&b.length)for(c=0,e=b.length;cn&&(c._wD(t("needfl9")),c.flashVersion=n=9);c.version=c.versionNumber+(c.html5Only?" (HTML5-only mode)":9===n?" (AS3/Flash 9)":" (AS2/Flash 8)");8'}if(W&&X)return!1;if(c.html5Only)return Da(),e(),c.oMC=E(c.movieID),ya(),X=W=!0,!1;var f=d||c.url,h=c.altURL||f,k=ma(),l=U(),n=null,n=m.getElementsByTagName("html")[0],p,r,q,n=n&&n.dir&&n.dir.match(/rtl/i);b=b===g?c.id:b;Da();c.url=cb(ga?f:h);d=c.url;c.wmode=!c.wmode&&c.useHighPerformance?"transparent":c.wmode;null!==c.wmode&&(v.match(/msie 8/i)||!N&&!c.useHighPerformance)&&navigator.platform.match(/win32|win64/i)&&(L.push(z.spcWmode),c.wmode=null);k= {name:b,id:b,src:d,quality:"high",allowScriptAccess:c.allowScriptAccess,bgcolor:c.bgColor,pluginspage:vb+"www.macromedia.com/go/getflashplayer",title:"JS/Flash audio component (SoundManager 2)",type:"application/x-shockwave-flash",wmode:c.wmode,hasPriority:"true"};c.debugFlash&&(k.FlashVars="debug=1");c.wmode||delete k.wmode;if(N)f=m.createElement("div"),r=['', a("movie",d),a("AllowScriptAccess",c.allowScriptAccess),a("quality",k.quality),c.wmode?a("wmode",c.wmode):"",a("bgcolor",c.bgColor),a("hasPriority","true"),c.debugFlash?a("FlashVars",k.FlashVars):"",""].join("");else for(p in f=m.createElement("embed"),k)k.hasOwnProperty(p)&&f.setAttribute(p,k[p]);Fa();l=U();if(k=ma())if(c.oMC=E(c.movieID)||m.createElement("div"),c.oMC.id)q=c.oMC.className,c.oMC.className=(q?q+" ":"movieContainer")+(l?" "+l:""),c.oMC.appendChild(f),N&&(p=c.oMC.appendChild(m.createElement("div")), p.className="sm2-object-box",p.innerHTML=r),X=!0;else{c.oMC.id=c.movieID;c.oMC.className="movieContainer "+l;p=l=null;c.useFlashBlock||(c.useHighPerformance?l={position:"fixed",width:"8px",height:"8px",bottom:"0px",left:"0px",overflow:"hidden"}:(l={position:"absolute",width:"6px",height:"6px",top:"-9999px",left:"-9999px"},n&&(l.left=Math.abs(parseInt(l.left,10))+"px")));ub&&(c.oMC.style.zIndex=1E4);if(!c.debugFlash)for(q in l)l.hasOwnProperty(q)&&(c.oMC.style[q]=l[q]);try{N||c.oMC.appendChild(f), k.appendChild(c.oMC),N&&(p=c.oMC.appendChild(m.createElement("div")),p.className="sm2-object-box",p.innerHTML=r),X=!0}catch(u){throw Error(t("domError")+" \n"+u.toString());}}W=!0;e();return!0};la=function(){if(c.html5Only)return na(),!1;if(l)return!1;if(!c.url)return p("noURL"),!1;l=c.getMovie(c.id);l||(aa?(N?c.oMC.innerHTML=Ia:c.oMC.appendChild(aa),aa=null,W=!0):na(c.id,c.url),l=c.getMovie(c.id));"function"===typeof c.oninitmovie&&setTimeout(c.oninitmovie,1);Oa();return!0};S=function(){setTimeout($a, 1E3)};Ca=function(){h.setTimeout(function(){K("soundManager: useFlashBlock is false, 100% HTML5 mode is possible. Rebooting with preferFlash: false...");c.setup({preferFlash:!1}).reboot();c.didFlashBlock=!0;c.beginDelayedInit()},1)};$a=function(){var b,d=!1;c.url&&!ba&&(ba=!0,x.remove(h,"load",S),G&&wa&&!Va?p("waitFocus"):(q||(b=c.getMoviePercent(),0b&&(d=!0)),setTimeout(function(){b=c.getMoviePercent();d?(ba=!1,c._wD(t("waitSWF")),h.setTimeout(S,1)):(q||(c._wD("soundManager: No Flash response within expected time. Likely causes: "+ (0===b?"SWF load failed, ":"")+"Flash blocked or JS-Flash security error."+(c.debugFlash?" "+t("checkSWF"):""),2),!ga&&b&&(p("localFail",2),c.debugFlash||p("tryDebug",2)),0===b&&c._wD(t("swf404",c.url),1),C("flashtojs",!1,": Timed out"+(ga?" (Check flash security or flash blockers)":" (No plugin/missing SWF?)"))),!q&&ob&&(null===b?c.useFlashBlock||0===c.flashLoadTimeout?(c.useFlashBlock&&Ja(),p("waitForever")):!c.useFlashBlock&&da?Ca():(p("waitForever"),O({type:"ontimeout",ignoreInit:!0,error:{type:"INIT_FLASHBLOCK"}})): 0===c.flashLoadTimeout?p("waitForever"):!c.useFlashBlock&&da?Ca():Ha(!0)))},c.flashLoadTimeout)))};ka=function(){if(Va||!wa)return x.remove(h,"focus",ka),!0;Va=ob=!0;p("gotFocus");ba=!1;S();x.remove(h,"focus",ka);return!0};Oa=function(){L.length&&(c._wD("SoundManager 2: "+L.join(" "),1),L=[])};mb=function(){Oa();var b,d=[];if(c.useHTML5Audio&&c.hasHTML5){for(b in c.audioFormats)c.audioFormats.hasOwnProperty(b)&&d.push(b+" = "+c.html5[b]+(!c.html5[b]&&u&&c.flash[b]?" (using flash)":c.preferFlash&& c.flash[b]&&u?" (preferring flash)":c.html5[b]?"":" ("+(c.audioFormats[b].required?"required, ":"")+"and no flash support)"));c._wD("SoundManager 2 HTML5 support: "+d.join(", "),1)}};Y=function(b){if(q)return!1;if(c.html5Only)return p("sm2Loaded",1),q=!0,R(),C("onload",!0),!0;var d=!0,e;c.useFlashBlock&&c.flashLoadTimeout&&!c.getMoviePercent()||(q=!0);e={type:!G&&u?"NO_FLASH":"INIT_TIMEOUT"};c._wD("SoundManager 2 "+(y?"failed to load":"loaded")+" ("+(y?"Flash security/load error":"OK")+") "+String.fromCharCode(y? 10006:10003),y?2:1);y||b?(c.useFlashBlock&&c.oMC&&(c.oMC.className=U()+" "+(null===c.getMoviePercent()?"swf_timedout":"swf_error")),O({type:"ontimeout",error:e,ignoreInit:!0}),C("onload",!1),T(e),d=!1):C("onload",!0);y||(c.waitForWindowLoad&&!ja?(p("waitOnload"),x.add(h,"load",R)):(c.waitForWindowLoad&&ja&&p("docLoaded"),R()));return d};Za=function(){var b,d=c.setupOptions;for(b in d)d.hasOwnProperty(b)&&(c[b]===g?c[b]=d[b]:c[b]!==d[b]&&(c.setupOptions[b]=c[b]))};ya=function(){if(q)return p("didInit"), !1;if(c.html5Only)return q||(x.remove(h,"load",c.beginDelayedInit),c.enabled=!0,Y()),!0;la();try{l._externalInterfaceTest(!1),ab(!0,c.flashPollingInterval||(c.useHighPerformance?10:50)),c.debugMode||l._disableDebug(),c.enabled=!0,C("jstoflash",!0),c.html5Only||x.add(h,"unload",xa)}catch(b){return c._wD("js/flash exception: "+b.toString()),C("jstoflash",!1),T({type:"JS_TO_FLASH_EXCEPTION",fatal:!0}),Ha(!0),Y(),!1}Y();x.remove(h,"load",c.beginDelayedInit);return!0};P=function(){if(Z)return!1;Z=!0;Za(); Fa();!G&&c.hasHTML5&&(c._wD("SoundManager 2: No Flash detected"+(c.useHTML5Audio?". Trying HTML5-only mode.":", enabling HTML5."),1),c.setup({useHTML5Audio:!0,preferFlash:!1}));jb();!G&&u&&(L.push(z.needFlash),c.setup({flashLoadTimeout:1}));m.removeEventListener&&m.removeEventListener("DOMContentLoaded",P,!1);la();return!0};La=function(){"complete"===m.readyState&&(P(),m.detachEvent("onreadystatechange",La));return!0};Ea=function(){ja=!0;P();x.remove(h,"load",Ea)};Na();x.add(h,"focus",ka);x.add(h, "load",S);x.add(h,"load",Ea);m.addEventListener?m.addEventListener("DOMContentLoaded",P,!1):m.attachEvent?m.attachEvent("onreadystatechange",La):(C("onload",!1),T({type:"NO_DOM2_EVENTS",fatal:!0}))}if(!h||!h.document)throw Error("SoundManager requires a browser with window and document objects.");var V=null;h.SM2_DEFER!==g&&SM2_DEFER||(V=new J);"object"===typeof module&&module&&"object"===typeof module.exports?(module.exports.SoundManager=J,module.exports.soundManager=V):"function"===typeof define&& define.amd&&define(function(){return{constructor:J,getInstance:function(g){!h.soundManager&&g instanceof Function&&(g=g(J),g instanceof J&&(h.soundManager=g));return h.soundManager}}});h.SoundManager=J;h.soundManager=V})(window);/** * Used for adding Audio captcha to existing captcha inputs. */ YUI().add("scriptlibrary.input.AudioCaptcha", function (Y) { "use strict"; var attributeConfig = function () { return { contextPath: {}, sequenceNumber: {}, resourceName: {}, resources: { value: null, setter: function(resources) { return resources && resources.CaptchaInput ? resources.CaptchaInput : resources; } } }; }; // @formatter:off var MODE_HTML = '' + '{{audioModeLabel}}', AUDIO_HTML = '{{replayButtonLabel}}'; // @formatter:on /** * Initializes the state selector. * @param {type} parameters Initialization parameters. */ var AudioCaptcha = function(parameters) { this.addAttrs(attributeConfig(), parameters); var me = this, resources = this.get("resources"), contextPath = this.get("contextPath"), resourceName = this.get("resourceName"), audioModeButton, textModeButton, modeContainer, audioContainer, currentMode = "text", managerReady = false, initialized = false, language, image, sound; var initialize = function(imageContainer) { if (initialized) { return; } language = Y.one("html").getAttribute("lang") || "en-US"; image = imageContainer.one("img"); me.after("sequenceNumberChange", refreshAudio); var template = new Y.Template(Y.Handlebars); audioContainer = imageContainer.one(".captcha-audio-container"); if (!audioContainer) { audioContainer = Y.Node.create("
"); audioContainer.setHTML(template.render(AUDIO_HTML, resources)); imageContainer.appendChild(audioContainer); } modeContainer = imageContainer.one(".captcha-mode-options"); if (!modeContainer) { modeContainer = Y.Node.create("
"); modeContainer.setHTML(template.render(MODE_HTML, resources)); imageContainer.appendChild(modeContainer); } modeContainer.show(); audioContainer.hide(); audioModeButton = modeContainer.one(".captcha-audio-mode"); textModeButton = modeContainer.one(".captcha-text-mode"); audioModeButton.on("click", audioMode); textModeButton.on("click", textMode); audioContainer.one(".captcha-replay-audio").on("click", replayAudio); initializeAudio(); initialized = true; }; var initializeAudio = function() { soundManager.setup({ url: contextPath + "/files/scriptlibrary/js/audio/", debugMode: false, ontimeout: function() { alert("Error initializing audio"); }, onready: function() { managerReady = true; if (currentMode === "audio") { initializeSound(); sound.play(); } } }); }; var textMode = function() { if (sound) { sound.stop(); } currentMode = "text"; audioContainer.hide(); image.show(); textModeButton.hide(); audioModeButton.show(); }; var replayAudio = function() { if (!sound) { initializeSound(); } sound.stop(); sound.play(); }; var audioMode = function() { image.hide(); audioContainer.show(); audioModeButton.hide(); textModeButton.show(); currentMode = "audio"; if (!sound) { initializeSound(); } sound.stop(); sound.play(); }; var initializeSound = function() { if (!managerReady) { return; } sound = new CaptchaSound(resourceName, me.get("sequenceNumber"), language); }; var refreshAudio = function() { if (sound) { sound.destruct(); sound.stop(); } sound = null; if (currentMode == "audio") { initializeSound(); sound.play(); } }; /** * Resets to text mode. */ this.resetToTextMode = function() { if (!initialized) { return; } textMode(); }; /** * Renders the state selector. * * @param {Node} imageContainer Image container. * @param {Node} refreshButton Refresh button. */ this.render = function(imageContainer) { if (!imageContainer) { return; } initialize(imageContainer); }; }; var CaptchaSound = function(resourceName, sequenceNumber, languageTag) { var me = this, index = 0, current, stopped = false; var playNext = function() { if (index > 4) { return; } current = soundManager.createSound({ url: contextPath + "/captcha.mp3?index=" + index + "&languageTag=" + languageTag + "&resourceName=" + resourceName + "&sequenceNumber=" + sequenceNumber, onfinish: function() { index++; playNext(); } }); current.play(); }; this.play = function() { stopped = false; index = 0; playNext(); }; this.destruct = function() { if (current) { current.destruct(); } } this.stop = function() { stopped = true; if (current) { current.stop(); } }; }; Y.augment(AudioCaptcha, Y.Attribute); Y.scriptlibrary = Y.scriptlibrary || {}; Y.scriptlibrary.input = Y.scriptlibrary.input || {}; Y.scriptlibrary.input.AudioCaptcha = AudioCaptcha; });YUI().add("scriptlibrary.ErrorHandlers", function (Y) { Y.scriptlibrary = Y.scriptlibrary || {}; var extractError = function(error) { if (Y.Lang.isString(error)) { return error; } else if (Y.Lang.isFunction(error)) { return error(); } return null; }; Y.scriptlibrary.ErrorHandlers = { /** * Gets error handler that will show message in a node. * * @param {Node|String} errorNode Node to display error message in. * @param {String} fallbackMessage Fall back message if the error is not a string. * @param {Function} [afterErrorFn] Called after the error reporting is complete. * @returns {Function} Error handler. */ handleErrorInNode: function(errorNode, fallbackMessage, afterErrorFn) { return function(error) { if (!error) { return; } var node = Y.one(errorNode); node.set("text", extractError(error) || fallbackMessage); if (Y.scriptlibrary.DynamicWindow) { Y.scriptlibrary.DynamicWindow.HideWaitMessage(); } node.show(); if (console && console.error) { console.error(error); } if (afterErrorFn) { afterErrorFn(); } }; }, /** * Gets error handler that will show message in a modal popup. * * @param {String} fallbackMessage Fall back message if the error is not a string. * @param {Number} [zIndex] Z-index to use. * @param {Function} [afterErrorFn] Function to call after the error is handled. * @returns {Function} Error handler. */ handleErrorInPopup: function(fallbackMessage, zIndex, afterErrorFn) { return function(error) { if (!error) { return; } var message = extractError(error) || fallbackMessage; if (Y.scriptlibrary.DynamicWindow) { Y.scriptlibrary.DynamicWindow.HideWaitMessage(); Y.scriptlibrary.DynamicWindow.ShowCompletedMessage(message, "OK", zIndex); } else { alert(message); } if (console && console.error) { console.error(error); } if (afterErrorFn) { afterErrorFn(); } }; } }; }); /** * Contains internationialization helpers */ YUI().add("scriptlibrary.i18n.Locale", function (Y) { "use strict"; var getFormat = function(locale, precision, symbol, noThousands) { var format = ""; switch(normalizeTag(locale)) { case "fr_CA": format = { decimalPlaces: precision, decimalSeparator: ",", thousandsSeparator: noThousands ? "" : " ", suffix: symbol ? " " + symbol : "" }; break; case "nl_NL": format = { decimalPlaces: precision, decimalSeparator: ",", thousandsSeparator: noThousands ? "" : " ", prefix: symbol ? symbol : "" }; break; default: format = { decimalPlaces: precision, decimalSeparator: ".", thousandsSeparator: noThousands ? "" : ",", prefix: symbol ? symbol : "" }; }; return format; }; var normalizeTag = function(locale){ if (locale) { return locale.replace("-","_"); } else { return null; } }; Y.scriptlibrary = Y.scriptlibrary || {}; Y.scriptlibrary.i18n = Y.scriptlibrary.i18n|| {}; Y.scriptlibrary.i18n.Locale = { /** * Normalizes the given tag. * @param {String} locale Locale. * @return {String} normalized tag. */ normalize: function(locale) { return normalizeTag(locale); }, /** * Formats the given number to the locale. * @param {String} locale Locale to use. * @param {Number} precision Number of decimal places * @param {String} symbol Currency symbol * @param {Boolean} noThousands True if no thousands separator should be used. * @returns {String} precision Precision. */ getNumberFormat: function(locale, precision, symbol, noThousands) { if (!precision) { precision = 2; } return getFormat(locale, precision, symbol, noThousands); } }; });/** * Contains number formatter. */ YUI().add("scriptlibrary.formatters.NumberFormatter", function (Y) { "use strict"; var getFormattedNumber = function(number, locale, precision, noThousands) { var formattedNumber; if (number !== null && number !== "") { formattedNumber = Y.DataType.Number.format(number, Y.scriptlibrary.i18n.Locale.getNumberFormat(locale, precision, null, noThousands)); } else { formattedNumber = ""; } return formattedNumber; }; Y.scriptlibrary = Y.scriptlibrary || {}; Y.scriptlibrary.formatters = Y.scriptlibrary.formatters || {}; Y.scriptlibrary.formatters.NumberFormatter = { /** * Formats the given number to the locale. * @param {type} number Number to format. * @param {type} locale Locale to use. * @returns {String} precision Precision. */ format: function(number, locale, precision, noThousands) { if (!precision) { precision = 2; } return getFormattedNumber(number, locale, precision, noThousands); }, getParsedNumber: function(number, locale, symbol, suffix){ var formattedNumber; if (number !== null && number !== "") { if(suffix) { number = number.replace(suffix, ""); } formattedNumber = Y.DataType.Number.parse(number, Y.scriptlibrary.i18n.Locale.getNumberFormat(locale, 2, symbol)); } else { formattedNumber = ""; } return formattedNumber; } }; });/** * Contains currency formatter. */ YUI().add("scriptlibrary.formatters.CurrencyFormatters", function (Y) { "use strict"; var getFormattedPrice = function(price, locale, symbol, suffix) { var formattedPrice; if (price !== null && price !== "") { formattedPrice = Y.DataType.Number.format(price, Y.scriptlibrary.i18n.Locale.getNumberFormat(locale, 2, symbol)); if(suffix) { formattedPrice += " " + suffix; } } else { formattedPrice = ""; } return formattedPrice; }; Y.scriptlibrary = Y.scriptlibrary || {}; Y.scriptlibrary.formatters = Y.scriptlibrary.formatters || {}; Y.scriptlibrary.formatters.CurrencyFormatters = { /** * Formats the given value as currency. * @param {Number} price Price to format. * @param {String} locale Locale to use. * @returns {String} Formatted price. */ format: function(price, locale, symbol, suffix) { var currencySymbol = symbol ? symbol : "$"; return getFormattedPrice(price, locale, currencySymbol, suffix); }, /** * Reverses the formatting on currency * @param {String} number Number to parse * @param {String} locale Locale to parse from * @param {String} symbol Symbol used * @param {String} suffix Suffix if there is one * @returns Number string */ parse: function(number, locale, symbol, suffix) { var currencySymbol = symbol ? symbol : "$"; return Y.scriptlibrary.formatters.NumberFormatter.getParsedNumber(number, locale, currencySymbol, suffix); } }; });YUI().add("scriptlibrary.ValidationHelpers", function (Y) { Y.scriptlibrary = Y.scriptlibrary || {}; var ZipCodeRegexes = { US: /^\d{5}(?:[-\s]\d{4})?$/, CA: /^[a-zA-Z]{1}[0-9]{1}[a-zA-Z]{1}[\s]*[0-9]{1}[a-zA-Z]{1}[0-9]{1}$/, UK: /^(((([A-PR-UWYZ][0-9][0-9A-HJKS-UW]?)|([A-PR-UWYZ][A-HK-Y][0-9][0-9ABEHMNPRV-Y]?))\s{0,2}[0-9]([ABD-HJLNP-UW-Z]{2}))|(GIR\s{0,2}0AA))$/, IE: /^[AC-FHKNPRTV-Y][0-9][0-9W][A-Z0-9]{4}$/, AU: /^\d{4}$/, NZ: /^\d{4}$/ }, PhoneRegexes = { US: /^([(]?[2-9]\d{2}[)]?)[ ]*-?[ ]*(\d{3})[ ]*-?[ ]*(\d{4})$/, CA: /^([(]?[2-9]\d{2}[)]?)[ ]*-?[ ]*(\d{3})[ ]*-?[ ]*(\d{4})$/ }, DoubleRegex = /(^-?\d\d*\.\d+$)|(^-?\d\d*$)|(^-?\.\d\d*$)/, EmojiDetectionRegex = /([\uD800-\uDBFF][\uDC00-\uDFFF])/, PasswordRegex = /^(?=.*\d)(?=.*[a-zA-Z])[A-Za-z0-9!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?\s]{8,}$/, PhoneValid = function(value, country) { var regex = PhoneRegexes[country]; if (regex) { return regex.test(value); } else if (value === "AU" || value === "NZ") { var number = value.replace(eval('/[)(\\- ]/g'),''); /^\d*$/.test(number) && number.length >= 6 && number <= 10; } else { var number = value.replace(eval('/[)(\\- ]/g'),''); return /^\d*$/.test(number) && number.length >= 10; } }, CreditCardTypes = [ { matches: function(cardType) { return cardType === "VISA"; }, cardType:"VISA", cardRegex: /^4[0-9]{12}(?:[0-9]{3})?$/, secretCodeRegex: /^[0-9]{3}$/ }, { matches: function(cardType) { return cardType === "MASTERCARD"; }, cardType: "MASTERCARD", cardRegex: /^5[1-5][0-9]{14}$/, secretCodeRegex: /^[0-9]{3}$/ }, { matches: function(cardType) { return cardType === "AMEX" || cardType === "AMERICANEXPRESS"; }, cardType: "AMEX", cardRegex: /^3[47][0-9]{13}$/, secretCodeRegex: /^[0-9]{4}$/ }, { matches: function(cardType) { return cardType === "DISCOVER"; }, cardType: "DISCOVER", cardRegex: /^6(?:011|5[0-9]{2})[0-9]{12}$/, secretCodeRegex: /^[0-9]{3}$/ }, { matches: function(cardType) { return cardType === "JCB"; }, cardType: "JCB", cardRegex: /^(?:2131|1800|35\d{3})\d{11}$/, secretCodeRegex: /^[0-9]{3}$/ } ], PasswordErrors = { CORRECT: 0, MISMATCH: 1, EMPTY: 2, WEAK: 3 }, getDecimalSeparator = function(locale) { var separator = ""; switch (locale) { case "nl-NL": case "fr-CA": separator = "(\\,|\\.)"; break; default: separator = "\\."; } return separator; }; Y.scriptlibrary.ValidationHelpers = { IsValidEmail: function(email, optional) { var trimmedEmail = email ? email.trim() : null; if (optional && !trimmedEmail) { return true; } return /^(?!\.)(?:[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(trimmedEmail); }, ContainsEmoji: function(value) { return EmojiDetectionRegex.test(value); }, IsLatitude: function(value) { if (!DoubleRegex.test(value)) { return false; } var lat = parseFloat(value, 10); return lat >= -90 && lat <= 90; }, IsLongitude: function(value) { if (!DoubleRegex.test(value)) { return false; } var lng = parseFloat(value, 10); return lng <= 180 && lng >= -180; }, IsValidInteger: function(value, min, max) { if (!/^-?\d\d*$/.test(value)) { return false; } var number = parseInt(value, 10); if ((min || min === 0) && number < min) { return false; } else if ((max || max === 0) && number > max) { return false; } return true; }, IsValidDouble: function(value, min, max, maxDecimalPlaces, locale) { var pattern, decimalSeparator = getDecimalSeparator(locale); if (!value) { return false; } if (maxDecimalPlaces) { pattern = new RegExp("^\\-?\\d*" + decimalSeparator + "?\\d{0,"+maxDecimalPlaces+"}$"); } else { pattern = new RegExp("^\\-?\\d*" + decimalSeparator + "?\\d*$"); } if (!pattern.test(value)) { return false; } var number = parseFloat(value); if ((min || min === 0) && number < min) { return false; } else if ((max || max === 0) && number > max) { return false; } return true; }, IsPhoneValid: PhoneValid, IsPhoneValidAnyCountry: function(value) { for (var country in PhoneRegexes) { if (PhoneValid(value, function() { return country; })) { return true; } } return false; }, GetPostalCodeRegex: function(country) { return ZipCodeRegexes[country]; }, IsValidCode: function(code, country) { var regex = ZipCodeRegexes[country]; if (regex) { return regex.test(code); } else { return true; } }, anySecretCode: function(secretNumber) { var valid = false; if (secretNumber === "***" || secretNumber === "****") { return true; } Y.Array.each(CreditCardTypes, function(ccType) { valid = valid || ccType.secretCodeRegex.test(secretNumber); }); return valid; }, IsSecretCodeValid: function(secretNumber, cardType) { var creditCard = this.GetCreditCardByCardType(cardType); if(creditCard) { return creditCard.secretCodeRegex.test(secretNumber); } return false; }, GetCreditCardByCardType: function(cardType) { for(var i = 0; i < CreditCardTypes.length; ++i) { if(CreditCardTypes[i].matches(cardType)) { return CreditCardTypes[i]; } } return null; }, GetCreditCardType: function(cardNumber) { for(var i = 0; i < CreditCardTypes.length; ++i) { if(CreditCardTypes[i].cardRegex.test(cardNumber)) { return CreditCardTypes[i].cardType; } } return null; }, IsValidCreditCard: function(cardNumber, cardType) { cardNumber = cardNumber.replace(/[^0-9]*/g, ""); var creditCard = this.GetCreditCardByCardType(cardType); if(creditCard) { return creditCard.cardRegex.test(cardNumber); } return false; }, /** * True if the value contains only alpha numeric characters. * @param {type} value Value to check. * @returns {boolean} True if the value only contains alpha numeric characters. */ isAlphaNumeric: function(value) { return /^[a-zA-Z0-9]*$/.test(value); }, /** * True if the value contains only symbols allowed for variable declaration. * @param {type} value Value to check. * @returns {boolean} True if the value only contains valid variable characters. */ isVariableName: function(value) { return /^[a-zA-Z][a-zA-Z_0-9]*$/.test(value); }, PasswordErrors: PasswordErrors, ValidatePassword: function (password, confirmPassword, canBeEmpty) { if(!password && !confirmPassword && canBeEmpty) { return {valid: true, error: PasswordErrors.CORRECT}; } else if((!password || !confirmPassword) && !canBeEmpty) { return {valid: false, error: PasswordErrors.EMPTY}; } else if (password !== confirmPassword) { return {valid: false, error: PasswordErrors.MISMATCH}; } else if (!PasswordRegex.test(password)) { return {valid: false, error: PasswordErrors.WEAK}; } else { return {valid: true, error: PasswordErrors.CORRECT}; } } }; });YUI().add("scriptlibrary.ArrayExtensions", function (Y) { if (!Array.prototype.filter) { Array.prototype.filter = function(fn /*, scope*/) { var len = this.length, res = new Array(), scope = arguments[1]; for (var i = 0; i < len; i++) { if (i in this) { var val = this[i]; if (fn.call(scope, val, i, this)) { res.push(val); } } } return res; }; } if (!Array.prototype.findFirst) { Array.prototype.findFirst = function(fn, scope) { var len = this.length; return Y.Array.find(this, function(value) { return fn.call(scope, value); }); }; } if (!Array.prototype.containsAll) { Array.prototype.containsAll = function(otherArray, toMapKeyFn) { var len = this.length, mapKeyFn = toMapKeyFn || function(val) { return val; }, me = this, thisMap = {}; Y.Array.each(this, function(value) { thisMap[mapKeyFn(value)] = true; }); return Y.Array.every(otherArray, function(otherValue) { return thisMap[mapKeyFn(otherValue)]; }); }; } });YUI().add("scriptlibrary.StringExtensions", function (Y) { String.prototype.trim = String.prototype.trim || function(){ return this.replace(/^\s+|\s+$/g, ''); }; /** * Appends the given value to this string if it is not empty. * @param {String} delimiter (DEFAULT " ") Delimiter for the values. * @param {String} value Value to append with delimiter. * @return String with value appended, this string if value is empty. */ String.prototype.appendNonEmpty = function(delimiter, value) { var delim = delimiter || " "; if (this.trim() === "") { return value || ""; } if (value) { return this + delim + value; } return this; }; Y.scriptlibrary = Y.scriptlibrary || {}; Y.scriptlibrary.StringExtensions = { EscapeHtml: function(value) { return value .replace(/&/g, '&') .replace(/>/g, '>') .replace(/= max) { return false; } return true; } }; fields.push(registration); }; /** * Registers the given validator as the nested validator. * @param {Node} node Container or input represented by the validator. * @param {Validator} validator Validator to nest. * @param {Boolean} optional True if optional. * @param {Function} beforeValidation Called before validation occurs. * @param {Function} isEmpty Optional method that determines if the validator is empty or not. */ this.nestedValidator = function(node, validator, optional, beforeValidation, isEmpty) { if (this === validator) { throw "Cannot nest validator in itself."; } var isEmptyFn = isEmpty || function() { return validator.isFormEmpty(); }; this.nonInputCustom( node, function() { return isEmptyFn() ? null : { valid: validator.isValid() }; }, function(value) { return value && value.valid; }, optional, beforeValidation || function() { validator.clear(); }, isEmptyFn); }; this.nonInputCustom = function(node, getValuesFn, isValidFn, optional, beforeValidation, isEmpty) { var registration = { node: node, isValidFn: isValidFn, optional: optional, getValueFn: getValuesFn, emptyFn: isEmpty, beforeValidation: beforeValidation, maxLength: -1 }; fields.push(registration); }; this.double = function(node, min, max, maxDecimalPlaces, optional, languageTag) { var registration = { node: node, isValidFn: function(value) { return Y.scriptlibrary.ValidationHelpers.IsValidDouble(value, min, max, maxDecimalPlaces, languageTag); }, optional: optional }; fields.push(registration); }; this.customField = function(node, validFn, maxLength, optional, onValidation, onMaxLengthCheck) { var registration = { node: node, isValidFn: validFn, optional: optional, maxLength: maxLength, onValidation: onValidation, onMaxLengthCheck: onMaxLengthCheck }; fields.push(registration); }; this.cvv2Code = function(node) { var registration = { node: node, isValidFn: function(value) { return Y.scriptlibrary.ValidationHelpers.anySecretCode(value); } }; fields.push(registration); }; this.creditCardSecretCode = function(node, getCardTypeFn, onValidation, existingHiddenValue) { var registration = { node: node, isValidFn: function(value) { return Y.scriptlibrary.ValidationHelpers.IsSecretCodeValid(value, getCardTypeFn()); }, onValidation: onValidation }; if (existingHiddenValue) { registration.isValidFn = handleHiddenExistingValue(node, registration.isValidFn); } fields.push(registration); }; this.creditCardField = function(node, getCardTypeFn, onValidation, existingHiddenValue) { var registration = { node: node, isValidFn: function(value) { return Y.scriptlibrary.ValidationHelpers.IsValidCreditCard(value, getCardTypeFn()); }, onValidation: onValidation }; if (existingHiddenValue) { registration.isValidFn = handleHiddenExistingValue(node, registration.isValidFn); } fields.push(registration); }; this.acceptCheckbox = function(node) { var registration = { node: node, getValueFn: function(node) { return node.getDOMNode().checked; }, isValidFn: function(value) {return value; }, optional: false }; fields.push(registration); }; this.emailField = function(node, maxLength, optional, onValidation, onMaxLengthCheck) { var registration = { node: node, isValidFn: Y.scriptlibrary.ValidationHelpers.IsValidEmail, optional: optional, maxLength: maxLength, onValidation: onValidation, onMaxLengthCheck: onMaxLengthCheck }; fields.push(registration); }; /** * Email list validator. * @param {String|Node} node Input node. * @param {Boolean} optional True if optional. * @param {Function} onValidation Called when validation occurs. * @param {String} delimiter Delimiter for splitting the email addresses. * @param {Boolean} filterOutBlanks True if blank entries can be ignored. */ this.emailList = function(node, optional, onValidation, delimiter, filterOutBlanks) { var listDelimiter = delimiter || ",", registration = { node: node, isValidFn: function(value) { if (!value) { return true; } var parts = value.split(listDelimiter); if (filterOutBlanks) { var newParts = []; for (var i = 0; i < parts.length; ++i) { if (parts[i].trim()) { newParts.push(parts[i]); } } parts = newParts; } for (var i = 0; i < parts.length; ++i) { if (!Y.scriptlibrary.ValidationHelpers.IsValidEmail(parts[i].trim())) { return false; } else if (parts[i].length > SHORT_TEXT) { return false; } } return true; }, optional: optional, onValidation: onValidation }; fields.push(registration); this.setFieldOption(node, "maxLength", LONG_TEXT); }; this.zipField = function(node, maxLength, optional, getCountryFn, onValidation, onMaxLengthCheck) { var registration = { node: node, isValidFn: function(value) { return Y.scriptlibrary.ValidationHelpers.IsValidCode(value, getCountryFn()); }, optional: optional, maxLength: maxLength, onValidation: onValidation, onMaxLengthCheck: onMaxLengthCheck }; fields.push(registration); }; this.phoneFieldAnyCountry = function(node, optional) { var registration = { node: node, isValidFn: function(value) { return Y.scriptlibrary.ValidationHelpers.IsPhoneValidAnyCountry(value); }, optional: optional }; fields.push(registration); }; /** * Configures validation on an input to ensure north american phone numbers only. * * @param node Current node. * @param optional True if optional. */ this.northAmericanPhone = function(node, optional) { var registration = { node: node, isValidFn: function(value) {return Y.scriptlibrary.ValidationHelpers.IsPhoneValid(value, function() { return "US"; }); }, getValueFn: function(input) { var value = input.get("value"); if (!value) { return value; } return value.replace(/\D/g,'').trim(); }, optional: optional, maxLength: 11 }; fields.push(registration); }; this.phoneField = function(node, maxLength, optional, getCountryFn, onValidation, onMaxLengthCheck) { var registration = { node: node, isValidFn: function(value) { return Y.scriptlibrary.ValidationHelpers.IsPhoneValid(value, getCountryFn()); }, optional: optional, maxLength: maxLength, onValidation: onValidation, onMaxLengthCheck: onMaxLengthCheck }; fields.push(registration); }; /** * Checks whether all the given nodes have valid lengths. * @param {Node[]} nodes List of nodes. * @returns {Boolean} True if they all have valid lengths. */ this.hasValidLengths = function(nodes) { for (var i = 0; i < nodes.length; ++i) { if (!this.hasValidLength(nodes[i])) { return false; } } return true; }; /** * Checks whether the given node has a value that is a valid length. * @param {Node} node Node to check. * @returns {Boolean} True if field is valid length. */ this.hasValidLength = function(node) { var field = getFieldByNode(node), maxLength = field ? field.maxLength || SHORT_TEXT : null; if (!maxLength) { return true; } var getValueFn = field.getValueFn || defaultGetValueFn, value = getValueFn(node); // BUG: string length in JS is the number of utf-16 code units. // e.g.: '𠜎'.length == 2, despite requiring 4 UTF-8 bytes. if (Y.Lang.isString(value) || Y.Lang.isArray(value)) { return maxLength === -1 || value.length <= maxLength; } return true; }; this.partialAddress = function(address1, address2, city, countrySelector, zip, phone) { this.addressWithCountrySelector( address1, address2, city, countrySelector, zip, null, true, null, true, null, true); }; this.addressWithCountrySelector = function(address1, address2, city, countrySelector, zip, phone, optional, container, phoneOptional, additionalFields, allowPartial) { this.nonEmptyField(address1, null, optional); if (address2) { this.nonEmptyField(address2, null, true); } this.nonEmptyField(city, null, optional); this.countrySelector(countrySelector, optional); if (additionalFields) { for (var i = 0; i < additionalFields.length; ++i) { this.nonEmptyField(additionalFields[i], null, optional); } } this.zipField(zip, null, optional, function() { return countrySelector.getCountry(); }); if (phone) { this.phoneField(phone, 15, optional || phoneOptional, function() { return countrySelector.getCountry(); }); } if (optional && !allowPartial) { this.nonInputCustom( container, function() { var requiredFieldEmpty = false, oneFieldFilledIn = false, checkField = function(node, required) { if (node.getDOMNode().value !== "") { oneFieldFilledIn = true; } else { requiredFieldEmpty = requiredFieldEmpty || required; } }; if (additionalFields) { for (var i = 0; i < additionalFields.length; ++i) { checkField(additionalFields[i], true); } } checkField(address1, true); checkField(address2, false); checkField(city, true); checkField(zip, true); if (phone) { checkField(phone); } if (!countrySelector.isEmpty()) { oneFieldFilledIn = true; } else { requiredFieldEmpty = requiredFieldEmpty || (!countrySelector.getCountry() && !countrySelector.getState()); } return { empty: !oneFieldFilledIn, valid: !oneFieldFilledIn || !requiredFieldEmpty }; }, function(result) { return result.valid; }, null, null, function(value) { return value.empty; }); } }; this.removeCountrySelector = function(countrySelector) { fields = fields.filter(function(field) { return field.selector !== countrySelector; }); }; this.countrySelector = function(countrySelector, optional) { var stateRegistration = { selector: countrySelector, nodeFn: function() { return countrySelector.getCurrentStateInput(); }, emptyFn: function() { return countrySelector.isEmpty(); }, optional: optional }, countryRegistration = { selector: countrySelector, nodeFn: function() { return countrySelector.getCountryNode(); }, emptyFn: function() { return countrySelector.isEmpty(); }, optional: optional }; fields.push(stateRegistration); fields.push(countryRegistration); }; this.address = function(address1, address2, city, state, country, zip, phone) { this.nonEmptyField(address1); if (address2) { this.nonEmptyField(address2, null, true); } this.nonEmptyField(city, null); this.nonEmptyField(state, null); this.nonEmptyField(country, null); this.zipField(zip, null, true, function() { return Y.one(country).getDOMNode().value; }); if (phone) { this.phoneField(phone, 15, true, function() { return Y.one(country).getDOMNode().value; }); } }; /** * Removes date picker from the validator. * @param {scriptlibrary.DatePicker} datePicker Date picker to remove. */ this.removeDate = function(datePicker) { this.remove(datePicker.getDisplayNode()); }; /** * Removes time picker from the validator. * @param {scriptlibrary.TimePicker} timePicker Time picker to remove. */ this.removeTime = function(timePicker) { this.remove(timePicker.getContainer()); }; this.date = function(datePicker, timePicker, optional) { var getValueFn = function() { return datePicker.getInputNode() ? datePicker.getInputNode().get("value") : datePicker.getDisplayNode().get("text"); }, registration = { node: datePicker.validationNode(), getValueFn: getValueFn, optional: function() { return optional && (!timePicker || timePicker.isEmpty()); } }; fields.push(registration); }; this.time = function(timePicker, datePicker, optional) { var registration = { node: timePicker.getContainer(), beforeValidation: function() { timePicker.clearError(); }, getValueFn: function() { return timePicker.getTime(); }, emptyFn: function() { return timePicker.isEmpty(); }, isValidFn: function(value, silent) { return timePicker.isValid(datePicker && datePicker.isEmpty(), silent); }, optional: function() { return datePicker && datePicker.isEmpty() || optional; } }; fields.push(registration); }; this.password = function(node, confirmNode, canBeEmpty) { var firstInput = Y.one(node), secondInput = Y.one(confirmNode), registration = { node: node, isValidFn: function(value) { return secondInput.getDOMNode().value === value; }, optional: canBeEmpty }, confirmRegistration = { node: confirmNode, isValidFn: function(value) { return firstInput.getDOMNode().value === value; }, optional: canBeEmpty }; fields.push(registration); fields.push(confirmRegistration); }; this.strongPassword = function(node, confirmNode, canBeEmpty, callback) { var firstInput = Y.one(node), secondInput = Y.one(confirmNode), registration = { node: node, beforeValidation: function () { var validationResult = Y.scriptlibrary.ValidationHelpers.ValidatePassword(firstInput.getDOMNode().value, secondInput.getDOMNode().value, canBeEmpty); callback(validationResult); }, isValidFn: function() { return Y.scriptlibrary.ValidationHelpers.ValidatePassword(firstInput.getDOMNode().value, secondInput.getDOMNode().value, canBeEmpty).valid; }, optional: canBeEmpty }; confirmRegistration = { node: confirmNode, beforeValidation: function () { var validationResult = Y.scriptlibrary.ValidationHelpers.ValidatePassword(firstInput.getDOMNode().value, secondInput.getDOMNode().value, canBeEmpty); callback(validationResult); }, isValidFn: function() { return Y.scriptlibrary.ValidationHelpers.ValidatePassword(firstInput.getDOMNode().value, secondInput.getDOMNode().value, canBeEmpty).valid; }, optional: canBeEmpty }; fields.push(registration); fields.push(confirmRegistration); }; this.gpsCoordinates = function(latitude, longitude, onValidation, required) { var latRegistration, longRegistration, latValid = false; latRegistration = { node: latitude, isValidFn: Y.scriptlibrary.ValidationHelpers.IsLatitude, onValidation: function(value) { latValid = value; if (onValidation) { onValidation(value); } }, optional: function() { return inputIsEmpty(longRegistration) && !required; } }; longRegistration = { node: longitude, isValidFn: Y.scriptlibrary.ValidationHelpers.IsLongitude, onValidation: function(value) { if (onValidation) { onValidation(value && latValid); } }, optional: function() { return inputIsEmpty(latRegistration); } }; fields.push(latRegistration); fields.push(longRegistration); }; /** * Validates a file input's file extension. * @param {DOMNode} node The node to validate the value of. * @param {array} validExtensions An array of valid file extensions. * @param {boolean} optional True if the input can be empty. */ this.fileExtension = function(node, validExtensions, optional) { var validExtensionHashTable = {}; for (var i = validExtensions.length - 1; i >= 0; i--) { validExtensionHashTable[validExtensions[i]] = true; } var registration = { node: node, isValidFn: function(fileName) { var extension = fileName.substring(fileName.lastIndexOf(".") + 1); return (true === validExtensionHashTable[extension.toLowerCase()]); }, optional: optional }; fields.push(registration); }; this.attachErrorMessage = function(node, errorMessageSelector) { var errorNode = Y.one(errorMessageSelector); this.setFieldOption(node, "onValidation", function(valid) { if (valid) { errorNode.hide(); } else { errorNode.show(); } }); }; this.remove = function(node) { fields = fields.filter(function(field) { var fieldNode = getNode(field); return Y.one(node).getDOMNode() !== Y.one(fieldNode).getDOMNode(); }); }; Y.scriptlibrary = Y.scriptlibrary || {}; Y.scriptlibrary.Validator = Validator; }; Y.scriptlibrary = Y.scriptlibrary || {}; Y.scriptlibrary.Validator = Validator; }, '0.0.1', { requires: ["yui-base", "node", "event", "scriptlibrary.ValidationHelpers", "scriptlibrary.StringExtensions"] });YUI().add("arrangement.TributeDirectionsController", function (Y) { "use strict"; var attributeConfig = function() { return { view: { value: null }, request: { value: null }, contextPath: { value: null, validator: Y.Lang.isString }, eventId: { value: null }, eventSource: { value: null }, deliveryMode: {} }; }; var ErrorHandlers = Y.scriptlibrary.ErrorHandlers, Validator = Y.scriptlibrary.Validator, Request = Y.scriptlibrary.Request, DynamicWindow = Y.scriptlibrary.DynamicWindow; var TributeDirectionsController = function(parameters) { this.addAttrs(attributeConfig(), parameters); var me = this, configurationData = me.get("request").get("data"), container = Y.one(this.get("view").getContainer()), recipientInput = container.one(".recipient-input"), captchaImage = container.one(".captcha-image"), refreshButton = container.one(".refresh-captcha"), captchaInput = container.one(".guess"), sendButton = container.one(".send-directions"), directionsContainer = container.one(".tribute-directions-panel-container"), closeWindowButton = container.one(".close-window"), directionsSentPage = container.one(".directions-sent-container"), recipientError = container.one(".recipient-error"), sendDirectionsError = container.one(".send-directions-error"), contextPath = me.get("contextPath"), mode = me.get("deliveryMode"), captchaSequenceNumber = 0, audioCaptcha = new Y.scriptlibrary.input.AudioCaptcha({ resources: {}, resourceName: "DIRECTIONS", contextPath: contextPath, sequenceNumber: 0 }), validator = new Validator(); var sendDirections = function() { recipientError.hide(); if (!validator.isValid()) { recipientError.show(); return; } if (mode === "email") { sendDirectionsByEmail(); } else { sendDirectionsByText(); } }; var sendDirectionsByText = function() { var data = { eventId: me.get("eventId"), eventSource: me.get("eventSource"), phoneNumber: recipientInput.get("value") }; sendButton.set("disabled", true); new Request(contextPath + "/tribute/text-directions.html") .postJson(data) .then(Request.processCmsResult) .then(function() { sendButton.set("disabled", false); showNextPage(); }) .then(undefined, function(e) { sendButton.set("disabled", false); ErrorHandlers.handleErrorInNode(sendDirectionsError, "Unexpected error")(e); }); }; var sendDirectionsByEmail = function() { var emails = Y.Array.map(recipientInput.get("value").split(","), function(email) { return email.trim(); }); var data = { guess: captchaInput.get("value"), captchaSequenceNumber: captchaSequenceNumber, eventId: me.get("eventId"), eventSource: me.get("eventSource"), emails: emails }; new Request(contextPath + "/tribute/email-directions.html") .postJson(data) .then(Request.processCmsResult) .then(showNextPage) .then(undefined, ErrorHandlers.handleErrorInNode( sendDirectionsError, "Unexpected error", refreshCaptcha)); }; var showNextPage = function() { directionsContainer.hide(); directionsSentPage.show(); }; var refreshCaptcha = function(e) { e.preventDefault(); ++captchaSequenceNumber; var url = contextPath + "/captcha.html" + "?resourceName=" + configurationData.captchaResourceName + "&sequenceNumber=" + captchaSequenceNumber + "&rand=" + Math.random(); captchaImage.setAttribute("src", url); audioCaptcha.set("sequenceNumber", captchaSequenceNumber); }; var initialize = function() { container.show(); if (mode === "email") { directionsContainer.one(".captcha-row").show(); directionsContainer.one(".captcha-info").show(); validator.emailList(recipientInput); } else { validator.northAmericanPhone(recipientInput); directionsContainer.one(".captcha-row").hide(); directionsContainer.one(".captcha-info").hide(); } sendButton.on("click", sendDirections); refreshButton.on("click", refreshCaptcha); closeWindowButton.on("click", function() { container.hide() }); audioCaptcha.render(container.one(".captcha-row")); }; initialize(); }; Y.augment(TributeDirectionsController, Y.Attribute); Y.arrangement = Y.arrangement || {}; Y.arrangement.TributeDirectionsController = TributeDirectionsController; }); YUI().add("scriptlibrary.NodeExtensions", function (Y) { var parseToInt = function(dimension) { if (!dimension) { return 0; } return parseInt(dimension.replace("px", ""), 10); }; /** * Gets the width of the item on the screen. */ Y.Node.prototype.getClientWidth = function () { var element = this.getDOMNode(); return element.clientWidth || element.scrollWidth; }; /** * Gets the height of the item on the screen. */ Y.Node.prototype.getClientHeight = function () { var element = this.getDOMNode(); return element.clientHeight || element.scrollHeight; }; /** * Parses a style with a value in pixels and returns the numeric property. * @param {String} styleProperty Name of the style property e.g. paddingLeft, paddingTop, marginLeft. * @returns {Number} Numerical value of that style property. */ Y.Node.prototype.parseStylePx = function(styleProperty) { var computed = this.getComputedStyle(styleProperty); return computed ? parseInt(computed.replace("px", ""), 10) : 0; }; /** * Gets the horizontal spacing. * @returns {Number} Horizontal spacing in pixels. */ Y.Node.prototype.getHorizontalSpacing = function() { var padding = this.parseStylePx("paddingLeft") + this.parseStylePx("paddingRight"), margin = this.parseStylePx("marginLeft") + this.parseStylePx("marginRight"), border = this.parseStylePx("borderWidth") / 2; return padding + margin + border; }; /** * Gets the vertical spacing. * @returns {Number} Vertical spacing in pixels. */ Y.Node.prototype.getVerticalSpacing = function() { var padding = this.parseStylePx("paddingTop") + this.parseStylePx("paddingBottom"), margin = this.parseStylePx("marginTop") + this.parseStylePx("marginBottom"), border = this.parseStylePx("borderWidth") / 2; return padding + margin + border; }; });var allWindows = []; YUI().add("scriptlibrary.DynamicWindow", function (Y) { var elSetter = function (val) { if (Y.Lang.isString(val)){ return Y.one(val); } return val; }, elValidator = function (val) {return Y.Lang.isObject(val) || Y.Lang.isString(val);}; function getAttributeConfig() { return { id: { value: null, validator: Y.Lang.isString }, containerCss: { value: null, validator: Y.Lang.isString }, viewContainer: { value: null, validator: elValidator, setter: elSetter }, top: { value: 100, validator: Y.Lang.isNumber }, width: { value: null, validator: Y.Lang.isNumber }, height: { value: null, validator: Y.Lang.isNumber }, zIndex: { value: null, validator: Y.Lang.isNumber }, maskZIndex: { value: null, validator: Y.Lang.isNumber }, hideCloseButton: { value: false, validator: Y.Lang.isBoolean }, maskCss: { value: null, validator: Y.Lang.isString }, container: { value: null }, positionManually: { value: false } }; } function DynamicWindow(config){ this.addAttrs(getAttributeConfig(), config); initializeMembers.call(this); allWindows.push(this); } DynamicWindow.CloseAllWindows = function () { for (var i = 0; i < allWindows.length; ++i) { allWindows[i].hide(); } }; function updateEmbeddedContent() { for (var i = 0; i < allWindows.length; ++i) { if (allWindows[i].isVisible()) { return; } } showEmbeddedContent(); } function hideEmbeddedContent() { Y.all("iframe.video-display").each(function(node) { node.getDOMNode().style.visibility = "hidden"; }); } function showEmbeddedContent() { Y.all("iframe.video-display").each(function(node) { node.getDOMNode().style.visibility = ""; }); } var initializeMembers = function() { var me = this, onHideHandlers = [], container, closeButton, content, mask, positionWindow = function () { var viewPortWidth = document.body.clientWidth || document.body.scrollWidth, left = Math.round((viewPortWidth - me.get("width"))/2); container.style.left = left + "px"; if (!me.get("positionManually")) { updateY(); } }, setInitialYPosition = function() { var doc = document.documentElement, body = document.body, scrollTop = (doc && doc.scrollTop || body && body.scrollTop || 0), top = (doc && doc.scrollTop || body && body.scrollTop || 0); container.style.top = (top + scrollTop) + "px"; }, showMask = function () { updateMask(); mask.style.display = ""; }, updateMask = function () { var winHeight = Y.one("body").get("winHeight"), bodyHeight = Y.one("body").get("docHeight"), maskHeight = Math.max(winHeight, bodyHeight); mask.style.height = maskHeight + "px"; }, updateY = function() { var winHeight = Y.one("body").get("winHeight"), containerHeight = Y.one(container).getClientHeight(), doc = document.documentElement, body = document.body, scrollTop = (doc && doc.scrollTop || body && body.scrollTop || 0), top = me.get("top"); if (winHeight < containerHeight) { return; } else if (winHeight < (containerHeight + top * 2)) { top = Math.round((winHeight - containerHeight)/2); top = Math.max(0, top); } container.style.top = (top + scrollTop) + "px"; }, initializeDom = function () { container = document.createElement("DIV"); var zIndex = me.get("zIndex"), id = me.get("id"), height = me.get("height"), width = me.get("width"), containerCss = me.get("containerCss"); if (width) { container.style.width = width + "px"; } if (id) { container.id = id; } if (containerCss) { container.className = "popup-container " + containerCss; } else { container.className = "popup-container"; } if (zIndex) { container.style.zIndex = zIndex; } if (height) { container.style.height = height + "px"; } if (!me.get("hideCloseButton")) { closeButton = document.createElement("DIV"); closeButton.className = "close-button"; closeButton.innerHTML = "X"; container.appendChild(closeButton); } content = document.createElement("DIV"); content.className = "content"; container.appendChild(content); mask = document.createElement("DIV"); var maskCss = "mask", maskCssSuffix = me.get("maskCss"); if (maskCssSuffix) { maskCss += " " + maskCssSuffix; } mask.className = maskCss; if (me.get("maskZIndex")) { mask.style.zIndex = me.get("maskZIndex"); } var viewContainer = me.get("viewContainer"); viewContainer.appendChild(container); viewContainer.appendChild(mask); if (closeButton) { Y.on("click", function () { me.hide(); }, closeButton); } }, executeScriptBlocks = function (request) { var scriptBlocks = Y.one(container).all("script"), executeBlock = function (node){ if (node.getAttribute("type") === "text/javascript") { var fn = eval(node.getHTML()); fn(me, request); } }; scriptBlocks.each(executeBlock); }; Y.one("window").on("resize", function () { if (mask && mask.style.display == ""){ updateMask(); if (!me.get("positionManually")) { positionWindow(); } } }); Y.one("window").on("scroll", function() { if (container && container.style.display !== "none" && !me.get("positionManually")) { updateY(); } }); this.onHide = function (fn, scope, clearOnHide){ onHideHandlers.push({ fn: fn, scope: scope, clearOnHide: clearOnHide }); }; this.isVisible = function() { return container && container.style.display !== "none"; }; this.hide = function () { if (!container) { return; } Y.one(container).remove(true); Y.one(mask).remove(true); container = null; content = null; mask = null; var newHideHandlers = []; for (var i = 0; i < onHideHandlers.length; ++i){ onHideHandlers[i].fn.call(onHideHandlers[i].scope); if (!onHideHandlers[i].clearOnHide){ newHideHandlers.push(onHideHandlers[i]); } } onHideHandlers = newHideHandlers; updateEmbeddedContent(); }; this.setWidth = function(width) { this.set("width", width); container.style.width = width + "px"; if (!me.get("positionManually")) { positionWindow(); } }, this.updateWindowPosition = function() { updateY(); }; this.listenTo = function (dataRequest) { dataRequest.addHtmlHandler(function (html) {this.render(html, dataRequest);}, this); // NOTE: This could cause an issue if there are multiple windows listening to the data requests. As long // as only one window is open at a time, this should not be an issue. this.onHide(function () { dataRequest.resetUrl(); }, this); }; this.getContainer = function() { return container; }; this.getContent = function() { return content; }; /** * Renders the given html in the popup. * @param {string} popupHtml Html to render in the popup. * @param {object} data Data. * @param {object[]} [resources] Additional resources to include in the data. * @param {function} [scriptFn] Function to call afterwards. */ this.renderTemplate = function(popupHtml, data, resources, scriptFn) { var templateHtml = '' + '' + '
', template = new Y.Template(Y.Handlebars); data.id = data.id || ""; Y.Array.each(resources || [], function(resource) { Y.Object.each(resource, function(value, key) { if (!data[key]) { data[key] = value; } }); }); this.render(template.render(templateHtml, data)); if (scriptFn) { scriptFn(me); } }; this.render = function(html, request){ if (!content) { initializeDom(); } Y.one(content).empty(); content.innerHTML = html; var redirectElement = Y.one(content).one("*[data-dynamic-window-redirect]"); if (redirectElement) { window.location = redirectElement.getAttribute("data-dynamic-window-redirect"); return; } if (!me.get("positionManually")) { setInitialYPosition(); positionWindow(); } showMask(); if (request) { executeScriptBlocks(request); } hideEmbeddedContent(); container.style.display = ""; }; }; var waitMessagePopup, confirmationPopup; /** * Shows a wait message in a modal popup. * @param {String} title Please wait message title. * @param {String} message Message to display. */ DynamicWindow.ShowWaitMessage = function (title, message) { if (!waitMessagePopup) { waitMessagePopup = new DynamicWindow({ id: "global-wait-popup", viewContainer: "#popup-container", width: 400, hideCloseButton: true, zIndex: 100000, maskZIndex: 99999 }); } var html = "
" + title + "
"; html += "
" + message + "
"; waitMessagePopup.render(html); }; /** * Hides the wait message popup. */ DynamicWindow.HideWaitMessage = function () { if (waitMessagePopup) { waitMessagePopup.hide(); } }; /** * Shows a confirmation message * @param {string} message */ DynamicWindow.ShowCompletedMessage = function (message, buttonLabel, zIndex) { DynamicWindow.HideWaitMessage(); if (!confirmationPopup) { var maskZIndex = zIndex ? zIndex - 1 : null; confirmationPopup = new DynamicWindow({ id: "global-complete-popup", viewContainer: "#popup-container", width: 400, zIndex: zIndex, maskZIndex: maskZIndex }); } var html = "
 
"; html += "
" + message + "
"; confirmationPopup.render(html); Y.one("#global-complete-ok").on("click", function () { confirmationPopup.hide(); }); }; /** * Gets a prompt displaying the given options. * @param {String} message Message to display. * @param {object[]} options List of option objects which have the format { label: "My Label", fn: function() {} }. * @param {type} [zIndex] z-index of window. * @param {int} [width] Window width. * @param {string} [id] Dynamic Window id. * @param {boolean} [positionManually=false] true if a position should not be automatically set. * @returns {_L2.DynamicWindow} Dynamic window created. */ DynamicWindow.promptWithOptions = function(message, options, zIndex, width, id, positionManually) { var html = "
 
"; var prompt = new DynamicWindow({ id: id || "global-prompt-popup", viewContainer: "#popup-container", width: width || 400, hideCloseButton: true, zIndex: zIndex, maskZIndex: zIndex - 1, positionManually: positionManually || false }); html += "
"; prompt.render(html); var container = Y.one(prompt.getContainer()), optionContainer = container.one(".prompt-with-options-container"); container.one(".prompt-message").set("text", message); for (var i = 0; i < options.length; ++i) { var button = Y.Node.create(""); button.set("value", options[i].label); if (options[i].fn) { button.on("click", options[i].fn); } button.on("click", function() { prompt.hide(); }); optionContainer.appendChild(button); } return prompt; }; DynamicWindow.ShowPrompt = function(message, option1Label, option2Label, option1Fn, option2Fn) { var option1Id = "option1-button", option2Id = "option2-button", html = "
 
", prompt = new DynamicWindow({ id: "global-prompt-popup", viewContainer: "#popup-container", width: 400, hideCloseButton: true }); html += "
" + message + "
"; prompt.render(html); Y.one("#" + option1Id).on("click", function () { option1Fn(); prompt.hide(); }); Y.one("#" + option2Id).on("click", function () { option2Fn(); prompt.hide(); }); return prompt; }; Y.augment(DynamicWindow, Y.Attribute); Y.scriptlibrary = Y.scriptlibrary || {}; Y.scriptlibrary.DynamicWindow = DynamicWindow; }); /** * Contains re-usable logic for captcha inputs. */ YUI().add("scriptlibrary.input.CaptchaInput", function(Y) { "use strict"; var attributeConfig = function() { return { container: { setter: Y.one.bind(Y) }, resources: { value: null, setter: function(resources) { return resources && resources.CaptchaInput ? resources.CaptchaInput : resources; } }, resourceName: {}, contextPath: {}, refreshButtonPosition: { value: "after-image" } }; }; /** * Initializes the captcha input * @param {Object} parameters Contains parameters that adhere to the attribute config above. */ function CaptchaInput(parameters) { this.addAttrs(attributeConfig(), parameters); var sequenceNumber = 0, container = this.get("container"), resources = this.get("resources"), contextPath = this.get("contextPath"), resourceName = this.get("resourceName"), image = Y.Node.create(""), guess = Y.Node.create(''), refreshButton = Y.Node.create(''), audioCaptcha = new Y.scriptlibrary.input.AudioCaptcha({ resources: resources, resourceName: resourceName, contextPath: contextPath, sequenceNumber: sequenceNumber }), refreshButtonPosition = this.get("refreshButtonPosition"); var initializeMarkup = function() { var instructions = Y.Node.create("
"), imageContainer = Y.Node.create("
"), guessContainer = Y.Node.create("
"), template = new Y.Template(Y.Handlebars); instructions.set("text", resources.captchaInstructions); refreshButton.setAttribute("href", "#"); refreshButton.setAttribute("onclick", "return false;"); refreshButton.set("text", resources.refreshButtonLabel); var url = contextPath + "/captcha.html" + "?resourceName=" + resourceName + "&rand=" + Math.random(); image.setAttribute("src", url); container.appendChild(instructions); container.appendChild(imageContainer); container.appendChild(guessContainer); imageContainer.appendChild(image); audioCaptcha.render(imageContainer); if (refreshButtonPosition === "after-image") { imageContainer.appendChild(refreshButton); } imageContainer.appendChild("
"); guessContainer.appendChild(guess); if (refreshButtonPosition === "after-guess") { guessContainer.appendChild(refreshButton); } guessContainer.appendChild("
"); container.addClass("captcha-container"); }; /** * Sets captcha to text mode */ this.textMode = function() { audioCaptcha.resetToTextMode(); }; /** * Refreshes the captcha. */ this.refresh = function() { ++sequenceNumber; var url = contextPath + "/captcha.html" + "?resourceName=" + resourceName + "&sequenceNumber=" + sequenceNumber + "&rand=" + Math.random(); image.setAttribute("src", url); audioCaptcha.set("sequenceNumber", sequenceNumber); }; /** * Clears the captcha. */ this.clear = function() { guess.set("value", ""); }; /** * Gets the user guess. * @returns {String} Current guess value. */ this.getGuess = function() { return guess.get("value"); }; /** * Adds the guess to the validator. * @param {type} validator Validator to register the guess with. */ this.addToValidator = function(validator) { validator.nonEmptyField(guess); }; /** * Highlights the captcha as an error. */ this.highlightAsError = function() { if (!container.hasClass("invalid-field")) { container.addClass("invalid-field"); } }; /** * Clears the error. */ this.clearError = function() { container.removeClass("invalid-field"); }; /** * Gets the current sequence number. * @returns {Number} Current sequence number. */ this.getSequenceNumber = function() { return sequenceNumber; }; initializeMarkup(); refreshButton.on("click", this.refresh, this); } Y.augment(CaptchaInput, Y.Attribute); Y.scriptlibrary = Y.scriptlibrary || {}; Y.scriptlibrary.input = Y.scriptlibrary.input || {}; Y.scriptlibrary.input.CaptchaInput = CaptchaInput; }); YUI().add("arrangement.TributeRsvpController", function(Y) { "use strict"; function attributeConfig() { return { contextPath: {}, resources: {}, generalResources: {}, deathRecordId: {}, scriptlibraryResources: {} } } // @formatter:off var MESSAGE_TEMPLATE = '
' + '
{{text}}
' + '
' + '' + '
' + '
'; var RSVP_TEMPLATE = '
' + '' + '

{{title}}

' + '
' + '
' + '' + '' + '
' + '
' + '' + '' + '
' + '
' + '' + '' + '
' + '
' + '' + '' + '
' + '' + '' + '
' + '' + '' + '
' + '
' + '' + '' + '
' + '
'; // @formatter:on var Request = Y.scriptlibrary.Request, DynamicWindow = Y.scriptlibrary.DynamicWindow, CaptchaInput = Y.scriptlibrary.input.CaptchaInput, Validator = Y.scriptlibrary.Validator, ValidationHelpers = Y.scriptlibrary.ValidationHelpers; function TributeRsvpController(parameters) { this.addAttrs(attributeConfig(), parameters); var me = this, resources = me.get("resources"), generalResources = me.get("generalResources"), scriptlibraryResources = me.get("scriptlibraryResources"), contextPath = me.get("contextPath"), deathRecordId = me.get("deathRecordId"), rsvpPopup, messagePopup, validator, maskClickEventSubscription, captchaInput; function any(predicate, array) { for (var i = 0; i < array.length; ++i) { if (predicate(array[i])) { return true; } } return false; } function inputValues() { return { name: rsvpPopupContainer().one("#tribute-rsvp-popup-name").get("value"), email: rsvpPopupContainer().one("#tribute-rsvp-popup-email").get("value"), phone: rsvpPopupContainer().one("#tribute-rsvp-popup-phone").get("value"), additionalGuests: rsvpPopupContainer().one("#tribute-rsvp-popup-guests").get("value"), notes: rsvpPopupContainer().one("#tribute-rsvp-popup-notes").get("value") } } function handleSaveClick(eventId, eventProvider) { hideError(); var inputs = inputValues(); if (!captchaInput) { // The current CAPTCHA input is ugly. Maybe: https://codepen.io/jabraxion/pen/qBbGKeR . if (!validator.isValid()) { return; } if (any(ValidationHelpers.ContainsEmoji, Y.Object.values(inputs))) { showError(resources.emojiErrorMessage); return; } var captchaContainer = rsvpPopupContainer().one("#tribute-rsvp-captcha-container"); captchaContainer.show(); captchaInput = new CaptchaInput({ container: captchaContainer, resources: scriptlibraryResources, resourceName: "captcha-" + deathRecordId + "-" + eventId + "-" + (eventProvider || "record"), contextPath: contextPath, refreshButtonPosition: "after-guess" }); rsvpPopupContainer().one("#tribute-rsvp-popup-vitals").hide(); return; } showWait(); new Request(contextPath + "/tribute-ajax/rsvp") .postJson({ deathRecordId: deathRecordId, eventId: eventId, eventProvider: eventProvider, name: inputs.name, email: inputs.email, phone: inputs.phone, additionalGuests: parseInt(inputs.additionalGuests), notes: inputs.notes, captchaGuess: captchaInput.getGuess(), captchaSequenceNumber: captchaInput.getSequenceNumber() }) .then(Request.processCmsResult) .then(function() { hideWait(); rsvpPopup.hide(); messagePopup.renderTemplate(MESSAGE_TEMPLATE, { text: resources.rsvpSent, ok: generalResources.okayButton, }); Y.one(messagePopup.getContainer()) .one(".tribute-rsvp-message-popup-ok-button") .on("click", function() { messagePopup.hide(); }); center(messagePopupContainer); }) .then(undefined, function(e) { console.error(e); hideWait(); var message = typeof e === 'string' ? e : generalResources.errorNoSupportMessage; showError(message); }); } function renderRsvpModal(eventId, eventProvider, eventName) { rsvpPopup.renderTemplate(RSVP_TEMPLATE, { title: resources.popupTitle.replace("${eventName}", eventName), name: generalResources.nameLabel, email: generalResources.emailLabel, phone: generalResources.phoneLabel, additionalGuests: resources.additionalGuests, rsvp: resources.rsvp, cancel: generalResources.cancelButton, notes: resources.notes }); validator = new Validator(); validator.nonEmptyField(rsvpPopupContainer().one("#tribute-rsvp-popup-name")) validator.emailField(rsvpPopupContainer().one("#tribute-rsvp-popup-email")); validator.phoneFieldAnyCountry(rsvpPopupContainer().one("#tribute-rsvp-popup-phone")); validator.integer(rsvpPopupContainer().one("#tribute-rsvp-popup-guests")); captchaInput = null; rsvpPopupContainer() .one("#tribute-rsvp-cancel-button") .on("click", rsvpPopup.hide.bind(rsvpPopup)); rsvpPopupContainer() .one("#tribute-rsvp-save-button") .on("click", handleSaveClick.bind(me, eventId, eventProvider)); addClickOutsidePopupHandler(rsvpPopup.hide.bind(rsvpPopup)); center(rsvpPopupContainer); } function showWait() { rsvpPopupContainer().one("#tribute-rsvp-wait-overlay").show(); } function hideWait() { rsvpPopupContainer().one("#tribute-rsvp-wait-overlay").hide(); } function rsvpPopupContainer() { return Y.one(rsvpPopup.getContainer()); } function messagePopupContainer() { return Y.one(messagePopup.getContainer()); } function showError(error) { var errorMessageNode = rsvpPopupContainer().one("#tribute-rsvp-error-message"); errorMessageNode.set("text", error); errorMessageNode.show(); } function hideError() { rsvpPopupContainer().one("#tribute-rsvp-error-message").hide(); } function init() { rsvpPopup = new DynamicWindow({ id: "tribute-rsvp-popup-window", viewContainer: "#popup-container", positionManually: true }); messagePopup = new DynamicWindow({ id: "tribute-rsvp-message-popup-window", viewContainer: "#popup-container", positionManually: true }); Y.all("div[data-tribute-event-id]").each(function(event) { var rsvpLink = event.one(".tribute-event-rsvp-link"); if (!rsvpLink) { return; } var id = parseInt(event.getAttribute("data-tribute-event-id")); var provider = event.getAttribute("data-tribute-event-provider"); var name = event.getAttribute("data-tribute-event-name"); rsvpLink.on("click", function(e) { e.stopPropagation(); e.preventDefault(); renderRsvpModal(id, provider, name) }); }); Y.on("windowresize", center.bind(undefined, rsvpPopupContainer)); Y.on("windowresize", center.bind(undefined, messagePopupContainer)); } function center(containerFn) { var container = containerFn(); if (!container) { return; } var ph = container.get("clientHeight"); var wh = Y.one("body").get("winHeight"); container.setStyle("top", (wh / 2 - ph / 2) + "px"); } function addClickOutsidePopupHandler(handler) { if (maskClickEventSubscription) { maskClickEventSubscription.detach(); } maskClickEventSubscription = Y.one("#popup-container > .mask").on("click", handler); rsvpPopupContainer().on("click", function(e) { if (e.target === rsvpPopupContainer()) { handler(); } }); } init(); } Y.augment(TributeRsvpController, Y.Attribute) Y.arrangement = Y.arrangement || {}; Y.arrangement.TributeRsvpController = TributeRsvpController; }); YUI().add('scriptlibrary.yuiport-slideshow', function(Y) { /** * A simple YUI3 slideshow kit inspired by the jQuery Cycle plugin. * @module gallery-yui-slideshow * @requires widget, transition, event-mouseenter * @author Josh Lizarraga */ /** * A simple YUI3 slideshow kit inspired by the jQuery Cycle plugin. * @class Slideshow * @constructor * @param {Object} config Widget configuration object. */ var Slideshow = function(config){ Slideshow.superclass.constructor.apply(this, arguments); }; /** * @property NAME * @type String * @default Slideshow */ Slideshow.NAME = 'Slideshow'; /* ! Slideshow.PRESETS */ Slideshow.PRESETS = { /** * Incoming slide fades in. * Outgoing slide fades out. * @property PRESETS.fade * @type Preset Slide Transition */ fade: { slideIn: { before: { opacity: 0 }, transition: { opacity: 1 } }, slideOut: { before: { opacity: 1 }, transition: { opacity: 0 } } }, /** * Both slides slide from top to bottom. * @property PRESETS.slideDown * @type Preset Slide Transition */ slideDown: { slideIn: { before: { bottom: 'cH' }, transition: { bottom: '0px' } }, slideOut: { before: { bottom: '0px' }, transition: { bottom: '-cH' } } }, /** * Both slides slide from right to left. * @property PRESETS.slideLeft * @type Preset Slide Transition */ slideLeft: { slideIn: { before: { right: '-cW' }, transition: { right: '0px' } }, slideOut: { before: { right: '0px' }, transition: { right: 'cW' } } }, /** * Both slides slide from bottom to top. * @property PRESETS.slideUp * @type Preset Slide Transition */ slideUp: { slideIn: { before: { top: 'cH' }, transition: { top: '0px' } }, slideOut: { before: { top: '0px' }, transition: { top: '-cH' } } }, /** * Both slides slide from left to right. * @property PRESETS.slideRight * @type Preset Slide Transition */ slideRight: { slideIn: { before: { left: '-cW' }, transition: { left: '0px' } }, slideOut: { before: { left: '0px' }, transition: { left: 'cW' } } } }; /* ! Attributes */ Slideshow.ATTRS = { /** * Slides NodeList instance. * @attribute slides * @type NodeList * @default null */ slides: { value: null }, /** * Current slide index. * @attribute currentIndex * @type Integer * @default 0 */ currentIndex: { value: 0 }, /** * Previous slide button Node/NodeList/selector string. * @attribute previousButton * @type Mixed * @default null */ previousButton: { value: null }, /** * Next slide button Node/NodeList/selector string. * @attribute nextButton * @type Mixed * @default null */ nextButton: { value: null }, /** * Pause button Node/NodeList/selector string. * @attribute pauseButton * @type Mixed * @default null */ pauseButton: { value: null }, /** * Play button Node/NodeList/selector string. * @attribute playButton * @type Mixed * @default null */ playButton: { value: null }, /** * NodeList/selector string for the elements that correspond to slides. * This setting works best with list items. * @attribute pages * @type Mixed * @default null */ pages: { value: null }, /** * Time interval(s) between slide transitions in seconds. * @attribute interval * @type Float|Array * @default 4 */ interval: { value: 4 }, /** * Set to false to disable autoplay. * @attribute autoplay * @type Boolean * @default true */ autoplay: { value: true }, /** * Set to false to continue autoplay when the user changes slides. * @attribute pauseOnChange * @type Boolean * @default true */ pauseOnChange: { value: true }, /** * Set to false to continue playing when the user hovers over the slideshow. * @attribute pauseOnHover * @type Boolean * @default true */ pauseOnHover: { value: true }, /** * Default duration for slide transitions. * @attribute duration * @type Float * @default null */ duration: { value: 0.5 }, /** * Default slide transition easing. * @attribute easing * @type String * @default ease-out */ easing: { value: 'ease-out' }, /** * Default slide transition. * @attribute transition * @type Slide Transition * @default Slideshow.PRESETS.fade */ transition: { value: Slideshow.PRESETS.fade }, /** * Default transIn (incoming slide) "before" settings. Sets visibility:visible and z-index:1. * @attribute transInBefore * @type Object * @default (See source) */ transInBefore: { value: { visibility: 'visible', zIndex: 1 } }, /** * Default transIn (incoming slide) "after" settings. Sets z-index:2. * @attribute transInAfter * @type Object * @default (See source) */ transInAfter: { value: { zIndex: 2 } }, /** * Default transOut (outgoing slide) "before" settings. Sets z-index:2. * @attribute transOutBefore * @type Object * @default (See source) */ transOutBefore: { value: { zIndex: 2 } }, /** * Default transOut (outgoing slide) "after" settings. Sets visibility:hidden and z-index:-30000. * @attribute transOutAfter * @type Object * @default (See source) */ transOutAfter: { value: { visibility: 'hidden', zIndex: -30000 } }, /** * The ID returned by autoplay's setTimeout call. * @attribute _timeoutID * @protected * @type Integer * @default null */ _timeoutID: { value: null }, /** * Paused flag. * @attribute _isPaused * @protected * @type Boolean * @default true */ _isPaused: { value: true }, /** * Hover paused flag. * @attribute _isHoverPaused * @protected * @type Boolean * @default false */ _isHoverPaused: { value: false }, /** * Outgoing slide Node. * @attribute _outgoingSlide * @protected * @type Node Instance * @default null */ _outgoingSlide: { value: null }, /** * Incoming slide Node. * @attribute _incomingSlide * @protected * @type Node Instance * @default null */ _incomingSlide: { value: null } }; Y.extend(Slideshow, Y.Widget, { /* ! Public Methods */ /** * Fires "slideshow:next" event. * This method is chainable. * @method next */ next: function(){ this.fire('next'); return this; }, /** * Fires "slideshow:pause" event. * This method is chainable. * @method pause */ pause: function(){ this.fire('pause'); return this; }, /** * Fires "slideshow:play" event. * This method is chainable. * @method play */ play: function(){ this.fire('play'); return this; }, /** * Fires "slideshow:previous" event. * This method is chainable. * @method previous */ previous: function(){ this.fire('previous'); return this; }, /** * Adjusts the currentIndex and fires the "slideshow:slide" event. * This method is chainable. * @method slide * @param $which {Mixed} The slide to advance to. Can be a slide index, "next", or "previous". */ slide: function($which){ var $currentIndex = this.get('currentIndex'), $slides = this.get('slides'); this.set('_outgoingSlide', $slides.item($currentIndex)); switch( $which ){ case 'next': if( $currentIndex + 1 < $slides.size() ){ $currentIndex++; } else { $currentIndex = 0; } break; case 'previous': if( $currentIndex - 1 > -1 ){ $currentIndex--; } else { $currentIndex = $slides.size() - 1; } break; default: if( $which == $currentIndex ){ return false; } else { $currentIndex = parseInt( $which, 10 ); } break; } this.set('currentIndex', $currentIndex); this.set('_incomingSlide', $slides.item($currentIndex)); this.fire('slide'); return this; }, /* ! Protected Methods */ /** * Checks CSS values for shortcut keywords. * @method _checkCSSValue * @protected * @param {Node} $node The Node to use if shortcut keywords are found. * @param {Mixed} $value The CSS value to check. * @return {Mixed} The parsed CSS value. */ _checkCSSValue: function($node, $value){ switch( $value ){ case 'cW': case 'containerWidth': return parseInt(this.get('contentBox').get('offsetWidth'), 10) + 'px'; case '-cW': case '-containerWidth': return (parseInt(this.get('contentBox').get('offsetWidth'), 10) * -1) + 'px'; case 'cH': case 'containerHeight': return parseInt(this.get('contentBox').get('offsetHeight'), 10) + 'px'; case '-cH': case '-containerHeight': return (parseInt(this.get('contentBox').get('offsetHeight'), 10) * -1) + 'px'; case 'sW': case 'slideWidth': return parseInt($node.get('offsetWidth'), 10) + 'px'; case '-sW': case '-slideWidth': return (parseInt($node.get('offsetWidth'), 10) * -1) + 'px'; case 'sH': case 'slideHeight': return parseInt($node.get('offsetHeight'), 10) + 'px'; case '-sH': case '-slideHeight': return (parseInt($node.get('offsetHeight'), 10) * -1) + 'px'; default: return $value; } }, /** * Sanitizes transition values with _checkCSSValue. * @method _checkTransitionValues * @protected * @param {Node} $node The Node to use if shortcut keywords are found. * @param {Object} $transition The transition to check. * @return {Mixed} The sanitized transition. */ _checkTransitionValues: function($node, $transition){ var $sanitized = {}, i; for( i in $transition ){ if( i == 'duration' || i == 'easing' || i == 'delay' ){ $sanitized[i] = $transition[i]; } else { $sanitized[i] = this._checkCSSValue($node, $transition[i]); } } return $sanitized; }, /** * Handles mouseenter on the contentBox. * @method _handleMouseenter * @protected * @param {Event} e The Event object. */ _handleMouseenter: function(e){ if( !this.get('_isPaused') && !this.get('_isHoverPaused') ){ this.fire('hoverpause'); } }, /** * Handles mouseleave on the contentBox. * @method _handleMouseleave * @protected * @param {Event} e The Event object. */ _handleMouseleave: function(e){ if( this.get('_isHoverPaused') ){ this.fire('hoverplay'); } }, /** * Handles clicks on pagination elements. * @method _handlePageClick * @protected * @param {Event} e The Event object. */ _handlePageClick: function(e){ if( this.get('pauseOnChange') ){ this.pause(); } e.target = e.target.ancestor( '.' + this.getClassName('page') ); this.slide( this.get('pages').indexOf(e.target) ); }, /** * Pauses the slideshow momentarily if not already paused. * @event _hoverpause * @param {Event} e The Event object. */ _hoverpause: function(e){ if( !this.get('_isPaused') && !this.get('_isHoverPaused') ){ this.pause(); this.set('_isHoverPaused', true); } }, /** * Plays the slideshow if it was hoverpaused. * @event _hoverplay * @param {Event} e The Event object. */ _hoverplay: function(e){ if( this.get('_isHoverPaused') ){ this.play(); } }, /** * Advances the slideshow by one slide. * @event _next * @param {Event} e The Event object. */ _next: function(e){ if( this.get('pauseOnChange') ){ this.pause(); } this.slide('next'); }, /** * Pauses the slideshow. * @event _pause * @param {Event} e The Event object. */ _pause: function(e){ if( !this.get('_isPaused') ){ window.clearInterval( this.get('_timeoutID') ); this.set('_isPaused', true); } }, /** * Plays the slideshow. * @event _play * @param {Event} e The Event object. */ _play: function(e){ if( this.get('_isPaused') ){ this.set('_isPaused', false); this.set('_isHoverPaused', false); this.fire('timeout'); } }, /** * Reverses the slideshow by one slide. * @event _previous * @param {Event} e The Event object. */ _previous: function(e){ if( this.get('pauseOnChange') ){ this.pause(); } this.slide('previous'); }, /** * Sets active class on current page Node. * @method _setActivePage * @protected */ _setActivePage: function(){ var $pages = this.get('pages'); if( !Y.Lang.isNull($pages) ){ $pages.removeClass( this.getClassName('active') ); $pages.item( this.get('currentIndex') ).addClass( this.getClassName('active') ); } }, /** * Sets styles after they are sanitized by _checkCSSValue. * @method _setStyles * @protected * @param {Node} $node The node to style. * @param {Object} $styles The styles to set. */ _setStyles: function($node, $styles){ for( var i in $styles ){ if( $styles.hasOwnProperty(i) ){ $node.setStyle(i, this._checkCSSValue($node, $styles[i])); } } }, /** * Performs the slide transition. * @event _slide * @param {Event} e The Event object. */ _slide: function(e){ var $that = this, $slideTransition = this.get('transition'), $outgoingSlide = this.get('_outgoingSlide'), $incomingSlide = this.get('_incomingSlide'); // Duraton and easing: if( Y.Lang.isUndefined($slideTransition.slideOut.transition.duration) ){ $slideTransition.slideOut.transition.duration = this.get('duration'); } if( Y.Lang.isUndefined($slideTransition.slideOut.transition.easing) ){ $slideTransition.slideOut.transition.easing = this.get('easing'); } if( Y.Lang.isUndefined($slideTransition.slideIn.transition.duration) ){ $slideTransition.slideIn.transition.duration = this.get('duration'); } if( Y.Lang.isUndefined($slideTransition.slideIn.transition.easing) ){ $slideTransition.slideIn.transition.easing = this.get('easing'); } // Default "before" styles: this._setStyles( $outgoingSlide, this.get('transOutBefore') ); this._setStyles( $incomingSlide, this.get('transInBefore') ); // Transition "before" styles: if( Y.Lang.isObject($slideTransition.slideOut.before) ){ this._setStyles( $outgoingSlide, $slideTransition.slideOut.before ); } if( Y.Lang.isObject($slideTransition.slideIn.before) ){ this._setStyles( $incomingSlide, $slideTransition.slideIn.before ); } // Transitions: $slideTransition.slideOut.transition = this._checkTransitionValues($outgoingSlide, $slideTransition.slideOut.transition); $slideTransition.slideIn.transition = this._checkTransitionValues($incomingSlide, $slideTransition.slideIn.transition); $outgoingSlide.transition($slideTransition.slideOut.transition, function(){ // Default "after" styles: $that._setStyles( $outgoingSlide, $that.get('transOutAfter') ); // Transition "after" styles: if( Y.Lang.isObject($slideTransition.slideOut.after) ){ $that._setStyles( $outgoingSlide, $slideTransition.slideOut.after ); } }); $incomingSlide.transition($slideTransition.slideIn.transition, function(){ // Default "after" styles: $that._setStyles( $incomingSlide, $that.get('transInAfter') ); // Transition "after" styles: if( Y.Lang.isObject($slideTransition.slideIn.after) ){ $that._setStyles( $incomingSlide, $slideTransition.slideIn.after ); } }); // Update active page: this._setActivePage(); }, /** * Calls setTimeout for autoplay. * @event _timeout * @param {Event} e The Event object. */ _timeout: function(e){ var $that = this, $interval = this.get('interval'); if( Y.Lang.isArray($interval) ){ $interval = $interval[ this.get('currentIndex') ]; } if( !this.get('_isPaused') && !this.get('_isHoverPaused') ){ this.set( '_timeoutID', window.setTimeout(function(){ $that.slide('next'); $that.fire('timeout'); }, parseInt( $interval * 1000, 10 )) ); } }, /* ! Lifecycle Methods */ /** * Binds event handlers and publishes custom events. * @method bindUI */ bindUI: function(){ var $buttons = ['previous', 'next', 'pause', 'play'], $pages = this.get('pages'), $target, i; // Buttons and Published Events: this.publish('timeout', { defaultFn: this._timeout }); this.publish('slide', { defaultFn: this._slide }); this.publish('hoverpause', { defaultFn: this._hoverpause }); this.publish('hoverplay', { defaultFn: this._hoverplay }); for( i = 0; i < $buttons.length; i++ ){ this.publish($buttons[i], { defaultFn: this['_' + $buttons[i]] }); $target = this.get( $buttons[i] + 'Button' ); if( !Y.Lang.isNull($target) ){ Y.on('click', this[$buttons[i]], $target, this); } } // Pages: if( !Y.Lang.isNull($pages) ){ $pages.on('click', this._handlePageClick, this); } // Hover: if( this.get('pauseOnHover') ){ this.get('contentBox').on('mouseenter', this._handleMouseenter, this); this.get('contentBox').on('mouseleave', this._handleMouseleave, this); } }, /** * Sets initial widget state. * @method syncUI */ syncUI: function(){ var $that = this, $pages = this.get('pages'), $count = 0; // Slides: this.get('slides').each(function( $node ){ $count++; if( $count > 1 ){ $node.setStyles( $that.get('transOutAfter') ); } }); // Pages: if( !Y.Lang.isNull($pages) ){ $pages.addClass( this.getClassName('page') ); } // Autoplay: if( this.get('autoplay') ){ this.play(); } }, /** * Initializes the Slideshow. * @method initializer */ initializer: function(){ // Slides: this.set('slides', this.get('contentBox').get('children')); // Pages: if( Y.Lang.isString(this.get('pages')) ){ this.set('pages', Y.all(this.get('pages'))); } } }); Y.Slideshow = Slideshow; }); YUI().add( "scriptlibrary.SlideShow", function (Y) { function SlideShow(container, interval) { initializeMembers.call(this, Y.one(container), interval); } var initializeMembers = function (container, interval) { var me = this, slideShow = new Y.Slideshow({autoPlay: false, srcNode: container, duration: 1}), goNext = function () { setTimeout( function () { slideShow.next(); goNext(); }, interval * 1000); }; this.render = function () { slideShow.render(); setTimeout(function () { slideShow.next(); goNext(); }, interval/2 * 1000); }; this.destroy = function () { slideShow.pause(); slideShow.disable(); }; if (!interval || interval < 0) { interval = 7; } }; Y.scriptlibrary = Y.scriptlibrary || {}; Y.scriptlibrary.SlideShow = SlideShow; });