/**
* This script track the outgoing links and record them as event in Analytics
* This script has ben inspired from:
* Stéphane Hamel - shamel@cardinalpath.com - http://immeria.net
// See http://code.google.com/p/gaddons/people/list for contributors
//
// Google Groups forum: http://groups.google.com/group/gaaddons
// Google Code repository: http://code.google.com/p/gaddons/
*
*/
var _gaq = _gaq || [];
var gaAddons = function () {
/**
* startListening: add a new listner for onclick event, handle Mozilla or IE methods
* @param {Object} obj HREF object to listen to
* @param {String} evnt event type (usually "click")
* @param {Object} func Function to call when evnt is triggered
*/
var startListening = function (obj, evnt, func) {
if (obj.addEventListener) {
obj.addEventListener(evnt, func, false);
} else if (obj.attachEvent) {
obj.attachEvent("on" + evnt, func);
}
};
/**
* trackDocument: make GA call when downloading one of detected file extension, use trackEvent() or trackPageView() methods
* @param {Object} evnt Object where the event happened
*/
var trackDocument = function (evnt) {
evnt = evnt || event;
var elmnt = evnt.srcElement || evnt.target;
if (elmnt) {
while (elmnt.tagName != "A") elmnt = elmnt.parentNode;
}
var pathname = ("/" + elmnt.pathname).replace(/\/\//, '');
_gaq.push(['_trackEvent', 'download', 'click', pathname]);
};
/**
* trackExternalLink: make GA call when clicking an outbound link, use trackEvent() or trackPageView() methods
* @param {Object} evnt Object where the event happened
*/
var trackExternalLink = function (evnt) {
evnt = evnt || event;
var elmnt = evnt.srcElement || evnt.target, url;
if (elmnt) {
while (elmnt.tagName != "A") elmnt = elmnt.parentNode;
if (/http/.test(elmnt.protocol)) {
url = elmnt.href.substr(elmnt.href.indexOf('//') + 2, Infinity);
_gaq.push(['_trackEvent', 'outbound', 'click', url]);
}
if (elmnt.protocol == "mailto:") {
_gaq.push(['_trackEvent', 'mailto', 'click', elmnt.href.replace(/mailto:/, "")]);
}
}
else {
if (/http/.test(this.protocol)) {
url = this.href.substr(this.href.indexOf('//') + 2, Infinity);
_gaq.push(['_trackEvent', 'outbound', 'click', url]);
}
if (this.protocol == "mailto:") {
_gaq.push(['_trackEvent', 'mailto', 'click', this.href.replace(/mailto:/, "")]);
}
}
};
/**
* Initialize gaAddons
*/
if (document.getElementsByTagName) {
var hrefs = document.getElementsByTagName('a');
for (var l = 0, m = hrefs.length; l < m; l++) {
if (/download\.php/i.test(hrefs[l].pathname)) {
startListening(hrefs[l], "click", trackDocument);
} else if (hrefs[l].hostname != location.hostname) {
startListening(hrefs[l], "click", trackExternalLink);
}
}
}
};
if (window.addEventListener) { // Standard
window.addEventListener('load', gaAddons, false);
} else if (window.attachEvent) { // old IE
window.attachEvent('onload', gaAddons);
}
/*! jQuery v1.9.0 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license */(function(e,t){"use strict";function n(e){var t=e.length,n=st.type(e);return st.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}function r(e){var t=Tt[e]={};return st.each(e.match(lt)||[],function(e,n){t[n]=!0}),t}function i(e,n,r,i){if(st.acceptData(e)){var o,a,s=st.expando,u="string"==typeof n,l=e.nodeType,c=l?st.cache:e,f=l?e[s]:e[s]&&s;if(f&&c[f]&&(i||c[f].data)||!u||r!==t)return f||(l?e[s]=f=K.pop()||st.guid++:f=s),c[f]||(c[f]={},l||(c[f].toJSON=st.noop)),("object"==typeof n||"function"==typeof n)&&(i?c[f]=st.extend(c[f],n):c[f].data=st.extend(c[f].data,n)),o=c[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[st.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[st.camelCase(n)])):a=o,a}}function o(e,t,n){if(st.acceptData(e)){var r,i,o,a=e.nodeType,u=a?st.cache:e,l=a?e[st.expando]:st.expando;if(u[l]){if(t&&(r=n?u[l]:u[l].data)){st.isArray(t)?t=t.concat(st.map(t,st.camelCase)):t in r?t=[t]:(t=st.camelCase(t),t=t in r?[t]:t.split(" "));for(i=0,o=t.length;o>i;i++)delete r[t[i]];if(!(n?s:st.isEmptyObject)(r))return}(n||(delete u[l].data,s(u[l])))&&(a?st.cleanData([e],!0):st.support.deleteExpando||u!=u.window?delete u[l]:u[l]=null)}}}function a(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(Nt,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:wt.test(r)?st.parseJSON(r):r}catch(o){}st.data(e,n,r)}else r=t}return r}function s(e){var t;for(t in e)if(("data"!==t||!st.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function u(){return!0}function l(){return!1}function c(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function f(e,t,n){if(t=t||0,st.isFunction(t))return st.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return st.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=st.grep(e,function(e){return 1===e.nodeType});if(Wt.test(t))return st.filter(t,r,!n);t=st.filter(t,r)}return st.grep(e,function(e){return st.inArray(e,t)>=0===n})}function p(e){var t=zt.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function d(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function h(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function g(e){var t=nn.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function m(e,t){for(var n,r=0;null!=(n=e[r]);r++)st._data(n,"globalEval",!t||st._data(t[r],"globalEval"))}function y(e,t){if(1===t.nodeType&&st.hasData(e)){var n,r,i,o=st._data(e),a=st._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)st.event.add(t,n,s[n][r])}a.data&&(a.data=st.extend({},a.data))}}function v(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!st.support.noCloneEvent&&t[st.expando]){r=st._data(t);for(i in r.events)st.removeEvent(t,i,r.handle);t.removeAttribute(st.expando)}"script"===n&&t.text!==e.text?(h(t).text=e.text,g(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),st.support.html5Clone&&e.innerHTML&&!st.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Zt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}function b(e,n){var r,i,o=0,a=e.getElementsByTagName!==t?e.getElementsByTagName(n||"*"):e.querySelectorAll!==t?e.querySelectorAll(n||"*"):t;if(!a)for(a=[],r=e.childNodes||e;null!=(i=r[o]);o++)!n||st.nodeName(i,n)?a.push(i):st.merge(a,b(i,n));return n===t||n&&st.nodeName(e,n)?st.merge([e],a):a}function x(e){Zt.test(e.type)&&(e.defaultChecked=e.checked)}function T(e,t){if(t in e)return t;for(var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Nn.length;i--;)if(t=Nn[i]+n,t in e)return t;return r}function w(e,t){return e=t||e,"none"===st.css(e,"display")||!st.contains(e.ownerDocument,e)}function N(e,t){for(var n,r=[],i=0,o=e.length;o>i;i++)n=e[i],n.style&&(r[i]=st._data(n,"olddisplay"),t?(r[i]||"none"!==n.style.display||(n.style.display=""),""===n.style.display&&w(n)&&(r[i]=st._data(n,"olddisplay",S(n.nodeName)))):r[i]||w(n)||st._data(n,"olddisplay",st.css(n,"display")));for(i=0;o>i;i++)n=e[i],n.style&&(t&&"none"!==n.style.display&&""!==n.style.display||(n.style.display=t?r[i]||"":"none"));return e}function C(e,t,n){var r=mn.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function k(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;4>o;o+=2)"margin"===n&&(a+=st.css(e,n+wn[o],!0,i)),r?("content"===n&&(a-=st.css(e,"padding"+wn[o],!0,i)),"margin"!==n&&(a-=st.css(e,"border"+wn[o]+"Width",!0,i))):(a+=st.css(e,"padding"+wn[o],!0,i),"padding"!==n&&(a+=st.css(e,"border"+wn[o]+"Width",!0,i)));return a}function E(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=ln(e),a=st.support.boxSizing&&"border-box"===st.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=un(e,t,o),(0>i||null==i)&&(i=e.style[t]),yn.test(i))return i;r=a&&(st.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+k(e,t,n||(a?"border":"content"),r,o)+"px"}function S(e){var t=V,n=bn[e];return n||(n=A(e,t),"none"!==n&&n||(cn=(cn||st("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(cn[0].contentWindow||cn[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=A(e,t),cn.detach()),bn[e]=n),n}function A(e,t){var n=st(t.createElement(e)).appendTo(t.body),r=st.css(n[0],"display");return n.remove(),r}function j(e,t,n,r){var i;if(st.isArray(t))st.each(t,function(t,i){n||kn.test(e)?r(e,i):j(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==st.type(t))r(e,t);else for(i in t)j(e+"["+i+"]",t[i],n,r)}function D(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(lt)||[];if(st.isFunction(n))for(;r=o[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function L(e,n,r,i){function o(u){var l;return a[u]=!0,st.each(e[u]||[],function(e,u){var c=u(n,r,i);return"string"!=typeof c||s||a[c]?s?!(l=c):t:(n.dataTypes.unshift(c),o(c),!1)}),l}var a={},s=e===$n;return o(n.dataTypes[0])||!a["*"]&&o("*")}function H(e,n){var r,i,o=st.ajaxSettings.flatOptions||{};for(r in n)n[r]!==t&&((o[r]?e:i||(i={}))[r]=n[r]);return i&&st.extend(!0,e,i),e}function M(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(o in c)o in r&&(n[c[o]]=r[o]);for(;"*"===l[0];)l.shift(),i===t&&(i=e.mimeType||n.getResponseHeader("Content-Type"));if(i)for(o in u)if(u[o]&&u[o].test(i)){l.unshift(o);break}if(l[0]in r)a=l[0];else{for(o in r){if(!l[0]||e.converters[o+" "+l[0]]){a=o;break}s||(s=o)}a=a||s}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function q(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(n in e.converters)a[n.toLowerCase()]=e.converters[n];for(;i=u[++s];)if("*"!==i){if("*"!==l&&l!==i){if(n=a[l+" "+i]||a["* "+i],!n)for(r in a)if(o=r.split(" "),o[1]===i&&(n=a[l+" "+o[0]]||a["* "+o[0]])){n===!0?n=a[r]:a[r]!==!0&&(i=o[0],u.splice(s--,0,i));break}if(n!==!0)if(n&&e["throws"])t=n(t);else try{t=n(t)}catch(c){return{state:"parsererror",error:n?c:"No conversion from "+l+" to "+i}}}l=i}return{state:"success",data:t}}function _(){try{return new e.XMLHttpRequest}catch(t){}}function F(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}function O(){return setTimeout(function(){Qn=t}),Qn=st.now()}function B(e,t){st.each(t,function(t,n){for(var r=(rr[t]||[]).concat(rr["*"]),i=0,o=r.length;o>i;i++)if(r[i].call(e,t,n))return})}function P(e,t,n){var r,i,o=0,a=nr.length,s=st.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;for(var t=Qn||O(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:st.extend({},t),opts:st.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Qn||O(),duration:n.duration,tweens:[],createTween:function(t,n){var r=st.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(R(c,l.opts.specialEasing);a>o;o++)if(r=nr[o].call(l,e,c,l.opts))return r;return B(l,c),st.isFunction(l.opts.start)&&l.opts.start.call(e,l),st.fx.timer(st.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function R(e,t){var n,r,i,o,a;for(n in e)if(r=st.camelCase(n),i=t[r],o=e[n],st.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=st.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function W(e,t,n){var r,i,o,a,s,u,l,c,f,p=this,d=e.style,h={},g=[],m=e.nodeType&&w(e);n.queue||(c=st._queueHooks(e,"fx"),null==c.unqueued&&(c.unqueued=0,f=c.empty.fire,c.empty.fire=function(){c.unqueued||f()}),c.unqueued++,p.always(function(){p.always(function(){c.unqueued--,st.queue(e,"fx").length||c.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],"inline"===st.css(e,"display")&&"none"===st.css(e,"float")&&(st.support.inlineBlockNeedsLayout&&"inline"!==S(e.nodeName)?d.zoom=1:d.display="inline-block")),n.overflow&&(d.overflow="hidden",st.support.shrinkWrapBlocks||p.done(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(r in t)if(o=t[r],Zn.exec(o)){if(delete t[r],u=u||"toggle"===o,o===(m?"hide":"show"))continue;g.push(r)}if(a=g.length){s=st._data(e,"fxshow")||st._data(e,"fxshow",{}),"hidden"in s&&(m=s.hidden),u&&(s.hidden=!m),m?st(e).show():p.done(function(){st(e).hide()}),p.done(function(){var t;st._removeData(e,"fxshow");for(t in h)st.style(e,t,h[t])});for(r=0;a>r;r++)i=g[r],l=p.createTween(i,m?s[i]:0),h[i]=s[i]||st.style(e,i),i in s||(s[i]=l.start,m&&(l.end=l.start,l.start="width"===i||"height"===i?1:0))}}function $(e,t,n,r,i){return new $.prototype.init(e,t,n,r,i)}function I(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n=wn[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}function z(e){return st.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}var X,U,V=e.document,Y=e.location,J=e.jQuery,G=e.$,Q={},K=[],Z="1.9.0",et=K.concat,tt=K.push,nt=K.slice,rt=K.indexOf,it=Q.toString,ot=Q.hasOwnProperty,at=Z.trim,st=function(e,t){return new st.fn.init(e,t,X)},ut=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,lt=/\S+/g,ct=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,ft=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,pt=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,dt=/^[\],:{}\s]*$/,ht=/(?:^|:|,)(?:\s*\[)+/g,gt=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,mt=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,yt=/^-ms-/,vt=/-([\da-z])/gi,bt=function(e,t){return t.toUpperCase()},xt=function(){V.addEventListener?(V.removeEventListener("DOMContentLoaded",xt,!1),st.ready()):"complete"===V.readyState&&(V.detachEvent("onreadystatechange",xt),st.ready())};st.fn=st.prototype={jquery:Z,constructor:st,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:ft.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof st?n[0]:n,st.merge(this,st.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:V,!0)),pt.test(i[1])&&st.isPlainObject(n))for(i in n)st.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=V.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=V,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):st.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),st.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return nt.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=st.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return st.each(this,e,t)},ready:function(e){return st.ready.promise().done(e),this},slice:function(){return this.pushStack(nt.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(st.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:tt,sort:[].sort,splice:[].splice},st.fn.init.prototype=st.fn,st.extend=st.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||st.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(e=arguments[u]))for(n in e)r=s[n],i=e[n],s!==i&&(c&&i&&(st.isPlainObject(i)||(o=st.isArray(i)))?(o?(o=!1,a=r&&st.isArray(r)?r:[]):a=r&&st.isPlainObject(r)?r:{},s[n]=st.extend(c,a,i)):i!==t&&(s[n]=i));return s},st.extend({noConflict:function(t){return e.$===st&&(e.$=G),t&&e.jQuery===st&&(e.jQuery=J),st},isReady:!1,readyWait:1,holdReady:function(e){e?st.readyWait++:st.ready(!0)},ready:function(e){if(e===!0?!--st.readyWait:!st.isReady){if(!V.body)return setTimeout(st.ready);st.isReady=!0,e!==!0&&--st.readyWait>0||(U.resolveWith(V,[st]),st.fn.trigger&&st(V).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===st.type(e)},isArray:Array.isArray||function(e){return"array"===st.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?Q[it.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==st.type(e)||e.nodeType||st.isWindow(e))return!1;try{if(e.constructor&&!ot.call(e,"constructor")&&!ot.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||ot.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||V;var r=pt.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=st.buildFragment([e],t,i),i&&st(i).remove(),st.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=st.trim(n),n&&dt.test(n.replace(gt,"@").replace(mt,"]").replace(ht,"")))?Function("return "+n)():(st.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||st.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&st.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(yt,"ms-").replace(vt,bt)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,r){var i,o=0,a=e.length,s=n(e);if(r){if(s)for(;a>o&&(i=t.apply(e[o],r),i!==!1);o++);else for(o in e)if(i=t.apply(e[o],r),i===!1)break}else if(s)for(;a>o&&(i=t.call(e[o],o,e[o]),i!==!1);o++);else for(o in e)if(i=t.call(e[o],o,e[o]),i===!1)break;return e},trim:at&&!at.call("\ufeff\u00a0")?function(e){return null==e?"":at.call(e)}:function(e){return null==e?"":(e+"").replace(ct,"")},makeArray:function(e,t){var r=t||[];return null!=e&&(n(Object(e))?st.merge(r,"string"==typeof e?[e]:e):tt.call(r,e)),r},inArray:function(e,t,n){var r;if(t){if(rt)return rt.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else for(;n[o]!==t;)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,r){var i,o=0,a=e.length,s=n(e),u=[];if(s)for(;a>o;o++)i=t(e[o],o,r),null!=i&&(u[u.length]=i);else for(o in e)i=t(e[o],o,r),null!=i&&(u[u.length]=i);return et.apply([],u)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(r=e[n],n=e,e=r),st.isFunction(e)?(i=nt.call(arguments,2),o=function(){return e.apply(n||this,i.concat(nt.call(arguments)))},o.guid=e.guid=e.guid||st.guid++,o):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===st.type(r)){o=!0;for(u in r)st.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,st.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(st(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),st.ready.promise=function(t){if(!U)if(U=st.Deferred(),"complete"===V.readyState)setTimeout(st.ready);else if(V.addEventListener)V.addEventListener("DOMContentLoaded",xt,!1),e.addEventListener("load",st.ready,!1);else{V.attachEvent("onreadystatechange",xt),e.attachEvent("onload",st.ready);var n=!1;try{n=null==e.frameElement&&V.documentElement}catch(r){}n&&n.doScroll&&function i(){if(!st.isReady){try{n.doScroll("left")}catch(e){return setTimeout(i,50)}st.ready()}}()}return U.promise(t)},st.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){Q["[object "+t+"]"]=t.toLowerCase()}),X=st(V);var Tt={};st.Callbacks=function(e){e="string"==typeof e?Tt[e]||r(e):st.extend({},e);var n,i,o,a,s,u,l=[],c=!e.once&&[],f=function(t){for(n=e.memory&&t,i=!0,u=a||0,a=0,s=l.length,o=!0;l&&s>u;u++)if(l[u].apply(t[0],t[1])===!1&&e.stopOnFalse){n=!1;break}o=!1,l&&(c?c.length&&f(c.shift()):n?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function r(t){st.each(t,function(t,n){var i=st.type(n);"function"===i?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==i&&r(n)})})(arguments),o?s=l.length:n&&(a=t,f(n))}return this},remove:function(){return l&&st.each(arguments,function(e,t){for(var n;(n=st.inArray(t,l,n))>-1;)l.splice(n,1),o&&(s>=n&&s--,u>=n&&u--)}),this},has:function(e){return st.inArray(e,l)>-1},empty:function(){return l=[],this},disable:function(){return l=c=n=t,this},disabled:function(){return!l},lock:function(){return c=t,n||p.disable(),this},locked:function(){return!c},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!l||i&&!c||(o?c.push(t):f(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},st.extend({Deferred:function(e){var t=[["resolve","done",st.Callbacks("once memory"),"resolved"],["reject","fail",st.Callbacks("once memory"),"rejected"],["notify","progress",st.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return st.Deferred(function(n){st.each(t,function(t,o){var a=o[0],s=st.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&st.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?st.extend(e,r):r}},i={};return r.pipe=r.then,st.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=nt.call(arguments),a=o.length,s=1!==a||e&&st.isFunction(e.promise)?a:0,u=1===s?e:st.Deferred(),l=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?nt.call(arguments):i,r===t?u.notifyWith(n,r):--s||u.resolveWith(n,r)}};if(a>1)for(t=Array(a),n=Array(a),r=Array(a);a>i;i++)o[i]&&st.isFunction(o[i].promise)?o[i].promise().done(l(i,r,o)).fail(u.reject).progress(l(i,n,t)):--s;return s||u.resolveWith(r,o),u.promise()}}),st.support=function(){var n,r,i,o,a,s,u,l,c,f,p=V.createElement("div");if(p.setAttribute("className","t"),p.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",r=p.getElementsByTagName("*"),i=p.getElementsByTagName("a")[0],!r||!i||!r.length)return{};o=V.createElement("select"),a=o.appendChild(V.createElement("option")),s=p.getElementsByTagName("input")[0],i.style.cssText="top:1px;float:left;opacity:.5",n={getSetAttribute:"t"!==p.className,leadingWhitespace:3===p.firstChild.nodeType,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(i.getAttribute("style")),hrefNormalized:"/a"===i.getAttribute("href"),opacity:/^0.5/.test(i.style.opacity),cssFloat:!!i.style.cssFloat,checkOn:!!s.value,optSelected:a.selected,enctype:!!V.createElement("form").enctype,html5Clone:"<:nav></:nav>"!==V.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===V.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},s.checked=!0,n.noCloneChecked=s.cloneNode(!0).checked,o.disabled=!0,n.optDisabled=!a.disabled;try{delete p.test}catch(d){n.deleteExpando=!1}s=V.createElement("input"),s.setAttribute("value",""),n.input=""===s.getAttribute("value"),s.value="t",s.setAttribute("type","radio"),n.radioValue="t"===s.value,s.setAttribute("checked","t"),s.setAttribute("name","t"),u=V.createDocumentFragment(),u.appendChild(s),n.appendChecked=s.checked,n.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,p.attachEvent&&(p.attachEvent("onclick",function(){n.noCloneEvent=!1}),p.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})p.setAttribute(l="on"+f,"t"),n[f+"Bubbles"]=l in e||p.attributes[l].expando===!1;return p.style.backgroundClip="content-box",p.cloneNode(!0).style.backgroundClip="",n.clearCloneStyle="content-box"===p.style.backgroundClip,st(function(){var r,i,o,a="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",s=V.getElementsByTagName("body")[0];s&&(r=V.createElement("div"),r.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",s.appendChild(r).appendChild(p),p.innerHTML="<table><tr><td></td><td>t</td></tr></table>",o=p.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",c=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",n.reliableHiddenOffsets=c&&0===o[0].offsetHeight,p.innerHTML="",p.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",n.boxSizing=4===p.offsetWidth,n.doesNotIncludeMarginInBodyOffset=1!==s.offsetTop,e.getComputedStyle&&(n.pixelPosition="1%"!==(e.getComputedStyle(p,null)||{}).top,n.boxSizingReliable="4px"===(e.getComputedStyle(p,null)||{width:"4px"}).width,i=p.appendChild(V.createElement("div")),i.style.cssText=p.style.cssText=a,i.style.marginRight=i.style.width="0",p.style.width="1px",n.reliableMarginRight=!parseFloat((e.getComputedStyle(i,null)||{}).marginRight)),p.style.zoom!==t&&(p.innerHTML="",p.style.cssText=a+"width:1px;padding:1px;display:inline;zoom:1",n.inlineBlockNeedsLayout=3===p.offsetWidth,p.style.display="block",p.innerHTML="<div></div>",p.firstChild.style.width="5px",n.shrinkWrapBlocks=3!==p.offsetWidth,s.style.zoom=1),s.removeChild(r),r=p=o=i=null)}),r=o=u=a=i=s=null,n}();var wt=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,Nt=/([A-Z])/g;st.extend({cache:{},expando:"jQuery"+(Z+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?st.cache[e[st.expando]]:e[st.expando],!!e&&!s(e)},data:function(e,t,n){return i(e,t,n,!1)},removeData:function(e,t){return o(e,t,!1)},_data:function(e,t,n){return i(e,t,n,!0)},_removeData:function(e,t){return o(e,t,!0)},acceptData:function(e){var t=e.nodeName&&st.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),st.fn.extend({data:function(e,n){var r,i,o=this[0],s=0,u=null;if(e===t){if(this.length&&(u=st.data(o),1===o.nodeType&&!st._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>s;s++)i=r[s].name,i.indexOf("data-")||(i=st.camelCase(i.substring(5)),a(o,i,u[i]));st._data(o,"parsedAttrs",!0)}return u}return"object"==typeof e?this.each(function(){st.data(this,e)}):st.access(this,function(n){return n===t?o?a(o,e,st.data(o,e)):null:(this.each(function(){st.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){st.removeData(this,e)})}}),st.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=st._data(e,n),r&&(!i||st.isArray(r)?i=st._data(e,n,st.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=st.queue(e,t),r=n.length,i=n.shift(),o=st._queueHooks(e,t),a=function(){st.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return st._data(e,n)||st._data(e,n,{empty:st.Callbacks("once memory").add(function(){st._removeData(e,t+"queue"),st._removeData(e,n)})})}}),st.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?st.queue(this[0],e):n===t?this:this.each(function(){var t=st.queue(this,e,n);st._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&st.dequeue(this,e)})},dequeue:function(e){return this.each(function(){st.dequeue(this,e)})},delay:function(e,t){return e=st.fx?st.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=st.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};for("string"!=typeof e&&(n=e,e=t),e=e||"fx";s--;)r=st._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var Ct,kt,Et=/[\t\r\n]/g,St=/\r/g,At=/^(?:input|select|textarea|button|object)$/i,jt=/^(?:a|area)$/i,Dt=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,Lt=/^(?:checked|selected)$/i,Ht=st.support.getSetAttribute,Mt=st.support.input;st.fn.extend({attr:function(e,t){return st.access(this,st.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){st.removeAttr(this,e)})},prop:function(e,t){return st.access(this,st.prop,e,t,arguments.length>1)},removeProp:function(e){return e=st.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(st.isFunction(e))return this.each(function(t){st(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(lt)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(Et," "):" ")){for(o=0;i=t[o++];)0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=st.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(st.isFunction(e))return this.each(function(t){st(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(lt)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(Et," "):"")){for(o=0;i=t[o++];)for(;r.indexOf(" "+i+" ")>=0;)r=r.replace(" "+i+" "," ");n.className=e?st.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return st.isFunction(e)?this.each(function(n){st(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n)for(var i,o=0,a=st(this),s=t,u=e.match(lt)||[];i=u[o++];)s=r?s:!a.hasClass(i),a[s?"addClass":"removeClass"](i);else("undefined"===n||"boolean"===n)&&(this.className&&st._data(this,"__className__",this.className),this.className=this.className||e===!1?"":st._data(this,"__className__")||"")})},hasClass:function(e){for(var t=" "+e+" ",n=0,r=this.length;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(Et," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=st.isFunction(e),this.each(function(r){var o,a=st(this);1===this.nodeType&&(o=i?e.call(this,r,a.val()):e,null==o?o="":"number"==typeof o?o+="":st.isArray(o)&&(o=st.map(o,function(e){return null==e?"":e+""})),n=st.valHooks[this.type]||st.valHooks[this.nodeName.toLowerCase()],n&&"set"in n&&n.set(this,o,"value")!==t||(this.value=o))});if(o)return n=st.valHooks[o.type]||st.valHooks[o.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(o,"value"))!==t?r:(r=o.value,"string"==typeof r?r.replace(St,""):null==r?"":r)}}}),st.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(st.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&st.nodeName(n.parentNode,"optgroup"))){if(t=st(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=st.makeArray(t);return st(e).find("option").each(function(){this.selected=st.inArray(st(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return e.getAttribute===t?st.prop(e,n,r):(a=1!==s||!st.isXMLDoc(e),a&&(n=n.toLowerCase(),o=st.attrHooks[n]||(Dt.test(n)?kt:Ct)),r===t?o&&a&&"get"in o&&null!==(i=o.get(e,n))?i:(e.getAttribute!==t&&(i=e.getAttribute(n)),null==i?t:i):null!==r?o&&a&&"set"in o&&(i=o.set(e,r,n))!==t?i:(e.setAttribute(n,r+""),r):(st.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(lt);if(o&&1===e.nodeType)for(;n=o[i++];)r=st.propFix[n]||n,Dt.test(n)?!Ht&&Lt.test(n)?e[st.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:st.attr(e,n,""),e.removeAttribute(Ht?n:r)},attrHooks:{type:{set:function(e,t){if(!st.support.radioValue&&"radio"===t&&st.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!st.isXMLDoc(e),a&&(n=st.propFix[n]||n,o=st.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):At.test(e.nodeName)||jt.test(e.nodeName)&&e.href?0:t}}}}),kt={get:function(e,n){var r=st.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?Mt&&Ht?null!=i:Lt.test(n)?e[st.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?st.removeAttr(e,n):Mt&&Ht||!Lt.test(n)?e.setAttribute(!Ht&&st.propFix[n]||n,n):e[st.camelCase("default-"+n)]=e[n]=!0,n}},Mt&&Ht||(st.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return st.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t
},set:function(e,n,r){return st.nodeName(e,"input")?(e.defaultValue=n,t):Ct&&Ct.set(e,n,r)}}),Ht||(Ct=st.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},st.attrHooks.contenteditable={get:Ct.get,set:function(e,t,n){Ct.set(e,""===t?!1:t,n)}},st.each(["width","height"],function(e,n){st.attrHooks[n]=st.extend(st.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),st.support.hrefNormalized||(st.each(["href","src","width","height"],function(e,n){st.attrHooks[n]=st.extend(st.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),st.each(["href","src"],function(e,t){st.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),st.support.style||(st.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),st.support.optSelected||(st.propHooks.selected=st.extend(st.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),st.support.enctype||(st.propFix.enctype="encoding"),st.support.checkOn||st.each(["radio","checkbox"],function(){st.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),st.each(["radio","checkbox"],function(){st.valHooks[this]=st.extend(st.valHooks[this],{set:function(e,n){return st.isArray(n)?e.checked=st.inArray(st(e).val(),n)>=0:t}})});var qt=/^(?:input|select|textarea)$/i,_t=/^key/,Ft=/^(?:mouse|contextmenu)|click/,Ot=/^(?:focusinfocus|focusoutblur)$/,Bt=/^([^.]*)(?:\.(.+)|)$/;st.event={global:{},add:function(e,n,r,i,o){var a,s,u,l,c,f,p,d,h,g,m,y=3!==e.nodeType&&8!==e.nodeType&&st._data(e);if(y){for(r.handler&&(a=r,r=a.handler,o=a.selector),r.guid||(r.guid=st.guid++),(l=y.events)||(l=y.events={}),(s=y.handle)||(s=y.handle=function(e){return st===t||e&&st.event.triggered===e.type?t:st.event.dispatch.apply(s.elem,arguments)},s.elem=e),n=(n||"").match(lt)||[""],c=n.length;c--;)u=Bt.exec(n[c])||[],h=m=u[1],g=(u[2]||"").split(".").sort(),p=st.event.special[h]||{},h=(o?p.delegateType:p.bindType)||h,p=st.event.special[h]||{},f=st.extend({type:h,origType:m,data:i,handler:r,guid:r.guid,selector:o,needsContext:o&&st.expr.match.needsContext.test(o),namespace:g.join(".")},a),(d=l[h])||(d=l[h]=[],d.delegateCount=0,p.setup&&p.setup.call(e,i,g,s)!==!1||(e.addEventListener?e.addEventListener(h,s,!1):e.attachEvent&&e.attachEvent("on"+h,s))),p.add&&(p.add.call(e,f),f.handler.guid||(f.handler.guid=r.guid)),o?d.splice(d.delegateCount++,0,f):d.push(f),st.event.global[h]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,m=st.hasData(e)&&st._data(e);if(m&&(u=m.events)){for(t=(t||"").match(lt)||[""],l=t.length;l--;)if(s=Bt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){for(f=st.event.special[d]||{},d=(r?f.delegateType:f.bindType)||d,p=u[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;o--;)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&f.teardown.call(e,h,m.handle)!==!1||st.removeEvent(e,d,m.handle),delete u[d])}else for(d in u)st.event.remove(e,d+t[l],n,r,!0);st.isEmptyObject(u)&&(delete m.handle,st._removeData(e,"events"))}},trigger:function(n,r,i,o){var a,s,u,l,c,f,p,d=[i||V],h=n.type||n,g=n.namespace?n.namespace.split("."):[];if(s=u=i=i||V,3!==i.nodeType&&8!==i.nodeType&&!Ot.test(h+st.event.triggered)&&(h.indexOf(".")>=0&&(g=h.split("."),h=g.shift(),g.sort()),c=0>h.indexOf(":")&&"on"+h,n=n[st.expando]?n:new st.Event(h,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=g.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:st.makeArray(r,[n]),p=st.event.special[h]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!st.isWindow(i)){for(l=p.delegateType||h,Ot.test(l+h)||(s=s.parentNode);s;s=s.parentNode)d.push(s),u=s;u===(i.ownerDocument||V)&&d.push(u.defaultView||u.parentWindow||e)}for(a=0;(s=d[a++])&&!n.isPropagationStopped();)n.type=a>1?l:p.bindType||h,f=(st._data(s,"events")||{})[n.type]&&st._data(s,"handle"),f&&f.apply(s,r),f=c&&s[c],f&&st.acceptData(s)&&f.apply&&f.apply(s,r)===!1&&n.preventDefault();if(n.type=h,!(o||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===h&&st.nodeName(i,"a")||!st.acceptData(i)||!c||!i[h]||st.isWindow(i))){u=i[c],u&&(i[c]=null),st.event.triggered=h;try{i[h]()}catch(m){}st.event.triggered=t,u&&(i[c]=u)}return n.result}},dispatch:function(e){e=st.event.fix(e);var n,r,i,o,a,s=[],u=nt.call(arguments),l=(st._data(this,"events")||{})[e.type]||[],c=st.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){for(s=st.event.handlers.call(this,e,l),n=0;(o=s[n++])&&!e.isPropagationStopped();)for(e.currentTarget=o.elem,r=0;(a=o.handlers[r++])&&!e.isImmediatePropagationStopped();)(!e.namespace_re||e.namespace_re.test(a.namespace))&&(e.handleObj=a,e.data=a.data,i=((st.event.special[a.origType]||{}).handle||a.handler).apply(o.elem,u),i!==t&&(e.result=i)===!1&&(e.preventDefault(),e.stopPropagation()));return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(l.disabled!==!0||"click"!==e.type){for(i=[],r=0;u>r;r++)a=n[r],o=a.selector+" ",i[o]===t&&(i[o]=a.needsContext?st(o,this).index(l)>=0:st.find(o,this,null,[l]).length),i[o]&&i.push(a);i.length&&s.push({elem:l,handlers:i})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[st.expando])return e;var t,n,r=e,i=st.event.fixHooks[e.type]||{},o=i.props?this.props.concat(i.props):this.props;for(e=new st.Event(r),t=o.length;t--;)n=o[t],e[n]=r[n];return e.target||(e.target=r.srcElement||V),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,i.filter?i.filter(e,r):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,a=n.button,s=n.fromElement;return null==e.pageX&&null!=n.clientX&&(r=e.target.ownerDocument||V,i=r.documentElement,o=r.body,e.pageX=n.clientX+(i&&i.scrollLeft||o&&o.scrollLeft||0)-(i&&i.clientLeft||o&&o.clientLeft||0),e.pageY=n.clientY+(i&&i.scrollTop||o&&o.scrollTop||0)-(i&&i.clientTop||o&&o.clientTop||0)),!e.relatedTarget&&s&&(e.relatedTarget=s===e.target?n.toElement:s),e.which||a===t||(e.which=1&a?1:2&a?3:4&a?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return st.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==V.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===V.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=st.extend(new st.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?st.event.trigger(i,null,t):st.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},st.removeEvent=V.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,n,r){var i="on"+n;e.detachEvent&&(e[i]===t&&(e[i]=null),e.detachEvent(i,r))},st.Event=function(e,n){return this instanceof st.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?u:l):this.type=e,n&&st.extend(this,n),this.timeStamp=e&&e.timeStamp||st.now(),this[st.expando]=!0,t):new st.Event(e,n)},st.Event.prototype={isDefaultPrevented:l,isPropagationStopped:l,isImmediatePropagationStopped:l,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=u,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=u,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u,this.stopPropagation()}},st.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){st.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!st.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),st.support.submitBubbles||(st.event.special.submit={setup:function(){return st.nodeName(this,"form")?!1:(st.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=st.nodeName(n,"input")||st.nodeName(n,"button")?n.form:t;r&&!st._data(r,"submitBubbles")&&(st.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),st._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&st.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return st.nodeName(this,"form")?!1:(st.event.remove(this,"._submit"),t)}}),st.support.changeBubbles||(st.event.special.change={setup:function(){return qt.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(st.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),st.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),st.event.simulate("change",this,e,!0)})),!1):(st.event.add(this,"beforeactivate._change",function(e){var t=e.target;qt.test(t.nodeName)&&!st._data(t,"changeBubbles")&&(st.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||st.event.simulate("change",this.parentNode,e,!0)}),st._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return st.event.remove(this,"._change"),!qt.test(this.nodeName)}}),st.support.focusinBubbles||st.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){st.event.simulate(t,e.target,st.event.fix(e),!0)};st.event.special[t]={setup:function(){0===n++&&V.addEventListener(e,r,!0)},teardown:function(){0===--n&&V.removeEventListener(e,r,!0)}}}),st.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(s in e)this.on(s,n,r,e[s],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=l;else if(!i)return this;return 1===o&&(a=i,i=function(e){return st().off(e),a.apply(this,arguments)},i.guid=a.guid||(a.guid=st.guid++)),this.each(function(){st.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,st(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=l),this.each(function(){st.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){st.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?st.event.trigger(e,n,r,!0):t},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),st.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){st.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)},_t.test(t)&&(st.event.fixHooks[t]=st.event.keyHooks),Ft.test(t)&&(st.event.fixHooks[t]=st.event.mouseHooks)}),function(e,t){function n(e){return ht.test(e+"")}function r(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>C.cacheLength&&delete e[t.shift()],e[n]=r}}function i(e){return e[P]=!0,e}function o(e){var t=L.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function a(e,t,n,r){var i,o,a,s,u,l,c,d,h,g;if((t?t.ownerDocument||t:R)!==L&&D(t),t=t||L,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!M&&!r){if(i=gt.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&O(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return Q.apply(n,K.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&W.getByClassName&&t.getElementsByClassName)return Q.apply(n,K.call(t.getElementsByClassName(a),0)),n}if(W.qsa&&!q.test(e)){if(c=!0,d=P,h=t,g=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){for(l=f(e),(c=t.getAttribute("id"))?d=c.replace(vt,"\\$&"):t.setAttribute("id",d),d="[id='"+d+"'] ",u=l.length;u--;)l[u]=d+p(l[u]);h=dt.test(e)&&t.parentNode||t,g=l.join(",")}if(g)try{return Q.apply(n,K.call(h.querySelectorAll(g),0)),n}catch(m){}finally{c||t.removeAttribute("id")}}}return x(e.replace(at,"$1"),t,n,r)}function s(e,t){for(var n=e&&t&&e.nextSibling;n;n=n.nextSibling)if(n===t)return-1;return e?1:-1}function u(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function l(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function c(e){return i(function(t){return t=+t,i(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function f(e,t){var n,r,i,o,s,u,l,c=X[e+" "];if(c)return t?0:c.slice(0);for(s=e,u=[],l=C.preFilter;s;){(!n||(r=ut.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(i=[])),n=!1,(r=lt.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(at," ")}),s=s.slice(n.length));for(o in C.filter)!(r=pt[o].exec(s))||l[o]&&!(r=l[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?a.error(e):X(e,u).slice(0)}function p(e){for(var t=0,n=e.length,r="";n>t;t++)r+=e[t].value;return r}function d(e,t,n){var r=t.dir,i=n&&"parentNode"===t.dir,o=I++;return t.first?function(t,n,o){for(;t=t[r];)if(1===t.nodeType||i)return e(t,n,o)}:function(t,n,a){var s,u,l,c=$+" "+o;if(a){for(;t=t[r];)if((1===t.nodeType||i)&&e(t,n,a))return!0}else for(;t=t[r];)if(1===t.nodeType||i)if(l=t[P]||(t[P]={}),(u=l[r])&&u[0]===c){if((s=u[1])===!0||s===N)return s===!0}else if(u=l[r]=[c],u[1]=e(t,n,a)||N,u[1]===!0)return!0}}function h(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function g(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function m(e,t,n,r,o,a){return r&&!r[P]&&(r=m(r)),o&&!o[P]&&(o=m(o,a)),i(function(i,a,s,u){var l,c,f,p=[],d=[],h=a.length,m=i||b(t||"*",s.nodeType?[s]:s,[]),y=!e||!i&&t?m:g(m,p,e,s,u),v=n?o||(i?e:h||r)?[]:a:y;if(n&&n(y,v,s,u),r)for(l=g(v,d),r(l,[],s,u),c=l.length;c--;)(f=l[c])&&(v[d[c]]=!(y[d[c]]=f));if(i){if(o||e){if(o){for(l=[],c=v.length;c--;)(f=v[c])&&l.push(y[c]=f);o(null,v=[],l,u)}for(c=v.length;c--;)(f=v[c])&&(l=o?Z.call(i,f):p[c])>-1&&(i[l]=!(a[l]=f))}}else v=g(v===a?v.splice(h,v.length):v),o?o(null,a,v,u):Q.apply(a,v)})}function y(e){for(var t,n,r,i=e.length,o=C.relative[e[0].type],a=o||C.relative[" "],s=o?1:0,u=d(function(e){return e===t},a,!0),l=d(function(e){return Z.call(t,e)>-1},a,!0),c=[function(e,n,r){return!o&&(r||n!==j)||((t=n).nodeType?u(e,n,r):l(e,n,r))}];i>s;s++)if(n=C.relative[e[s].type])c=[d(h(c),n)];else{if(n=C.filter[e[s].type].apply(null,e[s].matches),n[P]){for(r=++s;i>r&&!C.relative[e[r].type];r++);return m(s>1&&h(c),s>1&&p(e.slice(0,s-1)).replace(at,"$1"),n,r>s&&y(e.slice(s,r)),i>r&&y(e=e.slice(r)),i>r&&p(e))}c.push(n)}return h(c)}function v(e,t){var n=0,r=t.length>0,o=e.length>0,s=function(i,s,u,l,c){var f,p,d,h=[],m=0,y="0",v=i&&[],b=null!=c,x=j,T=i||o&&C.find.TAG("*",c&&s.parentNode||s),w=$+=null==x?1:Math.E;for(b&&(j=s!==L&&s,N=n);null!=(f=T[y]);y++){if(o&&f){for(p=0;d=e[p];p++)if(d(f,s,u)){l.push(f);break}b&&($=w,N=++n)}r&&((f=!d&&f)&&m--,i&&v.push(f))}if(m+=y,r&&y!==m){for(p=0;d=t[p];p++)d(v,h,s,u);if(i){if(m>0)for(;y--;)v[y]||h[y]||(h[y]=G.call(l));h=g(h)}Q.apply(l,h),b&&!i&&h.length>0&&m+t.length>1&&a.uniqueSort(l)}return b&&($=w,j=x),v};return r?i(s):s}function b(e,t,n){for(var r=0,i=t.length;i>r;r++)a(e,t[r],n);return n}function x(e,t,n,r){var i,o,a,s,u,l=f(e);if(!r&&1===l.length){if(o=l[0]=l[0].slice(0),o.length>2&&"ID"===(a=o[0]).type&&9===t.nodeType&&!M&&C.relative[o[1].type]){if(t=C.find.ID(a.matches[0].replace(xt,Tt),t)[0],!t)return n;e=e.slice(o.shift().value.length)}for(i=pt.needsContext.test(e)?-1:o.length-1;i>=0&&(a=o[i],!C.relative[s=a.type]);i--)if((u=C.find[s])&&(r=u(a.matches[0].replace(xt,Tt),dt.test(o[0].type)&&t.parentNode||t))){if(o.splice(i,1),e=r.length&&p(o),!e)return Q.apply(n,K.call(r,0)),n;break}}return S(e,l)(r,t,M,n,dt.test(e)),n}function T(){}var w,N,C,k,E,S,A,j,D,L,H,M,q,_,F,O,B,P="sizzle"+-new Date,R=e.document,W={},$=0,I=0,z=r(),X=r(),U=r(),V=typeof t,Y=1<<31,J=[],G=J.pop,Q=J.push,K=J.slice,Z=J.indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(this[t]===e)return t;return-1},et="[\\x20\\t\\r\\n\\f]",tt="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",nt=tt.replace("w","w#"),rt="([*^$|!~]?=)",it="\\["+et+"*("+tt+")"+et+"*(?:"+rt+et+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+nt+")|)|)"+et+"*\\]",ot=":("+tt+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+it.replace(3,8)+")*)|.*)\\)|)",at=RegExp("^"+et+"+|((?:^|[^\\\\])(?:\\\\.)*)"+et+"+$","g"),ut=RegExp("^"+et+"*,"+et+"*"),lt=RegExp("^"+et+"*([\\x20\\t\\r\\n\\f>+~])"+et+"*"),ct=RegExp(ot),ft=RegExp("^"+nt+"$"),pt={ID:RegExp("^#("+tt+")"),CLASS:RegExp("^\\.("+tt+")"),NAME:RegExp("^\\[name=['\"]?("+tt+")['\"]?\\]"),TAG:RegExp("^("+tt.replace("w","w*")+")"),ATTR:RegExp("^"+it),PSEUDO:RegExp("^"+ot),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+et+"*(even|odd|(([+-]|)(\\d*)n|)"+et+"*(?:([+-]|)"+et+"*(\\d+)|))"+et+"*\\)|)","i"),needsContext:RegExp("^"+et+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+et+"*((?:-\\d)?\\d*)"+et+"*\\)|)(?=[^-]|$)","i")},dt=/[\x20\t\r\n\f]*[+~]/,ht=/\{\s*\[native code\]\s*\}/,gt=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,mt=/^(?:input|select|textarea|button)$/i,yt=/^h\d$/i,vt=/'|\\/g,bt=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,xt=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,Tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{K.call(H.childNodes,0)[0].nodeType}catch(wt){K=function(e){for(var t,n=[];t=this[e];e++)n.push(t);return n}}E=a.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},D=a.setDocument=function(e){var r=e?e.ownerDocument||e:R;return r!==L&&9===r.nodeType&&r.documentElement?(L=r,H=r.documentElement,M=E(r),W.tagNameNoComments=o(function(e){return e.appendChild(r.createComment("")),!e.getElementsByTagName("*").length}),W.attributes=o(function(e){e.innerHTML="<select></select>";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),W.getByClassName=o(function(e){return e.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),W.getByName=o(function(e){e.id=P+0,e.innerHTML="<a name='"+P+"'></a><div name='"+P+"'></div>",H.insertBefore(e,H.firstChild);var t=r.getElementsByName&&r.getElementsByName(P).length===2+r.getElementsByName(P+0).length;return W.getIdNotName=!r.getElementById(P),H.removeChild(e),t}),C.attrHandle=o(function(e){return e.innerHTML="<a href='#'></a>",e.firstChild&&typeof e.firstChild.getAttribute!==V&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},W.getIdNotName?(C.find.ID=function(e,t){if(typeof t.getElementById!==V&&!M){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},C.filter.ID=function(e){var t=e.replace(xt,Tt);return function(e){return e.getAttribute("id")===t}}):(C.find.ID=function(e,n){if(typeof n.getElementById!==V&&!M){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==V&&r.getAttributeNode("id").value===e?[r]:t:[]}},C.filter.ID=function(e){var t=e.replace(xt,Tt);return function(e){var n=typeof e.getAttributeNode!==V&&e.getAttributeNode("id");return n&&n.value===t}}),C.find.TAG=W.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==V?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i];i++)1===n.nodeType&&r.push(n);return r}return o},C.find.NAME=W.getByName&&function(e,n){return typeof n.getElementsByName!==V?n.getElementsByName(name):t},C.find.CLASS=W.getByClassName&&function(e,n){return typeof n.getElementsByClassName===V||M?t:n.getElementsByClassName(e)},_=[],q=[":focus"],(W.qsa=n(r.querySelectorAll))&&(o(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||q.push("\\["+et+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||q.push(":checked")}),o(function(e){e.innerHTML="<input type='hidden' i=''/>",e.querySelectorAll("[i^='']").length&&q.push("[*^$]="+et+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),q.push(",.*:")})),(W.matchesSelector=n(F=H.matchesSelector||H.mozMatchesSelector||H.webkitMatchesSelector||H.oMatchesSelector||H.msMatchesSelector))&&o(function(e){W.disconnectedMatch=F.call(e,"div"),F.call(e,"[s!='']:x"),_.push("!=",ot)}),q=RegExp(q.join("|")),_=RegExp(_.join("|")),O=n(H.contains)||H.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},B=H.compareDocumentPosition?function(e,t){var n;return e===t?(A=!0,0):(n=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&n||e.parentNode&&11===e.parentNode.nodeType?e===r||O(R,e)?-1:t===r||O(R,t)?1:0:4&n?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var n,i=0,o=e.parentNode,a=t.parentNode,u=[e],l=[t];if(e===t)return A=!0,0;if(e.sourceIndex&&t.sourceIndex)return(~t.sourceIndex||Y)-(O(R,e)&&~e.sourceIndex||Y);if(!o||!a)return e===r?-1:t===r?1:o?-1:a?1:0;if(o===a)return s(e,t);for(n=e;n=n.parentNode;)u.unshift(n);for(n=t;n=n.parentNode;)l.unshift(n);for(;u[i]===l[i];)i++;return i?s(u[i],l[i]):u[i]===R?-1:l[i]===R?1:0},A=!1,[0,0].sort(B),W.detectDuplicates=A,L):L},a.matches=function(e,t){return a(e,null,null,t)},a.matchesSelector=function(e,t){if((e.ownerDocument||e)!==L&&D(e),t=t.replace(bt,"='$1']"),!(!W.matchesSelector||M||_&&_.test(t)||q.test(t)))try{var n=F.call(e,t);if(n||W.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return a(t,L,null,[e]).length>0},a.contains=function(e,t){return(e.ownerDocument||e)!==L&&D(e),O(e,t)},a.attr=function(e,t){var n;return(e.ownerDocument||e)!==L&&D(e),M||(t=t.toLowerCase()),(n=C.attrHandle[t])?n(e):M||W.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},a.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},a.uniqueSort=function(e){var t,n=[],r=1,i=0;if(A=!W.detectDuplicates,e.sort(B),A){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));for(;i--;)e.splice(n[i],1)}return e},k=a.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=k(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=k(t);return n},C=a.selectors={cacheLength:50,createPseudo:i,match:pt,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(xt,Tt),e[3]=(e[4]||e[5]||"").replace(xt,Tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||a.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&a.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return pt.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&ct.test(n)&&(t=f(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(xt,Tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=z[e+" "];return t||(t=RegExp("(^|"+et+")"+e+"("+et+"|$)"))&&z(e,function(e){return t.test(e.className||typeof e.getAttribute!==V&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=a.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.substr(i.length-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){for(;g;){for(f=t;f=f[g];)if(s?f.nodeName.toLowerCase()===y:1===f.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){for(c=m[P]||(m[P]={}),l=c[e]||[],d=l[0]===$&&l[1],p=l[0]===$&&l[2],f=d&&m.childNodes[d];f=++d&&f&&f[g]||(p=d=0)||h.pop();)if(1===f.nodeType&&++p&&f===t){c[e]=[$,d,p];break}}else if(v&&(l=(t[P]||(t[P]={}))[e])&&l[0]===$)p=l[1];else for(;(f=++d&&f&&f[g]||(p=d=0)||h.pop())&&((s?f.nodeName.toLowerCase()!==y:1!==f.nodeType)||!++p||(v&&((f[P]||(f[P]={}))[e]=[$,p]),f!==t)););return p-=i,p===r||0===p%r&&p/r>=0}}},PSEUDO:function(e,t){var n,r=C.pseudos[e]||C.setFilters[e.toLowerCase()]||a.error("unsupported pseudo: "+e);return r[P]?r(t):r.length>1?(n=[e,e,"",t],C.setFilters.hasOwnProperty(e.toLowerCase())?i(function(e,n){for(var i,o=r(e,t),a=o.length;a--;)i=Z.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:i(function(e){var t=[],n=[],r=S(e.replace(at,"$1"));return r[P]?i(function(e,t,n,i){for(var o,a=r(e,null,i,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:i(function(e){return function(t){return a(e,t).length>0}}),contains:i(function(e){return function(t){return(t.textContent||t.innerText||k(t)).indexOf(e)>-1}}),lang:i(function(e){return ft.test(e||"")||a.error("unsupported lang: "+e),e=e.replace(xt,Tt).toLowerCase(),function(t){var n;do if(n=M?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===H},focus:function(e){return e===L.activeElement&&(!L.hasFocus||L.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!C.pseudos.empty(e)},header:function(e){return yt.test(e.nodeName)},input:function(e){return mt.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:c(function(){return[0]}),last:c(function(e,t){return[t-1]}),eq:c(function(e,t,n){return[0>n?n+t:n]}),even:c(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:c(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:c(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:c(function(e,t,n){for(var r=0>n?n+t:n;t>++r;)e.push(r);return e})}};for(w in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})C.pseudos[w]=u(w);for(w in{submit:!0,reset:!0})C.pseudos[w]=l(w);S=a.compile=function(e,t){var n,r=[],i=[],o=U[e+" "];if(!o){for(t||(t=f(e)),n=t.length;n--;)o=y(t[n]),o[P]?r.push(o):i.push(o);o=U(e,v(i,r))}return o},C.pseudos.nth=C.pseudos.eq,C.filters=T.prototype=C.pseudos,C.setFilters=new T,D(),a.attr=st.attr,st.find=a,st.expr=a.selectors,st.expr[":"]=st.expr.pseudos,st.unique=a.uniqueSort,st.text=a.getText,st.isXMLDoc=a.isXML,st.contains=a.contains}(e);var Pt=/Until$/,Rt=/^(?:parents|prev(?:Until|All))/,Wt=/^.[^:#\[\.,]*$/,$t=st.expr.match.needsContext,It={children:!0,contents:!0,next:!0,prev:!0};st.fn.extend({find:function(e){var t,n,r;if("string"!=typeof e)return r=this,this.pushStack(st(e).filter(function(){for(t=0;r.length>t;t++)if(st.contains(r[t],this))return!0}));for(n=[],t=0;this.length>t;t++)st.find(e,this[t],n);return n=this.pushStack(st.unique(n)),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=st(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(st.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(f(this,e,!1))},filter:function(e){return this.pushStack(f(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?$t.test(e)?st(e,this.context).index(this[0])>=0:st.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){for(var n,r=0,i=this.length,o=[],a=$t.test(e)||"string"!=typeof e?st(e,t||this.context):0;i>r;r++)for(n=this[r];n&&n.ownerDocument&&n!==t&&11!==n.nodeType;){if(a?a.index(n)>-1:st.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}return this.pushStack(o.length>1?st.unique(o):o)},index:function(e){return e?"string"==typeof e?st.inArray(this[0],st(e)):st.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?st(e,t):st.makeArray(e&&e.nodeType?[e]:e),r=st.merge(this.get(),n);return this.pushStack(st.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),st.fn.andSelf=st.fn.addBack,st.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return st.dir(e,"parentNode")},parentsUntil:function(e,t,n){return st.dir(e,"parentNode",n)},next:function(e){return c(e,"nextSibling")},prev:function(e){return c(e,"previousSibling")
},nextAll:function(e){return st.dir(e,"nextSibling")},prevAll:function(e){return st.dir(e,"previousSibling")},nextUntil:function(e,t,n){return st.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return st.dir(e,"previousSibling",n)},siblings:function(e){return st.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return st.sibling(e.firstChild)},contents:function(e){return st.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:st.merge([],e.childNodes)}},function(e,t){st.fn[e]=function(n,r){var i=st.map(this,t,n);return Pt.test(e)||(r=n),r&&"string"==typeof r&&(i=st.filter(r,i)),i=this.length>1&&!It[e]?st.unique(i):i,this.length>1&&Rt.test(e)&&(i=i.reverse()),this.pushStack(i)}}),st.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?st.find.matchesSelector(t[0],e)?[t[0]]:[]:st.find.matches(e,t)},dir:function(e,n,r){for(var i=[],o=e[n];o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!st(o).is(r));)1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});var zt="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",Xt=/ jQuery\d+="(?:null|\d+)"/g,Ut=RegExp("<(?:"+zt+")[\\s/>]","i"),Vt=/^\s+/,Yt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,Jt=/<([\w:]+)/,Gt=/<tbody/i,Qt=/<|&#?\w+;/,Kt=/<(?:script|style|link)/i,Zt=/^(?:checkbox|radio)$/i,en=/checked\s*(?:[^=]|=\s*.checked.)/i,tn=/^$|\/(?:java|ecma)script/i,nn=/^true\/(.*)/,rn=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,on={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:st.support.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},an=p(V),sn=an.appendChild(V.createElement("div"));on.optgroup=on.option,on.tbody=on.tfoot=on.colgroup=on.caption=on.thead,on.th=on.td,st.fn.extend({text:function(e){return st.access(this,function(e){return e===t?st.text(this):this.empty().append((this[0]&&this[0].ownerDocument||V).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(st.isFunction(e))return this.each(function(t){st(this).wrapAll(e.call(this,t))});if(this[0]){var t=st(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstChild&&1===e.firstChild.nodeType;)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return st.isFunction(e)?this.each(function(t){st(this).wrapInner(e.call(this,t))}):this.each(function(){var t=st(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=st.isFunction(e);return this.each(function(n){st(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){st.nodeName(this,"body")||st(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){for(var n,r=0;null!=(n=this[r]);r++)(!e||st.filter(e,[n]).length>0)&&(t||1!==n.nodeType||st.cleanData(b(n)),n.parentNode&&(t&&st.contains(n.ownerDocument,n)&&m(b(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){for(var e,t=0;null!=(e=this[t]);t++){for(1===e.nodeType&&st.cleanData(b(e,!1));e.firstChild;)e.removeChild(e.firstChild);e.options&&st.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return st.clone(this,e,t)})},html:function(e){return st.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(Xt,""):t;if(!("string"!=typeof e||Kt.test(e)||!st.support.htmlSerialize&&Ut.test(e)||!st.support.leadingWhitespace&&Vt.test(e)||on[(Jt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(Yt,"<$1></$2>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(st.cleanData(b(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=st.isFunction(e);return t||"string"==typeof e||(e=st(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;(n&&1===this.nodeType||11===this.nodeType)&&(st(this).remove(),t?t.parentNode.insertBefore(e,t):n.appendChild(e))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=et.apply([],e);var i,o,a,s,u,l,c=0,f=this.length,p=this,m=f-1,y=e[0],v=st.isFunction(y);if(v||!(1>=f||"string"!=typeof y||st.support.checkClone)&&en.test(y))return this.each(function(i){var o=p.eq(i);v&&(e[0]=y.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(f&&(i=st.buildFragment(e,this[0].ownerDocument,!1,this),o=i.firstChild,1===i.childNodes.length&&(i=o),o)){for(n=n&&st.nodeName(o,"tr"),a=st.map(b(i,"script"),h),s=a.length;f>c;c++)u=i,c!==m&&(u=st.clone(u,!0,!0),s&&st.merge(a,b(u,"script"))),r.call(n&&st.nodeName(this[c],"table")?d(this[c],"tbody"):this[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,st.map(a,g),c=0;s>c;c++)u=a[c],tn.test(u.type||"")&&!st._data(u,"globalEval")&&st.contains(l,u)&&(u.src?st.ajax({url:u.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):st.globalEval((u.text||u.textContent||u.innerHTML||"").replace(rn,"")));i=o=null}return this}}),st.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){st.fn[e]=function(e){for(var n,r=0,i=[],o=st(e),a=o.length-1;a>=r;r++)n=r===a?this:this.clone(!0),st(o[r])[t](n),tt.apply(i,n.get());return this.pushStack(i)}}),st.extend({clone:function(e,t,n){var r,i,o,a,s,u=st.contains(e.ownerDocument,e);if(st.support.html5Clone||st.isXMLDoc(e)||!Ut.test("<"+e.nodeName+">")?s=e.cloneNode(!0):(sn.innerHTML=e.outerHTML,sn.removeChild(s=sn.firstChild)),!(st.support.noCloneEvent&&st.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||st.isXMLDoc(e)))for(r=b(s),i=b(e),a=0;null!=(o=i[a]);++a)r[a]&&v(o,r[a]);if(t)if(n)for(i=i||b(e),r=r||b(s),a=0;null!=(o=i[a]);a++)y(o,r[a]);else y(e,s);return r=b(s,"script"),r.length>0&&m(r,!u&&b(e,"script")),r=i=o=null,s},buildFragment:function(e,t,n,r){for(var i,o,a,s,u,l,c,f=e.length,d=p(t),h=[],g=0;f>g;g++)if(o=e[g],o||0===o)if("object"===st.type(o))st.merge(h,o.nodeType?[o]:o);else if(Qt.test(o)){for(s=s||d.appendChild(t.createElement("div")),a=(Jt.exec(o)||["",""])[1].toLowerCase(),u=on[a]||on._default,s.innerHTML=u[1]+o.replace(Yt,"<$1></$2>")+u[2],c=u[0];c--;)s=s.lastChild;if(!st.support.leadingWhitespace&&Vt.test(o)&&h.push(t.createTextNode(Vt.exec(o)[0])),!st.support.tbody)for(o="table"!==a||Gt.test(o)?"<table>"!==u[1]||Gt.test(o)?0:s:s.firstChild,c=o&&o.childNodes.length;c--;)st.nodeName(l=o.childNodes[c],"tbody")&&!l.childNodes.length&&o.removeChild(l);for(st.merge(h,s.childNodes),s.textContent="";s.firstChild;)s.removeChild(s.firstChild);s=d.lastChild}else h.push(t.createTextNode(o));for(s&&d.removeChild(s),st.support.appendChecked||st.grep(b(h,"input"),x),g=0;o=h[g++];)if((!r||-1===st.inArray(o,r))&&(i=st.contains(o.ownerDocument,o),s=b(d.appendChild(o),"script"),i&&m(s),n))for(c=0;o=s[c++];)tn.test(o.type||"")&&n.push(o);return s=null,d},cleanData:function(e,n){for(var r,i,o,a,s=0,u=st.expando,l=st.cache,c=st.support.deleteExpando,f=st.event.special;null!=(o=e[s]);s++)if((n||st.acceptData(o))&&(i=o[u],r=i&&l[i])){if(r.events)for(a in r.events)f[a]?st.event.remove(o,a):st.removeEvent(o,a,r.handle);l[i]&&(delete l[i],c?delete o[u]:o.removeAttribute!==t?o.removeAttribute(u):o[u]=null,K.push(i))}}});var un,ln,cn,fn=/alpha\([^)]*\)/i,pn=/opacity\s*=\s*([^)]*)/,dn=/^(top|right|bottom|left)$/,hn=/^(none|table(?!-c[ea]).+)/,gn=/^margin/,mn=RegExp("^("+ut+")(.*)$","i"),yn=RegExp("^("+ut+")(?!px)[a-z%]+$","i"),vn=RegExp("^([+-])=("+ut+")","i"),bn={BODY:"block"},xn={position:"absolute",visibility:"hidden",display:"block"},Tn={letterSpacing:0,fontWeight:400},wn=["Top","Right","Bottom","Left"],Nn=["Webkit","O","Moz","ms"];st.fn.extend({css:function(e,n){return st.access(this,function(e,n,r){var i,o,a={},s=0;if(st.isArray(n)){for(i=ln(e),o=n.length;o>s;s++)a[n[s]]=st.css(e,n[s],!1,i);return a}return r!==t?st.style(e,n,r):st.css(e,n)},e,n,arguments.length>1)},show:function(){return N(this,!0)},hide:function(){return N(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:w(this))?st(this).show():st(this).hide()})}}),st.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=un(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":st.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=st.camelCase(n),l=e.style;if(n=st.cssProps[u]||(st.cssProps[u]=T(l,u)),s=st.cssHooks[n]||st.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=vn.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(st.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||st.cssNumber[u]||(r+="px"),st.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=st.camelCase(n);return n=st.cssProps[u]||(st.cssProps[u]=T(e.style,u)),s=st.cssHooks[n]||st.cssHooks[u],s&&"get"in s&&(o=s.get(e,!0,r)),o===t&&(o=un(e,n,i)),"normal"===o&&n in Tn&&(o=Tn[n]),r?(a=parseFloat(o),r===!0||st.isNumeric(a)?a||0:o):o},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(ln=function(t){return e.getComputedStyle(t,null)},un=function(e,n,r){var i,o,a,s=r||ln(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||st.contains(e.ownerDocument,e)||(u=st.style(e,n)),yn.test(u)&&gn.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):V.documentElement.currentStyle&&(ln=function(e){return e.currentStyle},un=function(e,n,r){var i,o,a,s=r||ln(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),yn.test(u)&&!dn.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u}),st.each(["height","width"],function(e,n){st.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&hn.test(st.css(e,"display"))?st.swap(e,xn,function(){return E(e,n,i)}):E(e,n,i):t},set:function(e,t,r){var i=r&&ln(e);return C(e,t,r?k(e,n,r,st.support.boxSizing&&"border-box"===st.css(e,"boxSizing",!1,i),i):0)}}}),st.support.opacity||(st.cssHooks.opacity={get:function(e,t){return pn.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=st.isNumeric(t)?"alpha(opacity="+100*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===st.trim(o.replace(fn,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=fn.test(o)?o.replace(fn,i):o+" "+i)}}),st(function(){st.support.reliableMarginRight||(st.cssHooks.marginRight={get:function(e,n){return n?st.swap(e,{display:"inline-block"},un,[e,"marginRight"]):t}}),!st.support.pixelPosition&&st.fn.position&&st.each(["top","left"],function(e,n){st.cssHooks[n]={get:function(e,r){return r?(r=un(e,n),yn.test(r)?st(e).position()[n]+"px":r):t}}})}),st.expr&&st.expr.filters&&(st.expr.filters.hidden=function(e){return 0===e.offsetWidth&&0===e.offsetHeight||!st.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||st.css(e,"display"))},st.expr.filters.visible=function(e){return!st.expr.filters.hidden(e)}),st.each({margin:"",padding:"",border:"Width"},function(e,t){st.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];4>r;r++)i[e+wn[r]+t]=o[r]||o[r-2]||o[0];return i}},gn.test(e)||(st.cssHooks[e+t].set=C)});var Cn=/%20/g,kn=/\[\]$/,En=/\r?\n/g,Sn=/^(?:submit|button|image|reset)$/i,An=/^(?:input|select|textarea|keygen)/i;st.fn.extend({serialize:function(){return st.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=st.prop(this,"elements");return e?st.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!st(this).is(":disabled")&&An.test(this.nodeName)&&!Sn.test(e)&&(this.checked||!Zt.test(e))}).map(function(e,t){var n=st(this).val();return null==n?null:st.isArray(n)?st.map(n,function(e){return{name:t.name,value:e.replace(En,"\r\n")}}):{name:t.name,value:n.replace(En,"\r\n")}}).get()}}),st.param=function(e,n){var r,i=[],o=function(e,t){t=st.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=st.ajaxSettings&&st.ajaxSettings.traditional),st.isArray(e)||e.jquery&&!st.isPlainObject(e))st.each(e,function(){o(this.name,this.value)});else for(r in e)j(r,e[r],n,o);return i.join("&").replace(Cn,"+")};var jn,Dn,Ln=st.now(),Hn=/\?/,Mn=/#.*$/,qn=/([?&])_=[^&]*/,_n=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Fn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,On=/^(?:GET|HEAD)$/,Bn=/^\/\//,Pn=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Rn=st.fn.load,Wn={},$n={},In="*/".concat("*");try{Dn=Y.href}catch(zn){Dn=V.createElement("a"),Dn.href="",Dn=Dn.href}jn=Pn.exec(Dn.toLowerCase())||[],st.fn.load=function(e,n,r){if("string"!=typeof e&&Rn)return Rn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(" ");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),st.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(o="POST"),s.length>0&&st.ajax({url:e,type:o,dataType:"html",data:n}).done(function(e){a=arguments,s.html(i?st("<div>").append(st.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,a||[e.responseText,t,e])}),this},st.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){st.fn[t]=function(e){return this.on(t,e)}}),st.each(["get","post"],function(e,n){st[n]=function(e,r,i,o){return st.isFunction(r)&&(o=o||i,i=r,r=t),st.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),st.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Dn,type:"GET",isLocal:Fn.test(jn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":In,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":st.parseJSON,"text xml":st.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?H(H(e,st.ajaxSettings),t):H(st.ajaxSettings,e)},ajaxPrefilter:D(Wn),ajaxTransport:D($n),ajax:function(e,n){function r(e,n,r,s){var l,f,v,b,T,N=n;2!==x&&(x=2,u&&clearTimeout(u),i=t,a=s||"",w.readyState=e>0?4:0,r&&(b=M(p,w,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=w.getResponseHeader("Last-Modified"),T&&(st.lastModified[o]=T),T=w.getResponseHeader("etag"),T&&(st.etag[o]=T)),304===e?(l=!0,N="notmodified"):(l=q(p,b),N=l.state,f=l.data,v=l.error,l=!v)):(v=N,(e||!N)&&(N="error",0>e&&(e=0))),w.status=e,w.statusText=(n||N)+"",l?g.resolveWith(d,[f,N,w]):g.rejectWith(d,[w,N,v]),w.statusCode(y),y=t,c&&h.trigger(l?"ajaxSuccess":"ajaxError",[w,p,l?f:v]),m.fireWith(d,[w,N]),c&&(h.trigger("ajaxComplete",[w,p]),--st.active||st.event.trigger("ajaxStop")))}"object"==typeof e&&(n=e,e=t),n=n||{};var i,o,a,s,u,l,c,f,p=st.ajaxSetup({},n),d=p.context||p,h=p.context&&(d.nodeType||d.jquery)?st(d):st.event,g=st.Deferred(),m=st.Callbacks("once memory"),y=p.statusCode||{},v={},b={},x=0,T="canceled",w={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!s)for(s={};t=_n.exec(a);)s[t[1].toLowerCase()]=t[2];t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=b[n]=b[n]||e,v[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)y[t]=[y[t],e[t]];else w.always(e[w.status]);return this},abort:function(e){var t=e||T;return i&&i.abort(t),r(0,t),this}};if(g.promise(w).complete=m.add,w.success=w.done,w.error=w.fail,p.url=((e||p.url||Dn)+"").replace(Mn,"").replace(Bn,jn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=st.trim(p.dataType||"*").toLowerCase().match(lt)||[""],null==p.crossDomain&&(l=Pn.exec(p.url.toLowerCase()),p.crossDomain=!(!l||l[1]===jn[1]&&l[2]===jn[2]&&(l[3]||("http:"===l[1]?80:443))==(jn[3]||("http:"===jn[1]?80:443)))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=st.param(p.data,p.traditional)),L(Wn,p,n,w),2===x)return w;c=p.global,c&&0===st.active++&&st.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!On.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(Hn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=qn.test(o)?o.replace(qn,"$1_="+Ln++):o+(Hn.test(o)?"&":"?")+"_="+Ln++)),p.ifModified&&(st.lastModified[o]&&w.setRequestHeader("If-Modified-Since",st.lastModified[o]),st.etag[o]&&w.setRequestHeader("If-None-Match",st.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&w.setRequestHeader("Content-Type",p.contentType),w.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+In+"; q=0.01":""):p.accepts["*"]);for(f in p.headers)w.setRequestHeader(f,p.headers[f]);if(p.beforeSend&&(p.beforeSend.call(d,w,p)===!1||2===x))return w.abort();T="abort";for(f in{success:1,error:1,complete:1})w[f](p[f]);if(i=L($n,p,n,w)){w.readyState=1,c&&h.trigger("ajaxSend",[w,p]),p.async&&p.timeout>0&&(u=setTimeout(function(){w.abort("timeout")},p.timeout));try{x=1,i.send(v,r)}catch(N){if(!(2>x))throw N;r(-1,N)}}else r(-1,"No Transport");return w},getScript:function(e,n){return st.get(e,t,n,"script")},getJSON:function(e,t,n){return st.get(e,t,n,"json")}}),st.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return st.globalEval(e),e}}}),st.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),st.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=V.head||st("head")[0]||V.documentElement;return{send:function(t,i){n=V.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var Xn=[],Un=/(=)\?(?=&|$)|\?\?/;st.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xn.pop()||st.expando+"_"+Ln++;return this[e]=!0,e}}),st.ajaxPrefilter("json jsonp",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Un.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Un.test(n.data)&&"data");return u||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=st.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Un,"$1"+o):n.jsonp!==!1&&(n.url+=(Hn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||st.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,Xn.push(o)),s&&st.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Vn,Yn,Jn=0,Gn=e.ActiveXObject&&function(){var e;for(e in Vn)Vn[e](t,!0)};st.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&_()||F()}:_,Yn=st.ajaxSettings.xhr(),st.support.cors=!!Yn&&"withCredentials"in Yn,Yn=st.support.ajax=!!Yn,Yn&&st.ajaxTransport(function(n){if(!n.crossDomain||st.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,f,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=st.noop,Gn&&delete Vn[a]),i)4!==u.readyState&&u.abort();else{f={},s=u.status,p=u.responseXML,c=u.getAllResponseHeaders(),p&&p.documentElement&&(f.xml=p),"string"==typeof u.responseText&&(f.text=u.responseText);try{l=u.statusText}catch(d){l=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=f.text?200:404}}catch(h){i||o(-1,h)}f&&o(s,l,f,c)},n.async?4===u.readyState?setTimeout(r):(a=++Jn,Gn&&(Vn||(Vn={},st(e).unload(Gn)),Vn[a]=r),u.onreadystatechange=r):r()},abort:function(){r&&r(t,!0)}}}});var Qn,Kn,Zn=/^(?:toggle|show|hide)$/,er=RegExp("^(?:([+-])=|)("+ut+")([a-z%]*)$","i"),tr=/queueHooks$/,nr=[W],rr={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=er.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(st.cssNumber[e]?"":"px"),"px"!==r&&s){s=st.css(i.elem,e,!0)||n||1;do u=u||".5",s/=u,st.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};st.Animation=st.extend(P,{tweener:function(e,t){st.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");for(var n,r=0,i=e.length;i>r;r++)n=e[r],rr[n]=rr[n]||[],rr[n].unshift(t)},prefilter:function(e,t){t?nr.unshift(e):nr.push(e)}}),st.Tween=$,$.prototype={constructor:$,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(st.cssNumber[n]?"":"px")},cur:function(){var e=$.propHooks[this.prop];return e&&e.get?e.get(this):$.propHooks._default.get(this)},run:function(e){var t,n=$.propHooks[this.prop];return this.pos=t=this.options.duration?st.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):$.propHooks._default.set(this),this}},$.prototype.init.prototype=$.prototype,$.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=st.css(e.elem,e.prop,"auto"),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){st.fx.step[e.prop]?st.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[st.cssProps[e.prop]]||st.cssHooks[e.prop])?st.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},$.propHooks.scrollTop=$.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},st.each(["toggle","show","hide"],function(e,t){var n=st.fn[t];st.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(I(t,!0),e,r,i)}}),st.fn.extend({fadeTo:function(e,t,n,r){return this.filter(w).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=st.isEmptyObject(e),o=st.speed(t,n,r),a=function(){var t=P(this,st.extend({},e),o);a.finish=function(){t.stop(!0)},(i||st._data(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=st.timers,a=st._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&tr.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&st.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=st._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=st.timers,a=r?r.length:0;for(n.finish=!0,st.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),st.each({slideDown:I("show"),slideUp:I("hide"),slideToggle:I("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){st.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),st.speed=function(e,t,n){var r=e&&"object"==typeof e?st.extend({},e):{complete:n||!n&&t||st.isFunction(e)&&e,duration:e,easing:n&&t||t&&!st.isFunction(t)&&t};return r.duration=st.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in st.fx.speeds?st.fx.speeds[r.duration]:st.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){st.isFunction(r.old)&&r.old.call(this),r.queue&&st.dequeue(this,r.queue)},r},st.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},st.timers=[],st.fx=$.prototype.init,st.fx.tick=function(){var e,n=st.timers,r=0;for(Qn=st.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.length||st.fx.stop(),Qn=t},st.fx.timer=function(e){e()&&st.timers.push(e)&&st.fx.start()},st.fx.interval=13,st.fx.start=function(){Kn||(Kn=setInterval(st.fx.tick,st.fx.interval))},st.fx.stop=function(){clearInterval(Kn),Kn=null},st.fx.speeds={slow:600,fast:200,_default:400},st.fx.step={},st.expr&&st.expr.filters&&(st.expr.filters.animated=function(e){return st.grep(st.timers,function(t){return e===t.elem}).length}),st.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){st.offset.setOffset(this,e,t)});var n,r,i={top:0,left:0},o=this[0],a=o&&o.ownerDocument;if(a)return n=a.documentElement,st.contains(n,o)?(o.getBoundingClientRect!==t&&(i=o.getBoundingClientRect()),r=z(a),{top:i.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:i.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):i},st.offset={setOffset:function(e,t,n){var r=st.css(e,"position");"static"===r&&(e.style.position="relative");var i,o,a=st(e),s=a.offset(),u=st.css(e,"top"),l=st.css(e,"left"),c=("absolute"===r||"fixed"===r)&&st.inArray("auto",[u,l])>-1,f={},p={};c?(p=a.position(),i=p.top,o=p.left):(i=parseFloat(u)||0,o=parseFloat(l)||0),st.isFunction(t)&&(t=t.call(e,n,s)),null!=t.top&&(f.top=t.top-s.top+i),null!=t.left&&(f.left=t.left-s.left+o),"using"in t?t.using.call(e,f):a.css(f)}},st.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===st.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),st.nodeName(e[0],"html")||(n=e.offset()),n.top+=st.css(e[0],"borderTopWidth",!0),n.left+=st.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-st.css(r,"marginTop",!0),left:t.left-n.left-st.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent||V.documentElement;e&&!st.nodeName(e,"html")&&"static"===st.css(e,"position");)e=e.offsetParent;return e||V.documentElement})}}),st.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);st.fn[e]=function(i){return st.access(this,function(e,i,o){var a=z(e);return o===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?st(a).scrollLeft():o,r?o:st(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}}),st.each({Height:"height",Width:"width"},function(e,n){st.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){st.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return st.access(this,function(n,r,i){var o;return st.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?st.css(n,r,s):st.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=st,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return st})})(window);
//@ sourceMappingURL=jquery.min.map
$.noConflict();
jQuery.noConflict();
/*
---
MooTools: the javascript framework
web build:
- http://mootools.net/core/7c56cfef9dddcf170a5d68e3fb61cfd7
packager build:
- packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff
/*
---
name: Core
description: The heart of MooTools.
license: MIT-style license.
copyright: Copyright (c) 2006-2010 [Valerio Proietti](http://mad4milk.net/).
authors: The MooTools production team (http://mootools.net/developers/)
inspiration:
- Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
- Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
...
*/
(function(){
this.MooTools = {
version: '1.3.2',
build: 'c9f1ff10e9e7facb65e9481049ed1b450959d587'
};
// typeOf, instanceOf
var typeOf = this.typeOf = function(item){
if (item == null) return 'null';
if (item.$family) return item.$family();
if (item.nodeName){
if (item.nodeType == 1) return 'element';
if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
} else if (typeof item.length == 'number'){
if (item.callee) return 'arguments';
if ('item' in item) return 'collection';
}
return typeof item;
};
var instanceOf = this.instanceOf = function(item, object){
if (item == null) return false;
var constructor = item.$constructor || item.constructor;
while (constructor){
if (constructor === object) return true;
constructor = constructor.parent;
}
return item instanceof object;
};
// Function overloading
var Function = this.Function;
var enumerables = true;
for (var i in {toString: 1}) enumerables = null;
if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
Function.prototype.overloadSetter = function(usePlural){
var self = this;
return function(a, b){
if (a == null) return this;
if (usePlural || typeof a != 'string'){
for (var k in a) self.call(this, k, a[k]);
if (enumerables) for (var i = enumerables.length; i--;){
k = enumerables[i];
if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
}
} else {
self.call(this, a, b);
}
return this;
};
};
Function.prototype.overloadGetter = function(usePlural){
var self = this;
return function(a){
var args, result;
if (usePlural || typeof a != 'string') args = a;
else if (arguments.length > 1) args = arguments;
if (args){
result = {};
for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
} else {
result = self.call(this, a);
}
return result;
};
};
Function.prototype.extend = function(key, value){
this[key] = value;
}.overloadSetter();
Function.prototype.implement = function(key, value){
this.prototype[key] = value;
}.overloadSetter();
// From
var slice = Array.prototype.slice;
Function.from = function(item){
return (typeOf(item) == 'function') ? item : function(){
return item;
};
};
// Issue with google api v3 rename Array.form => Array.convert (see mootool 1.6.0)
Array.convert = function(item){
if (item == null) return [];
return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
};
Number.from = function(item){
var number = parseFloat(item);
return isFinite(number) ? number : null;
};
String.from = function(item){
return item + '';
};
// hide, protect
Function.implement({
hide: function(){
this.$hidden = true;
return this;
},
protect: function(){
this.$protected = true;
return this;
}
});
// Type
var Type = this.Type = function(name, object){
if (name){
var lower = name.toLowerCase();
var typeCheck = function(item){
return (typeOf(item) == lower);
};
Type['is' + name] = typeCheck;
if (object != null){
object.prototype.$family = (function(){
return lower;
}).hide();
}
}
if (object == null) return null;
object.extend(this);
object.$constructor = Type;
object.prototype.$constructor = object;
return object;
};
var toString = Object.prototype.toString;
Type.isEnumerable = function(item){
return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
};
var hooks = {};
var hooksOf = function(object){
var type = typeOf(object.prototype);
return hooks[type] || (hooks[type] = []);
};
var implement = function(name, method){
if (method && method.$hidden) return;
var hooks = hooksOf(this);
for (var i = 0; i < hooks.length; i++){
var hook = hooks[i];
if (typeOf(hook) == 'type') implement.call(hook, name, method);
else hook.call(this, name, method);
}
var previous = this.prototype[name];
if (previous == null || !previous.$protected) this.prototype[name] = method;
if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
return method.apply(item, slice.call(arguments, 1));
});
};
var extend = function(name, method){
if (method && method.$hidden) return;
var previous = this[name];
if (previous == null || !previous.$protected) this[name] = method;
};
Type.implement({
implement: implement.overloadSetter(),
extend: extend.overloadSetter(),
alias: function(name, existing){
implement.call(this, name, this.prototype[existing]);
}.overloadSetter(),
mirror: function(hook){
hooksOf(this).push(hook);
return this;
}
});
new Type('Type', Type);
// Default Types
var force = function(name, object, methods){
var isType = (object != Object),
prototype = object.prototype;
if (isType) object = new Type(name, object);
for (var i = 0, l = methods.length; i < l; i++){
var key = methods[i],
generic = object[key],
proto = prototype[key];
if (generic) generic.protect();
if (isType && proto){
delete prototype[key];
prototype[key] = proto.protect();
}
}
if (isType) object.implement(prototype);
return force;
};
force('String', String, [
'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
'slice', 'split', 'substr', 'substring', 'toLowerCase', 'toUpperCase'
])('Array', Array, [
'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
])('Number', Number, [
'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
])('Function', Function, [
'apply', 'call', 'bind'
])('RegExp', RegExp, [
'exec', 'test'
])('Object', Object, [
'create', 'defineProperty', 'defineProperties', 'keys',
'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
])('Date', Date, ['now']);
Object.extend = extend.overloadSetter();
Date.extend('now', function(){
return +(new Date);
});
new Type('Boolean', Boolean);
// fixes NaN returning as Number
Number.prototype.$family = function(){
return isFinite(this) ? 'number' : 'null';
}.hide();
// Number.random
Number.extend('random', function(min, max){
return Math.floor(Math.random() * (max - min + 1) + min);
});
// forEach, each
var hasOwnProperty = Object.prototype.hasOwnProperty;
Object.extend('forEach', function(object, fn, bind){
for (var key in object){
if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
}
});
Object.each = Object.forEach;
Array.implement({
forEach: function(fn, bind){
for (var i = 0, l = this.length; i < l; i++){
if (i in this) fn.call(bind, this[i], i, this);
}
},
each: function(fn, bind){
Array.forEach(this, fn, bind);
return this;
}
});
// Array & Object cloning, Object merging and appending
var cloneOf = function(item){
switch (typeOf(item)){
case 'array': return item.clone();
case 'object': return Object.clone(item);
default: return item;
}
};
Array.implement('clone', function(){
var i = this.length, clone = new Array(i);
while (i--) clone[i] = cloneOf(this[i]);
return clone;
});
var mergeOne = function(source, key, current){
switch (typeOf(current)){
case 'object':
if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
else source[key] = Object.clone(current);
break;
case 'array': source[key] = current.clone(); break;
default: source[key] = current;
}
return source;
};
Object.extend({
merge: function(source, k, v){
if (typeOf(k) == 'string') return mergeOne(source, k, v);
for (var i = 1, l = arguments.length; i < l; i++){
var object = arguments[i];
for (var key in object) mergeOne(source, key, object[key]);
}
return source;
},
clone: function(object){
var clone = {};
for (var key in object) clone[key] = cloneOf(object[key]);
return clone;
},
append: function(original){
for (var i = 1, l = arguments.length; i < l; i++){
var extended = arguments[i] || {};
for (var key in extended) original[key] = extended[key];
}
return original;
}
});
// Object-less types
['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
new Type(name);
});
// Unique ID
var UID = Date.now();
String.extend('uniqueID', function(){
return (UID++).toString(36);
});
})();
/*
---
name: Array
description: Contains Array Prototypes like each, contains, and erase.
license: MIT-style license.
requires: Type
provides: Array
...
*/
Array.implement({
/*<!ES5>*/
every: function(fn, bind){
for (var i = 0, l = this.length; i < l; i++){
if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
}
return true;
},
filter: function(fn, bind){
var results = [];
for (var i = 0, l = this.length; i < l; i++){
if ((i in this) && fn.call(bind, this[i], i, this)) results.push(this[i]);
}
return results;
},
indexOf: function(item, from){
var len = this.length;
for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
if (this[i] === item) return i;
}
return -1;
},
map: function(fn, bind){
var results = [];
for (var i = 0, l = this.length; i < l; i++){
if (i in this) results[i] = fn.call(bind, this[i], i, this);
}
return results;
},
some: function(fn, bind){
for (var i = 0, l = this.length; i < l; i++){
if ((i in this) && fn.call(bind, this[i], i, this)) return true;
}
return false;
},
/*</!ES5>*/
clean: function(){
return this.filter(function(item){
return item != null;
});
},
invoke: function(methodName){
var args = Array.slice(arguments, 1);
return this.map(function(item){
return item[methodName].apply(item, args);
});
},
associate: function(keys){
var obj = {}, length = Math.min(this.length, keys.length);
for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
return obj;
},
link: function(object){
var result = {};
for (var i = 0, l = this.length; i < l; i++){
for (var key in object){
if (object[key](this[i])){
result[key] = this[i];
delete object[key];
break;
}
}
}
return result;
},
contains: function(item, from){
return this.indexOf(item, from) != -1;
},
append: function(array){
this.push.apply(this, array);
return this;
},
getLast: function(){
return (this.length) ? this[this.length - 1] : null;
},
getRandom: function(){
return (this.length) ? this[Number.random(0, this.length - 1)] : null;
},
include: function(item){
if (!this.contains(item)) this.push(item);
return this;
},
combine: function(array){
for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
return this;
},
erase: function(item){
for (var i = this.length; i--;){
if (this[i] === item) this.splice(i, 1);
}
return this;
},
empty: function(){
this.length = 0;
return this;
},
flatten: function(){
var array = [];
for (var i = 0, l = this.length; i < l; i++){
var type = typeOf(this[i]);
if (type == 'null') continue;
array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
}
return array;
},
pick: function(){
for (var i = 0, l = this.length; i < l; i++){
if (this[i] != null) return this[i];
}
return null;
},
hexToRgb: function(array){
if (this.length != 3) return null;
var rgb = this.map(function(value){
if (value.length == 1) value += value;
return value.toInt(16);
});
return (array) ? rgb : 'rgb(' + rgb + ')';
},
rgbToHex: function(array){
if (this.length < 3) return null;
if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
var hex = [];
for (var i = 0; i < 3; i++){
var bit = (this[i] - 0).toString(16);
hex.push((bit.length == 1) ? '0' + bit : bit);
}
return (array) ? hex : '#' + hex.join('');
}
});
/*
---
name: String
description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
license: MIT-style license.
requires: Type
provides: String
...
*/
String.implement({
test: function(regex, params){
return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
},
contains: function(string, separator){
return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
},
trim: function(){
return this.replace(/^\s+|\s+$/g, '');
},
clean: function(){
return this.replace(/\s+/g, ' ').trim();
},
camelCase: function(){
return this.replace(/-\D/g, function(match){
return match.charAt(1).toUpperCase();
});
},
hyphenate: function(){
return this.replace(/[A-Z]/g, function(match){
return ('-' + match.charAt(0).toLowerCase());
});
},
capitalize: function(){
return this.replace(/\b[a-z]/g, function(match){
return match.toUpperCase();
});
},
escapeRegExp: function(){
return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
},
toInt: function(base){
return parseInt(this, base || 10);
},
toFloat: function(){
return parseFloat(this);
},
hexToRgb: function(array){
var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
return (hex) ? hex.slice(1).hexToRgb(array) : null;
},
rgbToHex: function(array){
var rgb = this.match(/\d{1,3}/g);
return (rgb) ? rgb.rgbToHex(array) : null;
},
substitute: function(object, regexp){
return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
if (match.charAt(0) == '\\') return match.slice(1);
return (object[name] != null) ? object[name] : '';
});
}
});
/*
---
name: Number
description: Contains Number Prototypes like limit, round, times, and ceil.
license: MIT-style license.
requires: Type
provides: Number
...
*/
Number.implement({
limit: function(min, max){
return Math.min(max, Math.max(min, this));
},
round: function(precision){
precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
return Math.round(this * precision) / precision;
},
times: function(fn, bind){
for (var i = 0; i < this; i++) fn.call(bind, i, this);
},
toFloat: function(){
return parseFloat(this);
},
toInt: function(base){
return parseInt(this, base || 10);
}
});
Number.alias('each', 'times');
(function(math){
var methods = {};
math.each(function(name){
if (!Number[name]) methods[name] = function(){
return Math[name].apply(null, [this].concat(Array.convert(arguments)));
};
});
Number.implement(methods);
})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
/*
---
name: Function
description: Contains Function Prototypes like create, bind, pass, and delay.
license: MIT-style license.
requires: Type
provides: Function
...
*/
Function.extend({
attempt: function(){
for (var i = 0, l = arguments.length; i < l; i++){
try {
return arguments[i]();
} catch (e){}
}
return null;
}
});
Function.implement({
attempt: function(args, bind){
try {
return this.apply(bind, Array.convert(args));
} catch (e){}
return null;
},
/*<!ES5>*/
bind: function(bind){
var self = this,
args = (arguments.length > 1) ? Array.slice(arguments, 1) : null;
return function(){
if (!args && !arguments.length) return self.call(bind);
if (args && arguments.length) return self.apply(bind, args.concat(Array.convert(arguments)));
return self.apply(bind, args || arguments);
};
},
/*</!ES5>*/
pass: function(args, bind){
var self = this;
if (args != null) args = Array.convert(args);
return function(){
return self.apply(bind, args || arguments);
};
},
delay: function(delay, bind, args){
return setTimeout(this.pass((args == null ? [] : args), bind), delay);
},
periodical: function(periodical, bind, args){
return setInterval(this.pass((args == null ? [] : args), bind), periodical);
}
});
/*
---
name: Object
description: Object generic methods
license: MIT-style license.
requires: Type
provides: [Object, Hash]
...
*/
(function(){
var hasOwnProperty = Object.prototype.hasOwnProperty;
Object.extend({
subset: function(object, keys){
var results = {};
for (var i = 0, l = keys.length; i < l; i++){
var k = keys[i];
if (k in object) results[k] = object[k];
}
return results;
},
map: function(object, fn, bind){
var results = {};
for (var key in object){
if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
}
return results;
},
filter: function(object, fn, bind){
var results = {};
for (var key in object){
var value = object[key];
if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value;
}
return results;
},
every: function(object, fn, bind){
for (var key in object){
if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
}
return true;
},
some: function(object, fn, bind){
for (var key in object){
if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
}
return false;
},
keys: function(object){
var keys = [];
for (var key in object){
if (hasOwnProperty.call(object, key)) keys.push(key);
}
return keys;
},
values: function(object){
var values = [];
for (var key in object){
if (hasOwnProperty.call(object, key)) values.push(object[key]);
}
return values;
},
getLength: function(object){
return Object.keys(object).length;
},
keyOf: function(object, value){
for (var key in object){
if (hasOwnProperty.call(object, key) && object[key] === value) return key;
}
return null;
},
contains: function(object, value){
return Object.keyOf(object, value) != null;
},
toQueryString: function(object, base){
var queryString = [];
Object.each(object, function(value, key){
if (base) key = base + '[' + key + ']';
var result;
switch (typeOf(value)){
case 'object': result = Object.toQueryString(value, key); break;
case 'array':
var qs = {};
value.each(function(val, i){
qs[i] = val;
});
result = Object.toQueryString(qs, key);
break;
default: result = key + '=' + encodeURIComponent(value);
}
if (value != null) queryString.push(result);
});
return queryString.join('&');
}
});
})();
/*
---
name: Browser
description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.
license: MIT-style license.
requires: [Array, Function, Number, String]
provides: [Browser, Window, Document]
...
*/
(function(){
var document = this.document;
var window = document.window = this;
var UID = 1;
this.$uid = (window.ActiveXObject) ? function(item){
return (item.uid || (item.uid = [UID++]))[0];
} : function(item){
return item.uid || (item.uid = UID++);
};
$uid(window);
$uid(document);
var ua = navigator.userAgent.toLowerCase(),
platform = navigator.platform.toLowerCase(),
UA = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0],
mode = UA[1] == 'ie' && document.documentMode;
var Browser = this.Browser = {
extend: Function.prototype.extend,
name: (UA[1] == 'version') ? UA[3] : UA[1],
version: mode || parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
Platform: {
name: ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0]
},
Features: {
xpath: !!(document.evaluate),
air: !!(window.runtime),
query: !!(document.querySelector),
json: !!(window.JSON)
},
Plugins: {}
};
Browser[Browser.name] = true;
Browser[Browser.name + parseInt(Browser.version, 10)] = true;
Browser.Platform[Browser.Platform.name] = true;
// Request
Browser.Request = (function(){
var XMLHTTP = function(){
return new XMLHttpRequest();
};
var MSXML2 = function(){
return new ActiveXObject('MSXML2.XMLHTTP');
};
var MSXML = function(){
return new ActiveXObject('Microsoft.XMLHTTP');
};
return Function.attempt(function(){
XMLHTTP();
return XMLHTTP;
}, function(){
MSXML2();
return MSXML2;
}, function(){
MSXML();
return MSXML;
});
})();
Browser.Features.xhr = !!(Browser.Request);
// Flash detection
var version = (Function.attempt(function(){
return navigator.plugins['Shockwave Flash'].description;
}, function(){
return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
}) || '0 r0').match(/\d+/g);
Browser.Plugins.Flash = {
version: Number(version[0] || '0.' + version[1]) || 0,
build: Number(version[2]) || 0
};
// String scripts
Browser.exec = function(text){
if (!text) return text;
if (window.execScript){
window.execScript(text);
} else {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.text = text;
document.head.appendChild(script);
document.head.removeChild(script);
}
return text;
};
String.implement('stripScripts', function(exec){
var scripts = '';
var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
scripts += code + '\n';
return '';
});
if (exec === true) Browser.exec(scripts);
else if (typeOf(exec) == 'function') exec(scripts, text);
return text;
});
// Window, Document
Browser.extend({
Document: this.Document,
Window: this.Window,
Element: this.Element,
Event: this.Event
});
this.Window = this.$constructor = new Type('Window', function(){});
this.$family = Function.from('window').hide();
Window.mirror(function(name, method){
window[name] = method;
});
this.Document = document.$constructor = new Type('Document', function(){});
document.$family = Function.from('document').hide();
Document.mirror(function(name, method){
document[name] = method;
});
document.html = document.documentElement;
if (!document.head) document.head = document.getElementsByTagName('head')[0];
if (document.execCommand) try {
document.execCommand("BackgroundImageCache", false, true);
} catch (e){}
/*<ltIE9>*/
if (this.attachEvent && !this.addEventListener){
var unloadEvent = function(){
this.detachEvent('onunload', unloadEvent);
document.head = document.html = document.window = null;
};
this.attachEvent('onunload', unloadEvent);
}
// IE fails on collections and <select>.options (refers to <select>)
var arrayFrom = Array.convert;
try {
arrayFrom(document.html.childNodes);
} catch(e){
Array.convert = function(item){
if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){
var i = item.length, array = new Array(i);
while (i--) array[i] = item[i];
return array;
}
return arrayFrom(item);
};
var prototype = Array.prototype,
slice = prototype.slice;
['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){
var method = prototype[name];
Array[name] = function(item){
return method.apply(Array.convert(item), slice.call(arguments, 1));
};
});
}
/*</ltIE9>*/
})();
/*
---
name: Event
description: Contains the Event Class, to make the event object cross-browser.
license: MIT-style license.
requires: [Window, Document, Array, Function, String, Object]
provides: Event
...
*/
var Event = new Type('Event', function(event, win){
if (!win) win = window;
var doc = win.document;
event = event || win.event;
if (event.$extended) return event;
this.$extended = true;
var type = event.type,
target = event.target || event.srcElement,
page = {},
client = {},
related = null,
rightClick, wheel, code, key;
while (target && target.nodeType == 3) target = target.parentNode;
if (type.indexOf('key') != -1){
code = event.which || event.keyCode;
key = Object.keyOf(Event.Keys, code);
if (type == 'keydown'){
var fKey = code - 111;
if (fKey > 0 && fKey < 13) key = 'f' + fKey;
}
if (!key) key = String.fromCharCode(code).toLowerCase();
} else if ((/click|mouse|menu/i).test(type)){
doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
page = {
x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
};
client = {
x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
};
if ((/DOMMouseScroll|mousewheel/).test(type)){
wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
}
rightClick = (event.which == 3) || (event.button == 2);
if ((/over|out/).test(type)){
related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element'];
var testRelated = function(){
while (related && related.nodeType == 3) related = related.parentNode;
return true;
};
var hasRelated = (Browser.firefox2) ? testRelated.attempt() : testRelated();
related = (hasRelated) ? related : null;
}
} else if ((/gesture|touch/i).test(type)){
this.rotation = event.rotation;
this.scale = event.scale;
this.targetTouches = event.targetTouches;
this.changedTouches = event.changedTouches;
var touches = this.touches = event.touches;
if (touches && touches[0]){
var touch = touches[0];
page = {x: touch.pageX, y: touch.pageY};
client = {x: touch.clientX, y: touch.clientY};
}
}
return Object.append(this, {
event: event,
type: type,
page: page,
client: client,
rightClick: rightClick,
wheel: wheel,
relatedTarget: document.id(related),
target: document.id(target),
code: code,
key: key,
shift: event.shiftKey,
control: event.ctrlKey,
alt: event.altKey,
meta: event.metaKey
});
});
Event.Keys = {
'enter': 13,
'up': 38,
'down': 40,
'left': 37,
'right': 39,
'esc': 27,
'space': 32,
'backspace': 8,
'tab': 9,
'delete': 46
};
Event.implement({
stop: function(){
return this.stopPropagation().preventDefault();
},
stopPropagation: function(){
if (this.event.stopPropagation) this.event.stopPropagation();
else this.event.cancelBubble = true;
return this;
},
preventDefault: function(){
if (this.event.preventDefault) this.event.preventDefault();
else this.event.returnValue = false;
return this;
}
});
/*
---
name: Class
description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
license: MIT-style license.
requires: [Array, String, Function, Number]
provides: Class
...
*/
(function(){
var Class = this.Class = new Type('Class', function(params){
if (instanceOf(params, Function)) params = {initialize: params};
var newClass = function(){
reset(this);
if (newClass.$prototyping) return this;
this.$caller = null;
var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
this.$caller = this.caller = null;
return value;
}.extend(this).implement(params);
newClass.$constructor = Class;
newClass.prototype.$constructor = newClass;
newClass.prototype.parent = parent;
return newClass;
});
var parent = function(){
if (!this.$caller) throw new Error('The method "parent" cannot be called.');
var name = this.$caller.$name,
parent = this.$caller.$owner.parent,
previous = (parent) ? parent.prototype[name] : null;
if (!previous) throw new Error('The method "' + name + '" has no parent.');
return previous.apply(this, arguments);
};
var reset = function(object){
for (var key in object){
var value = object[key];
switch (typeOf(value)){
case 'object':
var F = function(){};
F.prototype = value;
object[key] = reset(new F);
break;
case 'array': object[key] = value.clone(); break;
}
}
return object;
};
var wrap = function(self, key, method){
if (method.$origin) method = method.$origin;
var wrapper = function(){
if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
var caller = this.caller, current = this.$caller;
this.caller = current; this.$caller = wrapper;
var result = method.apply(this, arguments);
this.$caller = current; this.caller = caller;
return result;
}.extend({$owner: self, $origin: method, $name: key});
return wrapper;
};
var implement = function(key, value, retain){
if (Class.Mutators.hasOwnProperty(key)){
value = Class.Mutators[key].call(this, value);
if (value == null) return this;
}
if (typeOf(value) == 'function'){
if (value.$hidden) return this;
this.prototype[key] = (retain) ? value : wrap(this, key, value);
} else {
Object.merge(this.prototype, key, value);
}
return this;
};
var getInstance = function(klass){
klass.$prototyping = true;
var proto = new klass;
delete klass.$prototyping;
return proto;
};
Class.implement('implement', implement.overloadSetter());
Class.Mutators = {
Extends: function(parent){
this.parent = parent;
this.prototype = getInstance(parent);
},
Implements: function(items){
Array.convert(items).each(function(item){
var instance = new item;
for (var key in instance) implement.call(this, key, instance[key], true);
}, this);
}
};
})();
/*
---
name: Class.Extras
description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
license: MIT-style license.
requires: Class
provides: [Class.Extras, Chain, Events, Options]
...
*/
(function(){
this.Chain = new Class({
$chain: [],
chain: function(){
this.$chain.append(Array.flatten(arguments));
return this;
},
callChain: function(){
return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
},
clearChain: function(){
this.$chain.empty();
return this;
}
});
var removeOn = function(string){
return string.replace(/^on([A-Z])/, function(full, first){
return first.toLowerCase();
});
};
this.Events = new Class({
$events: {},
addEvent: function(type, fn, internal){
type = removeOn(type);
this.$events[type] = (this.$events[type] || []).include(fn);
if (internal) fn.internal = true;
return this;
},
addEvents: function(events){
for (var type in events) this.addEvent(type, events[type]);
return this;
},
fireEvent: function(type, args, delay){
type = removeOn(type);
var events = this.$events[type];
if (!events) return this;
args = Array.convert(args);
events.each(function(fn){
if (delay) fn.delay(delay, this, args);
else fn.apply(this, args);
}, this);
return this;
},
removeEvent: function(type, fn){
type = removeOn(type);
var events = this.$events[type];
if (events && !fn.internal){
var index =  events.indexOf(fn);
if (index != -1) delete events[index];
}
return this;
},
removeEvents: function(events){
var type;
if (typeOf(events) == 'object'){
for (type in events) this.removeEvent(type, events[type]);
return this;
}
if (events) events = removeOn(events);
for (type in this.$events){
if (events && events != type) continue;
var fns = this.$events[type];
for (var i = fns.length; i--;) if (i in fns){
this.removeEvent(type, fns[i]);
}
}
return this;
}
});
this.Options = new Class({
setOptions: function(){
var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));
if (this.addEvent) for (var option in options){
if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
this.addEvent(option, options[option]);
delete options[option];
}
return this;
}
});
})();
/*
---
name: Slick.Parser
description: Standalone CSS3 Selector parser
provides: Slick.Parser
...
*/
;(function(){
var parsed,
separatorIndex,
combinatorIndex,
reversed,
cache = {},
reverseCache = {},
reUnescape = /\\/g;
var parse = function(expression, isReversed){
if (expression == null) return null;
if (expression.Slick === true) return expression;
expression = ('' + expression).replace(/^\s+|\s+$/g, '');
reversed = !!isReversed;
var currentCache = (reversed) ? reverseCache : cache;
if (currentCache[expression]) return currentCache[expression];
parsed = {
Slick: true,
expressions: [],
raw: expression,
reverse: function(){
return parse(this.raw, true);
}
};
separatorIndex = -1;
while (expression != (expression = expression.replace(regexp, parser)));
parsed.length = parsed.expressions.length;
return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
};
var reverseCombinator = function(combinator){
if (combinator === '!') return ' ';
else if (combinator === ' ') return '!';
else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
else return '!' + combinator;
};
var reverse = function(expression){
var expressions = expression.expressions;
for (var i = 0; i < expressions.length; i++){
var exp = expressions[i];
var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
for (var j = 0; j < exp.length; j++){
var cexp = exp[j];
if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
cexp.combinator = cexp.reverseCombinator;
delete cexp.reverseCombinator;
}
exp.reverse().push(last);
}
return expression;
};
var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){
return '\\' + match;
});
};
var regexp = new RegExp(
/*
#!/usr/bin/env ruby
puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
__END__
"(?x)^(?:\
\\s* ( , ) \\s*               # Separator          \n\
| \\s* ( <combinator>+ ) \\s*   # Combinator         \n\
|      ( \\s+ )                 # CombinatorChildren \n\
|      ( <unicode>+ | \\* )     # Tag                \n\
| \\#  ( <unicode>+       )     # ID                 \n\
| \\.  ( <unicode>+       )     # ClassName          \n\
|                               # Attribute          \n\
\\[  \
\\s* (<unicode1>+)  (?:  \
\\s* ([*^$!~|]?=)  (?:  \
\\s* (?:\
([\"']?)(.*?)\\9 \
)\
)  \
)?  \\s*  \
\\](?!\\]) \n\
|   :+ ( <unicode>+ )(?:\
\\( (?:\
(?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
) \\)\
)?\
)"
*/
"^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
.replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
.replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
.replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
);
function parser(
rawMatch,
separator,
combinator,
combinatorChildren,
tagName,
id,
className,
attributeKey,
attributeOperator,
attributeQuote,
attributeValue,
pseudoMarker,
pseudoClass,
pseudoQuote,
pseudoClassQuotedValue,
pseudoClassValue
){
if (separator || separatorIndex === -1){
parsed.expressions[++separatorIndex] = [];
combinatorIndex = -1;
if (separator) return '';
}
if (combinator || combinatorChildren || combinatorIndex === -1){
combinator = combinator || ' ';
var currentSeparator = parsed.expressions[separatorIndex];
if (reversed && currentSeparator[combinatorIndex])
currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
}
var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
if (tagName){
currentParsed.tag = tagName.replace(reUnescape, '');
} else if (id){
currentParsed.id = id.replace(reUnescape, '');
} else if (className){
className = className.replace(reUnescape, '');
if (!currentParsed.classList) currentParsed.classList = [];
if (!currentParsed.classes) currentParsed.classes = [];
currentParsed.classList.push(className);
currentParsed.classes.push({
value: className,
regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
});
} else if (pseudoClass){
pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
if (!currentParsed.pseudos) currentParsed.pseudos = [];
currentParsed.pseudos.push({
key: pseudoClass.replace(reUnescape, ''),
value: pseudoClassValue,
type: pseudoMarker.length == 1 ? 'class' : 'element'
});
} else if (attributeKey){
attributeKey = attributeKey.replace(reUnescape, '');
attributeValue = (attributeValue || '').replace(reUnescape, '');
var test, regexp;
switch (attributeOperator){
case '^=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue)            ); break;
case '$=' : regexp = new RegExp(            escapeRegExp(attributeValue) +'$'       ); break;
case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
case '|=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue) +'(-|$)'   ); break;
case  '=' : test = function(value){
return attributeValue == value;
}; break;
case '*=' : test = function(value){
return value && value.indexOf(attributeValue) > -1;
}; break;
case '!=' : test = function(value){
return attributeValue != value;
}; break;
default   : test = function(value){
return !!value;
};
}
if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
return false;
};
if (!test) test = function(value){
return value && regexp.test(value);
};
if (!currentParsed.attributes) currentParsed.attributes = [];
currentParsed.attributes.push({
key: attributeKey,
operator: attributeOperator,
value: attributeValue,
test: test
});
}
return '';
};
// Slick NS
var Slick = (this.Slick || {});
Slick.parse = function(expression){
return parse(expression);
};
Slick.escapeRegExp = escapeRegExp;
if (!this.Slick) this.Slick = Slick;
}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
/*
---
name: Slick.Finder
description: The new, superfast css selector engine.
provides: Slick.Finder
requires: Slick.Parser
...
*/
;(function(){
var local = {},
featuresCache = {},
toString = Object.prototype.toString;
// Feature / Bug detection
local.isNativeCode = function(fn){
return (/\{\s*\[native code\]\s*\}/).test('' + fn);
};
local.isXML = function(document){
return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') ||
(document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
};
local.setDocument = function(document){
// convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
var nodeType = document.nodeType;
if (nodeType == 9); // document
else if (nodeType) document = document.ownerDocument; // node
else if (document.navigator) document = document.document; // window
else return;
// check if it's the old document
if (this.document === document) return;
this.document = document;
// check if we have done feature detection on this document before
var root = document.documentElement,
rootUid = this.getUIDXML(root),
features = featuresCache[rootUid],
feature;
if (features){
for (feature in features){
this[feature] = features[feature];
}
return;
}
features = featuresCache[rootUid] = {};
features.root = root;
features.isXMLDocument = this.isXML(document);
features.brokenStarGEBTN
= features.starSelectsClosedQSA
= features.idGetsName
= features.brokenMixedCaseQSA
= features.brokenGEBCN
= features.brokenCheckedQSA
= features.brokenEmptyAttributeQSA
= features.isHTMLDocument
= features.nativeMatchesSelector
= false;
var starSelectsClosed, starSelectsComments,
brokenSecondClassNameGEBCN, cachedGetElementsByClassName,
brokenFormAttributeGetter;
var selected, id = 'slick_uniqueid';
var testNode = document.createElement('div');
var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
testRoot.appendChild(testNode);
// on non-HTML documents innerHTML and getElementsById doesnt work properly
try {
testNode.innerHTML = '<a id="'+id+'"></a>';
features.isHTMLDocument = !!document.getElementById(id);
} catch(e){};
if (features.isHTMLDocument){
testNode.style.display = 'none';
// IE returns comment nodes for getElementsByTagName('*') for some documents
testNode.appendChild(document.createComment(''));
starSelectsComments = (testNode.getElementsByTagName('*').length > 1);
// IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
try {
testNode.innerHTML = 'foo</foo>';
selected = testNode.getElementsByTagName('*');
starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
} catch(e){};
features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;
// IE returns elements with the name instead of just id for getElementsById for some documents
try {
testNode.innerHTML = '<a name="'+ id +'"></a><b id="'+ id +'"></b>';
features.idGetsName = document.getElementById(id) === testNode.firstChild;
} catch(e){};
if (testNode.getElementsByClassName){
// Safari 3.2 getElementsByClassName caches results
try {
testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
testNode.getElementsByClassName('b').length;
testNode.firstChild.className = 'b';
cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
} catch(e){};
// Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
try {
testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
} catch(e){};
features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
}
if (testNode.querySelectorAll){
// IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
try {
testNode.innerHTML = 'foo</foo>';
selected = testNode.querySelectorAll('*');
features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/');
} catch(e){};
// Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
try {
testNode.innerHTML = '<a class="MiX"></a>';
features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
} catch(e){};
// Webkit and Opera dont return selected options on querySelectorAll
try {
testNode.innerHTML = '<select><option selected="selected">a</option></select>';
features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
} catch(e){};
// IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
try {
testNode.innerHTML = '<a class=""></a>';
features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
} catch(e){};
}
// IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
try {
testNode.innerHTML = '<form action="s"><input id="action"/></form>';
brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
} catch(e){};
// native matchesSelector function
features.nativeMatchesSelector = root.matchesSelector || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector;
if (features.nativeMatchesSelector) try {
// if matchesSelector trows errors on incorrect sintaxes we can use it
features.nativeMatchesSelector.call(root, ':slick');
features.nativeMatchesSelector = null;
} catch(e){};
}
try {
root.slick_expando = 1;
delete root.slick_expando;
features.getUID = this.getUIDHTML;
} catch(e) {
features.getUID = this.getUIDXML;
}
testRoot.removeChild(testNode);
testNode = selected = testRoot = null;
// getAttribute
features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){
var method = this.attributeGetters[name];
if (method) return method.call(node);
var attributeNode = node.getAttributeNode(name);
return (attributeNode) ? attributeNode.nodeValue : null;
} : function(node, name){
var method = this.attributeGetters[name];
return (method) ? method.call(node) : node.getAttribute(name);
};
// hasAttribute
features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute) {
return node.hasAttribute(attribute);
} : function(node, attribute) {
node = node.getAttributeNode(attribute);
return !!(node && (node.specified || node.nodeValue));
};
// contains
// FIXME: Add specs: local.contains should be different for xml and html documents?
features.contains = (root && this.isNativeCode(root.contains)) ? function(context, node){
return context.contains(node);
} : (root && root.compareDocumentPosition) ? function(context, node){
return context === node || !!(context.compareDocumentPosition(node) & 16);
} : function(context, node){
if (node) do {
if (node === context) return true;
} while ((node = node.parentNode));
return false;
};
// document order sorting
// credits to Sizzle (http://sizzlejs.com/)
features.documentSorter = (root.compareDocumentPosition) ? function(a, b){
if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
} : ('sourceIndex' in root) ? function(a, b){
if (!a.sourceIndex || !b.sourceIndex) return 0;
return a.sourceIndex - b.sourceIndex;
} : (document.createRange) ? function(a, b){
if (!a.ownerDocument || !b.ownerDocument) return 0;
var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
aRange.setStart(a, 0);
aRange.setEnd(a, 0);
bRange.setStart(b, 0);
bRange.setEnd(b, 0);
return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
} : null ;
root = null;
for (feature in features){
this[feature] = features[feature];
}
};
// Main Method
var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
qsaFailExpCache = {};
local.search = function(context, expression, append, first){
var found = this.found = (first) ? null : (append || []);
if (!context) return found;
else if (context.navigator) context = context.document; // Convert the node from a window to a document
else if (!context.nodeType) return found;
// setup
var parsed, i,
uniques = this.uniques = {},
hasOthers = !!(append && append.length),
contextIsDocument = (context.nodeType == 9);
if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);
// avoid duplicating items already in the append array
if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;
// expression checks
if (typeof expression == 'string'){ // expression is a string
/*<simple-selectors-override>*/
var simpleSelector = expression.match(reSimpleSelector);
simpleSelectors: if (simpleSelector) {
var symbol = simpleSelector[1],
name = simpleSelector[2],
node, nodes;
if (!symbol){
if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
nodes = context.getElementsByTagName(name);
if (first) return nodes[0] || null;
for (i = 0; node = nodes[i++];){
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
}
} else if (symbol == '#'){
if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
node = context.getElementById(name);
if (!node) return found;
if (this.idGetsName && node.getAttributeNode('id').nodeValue != name) break simpleSelectors;
if (first) return node || null;
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
} else if (symbol == '.'){
if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
if (context.getElementsByClassName && !this.brokenGEBCN){
nodes = context.getElementsByClassName(name);
if (first) return nodes[0] || null;
for (i = 0; node = nodes[i++];){
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
}
} else {
var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)');
nodes = context.getElementsByTagName('*');
for (i = 0; node = nodes[i++];){
className = node.className;
if (!(className && matchClass.test(className))) continue;
if (first) return node;
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
}
}
}
if (hasOthers) this.sort(found);
return (first) ? null : found;
}
/*</simple-selectors-override>*/
/*<query-selector-override>*/
querySelector: if (context.querySelectorAll) {
if (!this.isHTMLDocument
|| qsaFailExpCache[expression]
//TODO: only skip when expression is actually mixed case
|| this.brokenMixedCaseQSA
|| (this.brokenCheckedQSA && expression.indexOf(':checked') > -1)
|| (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression))
|| (!contextIsDocument //Abort when !contextIsDocument and...
//  there are multiple expressions in the selector
//  since we currently only fix non-document rooted QSA for single expression selectors
&& expression.indexOf(',') > -1
)
|| Slick.disableQSA
) break querySelector;
var _expression = expression, _context = context;
if (!contextIsDocument){
// non-document rooted QSA
// credits to Andrew Dupont
var currentId = _context.getAttribute('id'), slickid = 'slickid__';
_context.setAttribute('id', slickid);
_expression = '#' + slickid + ' ' + _expression;
context = _context.parentNode;
}
try {
if (first) return context.querySelector(_expression) || null;
else nodes = context.querySelectorAll(_expression);
} catch(e) {
qsaFailExpCache[expression] = 1;
break querySelector;
} finally {
if (!contextIsDocument){
if (currentId) _context.setAttribute('id', currentId);
else _context.removeAttribute('id');
context = _context;
}
}
if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){
if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
} else for (i = 0; node = nodes[i++];){
if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
}
if (hasOthers) this.sort(found);
return found;
}
/*</query-selector-override>*/
parsed = this.Slick.parse(expression);
if (!parsed.length) return found;
} else if (expression == null){ // there is no expression
return found;
} else if (expression.Slick){ // expression is a parsed Slick object
parsed = expression;
} else if (this.contains(context.documentElement || context, expression)){ // expression is a node
(found) ? found.push(expression) : found = expression;
return found;
} else { // other junk
return found;
}
/*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
// cache elements for the nth selectors
this.posNTH = {};
this.posNTHLast = {};
this.posNTHType = {};
this.posNTHTypeLast = {};
/*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
// if append is null and there is only a single selector with one expression use pushArray, else use pushUID
this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;
if (found == null) found = [];
// default engine
var j, m, n;
var combinator, tag, id, classList, classes, attributes, pseudos;
var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;
search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){
combinator = 'combinator:' + currentBit.combinator;
if (!this[combinator]) continue search;
tag        = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
id         = currentBit.id;
classList  = currentBit.classList;
classes    = currentBit.classes;
attributes = currentBit.attributes;
pseudos    = currentBit.pseudos;
lastBit    = (j === (currentExpression.length - 1));
this.bitUniques = {};
if (lastBit){
this.uniques = uniques;
this.found = found;
} else {
this.uniques = {};
this.found = [];
}
if (j === 0){
this[combinator](context, tag, id, classes, attributes, pseudos, classList);
if (first && lastBit && found.length) break search;
} else {
if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){
this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
if (found.length) break search;
} else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
}
currentItems = this.found;
}
// should sort if there are nodes in append and if you pass multiple expressions.
if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);
return (first) ? (found[0] || null) : found;
};
// Utils
local.uidx = 1;
local.uidk = 'slick-uniqueid';
local.getUIDXML = function(node){
var uid = node.getAttribute(this.uidk);
if (!uid){
uid = this.uidx++;
node.setAttribute(this.uidk, uid);
}
return uid;
};
local.getUIDHTML = function(node){
return node.uniqueNumber || (node.uniqueNumber = this.uidx++);
};
// sort based on the setDocument documentSorter method.
local.sort = function(results){
if (!this.documentSorter) return results;
results.sort(this.documentSorter);
return results;
};
/*<pseudo-selectors>*//*<nth-pseudo-selectors>*/
local.cacheNTH = {};
local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;
local.parseNTHArgument = function(argument){
var parsed = argument.match(this.matchNTH);
if (!parsed) return false;
var special = parsed[2] || false;
var a = parsed[1] || 1;
if (a == '-') a = -1;
var b = +parsed[3] || 0;
parsed =
(special == 'n')	? {a: a, b: b} :
(special == 'odd')	? {a: 2, b: 1} :
(special == 'even')	? {a: 2, b: 0} : {a: 0, b: a};
return (this.cacheNTH[argument] = parsed);
};
local.createNTHPseudo = function(child, sibling, positions, ofType){
return function(node, argument){
var uid = this.getUID(node);
if (!this[positions][uid]){
var parent = node.parentNode;
if (!parent) return false;
var el = parent[child], count = 1;
if (ofType){
var nodeName = node.nodeName;
do {
if (el.nodeName != nodeName) continue;
this[positions][this.getUID(el)] = count++;
} while ((el = el[sibling]));
} else {
do {
if (el.nodeType != 1) continue;
this[positions][this.getUID(el)] = count++;
} while ((el = el[sibling]));
}
}
argument = argument || 'n';
var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument);
if (!parsed) return false;
var a = parsed.a, b = parsed.b, pos = this[positions][uid];
if (a == 0) return b == pos;
if (a > 0){
if (pos < b) return false;
} else {
if (b < pos) return false;
}
return ((pos - b) % a) == 0;
};
};
/*</nth-pseudo-selectors>*//*</pseudo-selectors>*/
local.pushArray = function(node, tag, id, classes, attributes, pseudos){
if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node);
};
local.pushUID = function(node, tag, id, classes, attributes, pseudos){
var uid = this.getUID(node);
if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){
this.uniques[uid] = true;
this.found.push(node);
}
};
local.matchNode = function(node, selector){
if (this.isHTMLDocument && this.nativeMatchesSelector){
try {
return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]'));
} catch(matchError) {}
}
var parsed = this.Slick.parse(selector);
if (!parsed) return true;
// simple (single) selectors
var expressions = parsed.expressions, reversedExpressions, simpleExpCounter = 0, i;
for (i = 0; (currentExpression = expressions[i]); i++){
if (currentExpression.length == 1){
var exp = currentExpression[0];
if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true;
simpleExpCounter++;
}
}
if (simpleExpCounter == parsed.length) return false;
var nodes = this.search(this.document, parsed), item;
for (i = 0; item = nodes[i++];){
if (item === node) return true;
}
return false;
};
local.matchPseudo = function(node, name, argument){
var pseudoName = 'pseudo:' + name;
if (this[pseudoName]) return this[pseudoName](node, argument);
var attribute = this.getAttribute(node, name);
return (argument) ? argument == attribute : !!attribute;
};
local.matchSelector = function(node, tag, id, classes, attributes, pseudos){
if (tag){
var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase();
if (tag == '*'){
if (nodeName < '@') return false; // Fix for comment nodes and closed nodes
} else {
if (nodeName != tag) return false;
}
}
if (id && node.getAttribute('id') != id) return false;
var i, part, cls;
if (classes) for (i = classes.length; i--;){
cls = node.getAttribute('class') || node.className;
if (!(cls && classes[i].regexp.test(cls))) return false;
}
if (attributes) for (i = attributes.length; i--;){
part = attributes[i];
if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false;
}
if (pseudos) for (i = pseudos.length; i--;){
part = pseudos[i];
if (!this.matchPseudo(node, part.key, part.value)) return false;
}
return true;
};
var combinators = {
' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level
var i, item, children;
if (this.isHTMLDocument){
getById: if (id){
item = this.document.getElementById(id);
if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){
// all[id] returns all the elements with that name or id inside node
// if theres just one it will return the element, else it will be a collection
children = node.all[id];
if (!children) return;
if (!children[0]) children = [children];
for (i = 0; item = children[i++];){
var idNode = item.getAttributeNode('id');
if (idNode && idNode.nodeValue == id){
this.push(item, tag, null, classes, attributes, pseudos);
break;
}
}
return;
}
if (!item){
// if the context is in the dom we return, else we will try GEBTN, breaking the getById label
if (this.contains(this.root, node)) return;
else break getById;
} else if (this.document !== node && !this.contains(node, item)) return;
this.push(item, tag, null, classes, attributes, pseudos);
return;
}
getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){
children = node.getElementsByClassName(classList.join(' '));
if (!(children && children.length)) break getByClass;
for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos);
return;
}
}
getByTag: {
children = node.getElementsByTagName(tag);
if (!(children && children.length)) break getByTag;
if (!this.brokenStarGEBTN) tag = null;
for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos);
}
},
'>': function(node, tag, id, classes, attributes, pseudos){ // direct children
if ((node = node.firstChild)) do {
if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
} while ((node = node.nextSibling));
},
'+': function(node, tag, id, classes, attributes, pseudos){ // next sibling
while ((node = node.nextSibling)) if (node.nodeType == 1){
this.push(node, tag, id, classes, attributes, pseudos);
break;
}
},
'^': function(node, tag, id, classes, attributes, pseudos){ // first child
node = node.firstChild;
if (node){
if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
else this['combinator:+'](node, tag, id, classes, attributes, pseudos);
}
},
'~': function(node, tag, id, classes, attributes, pseudos){ // next siblings
while ((node = node.nextSibling)){
if (node.nodeType != 1) continue;
var uid = this.getUID(node);
if (this.bitUniques[uid]) break;
this.bitUniques[uid] = true;
this.push(node, tag, id, classes, attributes, pseudos);
}
},
'++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling
this['combinator:+'](node, tag, id, classes, attributes, pseudos);
this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
},
'~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings
this['combinator:~'](node, tag, id, classes, attributes, pseudos);
this['combinator:!~'](node, tag, id, classes, attributes, pseudos);
},
'!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document
while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
},
'!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level)
node = node.parentNode;
if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos);
},
'!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling
while ((node = node.previousSibling)) if (node.nodeType == 1){
this.push(node, tag, id, classes, attributes, pseudos);
break;
}
},
'!^': function(node, tag, id, classes, attributes, pseudos){ // last child
node = node.lastChild;
if (node){
if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos);
else this['combinator:!+'](node, tag, id, classes, attributes, pseudos);
}
},
'!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings
while ((node = node.previousSibling)){
if (node.nodeType != 1) continue;
var uid = this.getUID(node);
if (this.bitUniques[uid]) break;
this.bitUniques[uid] = true;
this.push(node, tag, id, classes, attributes, pseudos);
}
}
};
for (var c in combinators) local['combinator:' + c] = combinators[c];
var pseudos = {
/*<pseudo-selectors>*/
'empty': function(node){
var child = node.firstChild;
return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length;
},
'not': function(node, expression){
return !this.matchNode(node, expression);
},
'contains': function(node, text){
return (node.innerText || node.textContent || '').indexOf(text) > -1;
},
'first-child': function(node){
while ((node = node.previousSibling)) if (node.nodeType == 1) return false;
return true;
},
'last-child': function(node){
while ((node = node.nextSibling)) if (node.nodeType == 1) return false;
return true;
},
'only-child': function(node){
var prev = node;
while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false;
var next = node;
while ((next = next.nextSibling)) if (next.nodeType == 1) return false;
return true;
},
/*<nth-pseudo-selectors>*/
'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'),
'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'),
'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true),
'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true),
'index': function(node, index){
return this['pseudo:nth-child'](node, '' + index + 1);
},
'even': function(node){
return this['pseudo:nth-child'](node, '2n');
},
'odd': function(node){
return this['pseudo:nth-child'](node, '2n+1');
},
/*</nth-pseudo-selectors>*/
/*<of-type-pseudo-selectors>*/
'first-of-type': function(node){
var nodeName = node.nodeName;
while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false;
return true;
},
'last-of-type': function(node){
var nodeName = node.nodeName;
while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false;
return true;
},
'only-of-type': function(node){
var prev = node, nodeName = node.nodeName;
while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false;
var next = node;
while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false;
return true;
},
/*</of-type-pseudo-selectors>*/
// custom pseudos
'enabled': function(node){
return !node.disabled;
},
'disabled': function(node){
return node.disabled;
},
'checked': function(node){
return node.checked || node.selected;
},
'focus': function(node){
return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex'));
},
'root': function(node){
return (node === this.root);
},
'selected': function(node){
return node.selected;
}
/*</pseudo-selectors>*/
};
for (var p in pseudos) local['pseudo:' + p] = pseudos[p];
// attributes methods
local.attributeGetters = {
'class': function(){
return this.getAttribute('class') || this.className;
},
'for': function(){
return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for');
},
'href': function(){
return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href');
},
'style': function(){
return (this.style) ? this.style.cssText : this.getAttribute('style');
},
'tabindex': function(){
var attributeNode = this.getAttributeNode('tabindex');
return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null;
},
'type': function(){
return this.getAttribute('type');
}
};
// Slick
var Slick = local.Slick = (this.Slick || {});
Slick.version = '1.1.5';
// Slick finder
Slick.search = function(context, expression, append){
return local.search(context, expression, append);
};
Slick.find = function(context, expression){
return local.search(context, expression, null, true);
};
// Slick containment checker
Slick.contains = function(container, node){
local.setDocument(container);
return local.contains(container, node);
};
// Slick attribute getter
Slick.getAttribute = function(node, name){
return local.getAttribute(node, name);
};
// Slick matcher
Slick.match = function(node, selector){
if (!(node && selector)) return false;
if (!selector || selector === node) return true;
local.setDocument(node);
return local.matchNode(node, selector);
};
// Slick attribute accessor
Slick.defineAttributeGetter = function(name, fn){
local.attributeGetters[name] = fn;
return this;
};
Slick.lookupAttributeGetter = function(name){
return local.attributeGetters[name];
};
// Slick pseudo accessor
Slick.definePseudo = function(name, fn){
local['pseudo:' + name] = function(node, argument){
return fn.call(node, argument);
};
return this;
};
Slick.lookupPseudo = function(name){
var pseudo = local['pseudo:' + name];
if (pseudo) return function(argument){
return pseudo.call(this, argument);
};
return null;
};
// Slick overrides accessor
Slick.override = function(regexp, fn){
local.override(regexp, fn);
return this;
};
Slick.isXML = local.isXML;
Slick.uidOf = function(node){
return local.getUIDHTML(node);
};
if (!this.Slick) this.Slick = Slick;
}).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);
/*
---
name: Element
description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements.
license: MIT-style license.
requires: [Window, Document, Array, String, Function, Number, Slick.Parser, Slick.Finder]
provides: [Element, Elements, $, $$, Iframe, Selectors]
...
*/
var Element = function(tag, props){
var konstructor = Element.Constructors[tag];
if (konstructor) return konstructor(props);
if (typeof tag != 'string') return document.id(tag).set(props);
if (!props) props = {};
if (!(/^[\w-]+$/).test(tag)){
var parsed = Slick.parse(tag).expressions[0][0];
tag = (parsed.tag == '*') ? 'div' : parsed.tag;
if (parsed.id && props.id == null) props.id = parsed.id;
var attributes = parsed.attributes;
if (attributes) for (var i = 0, l = attributes.length; i < l; i++){
var attr = attributes[i];
if (props[attr.key] != null) continue;
if (attr.value != null && attr.operator == '=') props[attr.key] = attr.value;
else if (!attr.value && !attr.operator) props[attr.key] = true;
}
if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' ');
}
return document.newElement(tag, props);
};
if (Browser.Element) Element.prototype = Browser.Element.prototype;
new Type('Element', Element).mirror(function(name){
if (Array.prototype[name]) return;
var obj = {};
obj[name] = function(){
var results = [], args = arguments, elements = true;
for (var i = 0, l = this.length; i < l; i++){
var element = this[i], result = results[i] = element[name].apply(element, args);
elements = (elements && typeOf(result) == 'element');
}
return (elements) ? new Elements(results) : results;
};
Elements.implement(obj);
});
if (!Browser.Element){
Element.parent = Object;
Element.Prototype = {'$family': Function.from('element').hide()};
Element.mirror(function(name, method){
Element.Prototype[name] = method;
});
}
Element.Constructors = {};
var IFrame = new Type('IFrame', function(){
var params = Array.link(arguments, {
properties: Type.isObject,
iframe: function(obj){
return (obj != null);
}
});
var props = params.properties || {}, iframe;
if (params.iframe) iframe = document.id(params.iframe);
var onload = props.onload || function(){};
delete props.onload;
props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.uniqueID()].pick();
iframe = new Element(iframe || 'iframe', props);
var onLoad = function(){
onload.call(iframe.contentWindow);
};
if (window.frames[props.id]) onLoad();
else iframe.addListener('load', onLoad);
return iframe;
});
var Elements = this.Elements = function(nodes){
if (nodes && nodes.length){
var uniques = {}, node;
for (var i = 0; node = nodes[i++];){
var uid = Slick.uidOf(node);
if (!uniques[uid]){
uniques[uid] = true;
this.push(node);
}
}
}
};
Elements.prototype = {length: 0};
Elements.parent = Array;
new Type('Elements', Elements).implement({
filter: function(filter, bind){
if (!filter) return this;
return new Elements(Array.filter(this, (typeOf(filter) == 'string') ? function(item){
return item.match(filter);
} : filter, bind));
}.protect(),
push: function(){
var length = this.length;
for (var i = 0, l = arguments.length; i < l; i++){
var item = document.id(arguments[i]);
if (item) this[length++] = item;
}
return (this.length = length);
}.protect(),
unshift: function(){
var items = [];
for (var i = 0, l = arguments.length; i < l; i++){
var item = document.id(arguments[i]);
if (item) items.push(item);
}
return Array.prototype.unshift.apply(this, items);
}.protect(),
concat: function(){
var newElements = new Elements(this);
for (var i = 0, l = arguments.length; i < l; i++){
var item = arguments[i];
if (Type.isEnumerable(item)) newElements.append(item);
else newElements.push(item);
}
return newElements;
}.protect(),
append: function(collection){
for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]);
return this;
}.protect(),
empty: function(){
while (this.length) delete this[--this.length];
return this;
}.protect()
});
(function(){
// FF, IE
var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2};
splice.call(object, 1, 1);
if (object[1] == 1) Elements.implement('splice', function(){
var length = this.length;
splice.apply(this, arguments);
while (length >= this.length) delete this[length--];
return this;
}.protect());
Elements.implement(Array.prototype);
Array.mirror(Elements);
/*<ltIE8>*/
var createElementAcceptsHTML;
try {
var x = document.createElement('<input name=x>');
createElementAcceptsHTML = (x.name == 'x');
} catch(e){}
var escapeQuotes = function(html){
return ('' + html).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
};
/*</ltIE8>*/
Document.implement({
newElement: function(tag, props){
if (props && props.checked != null) props.defaultChecked = props.checked;
/*<ltIE8>*/// Fix for readonly name and type properties in IE < 8
if (createElementAcceptsHTML && props){
tag = '<' + tag;
if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"';
if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"';
tag += '>';
delete props.name;
delete props.type;
}
/*</ltIE8>*/
return this.id(this.createElement(tag)).set(props);
}
});
})();
Document.implement({
newTextNode: function(text){
return this.createTextNode(text);
},
getDocument: function(){
return this;
},
getWindow: function(){
return this.window;
},
id: (function(){
var types = {
string: function(id, nocash, doc){
id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1'));
return (id) ? types.element(id, nocash) : null;
},
element: function(el, nocash){
$uid(el);
if (!nocash && !el.$family && !(/^(?:object|embed)$/i).test(el.tagName)){
Object.append(el, Element.Prototype);
}
return el;
},
object: function(obj, nocash, doc){
if (obj.toElement) return types.element(obj.toElement(doc), nocash);
return null;
}
};
types.textnode = types.whitespace = types.window = types.document = function(zero){
return zero;
};
return function(el, nocash, doc){
if (el && el.$family && el.uid) return el;
var type = typeOf(el);
return (types[type]) ? types[type](el, nocash, doc || document) : null;
};
})()
});
if (window.$ == null) Window.implement('$', function(el, nc){
return document.id(el, nc, this.document);
});
Window.implement({
getDocument: function(){
return this.document;
},
getWindow: function(){
return this;
}
});
[Document, Element].invoke('implement', {
getElements: function(expression){
return Slick.search(this, expression, new Elements);
},
getElement: function(expression){
return document.id(Slick.find(this, expression));
}
});
if (window.$$ == null) Window.implement('$$', function(selector){
if (arguments.length == 1){
if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements);
else if (Type.isEnumerable(selector)) return new Elements(selector);
}
return new Elements(arguments);
});
(function(){
var collected = {}, storage = {};
var formProps = {input: 'checked', option: 'selected', textarea: 'value'};
var get = function(uid){
return (storage[uid] || (storage[uid] = {}));
};
var clean = function(item){
var uid = item.uid;
if (item.removeEvents) item.removeEvents();
if (item.clearAttributes) item.clearAttributes();
if (uid != null){
delete collected[uid];
delete storage[uid];
}
return item;
};
var camels = ['defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly',
'rowSpan', 'tabIndex', 'useMap'
];
var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readOnly', 'multiple', 'selected',
'noresize', 'defer', 'defaultChecked'
];
var attributes = {
'html': 'innerHTML',
'class': 'className',
'for': 'htmlFor',
'text': (function(){
var temp = document.createElement('div');
return (temp.textContent == null) ? 'innerText' : 'textContent';
})()
};
var readOnly = ['type'];
var expandos = ['value', 'defaultValue'];
var uriAttrs = /^(?:href|src|usemap)$/i;
bools = bools.associate(bools);
camels = camels.associate(camels.map(String.toLowerCase));
readOnly = readOnly.associate(readOnly);
Object.append(attributes, expandos.associate(expandos));
var inserters = {
before: function(context, element){
var parent = element.parentNode;
if (parent) parent.insertBefore(context, element);
},
after: function(context, element){
var parent = element.parentNode;
if (parent) parent.insertBefore(context, element.nextSibling);
},
bottom: function(context, element){
element.appendChild(context);
},
top: function(context, element){
element.insertBefore(context, element.firstChild);
}
};
inserters.inside = inserters.bottom;
var injectCombinator = function(expression, combinator){
if (!expression) return combinator;
expression = Object.clone(Slick.parse(expression));
var expressions = expression.expressions;
for (var i = expressions.length; i--;)
expressions[i][0].combinator = combinator;
return expression;
};
Element.implement({
set: function(prop, value){
var property = Element.Properties[prop];
(property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value);
}.overloadSetter(),
get: function(prop){
var property = Element.Properties[prop];
return (property && property.get) ? property.get.apply(this) : this.getProperty(prop);
}.overloadGetter(),
erase: function(prop){
var property = Element.Properties[prop];
(property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
return this;
},
setProperty: function(attribute, value){
attribute = camels[attribute] || attribute;
if (value == null) return this.removeProperty(attribute);
var key = attributes[attribute];
(key) ? this[key] = value :
(bools[attribute]) ? this[attribute] = !!value : this.setAttribute(attribute, '' + value);
return this;
},
setProperties: function(attributes){
for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
return this;
},
getProperty: function(attribute){
attribute = camels[attribute] || attribute;
var key = attributes[attribute] || readOnly[attribute];
return (key) ? this[key] :
(bools[attribute]) ? !!this[attribute] :
(uriAttrs.test(attribute) ? this.getAttribute(attribute, 2) :
(key = this.getAttributeNode(attribute)) ? key.nodeValue : null) || null;
},
getProperties: function(){
var args = Array.convert(arguments);
return args.map(this.getProperty, this).associate(args);
},
removeProperty: function(attribute){
attribute = camels[attribute] || attribute;
var key = attributes[attribute];
(key) ? this[key] = '' :
(bools[attribute]) ? this[attribute] = false : this.removeAttribute(attribute);
return this;
},
removeProperties: function(){
Array.each(arguments, this.removeProperty, this);
return this;
},
hasClass: function(className){
return this.className.clean().contains(className, ' ');
},
addClass: function(className){
if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
return this;
},
removeClass: function(className){
this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
return this;
},
toggleClass: function(className, force){
if (force == null) force = !this.hasClass(className);
return (force) ? this.addClass(className) : this.removeClass(className);
},
adopt: function(){
var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length;
if (length > 1) parent = fragment = document.createDocumentFragment();
for (var i = 0; i < length; i++){
var element = document.id(elements[i], true);
if (element) parent.appendChild(element);
}
if (fragment) this.appendChild(fragment);
return this;
},
appendText: function(text, where){
return this.grab(this.getDocument().newTextNode(text), where);
},
grab: function(el, where){
inserters[where || 'bottom'](document.id(el, true), this);
return this;
},
inject: function(el, where){
inserters[where || 'bottom'](this, document.id(el, true));
return this;
},
replaces: function(el){
el = document.id(el, true);
el.parentNode.replaceChild(this, el);
return this;
},
wraps: function(el, where){
el = document.id(el, true);
return this.replaces(el).grab(el, where);
},
getPrevious: function(expression){
return document.id(Slick.find(this, injectCombinator(expression, '!~')));
},
getAllPrevious: function(expression){
return Slick.search(this, injectCombinator(expression, '!~'), new Elements);
},
getNext: function(expression){
return document.id(Slick.find(this, injectCombinator(expression, '~')));
},
getAllNext: function(expression){
return Slick.search(this, injectCombinator(expression, '~'), new Elements);
},
getFirst: function(expression){
return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]);
},
getLast: function(expression){
return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast());
},
getParent: function(expression){
return document.id(Slick.find(this, injectCombinator(expression, '!')));
},
getParents: function(expression){
return Slick.search(this, injectCombinator(expression, '!'), new Elements);
},
getSiblings: function(expression){
return Slick.search(this, injectCombinator(expression, '~~'), new Elements);
},
getChildren: function(expression){
return Slick.search(this, injectCombinator(expression, '>'), new Elements);
},
getWindow: function(){
return this.ownerDocument.window;
},
getDocument: function(){
return this.ownerDocument;
},
getElementById: function(id){
return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1')));
},
getSelected: function(){
this.selectedIndex; // Safari 3.2.1
return new Elements(Array.convert(this.options).filter(function(option){
return option.selected;
}));
},
toQueryString: function(){
var queryString = [];
this.getElements('input, select, textarea').each(function(el){
var type = el.type;
if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return;
var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){
// IE
return document.id(opt).get('value');
}) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value');
Array.convert(value).each(function(val){
if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val));
});
});
return queryString.join('&');
},
destroy: function(){
var children = clean(this).getElementsByTagName('*');
Array.each(children, clean);
Element.dispose(this);
return null;
},
empty: function(){
Array.convert(this.childNodes).each(Element.dispose);
return this;
},
dispose: function(){
return (this.parentNode) ? this.parentNode.removeChild(this) : this;
},
match: function(expression){
return !expression || Slick.match(this, expression);
}
});
var cleanClone = function(node, element, keepid){
if (!keepid) node.setAttributeNode(document.createAttribute('id'));
if (node.clearAttributes){
node.clearAttributes();
node.mergeAttributes(element);
node.removeAttribute('uid');
if (node.options){
var no = node.options, eo = element.options;
for (var i = no.length; i--;) no[i].selected = eo[i].selected;
}
}
var prop = formProps[element.tagName.toLowerCase()];
if (prop && element[prop]) node[prop] = element[prop];
};
Element.implement('clone', function(contents, keepid){
contents = contents !== false;
var clone = this.cloneNode(contents), i;
if (contents){
var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
for (i = ce.length; i--;) cleanClone(ce[i], te[i], keepid);
}
cleanClone(clone, this, keepid);
if (Browser.ie){
var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object');
for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML;
}
return document.id(clone);
});
var contains = {contains: function(element){
return Slick.contains(this, element);
}};
if (!document.contains) Document.implement(contains);
if (!document.createElement('div').contains) Element.implement(contains);
[Element, Window, Document].invoke('implement', {
addListener: function(type, fn){
if (type == 'unload'){
var old = fn, self = this;
fn = function(){
self.removeListener('unload', fn);
old();
};
} else {
collected[$uid(this)] = this;
}
if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]);
else this.attachEvent('on' + type, fn);
return this;
},
removeListener: function(type, fn){
if (this.removeEventListener) this.removeEventListener(type, fn, !!arguments[2]);
else this.detachEvent('on' + type, fn);
return this;
},
retrieve: function(property, dflt){
var storage = get($uid(this)), prop = storage[property];
if (dflt != null && prop == null) prop = storage[property] = dflt;
return prop != null ? prop : null;
},
store: function(property, value){
var storage = get($uid(this));
storage[property] = value;
return this;
},
eliminate: function(property){
var storage = get($uid(this));
delete storage[property];
return this;
}
});
/*<ltIE9>*/
if (window.attachEvent && !window.addEventListener) window.addListener('unload', function(){
Object.each(collected, clean);
if (window.CollectGarbage) CollectGarbage();
});
/*</ltIE9>*/
})();
Element.Properties = {};
Element.Properties.style = {
set: function(style){
this.style.cssText = style;
},
get: function(){
return this.style.cssText;
},
erase: function(){
this.style.cssText = '';
}
};
Element.Properties.tag = {
get: function(){
return this.tagName.toLowerCase();
}
};
/*<ltIE9>*/
(function(maxLength){
if (maxLength != null) Element.Properties.maxlength = Element.Properties.maxLength = {
get: function(){
var maxlength = this.getAttribute('maxLength');
return maxlength == maxLength ? null : maxlength;
}
};
})(document.createElement('input').getAttribute('maxLength'));
/*</ltIE9>*/
/*<!webkit>*/
Element.Properties.html = (function(){
var tableTest = Function.attempt(function(){
var table = document.createElement('table');
table.innerHTML = '<tr><td></td></tr>';
});
var wrapper = document.createElement('div');
var translations = {
table: [1, '<table>', '</table>'],
select: [1, '<select>', '</select>'],
tbody: [2, '<table><tbody>', '</tbody></table>'],
tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
};
translations.thead = translations.tfoot = translations.tbody;
var html = {
set: function(){
var html = Array.flatten(arguments).join('');
var wrap = (!tableTest && translations[this.get('tag')]);
if (wrap){
var first = wrapper;
first.innerHTML = wrap[1] + html + wrap[2];
for (var i = wrap[0]; i--;) first = first.firstChild;
this.empty().adopt(first.childNodes);
} else {
this.innerHTML = html;
}
}
};
html.erase = html.set;
return html;
})();
/*</!webkit>*/
/*
---
name: Element.Style
description: Contains methods for interacting with the styles of Elements in a fashionable way.
license: MIT-style license.
requires: Element
provides: Element.Style
...
*/
(function(){
var html = document.html;
Element.Properties.styles = {set: function(styles){
this.setStyles(styles);
}};
var hasOpacity = (html.style.opacity != null);
var reAlpha = /alpha\(opacity=([\d.]+)\)/i;
var setOpacity = function(element, opacity){
if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1;
if (hasOpacity){
element.style.opacity = opacity;
} else {
opacity = (opacity * 100).limit(0, 100).round();
opacity = (opacity == 100) ? '' : 'alpha(opacity=' + opacity + ')';
var filter = element.style.filter || element.getComputedStyle('filter') || '';
element.style.filter = reAlpha.test(filter) ? filter.replace(reAlpha, opacity) : filter + opacity;
}
};
Element.Properties.opacity = {
set: function(opacity){
var visibility = this.style.visibility;
if (opacity == 0 && visibility != 'hidden') this.style.visibility = 'hidden';
else if (opacity != 0 && visibility != 'visible') this.style.visibility = 'visible';
setOpacity(this, opacity);
},
get: (hasOpacity) ? function(){
var opacity = this.style.opacity || this.getComputedStyle('opacity');
return (opacity == '') ? 1 : opacity;
} : function(){
var opacity, filter = (this.style.filter || this.getComputedStyle('filter'));
if (filter) opacity = filter.match(reAlpha);
return (opacity == null || filter == null) ? 1 : (opacity[1] / 100);
}
};
var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat';
Element.implement({
getComputedStyle: function(property){
if (this.currentStyle) return this.currentStyle[property.camelCase()];
var defaultView = Element.getDocument(this).defaultView,
computed = defaultView ? defaultView.getComputedStyle(this, null) : null;
return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : null;
},
setOpacity: function(value){
setOpacity(this, value);
return this;
},
getOpacity: function(){
return this.get('opacity');
},
setStyle: function(property, value){
switch (property){
case 'opacity': return this.set('opacity', parseFloat(value));
case 'float': property = floatName;
}
property = property.camelCase();
if (typeOf(value) != 'string'){
var map = (Element.Styles[property] || '@').split(' ');
value = Array.convert(value).map(function(val, i){
if (!map[i]) return '';
return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
}).join(' ');
} else if (value == String(Number(value))){
value = Math.round(value);
}
this.style[property] = value;
return this;
},
getStyle: function(property){
switch (property){
case 'opacity': return this.get('opacity');
case 'float': property = floatName;
}
property = property.camelCase();
var result = this.style[property];
if (!result || property == 'zIndex'){
result = [];
for (var style in Element.ShortStyles){
if (property != style) continue;
for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
return result.join(' ');
}
result = this.getComputedStyle(property);
}
if (result){
result = String(result);
var color = result.match(/rgba?\([\d\s,]+\)/);
if (color) result = result.replace(color[0], color[0].rgbToHex());
}
if (Browser.opera || (Browser.ie && isNaN(parseFloat(result)))){
if ((/^(height|width)$/).test(property)){
var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
values.each(function(value){
size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
}, this);
return this['offset' + property.capitalize()] - size + 'px';
}
if (Browser.opera && String(result).indexOf('px') != -1) return result;
if ((/^border(.+)Width|margin|padding/).test(property)) return '0px';
}
return result;
},
setStyles: function(styles){
for (var style in styles) this.setStyle(style, styles[style]);
return this;
},
getStyles: function(){
var result = {};
Array.flatten(arguments).each(function(key){
result[key] = this.getStyle(key);
}, this);
return result;
}
});
Element.Styles = {
left: '@px', top: '@px', bottom: '@px', right: '@px',
width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
};
Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
var Short = Element.ShortStyles;
var All = Element.Styles;
['margin', 'padding'].each(function(style){
var sd = style + direction;
Short[style][sd] = All[sd] = '@px';
});
var bd = 'border' + direction;
Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
Short[bd] = {};
Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
});
})();
/*
---
name: Element.Event
description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events.
license: MIT-style license.
requires: [Element, Event]
provides: Element.Event
...
*/
(function(){
Element.Properties.events = {set: function(events){
this.addEvents(events);
}};
[Element, Window, Document].invoke('implement', {
addEvent: function(type, fn){
var events = this.retrieve('events', {});
if (!events[type]) events[type] = {keys: [], values: []};
if (events[type].keys.contains(fn)) return this;
events[type].keys.push(fn);
var realType = type,
custom = Element.Events[type],
condition = fn,
self = this;
if (custom){
if (custom.onAdd) custom.onAdd.call(this, fn);
if (custom.condition){
condition = function(event){
if (custom.condition.call(this, event)) return fn.call(this, event);
return true;
};
}
realType = custom.base || realType;
}
var defn = function(){
return fn.call(self);
};
var nativeEvent = Element.NativeEvents[realType];
if (nativeEvent){
if (nativeEvent == 2){
defn = function(event){
event = new Event(event, self.getWindow());
if (condition.call(self, event) === false) event.stop();
};
}
this.addListener(realType, defn, arguments[2]);
}
events[type].values.push(defn);
return this;
},
removeEvent: function(type, fn){
var events = this.retrieve('events');
if (!events || !events[type]) return this;
var list = events[type];
var index = list.keys.indexOf(fn);
if (index == -1) return this;
var value = list.values[index];
delete list.keys[index];
delete list.values[index];
var custom = Element.Events[type];
if (custom){
if (custom.onRemove) custom.onRemove.call(this, fn);
type = custom.base || type;
}
return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this;
},
addEvents: function(events){
for (var event in events) this.addEvent(event, events[event]);
return this;
},
removeEvents: function(events){
var type;
if (typeOf(events) == 'object'){
for (type in events) this.removeEvent(type, events[type]);
return this;
}
var attached = this.retrieve('events');
if (!attached) return this;
if (!events){
for (type in attached) this.removeEvents(type);
this.eliminate('events');
} else if (attached[events]){
attached[events].keys.each(function(fn){
this.removeEvent(events, fn);
}, this);
delete attached[events];
}
return this;
},
fireEvent: function(type, args, delay){
var events = this.retrieve('events');
if (!events || !events[type]) return this;
args = Array.convert(args);
events[type].keys.each(function(fn){
if (delay) fn.delay(delay, this, args);
else fn.apply(this, args);
}, this);
return this;
},
cloneEvents: function(from, type){
from = document.id(from);
var events = from.retrieve('events');
if (!events) return this;
if (!type){
for (var eventType in events) this.cloneEvents(from, eventType);
} else if (events[type]){
events[type].keys.each(function(fn){
this.addEvent(type, fn);
}, this);
}
return this;
}
});
Element.NativeEvents = {
click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
keydown: 2, keypress: 2, keyup: 2, //keyboard
orientationchange: 2, // mobile
touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch
gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture
focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements
load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
error: 1, abort: 1, scroll: 1 //misc
};
var check = function(event){
var related = event.relatedTarget;
if (related == null) return true;
if (!related) return false;
return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related));
};
Element.Events = {
mouseenter: {
base: 'mouseover',
condition: check
},
mouseleave: {
base: 'mouseout',
condition: check
},
mousewheel: {
base: (Browser.firefox) ? 'DOMMouseScroll' : 'mousewheel'
}
};
})();
/*
---
name: Element.Dimensions
description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
license: MIT-style license.
credits:
- Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
- Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
requires: [Element, Element.Style]
provides: [Element.Dimensions]
...
*/
(function(){
var element = document.createElement('div'),
child = document.createElement('div');
element.style.height = '0';
element.appendChild(child);
var brokenOffsetParent = (child.offsetParent === element);
element = child = null;
var isOffset = function(el){
return styleString(el, 'position') != 'static' || isBody(el);
};
var isOffsetStatic = function(el){
return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
};
Element.implement({
scrollTo: function(x, y){
if (isBody(this)){
this.getWindow().scrollTo(x, y);
} else {
this.scrollLeft = x;
this.scrollTop = y;
}
return this;
},
getSize: function(){
if (isBody(this)) return this.getWindow().getSize();
return {x: this.offsetWidth, y: this.offsetHeight};
},
getScrollSize: function(){
if (isBody(this)) return this.getWindow().getScrollSize();
return {x: this.scrollWidth, y: this.scrollHeight};
},
getScroll: function(){
if (isBody(this)) return this.getWindow().getScroll();
return {x: this.scrollLeft, y: this.scrollTop};
},
getScrolls: function(){
var element = this.parentNode, position = {x: 0, y: 0};
while (element && !isBody(element)){
position.x += element.scrollLeft;
position.y += element.scrollTop;
element = element.parentNode;
}
return position;
},
getOffsetParent: brokenOffsetParent ? function(){
var element = this;
if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
while ((element = element.parentNode)){
if (isOffsetCheck(element)) return element;
}
return null;
} : function(){
var element = this;
if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
try {
return element.offsetParent;
} catch(e) {}
return null;
},
getOffsets: function(){
if (this.getBoundingClientRect && !Browser.Platform.ios){
var bound = this.getBoundingClientRect(),
html = document.id(this.getDocument().documentElement),
htmlScroll = html.getScroll(),
elemScrolls = this.getScrolls(),
isFixed = (styleString(this, 'position') == 'fixed');
return {
x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
y: bound.top.toInt()  + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
};
}
var element = this, position = {x: 0, y: 0};
if (isBody(this)) return position;
while (element && !isBody(element)){
position.x += element.offsetLeft;
position.y += element.offsetTop;
if (Browser.firefox){
if (!borderBox(element)){
position.x += leftBorder(element);
position.y += topBorder(element);
}
var parent = element.parentNode;
if (parent && styleString(parent, 'overflow') != 'visible'){
position.x += leftBorder(parent);
position.y += topBorder(parent);
}
} else if (element != this && Browser.safari){
position.x += leftBorder(element);
position.y += topBorder(element);
}
element = element.offsetParent;
}
if (Browser.firefox && !borderBox(this)){
position.x -= leftBorder(this);
position.y -= topBorder(this);
}
return position;
},
getPosition: function(relative){
if (isBody(this)) return {x: 0, y: 0};
var offset = this.getOffsets(),
scroll = this.getScrolls();
var position = {
x: offset.x - scroll.x,
y: offset.y - scroll.y
};
if (relative && (relative = document.id(relative))){
var relativePosition = relative.getPosition();
return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
}
return position;
},
getCoordinates: function(element){
if (isBody(this)) return this.getWindow().getCoordinates();
var position = this.getPosition(element),
size = this.getSize();
var obj = {
left: position.x,
top: position.y,
width: size.x,
height: size.y
};
obj.right = obj.left + obj.width;
obj.bottom = obj.top + obj.height;
return obj;
},
computePosition: function(obj){
return {
left: obj.x - styleNumber(this, 'margin-left'),
top: obj.y - styleNumber(this, 'margin-top')
};
},
setPosition: function(obj){
return this.setStyles(this.computePosition(obj));
}
});
[Document, Window].invoke('implement', {
getSize: function(){
var doc = getCompatElement(this);
return {x: doc.clientWidth, y: doc.clientHeight};
},
getScroll: function(){
var win = this.getWindow(), doc = getCompatElement(this);
return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
},
getScrollSize: function(){
var doc = getCompatElement(this),
min = this.getSize(),
body = this.getDocument().body;
return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)};
},
getPosition: function(){
return {x: 0, y: 0};
},
getCoordinates: function(){
var size = this.getSize();
return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
}
});
// private methods
var styleString = Element.getComputedStyle;
function styleNumber(element, style){
return styleString(element, style).toInt() || 0;
}
function borderBox(element){
return styleString(element, '-moz-box-sizing') == 'border-box';
}
function topBorder(element){
return styleNumber(element, 'border-top-width');
}
function leftBorder(element){
return styleNumber(element, 'border-left-width');
}
function isBody(element){
return (/^(?:body|html)$/i).test(element.tagName);
}
function getCompatElement(element){
var doc = element.getDocument();
return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
}
})();
//aliases
Element.alias({position: 'setPosition'}); //compatability
[Window, Document, Element].invoke('implement', {
getHeight: function(){
return this.getSize().y;
},
getWidth: function(){
return this.getSize().x;
},
getScrollTop: function(){
return this.getScroll().y;
},
getScrollLeft: function(){
return this.getScroll().x;
},
getScrollHeight: function(){
return this.getScrollSize().y;
},
getScrollWidth: function(){
return this.getScrollSize().x;
},
getTop: function(){
return this.getPosition().y;
},
getLeft: function(){
return this.getPosition().x;
}
});
/*
---
name: Fx
description: Contains the basic animation logic to be extended by all other Fx Classes.
license: MIT-style license.
requires: [Chain, Events, Options]
provides: Fx
...
*/
(function(){
var Fx = this.Fx = new Class({
Implements: [Chain, Events, Options],
options: {
/*
onStart: nil,
onCancel: nil,
onComplete: nil,
*/
fps: 60,
unit: false,
duration: 500,
frames: null,
frameSkip: true,
link: 'ignore'
},
initialize: function(options){
this.subject = this.subject || this;
this.setOptions(options);
},
getTransition: function(){
return function(p){
return -(Math.cos(Math.PI * p) - 1) / 2;
};
},
step: function(now){
if (this.options.frameSkip){
var diff = (this.time != null) ? (now - this.time) : 0, frames = diff / this.frameInterval;
this.time = now;
this.frame += frames;
} else {
this.frame++;
}
if (this.frame < this.frames){
var delta = this.transition(this.frame / this.frames);
this.set(this.compute(this.from, this.to, delta));
} else {
this.frame = this.frames;
this.set(this.compute(this.from, this.to, 1));
this.stop();
}
},
set: function(now){
return now;
},
compute: function(from, to, delta){
return Fx.compute(from, to, delta);
},
check: function(){
if (!this.isRunning()) return true;
switch (this.options.link){
case 'cancel': this.cancel(); return true;
case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
}
return false;
},
start: function(from, to){
if (!this.check(from, to)) return this;
this.from = from;
this.to = to;
this.frame = (this.options.frameSkip) ? 0 : -1;
this.time = null;
this.transition = this.getTransition();
var frames = this.options.frames, fps = this.options.fps, duration = this.options.duration;
this.duration = Fx.Durations[duration] || duration.toInt();
this.frameInterval = 1000 / fps;
this.frames = frames || Math.round(this.duration / this.frameInterval);
this.fireEvent('start', this.subject);
pushInstance.call(this, fps);
return this;
},
stop: function(){
if (this.isRunning()){
this.time = null;
pullInstance.call(this, this.options.fps);
if (this.frames == this.frame){
this.fireEvent('complete', this.subject);
if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
} else {
this.fireEvent('stop', this.subject);
}
}
return this;
},
cancel: function(){
if (this.isRunning()){
this.time = null;
pullInstance.call(this, this.options.fps);
this.frame = this.frames;
this.fireEvent('cancel', this.subject).clearChain();
}
return this;
},
pause: function(){
if (this.isRunning()){
this.time = null;
pullInstance.call(this, this.options.fps);
}
return this;
},
resume: function(){
if ((this.frame < this.frames) && !this.isRunning()) pushInstance.call(this, this.options.fps);
return this;
},
isRunning: function(){
var list = instances[this.options.fps];
return list && list.contains(this);
}
});
Fx.compute = function(from, to, delta){
return (to - from) * delta + from;
};
Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
// global timers
var instances = {}, timers = {};
var loop = function(){
var now = Date.now();
for (var i = this.length; i--;){
var instance = this[i];
if (instance) instance.step(now);
}
};
var pushInstance = function(fps){
var list = instances[fps] || (instances[fps] = []);
list.push(this);
if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list);
};
var pullInstance = function(fps){
var list = instances[fps];
if (list){
list.erase(this);
if (!list.length && timers[fps]){
delete instances[fps];
timers[fps] = clearInterval(timers[fps]);
}
}
};
})();
/*
---
name: Fx.CSS
description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
license: MIT-style license.
requires: [Fx, Element.Style]
provides: Fx.CSS
...
*/
Fx.CSS = new Class({
Extends: Fx,
//prepares the base from/to object
prepare: function(element, property, values){
values = Array.convert(values);
if (values[1] == null){
values[1] = values[0];
values[0] = element.getStyle(property);
}
var parsed = values.map(this.parse);
return {from: parsed[0], to: parsed[1]};
},
//parses a value into an array
parse: function(value){
value = Function.from(value)();
value = (typeof value == 'string') ? value.split(' ') : Array.convert(value);
return value.map(function(val){
val = String(val);
var found = false;
Object.each(Fx.CSS.Parsers, function(parser, key){
if (found) return;
var parsed = parser.parse(val);
if (parsed || parsed === 0) found = {value: parsed, parser: parser};
});
found = found || {value: val, parser: Fx.CSS.Parsers.String};
return found;
});
},
//computes by a from and to prepared objects, using their parsers.
compute: function(from, to, delta){
var computed = [];
(Math.min(from.length, to.length)).times(function(i){
computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
});
computed.$family = Function.from('fx:css:value');
return computed;
},
//serves the value as settable
serve: function(value, unit){
if (typeOf(value) != 'fx:css:value') value = this.parse(value);
var returned = [];
value.each(function(bit){
returned = returned.concat(bit.parser.serve(bit.value, unit));
});
return returned;
},
//renders the change to an element
render: function(element, property, value, unit){
element.setStyle(property, this.serve(value, unit));
},
//searches inside the page css to find the values for a selector
search: function(selector){
if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
Array.each(document.styleSheets, function(sheet, j){
var href = sheet.href;
if (href && href.contains('://') && !href.contains(document.domain)) return;
var rules = sheet.rules || sheet.cssRules;
Array.each(rules, function(rule, i){
if (!rule.style) return;
var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
return m.toLowerCase();
}) : null;
if (!selectorText || !selectorTest.test(selectorText)) return;
Object.each(Element.Styles, function(value, style){
if (!rule.style[style] || Element.ShortStyles[style]) return;
value = String(rule.style[style]);
to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
});
});
});
return Fx.CSS.Cache[selector] = to;
}
});
Fx.CSS.Cache = {};
Fx.CSS.Parsers = {
Color: {
parse: function(value){
if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
},
compute: function(from, to, delta){
return from.map(function(value, i){
return Math.round(Fx.compute(from[i], to[i], delta));
});
},
serve: function(value){
return value.map(Number);
}
},
Number: {
parse: parseFloat,
compute: Fx.compute,
serve: function(value, unit){
return (unit) ? value + unit : value;
}
},
String: {
parse: Function.from(false),
compute: function(zero, one){
return one;
},
serve: function(zero){
return zero;
}
}
};
/*
---
name: Fx.Tween
description: Formerly Fx.Style, effect to transition any CSS property for an element.
license: MIT-style license.
requires: Fx.CSS
provides: [Fx.Tween, Element.fade, Element.highlight]
...
*/
Fx.Tween = new Class({
Extends: Fx.CSS,
initialize: function(element, options){
this.element = this.subject = document.id(element);
this.parent(options);
},
set: function(property, now){
if (arguments.length == 1){
now = property;
property = this.property || this.options.property;
}
this.render(this.element, property, now, this.options.unit);
return this;
},
start: function(property, from, to){
if (!this.check(property, from, to)) return this;
var args = Array.flatten(arguments);
this.property = this.options.property || args.shift();
var parsed = this.prepare(this.element, this.property, args);
return this.parent(parsed.from, parsed.to);
}
});
Element.Properties.tween = {
set: function(options){
this.get('tween').cancel().setOptions(options);
return this;
},
get: function(){
var tween = this.retrieve('tween');
if (!tween){
tween = new Fx.Tween(this, {link: 'cancel'});
this.store('tween', tween);
}
return tween;
}
};
Element.implement({
tween: function(property, from, to){
this.get('tween').start(arguments);
return this;
},
fade: function(how){
var fade = this.get('tween'), o = 'opacity', toggle;
how = [how, 'toggle'].pick();
switch (how){
case 'in': fade.start(o, 1); break;
case 'out': fade.start(o, 0); break;
case 'show': fade.set(o, 1); break;
case 'hide': fade.set(o, 0); break;
case 'toggle':
var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
fade.start(o, (flag) ? 0 : 1);
this.store('fade:flag', !flag);
toggle = true;
break;
default: fade.start(o, arguments);
}
if (!toggle) this.eliminate('fade:flag');
return this;
},
highlight: function(start, end){
if (!end){
end = this.retrieve('highlight:original', this.getStyle('background-color'));
end = (end == 'transparent') ? '#fff' : end;
}
var tween = this.get('tween');
tween.start('background-color', start || '#ffff88', end).chain(function(){
this.setStyle('background-color', this.retrieve('highlight:original'));
tween.callChain();
}.bind(this));
return this;
}
});
/*
---
name: Fx.Morph
description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
license: MIT-style license.
requires: Fx.CSS
provides: Fx.Morph
...
*/
Fx.Morph = new Class({
Extends: Fx.CSS,
initialize: function(element, options){
this.element = this.subject = document.id(element);
this.parent(options);
},
set: function(now){
if (typeof now == 'string') now = this.search(now);
for (var p in now) this.render(this.element, p, now[p], this.options.unit);
return this;
},
compute: function(from, to, delta){
var now = {};
for (var p in from) now[p] = this.parent(from[p], to[p], delta);
return now;
},
start: function(properties){
if (!this.check(properties)) return this;
if (typeof properties == 'string') properties = this.search(properties);
var from = {}, to = {};
for (var p in properties){
var parsed = this.prepare(this.element, p, properties[p]);
from[p] = parsed.from;
to[p] = parsed.to;
}
return this.parent(from, to);
}
});
Element.Properties.morph = {
set: function(options){
this.get('morph').cancel().setOptions(options);
return this;
},
get: function(){
var morph = this.retrieve('morph');
if (!morph){
morph = new Fx.Morph(this, {link: 'cancel'});
this.store('morph', morph);
}
return morph;
}
};
Element.implement({
morph: function(props){
this.get('morph').start(props);
return this;
}
});
/*
---
name: Fx.Transitions
description: Contains a set of advanced transitions to be used with any of the Fx Classes.
license: MIT-style license.
credits:
- Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
requires: Fx
provides: Fx.Transitions
...
*/
Fx.implement({
getTransition: function(){
var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
if (typeof trans == 'string'){
var data = trans.split(':');
trans = Fx.Transitions;
trans = trans[data[0]] || trans[data[0].capitalize()];
if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
}
return trans;
}
});
Fx.Transition = function(transition, params){
params = Array.convert(params);
var easeIn = function(pos){
return transition(pos, params);
};
return Object.append(easeIn, {
easeIn: easeIn,
easeOut: function(pos){
return 1 - transition(1 - pos, params);
},
easeInOut: function(pos){
return (pos <= 0.5 ? transition(2 * pos, params) : (2 - transition(2 * (1 - pos), params))) / 2;
}
});
};
Fx.Transitions = {
linear: function(zero){
return zero;
}
};
Fx.Transitions.extend = function(transitions){
for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
};
Fx.Transitions.extend({
Pow: function(p, x){
return Math.pow(p, x && x[0] || 6);
},
Expo: function(p){
return Math.pow(2, 8 * (p - 1));
},
Circ: function(p){
return 1 - Math.sin(Math.acos(p));
},
Sine: function(p){
return 1 - Math.cos(p * Math.PI / 2);
},
Back: function(p, x){
x = x && x[0] || 1.618;
return Math.pow(p, 2) * ((x + 1) * p - x);
},
Bounce: function(p){
var value;
for (var a = 0, b = 1; 1; a += b, b /= 2){
if (p >= (7 - 4 * a) / 11){
value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
break;
}
}
return value;
},
Elastic: function(p, x){
return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && x[0] || 1) / 3);
}
});
['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
Fx.Transitions[transition] = new Fx.Transition(function(p){
return Math.pow(p, i + 2);
});
});
/*
---
name: Request
description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
license: MIT-style license.
requires: [Object, Element, Chain, Events, Options, Browser]
provides: Request
...
*/
(function(){
var empty = function(){},
progressSupport = ('onprogress' in new Browser.Request);
var Request = this.Request = new Class({
Implements: [Chain, Events, Options],
options: {/*
onRequest: function(){},
onLoadstart: function(event, xhr){},
onProgress: function(event, xhr){},
onComplete: function(){},
onCancel: function(){},
onSuccess: function(responseText, responseXML){},
onFailure: function(xhr){},
onException: function(headerName, value){},
onTimeout: function(){},
user: '',
password: '',*/
url: '',
data: '',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
},
async: true,
format: false,
method: 'post',
link: 'ignore',
isSuccess: null,
emulation: true,
urlEncoded: true,
encoding: 'utf-8',
evalScripts: false,
evalResponse: false,
timeout: 0,
noCache: false
},
initialize: function(options){
this.xhr = new Browser.Request();
this.setOptions(options);
this.headers = this.options.headers;
},
onStateChange: function(){
var xhr = this.xhr;
if (xhr.readyState != 4 || !this.running) return;
this.running = false;
this.status = 0;
Function.attempt(function(){
var status = xhr.status;
this.status = (status == 1223) ? 204 : status;
}.bind(this));
xhr.onreadystatechange = empty;
if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
clearTimeout(this.timer);
this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML};
if (this.options.isSuccess.call(this, this.status))
this.success(this.response.text, this.response.xml);
else
this.failure();
},
isSuccess: function(){
var status = this.status;
return (status >= 200 && status < 300);
},
isRunning: function(){
return !!this.running;
},
processScripts: function(text){
if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.exec(text);
return text.stripScripts(this.options.evalScripts);
},
success: function(text, xml){
this.onSuccess(this.processScripts(text), xml);
},
onSuccess: function(){
this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
},
failure: function(){
this.onFailure();
},
onFailure: function(){
this.fireEvent('complete').fireEvent('failure', this.xhr);
},
loadstart: function(event){
this.fireEvent('loadstart', [event, this.xhr]);
},
progress: function(event){
this.fireEvent('progress', [event, this.xhr]);
},
timeout: function(){
this.fireEvent('timeout', this.xhr);
},
setHeader: function(name, value){
this.headers[name] = value;
return this;
},
getHeader: function(name){
return Function.attempt(function(){
return this.xhr.getResponseHeader(name);
}.bind(this));
},
check: function(){
if (!this.running) return true;
switch (this.options.link){
case 'cancel': this.cancel(); return true;
case 'chain': this.chain(this.caller.pass(arguments, this)); return false;
}
return false;
},
send: function(options){
if (!this.check(options)) return this;
this.options.isSuccess = this.options.isSuccess || this.isSuccess;
this.running = true;
var type = typeOf(options);
if (type == 'string' || type == 'element') options = {data: options};
var old = this.options;
options = Object.append({data: old.data, url: old.url, method: old.method}, options);
var data = options.data, url = String(options.url), method = options.method.toLowerCase();
switch (typeOf(data)){
case 'element': data = document.id(data).toQueryString(); break;
case 'object': case 'hash': data = Object.toQueryString(data);
}
if (this.options.format){
var format = 'format=' + this.options.format;
data = (data) ? format + '&' + data : format;
}
if (this.options.emulation && !['get', 'post'].contains(method)){
var _method = '_method=' + method;
data = (data) ? _method + '&' + data : _method;
method = 'post';
}
if (this.options.urlEncoded && ['post', 'put'].contains(method)){
var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding;
}
if (!url) url = document.location.pathname;
var trimPosition = url.lastIndexOf('/');
if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
if (this.options.noCache)
url += (url.contains('?') ? '&' : '?') + String.uniqueID();
if (data && method == 'get'){
url += (url.contains('?') ? '&' : '?') + data;
data = null;
}
var xhr = this.xhr;
if (progressSupport){
xhr.onloadstart = this.loadstart.bind(this);
xhr.onprogress = this.progress.bind(this);
}
xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password);
if (this.options.user && 'withCredentials' in xhr) xhr.withCredentials = true;
xhr.onreadystatechange = this.onStateChange.bind(this);
Object.each(this.headers, function(value, key){
try {
xhr.setRequestHeader(key, value);
} catch (e){
this.fireEvent('exception', [key, value]);
}
}, this);
this.fireEvent('request');
xhr.send(data);
if (!this.options.async) this.onStateChange();
if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this);
return this;
},
cancel: function(){
if (!this.running) return this;
this.running = false;
var xhr = this.xhr;
xhr.abort();
clearTimeout(this.timer);
xhr.onreadystatechange = empty;
if (progressSupport) xhr.onprogress = xhr.onloadstart = empty;
this.xhr = new Browser.Request();
this.fireEvent('cancel');
return this;
}
});
var methods = {};
['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
methods[method] = function(data){
var object = {
method: method
};
if (data != null) object.data = data;
return this.send(object);
};
});
Request.implement(methods);
Element.Properties.send = {
set: function(options){
var send = this.get('send').cancel();
send.setOptions(options);
return this;
},
get: function(){
var send = this.retrieve('send');
if (!send){
send = new Request({
data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
});
this.store('send', send);
}
return send;
}
};
Element.implement({
send: function(url){
var sender = this.get('send');
sender.send({data: this, url: url || sender.options.url});
return this;
}
});
})();
/*
---
name: Request.HTML
description: Extends the basic Request Class with additional methods for interacting with HTML responses.
license: MIT-style license.
requires: [Element, Request]
provides: Request.HTML
...
*/
Request.HTML = new Class({
Extends: Request,
options: {
update: false,
append: false,
evalScripts: true,
filter: false,
headers: {
Accept: 'text/html, application/xml, text/xml, */*'
}
},
success: function(text){
var options = this.options, response = this.response;
response.html = text.stripScripts(function(script){
response.javascript = script;
});
var match = response.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
if (match) response.html = match[1];
var temp = new Element('div').set('html', response.html);
response.tree = temp.childNodes;
response.elements = temp.getElements('*');
if (options.filter) response.tree = response.elements.filter(options.filter);
if (options.update) document.id(options.update).empty().set('html', response.html);
else if (options.append) document.id(options.append).adopt(temp.getChildren());
if (options.evalScripts) Browser.exec(response.javascript);
this.onSuccess(response.tree, response.elements, response.html, response.javascript);
}
});
Element.Properties.load = {
set: function(options){
var load = this.get('load').cancel();
load.setOptions(options);
return this;
},
get: function(){
var load = this.retrieve('load');
if (!load){
load = new Request.HTML({data: this, link: 'cancel', update: this, method: 'get'});
this.store('load', load);
}
return load;
}
};
Element.implement({
load: function(){
this.get('load').send(Array.link(arguments, {data: Type.isObject, url: Type.isString}));
return this;
}
});
/*
---
name: JSON
description: JSON encoder and decoder.
license: MIT-style license.
See Also: <http://www.json.org/>
requires: [Array, String, Number, Function]
provides: JSON
...
*/
if (typeof JSON == 'undefined') this.JSON = {};
(function(){
var special = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
var escape = function(chr){
return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
};
JSON.validate = function(string){
string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, '');
return (/^[\],:{}\s]*$/).test(string);
};
JSON.encode = JSON.stringify ? function(obj){
return JSON.stringify(obj);
} : function(obj){
if (obj && obj.toJSON) obj = obj.toJSON();
switch (typeOf(obj)){
case 'string':
return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
case 'array':
return '[' + obj.map(JSON.encode).clean() + ']';
case 'object': case 'hash':
var string = [];
Object.each(obj, function(value, key){
var json = JSON.encode(value);
if (json) string.push(JSON.encode(key) + ':' + json);
});
return '{' + string + '}';
case 'number': case 'boolean': return '' + obj;
case 'null': return 'null';
}
return null;
};
JSON.decode = function(string, secure){
if (!string || typeOf(string) != 'string') return null;
if (secure || JSON.secure){
if (JSON.parse) return JSON.parse(string);
if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.');
}
return eval('(' + string + ')');
};
})();
/*
---
name: Request.JSON
description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
license: MIT-style license.
requires: [Request, JSON]
provides: Request.JSON
...
*/
Request.JSON = new Class({
Extends: Request,
options: {
/*onError: function(text, error){},*/
secure: true
},
initialize: function(options){
this.parent(options);
Object.append(this.headers, {
'Accept': 'application/json',
'X-Request': 'JSON'
});
},
success: function(text){
var json;
try {
json = this.response.json = JSON.decode(text, this.options.secure);
} catch (error){
this.fireEvent('error', [text, error]);
return;
}
if (json == null) this.onFailure();
else this.onSuccess(json, text);
}
});
/*
---
name: Cookie
description: Class for creating, reading, and deleting browser Cookies.
license: MIT-style license.
credits:
- Based on the functions by Peter-Paul Koch (http://quirksmode.org).
requires: [Options, Browser]
provides: Cookie
...
*/
var Cookie = new Class({
Implements: Options,
options: {
path: '/',
domain: false,
duration: false,
secure: false,
document: document,
encode: true
},
initialize: function(key, options){
this.key = key;
this.setOptions(options);
},
write: function(value){
if (this.options.encode) value = encodeURIComponent(value);
if (this.options.domain) value += '; domain=' + this.options.domain;
if (this.options.path) value += '; path=' + this.options.path;
if (this.options.duration){
var date = new Date();
date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
value += '; expires=' + date.toGMTString();
}
if (this.options.secure) value += '; secure';
this.options.document.cookie = this.key + '=' + value;
return this;
},
read: function(){
var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
return (value) ? decodeURIComponent(value[1]) : null;
},
dispose: function(){
new Cookie(this.key, Object.merge({}, this.options, {duration: -1})).write('');
return this;
}
});
Cookie.write = function(key, value, options){
return new Cookie(key, options).write(value);
};
Cookie.read = function(key){
return new Cookie(key).read();
};
Cookie.dispose = function(key, options){
return new Cookie(key, options).dispose();
};
/*
---
name: DOMReady
description: Contains the custom event domready.
license: MIT-style license.
requires: [Browser, Element, Element.Event]
provides: [DOMReady, DomReady]
...
*/
(function(window, document){
var ready,
loaded,
checks = [],
shouldPoll,
timer,
testElement = document.createElement('div');
var domready = function(){
clearTimeout(timer);
if (ready) return;
Browser.loaded = ready = true;
document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check);
document.fireEvent('domready');
window.fireEvent('domready');
};
var check = function(){
for (var i = checks.length; i--;) if (checks[i]()){
domready();
return true;
}
return false;
};
var poll = function(){
clearTimeout(timer);
if (!check()) timer = setTimeout(poll, 10);
};
document.addListener('DOMContentLoaded', domready);
/*<ltIE8>*/
// doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
// testElement.doScroll() throws when the DOM is not ready, only in the top window
var doScrollWorks = function(){
try {
testElement.doScroll();
return true;
} catch (e){}
return false;
}
// If doScroll works already, it can't be used to determine domready
//   e.g. in an iframe
if (testElement.doScroll && !doScrollWorks()){
checks.push(doScrollWorks);
shouldPoll = true;
}
/*</ltIE8>*/
if (document.readyState) checks.push(function(){
var state = document.readyState;
return (state == 'loaded' || state == 'complete');
});
if ('onreadystatechange' in document) document.addListener('readystatechange', check);
else shouldPoll = true;
if (shouldPoll) poll();
Element.Events.domready = {
onAdd: function(fn){
if (ready) fn.call(this);
}
};
// Make sure that domready fires before load
Element.Events.load = {
base: 'load',
onAdd: function(fn){
if (loaded && this == window) fn.call(this);
},
condition: function(){
if (this == window){
domready();
delete Element.Events.load;
}
return true;
}
};
// This is based on the custom load event
window.addEvent('load', function(){
loaded = true;
});
})(window, document);
/*
---
name: Swiff
description: Wrapper for embedding SWF movies. Supports External Interface Communication.
license: MIT-style license.
credits:
- Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
requires: [Options, Object, Element]
provides: Swiff
...
*/
(function(){
var Swiff = this.Swiff = new Class({
Implements: Options,
options: {
id: null,
height: 1,
width: 1,
container: null,
properties: {},
params: {
quality: 'high',
allowScriptAccess: 'always',
wMode: 'window',
swLiveConnect: true
},
callBacks: {},
vars: {}
},
toElement: function(){
return this.object;
},
initialize: function(path, options){
this.instance = 'Swiff_' + String.uniqueID();
this.setOptions(options);
options = this.options;
var id = this.id = options.id || this.instance;
var container = document.id(options.container);
Swiff.CallBacks[this.instance] = {};
var params = options.params, vars = options.vars, callBacks = options.callBacks;
var properties = Object.append({height: options.height, width: options.width}, options.properties);
var self = this;
for (var callBack in callBacks){
Swiff.CallBacks[this.instance][callBack] = (function(option){
return function(){
return option.apply(self.object, arguments);
};
})(callBacks[callBack]);
vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
}
params.flashVars = Object.toQueryString(vars);
if (Browser.ie){
properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
params.movie = path;
} else {
properties.type = 'application/x-shockwave-flash';
}
properties.data = path;
var build = '<object id="' + id + '"';
for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
build += '>';
for (var param in params){
if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
}
build += '</object>';
this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
},
replaces: function(element){
element = document.id(element, true);
element.parentNode.replaceChild(this.toElement(), element);
return this;
},
inject: function(element){
document.id(element, true).appendChild(this.toElement());
return this;
},
remote: function(){
return Swiff.remote.apply(Swiff, [this.toElement()].append(arguments));
}
});
Swiff.CallBacks = {};
Swiff.remote = function(obj, fn){
var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
return eval(rs);
};
})();
// MooTools: the javascript framework.
// Load this file's selection again by visiting: http://mootools.net/more/d0493ff5465bd7f5f89a61adac4c7311
// Or build this file again with packager using: packager build More/Events.Pseudos More/Class.Refactor More/Class.Binds More/Class.Occlude More/Chain.Wait More/Array.Extras More/Date More/Date.Extras More/Number.Format More/Object.Extras More/String.Extras More/String.QueryString More/URI More/URI.Relative More/Hash More/Hash.Extras More/Element.Forms More/Elements.From More/Element.Event.Pseudos More/Element.Event.Pseudos.Keys More/Element.Delegation More/Element.Measure More/Element.Pin More/Element.Position More/Element.Shortcuts More/Form.Request More/Form.Request.Append More/Form.Validator More/Form.Validator.Inline More/Form.Validator.Extras More/OverText More/Fx.Elements More/Fx.Accordion More/Fx.Move More/Fx.Reveal More/Fx.Scroll More/Fx.Slide More/Fx.SmoothScroll More/Fx.Sort More/Drag More/Drag.Move More/Slider More/Sortables More/Request.JSONP More/Request.Queue More/Request.Periodical More/Assets More/Color More/Group More/Hash.Cookie More/IframeShim More/Table More/HtmlTable More/HtmlTable.Zebra More/HtmlTable.Sort More/HtmlTable.Select More/Keyboard More/Keyboard.Extras More/Mask More/Scroller More/Tips More/Spinner More/Locale More/Locale.en-US.Date More/Locale.en-US.Form.Validator
/*
---
script: More.js
name: More
description: MooTools More
license: MIT-style license
authors:
- Guillermo Rauch
- Thomas Aylott
- Scott Kyle
- Arian Stolwijk
- Tim Wienk
- Christoph Pojer
- Aaron Newton
- Jacob Thornton
requires:
- Core/MooTools
provides: [MooTools.More]
...
*/
MooTools.More = {
'version': '1.3.2.1',
'build': 'e586bcd2496e9b22acfde32e12f84d49ce09e59d'
};
/*
---
name: Events.Pseudos
description: Adds the functionality to add pseudo events
license: MIT-style license
authors:
- Arian Stolwijk
requires: [Core/Class.Extras, Core/Slick.Parser, More/MooTools.More]
provides: [Events.Pseudos]
...
*/
Events.Pseudos = function(pseudos, addEvent, removeEvent){
var storeKey = 'monitorEvents:';
var storageOf = function(object){
return {
store: object.store ? function(key, value){
object.store(storeKey + key, value);
} : function(key, value){
(object.$monitorEvents || (object.$monitorEvents = {}))[key] = value;
},
retrieve: object.retrieve ? function(key, dflt){
return object.retrieve(storeKey + key, dflt);
} : function(key, dflt){
if (!object.$monitorEvents) return dflt;
return object.$monitorEvents[key] || dflt;
}
};
};
var splitType = function(type){
if (type.indexOf(':') == -1 || !pseudos) return null;
var parsed = Slick.parse(type).expressions[0][0],
parsedPseudos = parsed.pseudos,
l = parsedPseudos.length,
splits = [];
while (l--) if (pseudos[parsedPseudos[l].key]){
splits.push({
event: parsed.tag,
value: parsedPseudos[l].value,
pseudo: parsedPseudos[l].key,
original: type
});
}
return splits.length ? splits : null;
};
var mergePseudoOptions = function(split){
return Object.merge.apply(this, split.map(function(item){
return pseudos[item.pseudo].options || {};
}));
};
return {
addEvent: function(type, fn, internal){
var split = splitType(type);
if (!split) return addEvent.call(this, type, fn, internal);
var storage = storageOf(this),
events = storage.retrieve(type, []),
eventType = split[0].event,
options = mergePseudoOptions(split),
stack = fn,
eventOptions = options[eventType] || {},
args = Array.slice(arguments, 2),
self = this,
monitor;
if (eventOptions.args) args.append(Array.convert(eventOptions.args));
if (eventOptions.base) eventType = eventOptions.base;
if (eventOptions.onAdd) eventOptions.onAdd(this);
split.each(function(item){
var stackFn = stack;
stack = function(){
(eventOptions.listener || pseudos[item.pseudo].listener).call(self, item, stackFn, arguments, monitor, options);
};
});
monitor = stack.bind(this);
events.include({event: fn, monitor: monitor});
storage.store(type, events);
addEvent.apply(this, [type, fn].concat(args));
return addEvent.apply(this, [eventType, monitor].concat(args));
},
removeEvent: function(type, fn){
var split = splitType(type);
if (!split) return removeEvent.call(this, type, fn);
var storage = storageOf(this),
events = storage.retrieve(type);
if (!events) return this;
var eventType = split[0].event,
options = mergePseudoOptions(split),
eventOptions = options[eventType] || {},
args = Array.slice(arguments, 2);
if (eventOptions.args) args.append(Array.convert(eventOptions.args));
if (eventOptions.base) eventType = eventOptions.base;
if (eventOptions.onRemove) eventOptions.onRemove(this);
removeEvent.apply(this, [type, fn].concat(args));
events.each(function(monitor, i){
if (!fn || monitor.event == fn) removeEvent.apply(this, [eventType, monitor.monitor].concat(args));
delete events[i];
}, this);
storage.store(type, events);
return this;
}
};
};
(function(){
var pseudos = {
once: {
listener: function(split, fn, args, monitor){
fn.apply(this, args);
this.removeEvent(split.event, monitor)
.removeEvent(split.original, fn);
}
},
throttle: {
listener: function(split, fn, args){
if (!fn._throttled){
fn.apply(this, args);
fn._throttled = setTimeout(function(){
fn._throttled = false;
}, split.value || 250);
}
}
},
pause: {
listener: function(split, fn, args){
clearTimeout(fn._pause);
fn._pause = fn.delay(split.value || 250, this, args);
}
}
};
Events.definePseudo = function(key, listener){
pseudos[key] = Type.isFunction(listener) ? {listener: listener} : listener;
return this;
};
Events.lookupPseudo = function(key){
return pseudos[key];
};
var proto = Events.prototype;
Events.implement(Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent));
['Request', 'Fx'].each(function(klass){
if (this[klass]) this[klass].implement(Events.prototype);
});
})();
/*
---
script: Class.Refactor.js
name: Class.Refactor
description: Extends a class onto itself with new property, preserving any items attached to the class's namespace.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Class
- /MooTools.More
# Some modules declare themselves dependent on Class.Refactor
provides: [Class.refactor, Class.Refactor]
...
*/
Class.refactor = function(original, refactors){
Object.each(refactors, function(item, name){
var origin = original.prototype[name];
origin = (origin && origin.$origin) || origin || function(){};
original.implement(name, (typeof item == 'function') ? function(){
var old = this.previous;
this.previous = origin;
var value = item.apply(this, arguments);
this.previous = old;
return value;
} : item);
});
return original;
};
/*
---
script: Class.Binds.js
name: Class.Binds
description: Automagically binds specified methods in a class to the instance of the class.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Class
- /MooTools.More
provides: [Class.Binds]
...
*/
Class.Mutators.Binds = function(binds){
if (!this.prototype.initialize) this.implement('initialize', function(){});
return Array.convert(binds).concat(this.prototype.Binds || []);
};
Class.Mutators.initialize = function(initialize){
return function(){
Array.convert(this.Binds).each(function(name){
var original = this[name];
if (original) this[name] = original.bind(this);
}, this);
return initialize.apply(this, arguments);
};
};
/*
---
script: Class.Occlude.js
name: Class.Occlude
description: Prevents a class from being applied to a DOM element twice.
license: MIT-style license.
authors:
- Aaron Newton
requires:
- Core/Class
- Core/Element
- /MooTools.More
provides: [Class.Occlude]
...
*/
Class.Occlude = new Class({
occlude: function(property, element){
element = document.id(element || this.element);
var instance = element.retrieve(property || this.property);
if (instance && !this.occluded)
return (this.occluded = instance);
this.occluded = false;
element.store(property || this.property, this);
return this.occluded;
}
});
/*
---
script: Chain.Wait.js
name: Chain.Wait
description: value, Adds a method to inject pauses between chained events.
license: MIT-style license.
authors:
- Aaron Newton
requires:
- Core/Chain
- Core/Element
- Core/Fx
- /MooTools.More
provides: [Chain.Wait]
...
*/
(function(){
var wait = {
wait: function(duration){
return this.chain(function(){
this.callChain.delay(duration == null ? 500 : duration, this);
return this;
}.bind(this));
}
};
Chain.implement(wait);
if (this.Fx) Fx.implement(wait);
if (this.Element && Element.implement && this.Fx){
Element.implement({
chains: function(effects){
Array.convert(effects || ['tween', 'morph', 'reveal']).each(function(effect){
effect = this.get(effect);
if (!effect) return;
effect.setOptions({
link:'chain'
});
}, this);
return this;
},
pauseFx: function(duration, effect){
this.chains(effect).get(effect || 'tween').wait(duration);
return this;
}
});
}
})();
/*
---
script: Array.Extras.js
name: Array.Extras
description: Extends the Array native object to include useful methods to work with arrays.
license: MIT-style license
authors:
- Christoph Pojer
- Sebastian Markbåge
requires:
- Core/Array
- MooTools.More
provides: [Array.Extras]
...
*/
(function(nil){
Array.implement({
min: function(){
return Math.min.apply(null, this);
},
max: function(){
return Math.max.apply(null, this);
},
average: function(){
return this.length ? this.sum() / this.length : 0;
},
sum: function(){
var result = 0, l = this.length;
if (l){
while (l--) result += this[l];
}
return result;
},
unique: function(){
return [].combine(this);
},
shuffle: function(){
for (var i = this.length; i && --i;){
var temp = this[i], r = Math.floor(Math.random() * ( i + 1 ));
this[i] = this[r];
this[r] = temp;
}
return this;
},
reduce: function(fn, value){
for (var i = 0, l = this.length; i < l; i++){
if (i in this) value = value === nil ? this[i] : fn.call(null, value, this[i], i, this);
}
return value;
},
reduceRight: function(fn, value){
var i = this.length;
while (i--){
if (i in this) value = value === nil ? this[i] : fn.call(null, value, this[i], i, this);
}
return value;
}
});
})();
/*
---
script: Object.Extras.js
name: Object.Extras
description: Extra Object generics, like getFromPath which allows a path notation to child elements.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Object
- /MooTools.More
provides: [Object.Extras]
...
*/
(function(){
var defined = function(value){
return value != null;
};
var hasOwnProperty = Object.prototype.hasOwnProperty;
Object.extend({
getFromPath: function(source, parts){
if (typeof parts == 'string') parts = parts.split('.');
for (var i = 0, l = parts.length; i < l; i++){
if (hasOwnProperty.call(source, parts[i])) source = source[parts[i]];
else return null;
}
return source;
},
cleanValues: function(object, method){
method = method || defined;
for (var key in object) if (!method(object[key])){
delete object[key];
}
return object;
},
erase: function(object, key){
if (hasOwnProperty.call(object, key)) delete object[key];
return object;
},
run: function(object){
var args = Array.slice(arguments, 1);
for (var key in object) if (object[key].apply){
object[key].apply(object, args);
}
return object;
}
});
})();
/*
---
script: Locale.js
name: Locale
description: Provides methods for localization.
license: MIT-style license
authors:
- Aaron Newton
- Arian Stolwijk
requires:
- Core/Events
- /Object.Extras
- /MooTools.More
provides: [Locale, Lang]
...
*/
(function(){
var current = null,
locales = {},
inherits = {};
var getSet = function(set){
if (instanceOf(set, Locale.Set)) return set;
else return locales[set];
};
var Locale = this.Locale = {
define: function(locale, set, key, value){
var name;
if (instanceOf(locale, Locale.Set)){
name = locale.name;
if (name) locales[name] = locale;
} else {
name = locale;
if (!locales[name]) locales[name] = new Locale.Set(name);
locale = locales[name];
}
if (set) locale.define(set, key, value);
/*<1.2compat>*/
if (set == 'cascade') return Locale.inherit(name, key);
/*</1.2compat>*/
if (!current) current = locale;
return locale;
},
use: function(locale){
locale = getSet(locale);
if (locale){
current = locale;
this.fireEvent('change', locale);
/*<1.2compat>*/
this.fireEvent('langChange', locale.name);
/*</1.2compat>*/
}
return this;
},
getCurrent: function(){
return current;
},
get: function(key, args){
return (current) ? current.get(key, args) : '';
},
inherit: function(locale, inherits, set){
locale = getSet(locale);
if (locale) locale.inherit(inherits, set);
return this;
},
list: function(){
return Object.keys(locales);
}
};
Object.append(Locale, new Events);
Locale.Set = new Class({
sets: {},
inherits: {
locales: [],
sets: {}
},
initialize: function(name){
this.name = name || '';
},
define: function(set, key, value){
var defineData = this.sets[set];
if (!defineData) defineData = {};
if (key){
if (typeOf(key) == 'object') defineData = Object.merge(defineData, key);
else defineData[key] = value;
}
this.sets[set] = defineData;
return this;
},
get: function(key, args, _base){
var value = Object.getFromPath(this.sets, key);
if (value != null){
var type = typeOf(value);
if (type == 'function') value = value.apply(null, Array.convert(args));
else if (type == 'object') value = Object.clone(value);
return value;
}
// get value of inherited locales
var index = key.indexOf('.'),
set = index < 0 ? key : key.substr(0, index),
names = (this.inherits.sets[set] || []).combine(this.inherits.locales).include('en-US');
if (!_base) _base = [];
for (var i = 0, l = names.length; i < l; i++){
if (_base.contains(names[i])) continue;
_base.include(names[i]);
var locale = locales[names[i]];
if (!locale) continue;
value = locale.get(key, args, _base);
if (value != null) return value;
}
return '';
},
inherit: function(names, set){
names = Array.convert(names);
if (set && !this.inherits.sets[set]) this.inherits.sets[set] = [];
var l = names.length;
while (l--) (set ? this.inherits.sets[set] : this.inherits.locales).unshift(names[l]);
return this;
}
});
/*<1.2compat>*/
var lang = MooTools.lang = {};
Object.append(lang, Locale, {
setLanguage: Locale.use,
getCurrentLanguage: function(){
var current = Locale.getCurrent();
return (current) ? current.name : null;
},
set: function(){
Locale.define.apply(this, arguments);
return this;
},
get: function(set, key, args){
if (key) set += '.' + key;
return Locale.get(set, args);
}
});
/*</1.2compat>*/
})();
/*
---
name: Locale.en-US.Date
description: Date messages for US English.
license: MIT-style license
authors:
- Aaron Newton
requires:
- /Locale
provides: [Locale.en-US.Date]
...
*/
Locale.define('en-US', 'Date', {
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
months_abbr: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
days_abbr: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
// Culture's date order: MM/DD/YYYY
dateOrder: ['month', 'date', 'year'],
shortDate: '%m/%d/%Y',
shortTime: '%I:%M%p',
AM: 'AM',
PM: 'PM',
firstDayOfWeek: 0,
// Date.Extras
ordinal: function(dayOfMonth){
// 1st, 2nd, 3rd, etc.
return (dayOfMonth > 3 && dayOfMonth < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][Math.min(dayOfMonth % 10, 4)];
},
lessThanMinuteAgo: 'less than a minute ago',
minuteAgo: 'about a minute ago',
minutesAgo: '{delta} minutes ago',
hourAgo: 'about an hour ago',
hoursAgo: 'about {delta} hours ago',
dayAgo: '1 day ago',
daysAgo: '{delta} days ago',
weekAgo: '1 week ago',
weeksAgo: '{delta} weeks ago',
monthAgo: '1 month ago',
monthsAgo: '{delta} months ago',
yearAgo: '1 year ago',
yearsAgo: '{delta} years ago',
lessThanMinuteUntil: 'less than a minute from now',
minuteUntil: 'about a minute from now',
minutesUntil: '{delta} minutes from now',
hourUntil: 'about an hour from now',
hoursUntil: 'about {delta} hours from now',
dayUntil: '1 day from now',
daysUntil: '{delta} days from now',
weekUntil: '1 week from now',
weeksUntil: '{delta} weeks from now',
monthUntil: '1 month from now',
monthsUntil: '{delta} months from now',
yearUntil: '1 year from now',
yearsUntil: '{delta} years from now'
});
/*
---
script: Date.js
name: Date
description: Extends the Date native object to include methods useful in managing dates.
license: MIT-style license
authors:
- Aaron Newton
- Nicholas Barthelemy - https://svn.nbarthelemy.com/date-js/
- Harald Kirshner - mail [at] digitarald.de; http://digitarald.de
- Scott Kyle - scott [at] appden.com; http://appden.com
requires:
- Core/Array
- Core/String
- Core/Number
- MooTools.More
- Locale
- Locale.en-US.Date
provides: [Date]
...
*/
(function(){
var Date = this.Date;
var DateMethods = Date.Methods = {
ms: 'Milliseconds',
year: 'FullYear',
min: 'Minutes',
mo: 'Month',
sec: 'Seconds',
hr: 'Hours'
};
['Date', 'Day', 'FullYear', 'Hours', 'Milliseconds', 'Minutes', 'Month', 'Seconds', 'Time', 'TimezoneOffset',
'Week', 'Timezone', 'GMTOffset', 'DayOfYear', 'LastMonth', 'LastDayOfMonth', 'UTCDate', 'UTCDay', 'UTCFullYear',
'AMPM', 'Ordinal', 'UTCHours', 'UTCMilliseconds', 'UTCMinutes', 'UTCMonth', 'UTCSeconds', 'UTCMilliseconds'].each(function(method){
Date.Methods[method.toLowerCase()] = method;
});
var pad = function(n, digits, string){
if (digits == 1) return n;
return n < Math.pow(10, digits - 1) ? (string || '0') + pad(n, digits - 1, string) : n;
};
Date.implement({
set: function(prop, value){
prop = prop.toLowerCase();
var method = DateMethods[prop] && 'set' + DateMethods[prop];
if (method && this[method]) this[method](value);
return this;
}.overloadSetter(),
get: function(prop){
prop = prop.toLowerCase();
var method = DateMethods[prop] && 'get' + DateMethods[prop];
if (method && this[method]) return this[method]();
return null;
}.overloadGetter(),
clone: function(){
return new Date(this.get('time'));
},
increment: function(interval, times){
interval = interval || 'day';
times = times != null ? times : 1;
switch (interval){
case 'year':
return this.increment('month', times * 12);
case 'month':
var d = this.get('date');
this.set('date', 1).set('mo', this.get('mo') + times);
return this.set('date', d.min(this.get('lastdayofmonth')));
case 'week':
return this.increment('day', times * 7);
case 'day':
return this.set('date', this.get('date') + times);
}
if (!Date.units[interval]) throw new Error(interval + ' is not a supported interval');
return this.set('time', this.get('time') + times * Date.units[interval]());
},
decrement: function(interval, times){
return this.increment(interval, -1 * (times != null ? times : 1));
},
isLeapYear: function(){
return Date.isLeapYear(this.get('year'));
},
clearTime: function(){
return this.set({hr: 0, min: 0, sec: 0, ms: 0});
},
diff: function(date, resolution){
if (typeOf(date) == 'string') date = Date.parse(date);
return ((date - this) / Date.units[resolution || 'day'](3, 3)).round(); // non-leap year, 30-day month
},
getLastDayOfMonth: function(){
return Date.daysInMonth(this.get('mo'), this.get('year'));
},
getDayOfYear: function(){
return (Date.UTC(this.get('year'), this.get('mo'), this.get('date') + 1)
- Date.UTC(this.get('year'), 0, 1)) / Date.units.day();
},
setDay: function(day, firstDayOfWeek){
if (firstDayOfWeek == null){
firstDayOfWeek = Date.getMsg('firstDayOfWeek');
if (firstDayOfWeek === '') firstDayOfWeek = 1;
}
day = (7 + Date.parseDay(day, true) - firstDayOfWeek) % 7;
var currentDay = (7 + this.get('day') - firstDayOfWeek) % 7;
return this.increment('day', day - currentDay);
},
getWeek: function(firstDayOfWeek){
if (firstDayOfWeek == null){
firstDayOfWeek = Date.getMsg('firstDayOfWeek');
if (firstDayOfWeek === '') firstDayOfWeek = 1;
}
var date = this,
dayOfWeek = (7 + date.get('day') - firstDayOfWeek) % 7,
dividend = 0,
firstDayOfYear;
if (firstDayOfWeek == 1){
// ISO-8601, week belongs to year that has the most days of the week (i.e. has the thursday of the week)
var month = date.get('month'),
startOfWeek = date.get('date') - dayOfWeek;
if (month == 11 && startOfWeek > 28) return 1; // Week 1 of next year
if (month == 0 && startOfWeek < -2){
// Use a date from last year to determine the week
date = new Date(date).decrement('day', dayOfWeek);
dayOfWeek = 0;
}
firstDayOfYear = new Date(date.get('year'), 0, 1).get('day') || 7;
if (firstDayOfYear > 4) dividend = -7; // First week of the year is not week 1
} else {
// In other cultures the first week of the year is always week 1 and the last week always 53 or 54.
// Days in the same week can have a different weeknumber if the week spreads across two years.
firstDayOfYear = new Date(date.get('year'), 0, 1).get('day');
}
dividend += date.get('dayofyear');
dividend += 6 - dayOfWeek; // Add days so we calculate the current date's week as a full week
dividend += (7 + firstDayOfYear - firstDayOfWeek) % 7; // Make up for first week of the year not being a full week
return (dividend / 7);
},
getOrdinal: function(day){
return Date.getMsg('ordinal', day || this.get('date'));
},
getTimezone: function(){
return this.toString()
.replace(/^.*? ([A-Z]{3}).[0-9]{4}.*$/, '$1')
.replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, '$1$2$3');
},
getGMTOffset: function(){
var off = this.get('timezoneOffset');
return ((off > 0) ? '-' : '+') + pad((off.abs() / 60).floor(), 2) + pad(off % 60, 2);
},
setAMPM: function(ampm){
ampm = ampm.toUpperCase();
var hr = this.get('hr');
if (hr > 11 && ampm == 'AM') return this.decrement('hour', 12);
else if (hr < 12 && ampm == 'PM') return this.increment('hour', 12);
return this;
},
getAMPM: function(){
return (this.get('hr') < 12) ? 'AM' : 'PM';
},
parse: function(str){
this.set('time', Date.parse(str));
return this;
},
isValid: function(date){
return !isNaN((date || this).valueOf());
},
format: function(f){
if (!this.isValid()) return 'invalid date';
if (!f) f = '%x %X';
var formatLower = f.toLowerCase();
if (formatters[formatLower]) return formatters[formatLower](this); // it's a formatter!
f = formats[formatLower] || f; // replace short-hand with actual format
var d = this;
return f.replace(/%([a-z%])/gi,
function($0, $1){
switch ($1){
case 'a': return Date.getMsg('days_abbr')[d.get('day')];
case 'A': return Date.getMsg('days')[d.get('day')];
case 'b': return Date.getMsg('months_abbr')[d.get('month')];
case 'B': return Date.getMsg('months')[d.get('month')];
case 'c': return d.format('%a %b %d %H:%M:%S %Y');
case 'd': return pad(d.get('date'), 2);
case 'e': return pad(d.get('date'), 2, ' ');
case 'H': return pad(d.get('hr'), 2);
case 'I': return pad((d.get('hr') % 12) || 12, 2);
case 'j': return pad(d.get('dayofyear'), 3);
case 'k': return pad(d.get('hr'), 2, ' ');
case 'l': return pad((d.get('hr') % 12) || 12, 2, ' ');
case 'L': return pad(d.get('ms'), 3);
case 'm': return pad((d.get('mo') + 1), 2);
case 'M': return pad(d.get('min'), 2);
case 'o': return d.get('ordinal');
case 'p': return Date.getMsg(d.get('ampm'));
case 's': return Math.round(d / 1000);
case 'S': return pad(d.get('seconds'), 2);
case 'T': return d.format('%H:%M:%S');
case 'U': return pad(d.get('week'), 2);
case 'w': return d.get('day');
case 'x': return d.format(Date.getMsg('shortDate'));
case 'X': return d.format(Date.getMsg('shortTime'));
case 'y': return d.get('year').toString().substr(2);
case 'Y': return d.get('year');
case 'z': return d.get('GMTOffset');
case 'Z': return d.get('Timezone');
}
return $1;
}
);
},
toISOString: function(){
return this.format('iso8601');
}
}).alias({
toJSON: 'toISOString',
compare: 'diff',
strftime: 'format'
});
var formats = {
db: '%Y-%m-%d %H:%M:%S',
compact: '%Y%m%dT%H%M%S',
'short': '%d %b %H:%M',
'long': '%B %d, %Y %H:%M'
};
// The day and month abbreviations are standardized, so we cannot use simply %a and %b because they will get localized
var rfcDayAbbr = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
rfcMonthAbbr = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var formatters = {
rfc822: function(date){
return rfcDayAbbr[date.get('day')] + date.format(', %d ') + rfcMonthAbbr[date.get('month')] + date.format(' %Y %H:%M:%S %Z');
},
rfc2822: function(date){
return rfcDayAbbr[date.get('day')] + date.format(', %d ') + rfcMonthAbbr[date.get('month')] + date.format(' %Y %H:%M:%S %z');
},
iso8601: function(date){
return (
date.getUTCFullYear() + '-' +
pad(date.getUTCMonth() + 1, 2) + '-' +
pad(date.getUTCDate(), 2) + 'T' +
pad(date.getUTCHours(), 2) + ':' +
pad(date.getUTCMinutes(), 2) + ':' +
pad(date.getUTCSeconds(), 2) + '.' +
pad(date.getUTCMilliseconds(), 3) + 'Z'
);
}
};
var parsePatterns = [],
nativeParse = Date.parse;
var parseWord = function(type, word, num){
var ret = -1,
translated = Date.getMsg(type + 's');
switch (typeOf(word)){
case 'object':
ret = translated[word.get(type)];
break;
case 'number':
ret = translated[word];
if (!ret) throw new Error('Invalid ' + type + ' index: ' + word);
break;
case 'string':
var match = translated.filter(function(name){
return this.test(name);
}, new RegExp('^' + word, 'i'));
if (!match.length) throw new Error('Invalid ' + type + ' string');
if (match.length > 1) throw new Error('Ambiguous ' + type);
ret = match[0];
}
return (num) ? translated.indexOf(ret) : ret;
};
var startCentury = 1900,
startYear = 70;
Date.extend({
getMsg: function(key, args){
return Locale.get('Date.' + key, args);
},
units: {
ms: Function.from(1),
second: Function.from(1000),
minute: Function.from(60000),
hour: Function.from(3600000),
day: Function.from(86400000),
week: Function.from(608400000),
month: function(month, year){
var d = new Date;
return Date.daysInMonth(month != null ? month : d.get('mo'), year != null ? year : d.get('year')) * 86400000;
},
year: function(year){
year = year || new Date().get('year');
return Date.isLeapYear(year) ? 31622400000 : 31536000000;
}
},
daysInMonth: function(month, year){
return [31, Date.isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
},
isLeapYear: function(year){
return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0);
},
parse: function(from){
var t = typeOf(from);
if (t == 'number') return new Date(from);
if (t != 'string') return from;
from = from.clean();
if (!from.length) return null;
var parsed;
parsePatterns.some(function(pattern){
var bits = pattern.re.exec(from);
return (bits) ? (parsed = pattern.handler(bits)) : false;
});
if (!(parsed && parsed.isValid())){
parsed = new Date(nativeParse(from));
if (!(parsed && parsed.isValid())) parsed = new Date(from.toInt());
}
return parsed;
},
parseDay: function(day, num){
return parseWord('day', day, num);
},
parseMonth: function(month, num){
return parseWord('month', month, num);
},
parseUTC: function(value){
var localDate = new Date(value);
var utcSeconds = Date.UTC(
localDate.get('year'),
localDate.get('mo'),
localDate.get('date'),
localDate.get('hr'),
localDate.get('min'),
localDate.get('sec'),
localDate.get('ms')
);
return new Date(utcSeconds);
},
orderIndex: function(unit){
return Date.getMsg('dateOrder').indexOf(unit) + 1;
},
defineFormat: function(name, format){
formats[name] = format;
return this;
},
defineFormats: function(formats){
for (var name in formats) Date.defineFormat(name, formats[name]);
return this;
},
//<1.2compat>
parsePatterns: parsePatterns,
//</1.2compat>
defineParser: function(pattern){
parsePatterns.push((pattern.re && pattern.handler) ? pattern : build(pattern));
return this;
},
defineParsers: function(){
Array.flatten(arguments).each(Date.defineParser);
return this;
},
define2DigitYearStart: function(year){
startYear = year % 100;
startCentury = year - startYear;
return this;
}
});
var regexOf = function(type){
return new RegExp('(?:' + Date.getMsg(type).map(function(name){
return name.substr(0, 3);
}).join('|') + ')[a-z]*');
};
var replacers = function(key){
switch (key){
case 'T':
return '%H:%M:%S';
case 'x': // iso8601 covers yyyy-mm-dd, so just check if month is first
return ((Date.orderIndex('month') == 1) ? '%m[-./]%d' : '%d[-./]%m') + '([-./]%y)?';
case 'X':
return '%H([.:]%M)?([.:]%S([.:]%s)?)? ?%p? ?%z?';
}
return null;
};
var keys = {
d: /[0-2]?[0-9]|3[01]/,
H: /[01]?[0-9]|2[0-3]/,
I: /0?[1-9]|1[0-2]/,
M: /[0-5]?\d/,
s: /\d+/,
o: /[a-z]*/,
p: /[ap]\.?m\.?/,
y: /\d{2}|\d{4}/,
Y: /\d{4}/,
z: /Z|[+-]\d{2}(?::?\d{2})?/
};
keys.m = keys.I;
keys.S = keys.M;
var currentLanguage;
var recompile = function(language){
currentLanguage = language;
keys.a = keys.A = regexOf('days');
keys.b = keys.B = regexOf('months');
parsePatterns.each(function(pattern, i){
if (pattern.format) parsePatterns[i] = build(pattern.format);
});
};
var build = function(format){
if (!currentLanguage) return {format: format};
var parsed = [];
var re = (format.source || format) // allow format to be regex
.replace(/%([a-z])/gi,
function($0, $1){
return replacers($1) || $0;
}
).replace(/\((?!\?)/g, '(?:') // make all groups non-capturing
.replace(/ (?!\?|\*)/g, ',? ') // be forgiving with spaces and commas
.replace(/%([a-z%])/gi,
function($0, $1){
var p = keys[$1];
if (!p) return $1;
parsed.push($1);
return '(' + p.source + ')';
}
).replace(/\[a-z\]/gi, '[a-z\\u00c0-\\uffff;\&]'); // handle unicode words
return {
format: format,
re: new RegExp('^' + re + '$', 'i'),
handler: function(bits){
bits = bits.slice(1).associate(parsed);
var date = new Date().clearTime(),
year = bits.y || bits.Y;
if (year != null) handle.call(date, 'y', year); // need to start in the right year
if ('d' in bits) handle.call(date, 'd', 1);
if ('m' in bits || bits.b || bits.B) handle.call(date, 'm', 1);
for (var key in bits) handle.call(date, key, bits[key]);
return date;
}
};
};
var handle = function(key, value){
if (!value) return this;
switch (key){
case 'a': case 'A': return this.set('day', Date.parseDay(value, true));
case 'b': case 'B': return this.set('mo', Date.parseMonth(value, true));
case 'd': return this.set('date', value);
case 'H': case 'I': return this.set('hr', value);
case 'm': return this.set('mo', value - 1);
case 'M': return this.set('min', value);
case 'p': return this.set('ampm', value.replace(/\./g, ''));
case 'S': return this.set('sec', value);
case 's': return this.set('ms', ('0.' + value) * 1000);
case 'w': return this.set('day', value);
case 'Y': return this.set('year', value);
case 'y':
value = +value;
if (value < 100) value += startCentury + (value < startYear ? 100 : 0);
return this.set('year', value);
case 'z':
if (value == 'Z') value = '+00';
var offset = value.match(/([+-])(\d{2}):?(\d{2})?/);
offset = (offset[1] + '1') * (offset[2] * 60 + (+offset[3] || 0)) + this.getTimezoneOffset();
return this.set('time', this - offset * 60000);
}
return this;
};
Date.defineParsers(
'%Y([-./]%m([-./]%d((T| )%X)?)?)?', // "1999-12-31", "1999-12-31 11:59pm", "1999-12-31 23:59:59", ISO8601
'%Y%m%d(T%H(%M%S?)?)?', // "19991231", "19991231T1159", compact
'%x( %X)?', // "12/31", "12.31.99", "12-31-1999", "12/31/2008 11:59 PM"
'%d%o( %b( %Y)?)?( %X)?', // "31st", "31st December", "31 Dec 1999", "31 Dec 1999 11:59pm"
'%b( %d%o)?( %Y)?( %X)?', // Same as above with month and day switched
'%Y %b( %d%o( %X)?)?', // Same as above with year coming first
'%o %b %d %X %z %Y', // "Thu Oct 22 08:11:23 +0000 2009"
'%T', // %H:%M:%S
'%H:%M( ?%p)?' // "11:05pm", "11:05 am" and "11:05"
);
Locale.addEvent('change', function(language){
if (Locale.get('Date')) recompile(language);
}).fireEvent('change', Locale.getCurrent());
})();
/*
---
script: Date.Extras.js
name: Date.Extras
description: Extends the Date native object to include extra methods (on top of those in Date.js).
license: MIT-style license
authors:
- Aaron Newton
- Scott Kyle
requires:
- /Date
provides: [Date.Extras]
...
*/
Date.implement({
timeDiffInWords: function(to){
return Date.distanceOfTimeInWords(this, to || new Date);
},
timeDiff: function(to, separator){
if (to == null) to = new Date;
var delta = ((to - this) / 1000).floor().abs();
var vals = [],
durations = [60, 60, 24, 365, 0],
names = ['s', 'm', 'h', 'd', 'y'],
value, duration;
for (var item = 0; item < durations.length; item++){
if (item && !delta) break;
value = delta;
if ((duration = durations[item])){
value = (delta % duration);
delta = (delta / duration).floor();
}
vals.unshift(value + (names[item] || ''));
}
return vals.join(separator || ':');
}
}).extend({
distanceOfTimeInWords: function(from, to){
return Date.getTimePhrase(((to - from) / 1000).toInt());
},
getTimePhrase: function(delta){
var suffix = (delta < 0) ? 'Until' : 'Ago';
if (delta < 0) delta *= -1;
var units = {
minute: 60,
hour: 60,
day: 24,
week: 7,
month: 52 / 12,
year: 12,
eon: Infinity
};
var msg = 'lessThanMinute';
for (var unit in units){
var interval = units[unit];
if (delta < 1.5 * interval){
if (delta > 0.75 * interval) msg = unit;
break;
}
delta /= interval;
msg = unit + 's';
}
delta = delta.round();
return Date.getMsg(msg + suffix, delta).substitute({delta: delta});
}
}).defineParsers(
{
// "today", "tomorrow", "yesterday"
re: /^(?:tod|tom|yes)/i,
handler: function(bits){
var d = new Date().clearTime();
switch (bits[0]){
case 'tom': return d.increment();
case 'yes': return d.decrement();
default: return d;
}
}
},
{
// "next Wednesday", "last Thursday"
re: /^(next|last) ([a-z]+)$/i,
handler: function(bits){
var d = new Date().clearTime();
var day = d.getDay();
var newDay = Date.parseDay(bits[2], true);
var addDays = newDay - day;
if (newDay <= day) addDays += 7;
if (bits[1] == 'last') addDays -= 7;
return d.set('date', d.getDate() + addDays);
}
}
).alias('timeAgoInWords', 'timeDiffInWords');
/*
---
name: Locale.en-US.Number
description: Number messages for US English.
license: MIT-style license
authors:
- Arian Stolwijk
requires:
- /Locale
provides: [Locale.en-US.Number]
...
*/
Locale.define('en-US', 'Number', {
decimal: '.',
group: ',',
/* 	Commented properties are the defaults for Number.format
decimals: 0,
precision: 0,
scientific: null,
prefix: null,
suffic: null,
// Negative/Currency/percentage will mixin Number
negative: {
prefix: '-'
},*/
currency: {
//		decimals: 2,
prefix: '$ '
}/*,
percentage: {
decimals: 2,
suffix: '%'
}*/
});
/*
---
name: Number.Format
description: Extends the Number Type object to include a number formatting method.
license: MIT-style license
authors: [Arian Stolwijk]
requires: [Core/Number, Locale.en-US.Number]
# Number.Extras is for compatibility
provides: [Number.Format, Number.Extras]
...
*/
Number.implement({
format: function(options){
// Thanks dojo and YUI for some inspiration
var value = this;
options = options ? Object.clone(options) : {};
var getOption = function(key){
if (options[key] != null) return options[key];
return Locale.get('Number.' + key);
};
var negative = value < 0,
decimal = getOption('decimal'),
precision = getOption('precision'),
group = getOption('group'),
decimals = getOption('decimals');
if (negative){
var negativeLocale = getOption('negative') || {};
if (negativeLocale.prefix == null && negativeLocale.suffix == null) negativeLocale.prefix = '-';
['prefix', 'suffix'].each(function(key){
if (negativeLocale[key]) options[key] = getOption(key) + negativeLocale[key];
});
value = -value;
}
var prefix = getOption('prefix'),
suffix = getOption('suffix');
if (decimals !== '' && decimals >= 0 && decimals <= 20) value = value.toFixed(decimals);
if (precision >= 1 && precision <= 21) value = (+value).toPrecision(precision);
value += '';
var index;
if (getOption('scientific') === false && value.indexOf('e') > -1){
var match = value.split('e'),
zeros = +match[1];
value = match[0].replace('.', '');
if (zeros < 0){
zeros = -zeros - 1;
index = match[0].indexOf('.');
if (index > -1) zeros -= index - 1;
while (zeros--) value = '0' + value;
value = '0.' + value;
} else {
index = match[0].lastIndexOf('.');
if (index > -1) zeros -= match[0].length - index - 1;
while (zeros--) value += '0';
}
}
if (decimal != '.') value = value.replace('.', decimal);
if (group){
index = value.lastIndexOf(decimal);
index = (index > -1) ? index : value.length;
var newOutput = value.substring(index),
i = index;
while (i--){
if ((index - i - 1) % 3 == 0 && i != (index - 1)) newOutput = group + newOutput;
newOutput = value.charAt(i) + newOutput;
}
value = newOutput;
}
if (prefix) value = prefix + value;
if (suffix) value += suffix;
return value;
},
formatCurrency: function(){
var locale = Locale.get('Number.currency') || {};
if (locale.scientific == null) locale.scientific = false;
if (locale.decimals == null) locale.decimals = 2;
return this.format(locale);
},
formatPercentage: function(){
var locale = Locale.get('Number.percentage') || {};
if (locale.suffix == null) locale.suffix = '%';
if (locale.decimals == null) locale.decimals = 2;
return this.format(locale);
}
});
/*
---
script: String.Extras.js
name: String.Extras
description: Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc).
license: MIT-style license
authors:
- Aaron Newton
- Guillermo Rauch
- Christopher Pitt
requires:
- Core/String
- Core/Array
- MooTools.More
provides: [String.Extras]
...
*/
(function(){
var special = {
'a': /[àáâãäåăą]/g,
'A': /[ÀÁÂÃÄÅĂĄ]/g,
'c': /[ćčç]/g,
'C': /[ĆČÇ]/g,
'd': /[ďđ]/g,
'D': /[ĎÐ]/g,
'e': /[èéêëěę]/g,
'E': /[ÈÉÊËĚĘ]/g,
'g': /[ğ]/g,
'G': /[Ğ]/g,
'i': /[ìíîï]/g,
'I': /[ÌÍÎÏ]/g,
'l': /[ĺľł]/g,
'L': /[ĹĽŁ]/g,
'n': /[ñňń]/g,
'N': /[ÑŇŃ]/g,
'o': /[òóôõöøő]/g,
'O': /[ÒÓÔÕÖØ]/g,
'r': /[řŕ]/g,
'R': /[ŘŔ]/g,
's': /[ššş]/g,
'S': /[ŠŞŚ]/g,
't': /[ťţ]/g,
'T': /[ŤŢ]/g,
'ue': /[ü]/g,
'UE': /[Ü]/g,
'u': /[ùúûůµ]/g,
'U': /[ÙÚÛŮ]/g,
'y': /[ÿý]/g,
'Y': /[ŸÝ]/g,
'z': /[žźż]/g,
'Z': /[ŽŹŻ]/g,
'th': /[þ]/g,
'TH': /[Þ]/g,
'dh': /[ð]/g,
'DH': /[Ð]/g,
'ss': /[ß]/g,
'oe': /[œ]/g,
'OE': /[Œ]/g,
'ae': /[æ]/g,
'AE': /[Æ]/g
},
tidy = {
' ': /[\xa0\u2002\u2003\u2009]/g,
'*': /[\xb7]/g,
'\'': /[\u2018\u2019]/g,
'"': /[\u201c\u201d]/g,
'...': /[\u2026]/g,
'-': /[\u2013]/g,
//	'--': /[\u2014]/g,
'&raquo;': /[\uFFFD]/g
};
var walk = function(string, replacements){
var result = string, key;
for (key in replacements) result = result.replace(replacements[key], key);
return result;
};
var getRegexForTag = function(tag, contents){
tag = tag || '';
var regstr = contents ? "<" + tag + "(?!\\w)[^>]*>([\\s\\S]*?)<\/" + tag + "(?!\\w)>" : "<\/?" + tag + "([^>]+)?>",
reg = new RegExp(regstr, "gi");
return reg;
};
String.implement({
standardize: function(){
return walk(this, special);
},
repeat: function(times){
return new Array(times + 1).join(this);
},
pad: function(length, str, direction){
if (this.length >= length) return this;
var pad = (str == null ? ' ' : '' + str)
.repeat(length - this.length)
.substr(0, length - this.length);
if (!direction || direction == 'right') return this + pad;
if (direction == 'left') return pad + this;
return pad.substr(0, (pad.length / 2).floor()) + this + pad.substr(0, (pad.length / 2).ceil());
},
getTags: function(tag, contents){
return this.match(getRegexForTag(tag, contents)) || [];
},
stripTags: function(tag, contents){
return this.replace(getRegexForTag(tag, contents), '');
},
tidy: function(){
return walk(this, tidy);
},
truncate: function(max, trail, atChar){
var string = this;
if (trail == null && arguments.length == 1) trail = '…';
if (string.length > max){
string = string.substring(0, max);
if (atChar){
var index = string.lastIndexOf(atChar);
if (index != -1) string = string.substr(0, index);
}
if (trail) string += trail;
}
return string;
}
});
})();
/*
---
script: String.QueryString.js
name: String.QueryString
description: Methods for dealing with URI query strings.
license: MIT-style license
authors:
- Sebastian Markbåge
- Aaron Newton
- Lennart Pilon
- Valerio Proietti
requires:
- Core/Array
- Core/String
- /MooTools.More
provides: [String.QueryString]
...
*/
String.implement({
parseQueryString: function(decodeKeys, decodeValues){
if (decodeKeys == null) decodeKeys = true;
if (decodeValues == null) decodeValues = true;
var vars = this.split(/[&;]/),
object = {};
if (!vars.length) return object;
vars.each(function(val){
var index = val.indexOf('=') + 1,
value = index ? val.substr(index) : '',
keys = index ? val.substr(0, index - 1).match(/([^\]\[]+|(\B)(?=\]))/g) : [val],
obj = object;
if (!keys) return;
if (decodeValues) value = decodeURIComponent(value);
keys.each(function(key, i){
if (decodeKeys) key = decodeURIComponent(key);
var current = obj[key];
if (i < keys.length - 1) obj = obj[key] = current || {};
else if (typeOf(current) == 'array') current.push(value);
else obj[key] = current != null ? [current, value] : value;
});
});
return object;
},
cleanQueryString: function(method){
return this.split('&').filter(function(val){
var index = val.indexOf('='),
key = index < 0 ? '' : val.substr(0, index),
value = val.substr(index + 1);
return method ? method.call(null, key, value) : (value || value === 0);
}).join('&');
}
});
/*
---
script: URI.js
name: URI
description: Provides methods useful in managing the window location and uris.
license: MIT-style license
authors:
- Sebastian Markbåge
- Aaron Newton
requires:
- Core/Object
- Core/Class
- Core/Class.Extras
- Core/Element
- /String.QueryString
provides: [URI]
...
*/
(function(){
var toString = function(){
return this.get('value');
};
var URI = this.URI = new Class({
Implements: Options,
options: {
/*base: false*/
},
regex: /^(?:(\w+):)?(?:\/\/(?:(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)?(\.\.?$|(?:[^?#\/]*\/)*)([^?#]*)(?:\?([^#]*))?(?:#(.*))?/,
parts: ['scheme', 'user', 'password', 'host', 'port', 'directory', 'file', 'query', 'fragment'],
schemes: {http: 80, https: 443, ftp: 21, rtsp: 554, mms: 1755, file: 0},
initialize: function(uri, options){
this.setOptions(options);
var base = this.options.base || URI.base;
if (!uri) uri = base;
if (uri && uri.parsed) this.parsed = Object.clone(uri.parsed);
else this.set('value', uri.href || uri.toString(), base ? new URI(base) : false);
},
parse: function(value, base){
var bits = value.match(this.regex);
if (!bits) return false;
bits.shift();
return this.merge(bits.associate(this.parts), base);
},
merge: function(bits, base){
if ((!bits || !bits.scheme) && (!base || !base.scheme)) return false;
if (base){
this.parts.every(function(part){
if (bits[part]) return false;
bits[part] = base[part] || '';
return true;
});
}
bits.port = bits.port || this.schemes[bits.scheme.toLowerCase()];
bits.directory = bits.directory ? this.parseDirectory(bits.directory, base ? base.directory : '') : '/';
return bits;
},
parseDirectory: function(directory, baseDirectory){
directory = (directory.substr(0, 1) == '/' ? '' : (baseDirectory || '/')) + directory;
if (!directory.test(URI.regs.directoryDot)) return directory;
var result = [];
directory.replace(URI.regs.endSlash, '').split('/').each(function(dir){
if (dir == '..' && result.length > 0) result.pop();
else if (dir != '.') result.push(dir);
});
return result.join('/') + '/';
},
combine: function(bits){
return bits.value || bits.scheme + '://' +
(bits.user ? bits.user + (bits.password ? ':' + bits.password : '') + '@' : '') +
(bits.host || '') + (bits.port && bits.port != this.schemes[bits.scheme] ? ':' + bits.port : '') +
(bits.directory || '/') + (bits.file || '') +
(bits.query ? '?' + bits.query : '') +
(bits.fragment ? '#' + bits.fragment : '');
},
set: function(part, value, base){
if (part == 'value'){
var scheme = value.match(URI.regs.scheme);
if (scheme) scheme = scheme[1];
if (scheme && this.schemes[scheme.toLowerCase()] == null) this.parsed = { scheme: scheme, value: value };
else this.parsed = this.parse(value, (base || this).parsed) || (scheme ? { scheme: scheme, value: value } : { value: value });
} else if (part == 'data'){
this.setData(value);
} else {
this.parsed[part] = value;
}
return this;
},
get: function(part, base){
switch (part){
case 'value': return this.combine(this.parsed, base ? base.parsed : false);
case 'data' : return this.getData();
}
return this.parsed[part] || '';
},
go: function(){
document.location.href = this.toString();
},
toURI: function(){
return this;
},
getData: function(key, part){
var qs = this.get(part || 'query');
if (!(qs || qs === 0)) return key ? null : {};
var obj = qs.parseQueryString();
return key ? obj[key] : obj;
},
setData: function(values, merge, part){
if (typeof values == 'string'){
var data = this.getData();
data[arguments[0]] = arguments[1];
values = data;
} else if (merge){
values = Object.merge(this.getData(), values);
}
return this.set(part || 'query', Object.toQueryString(values));
},
clearData: function(part){
return this.set(part || 'query', '');
},
toString: toString,
valueOf: toString
});
URI.regs = {
endSlash: /\/$/,
scheme: /^(\w+):/,
directoryDot: /\.\/|\.$/
};
URI.base = new URI(Array.convert(document.getElements('base[href]', true)).getLast(), {base: document.location});
String.implement({
toURI: function(options){
return new URI(this, options);
}
});
})();
/*
---
script: URI.Relative.js
name: URI.Relative
description: Extends the URI class to add methods for computing relative and absolute urls.
license: MIT-style license
authors:
- Sebastian Markbåge
requires:
- /Class.refactor
- /URI
provides: [URI.Relative]
...
*/
URI = Class.refactor(URI, {
combine: function(bits, base){
if (!base || bits.scheme != base.scheme || bits.host != base.host || bits.port != base.port)
return this.previous.apply(this, arguments);
var end = bits.file + (bits.query ? '?' + bits.query : '') + (bits.fragment ? '#' + bits.fragment : '');
if (!base.directory) return (bits.directory || (bits.file ? '' : './')) + end;
var baseDir = base.directory.split('/'),
relDir = bits.directory.split('/'),
path = '',
offset;
var i = 0;
for (offset = 0; offset < baseDir.length && offset < relDir.length && baseDir[offset] == relDir[offset]; offset++);
for (i = 0; i < baseDir.length - offset - 1; i++) path += '../';
for (i = offset; i < relDir.length - 1; i++) path += relDir[i] + '/';
return (path || (bits.file ? '' : './')) + end;
},
toAbsolute: function(base){
base = new URI(base);
if (base) base.set('directory', '').set('file', '');
return this.toRelative(base);
},
toRelative: function(base){
return this.get('value', new URI(base));
}
});
/*
---
name: Hash
description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.
license: MIT-style license.
requires:
- Core/Object
- /MooTools.More
provides: [Hash]
...
*/
(function(){
if (this.Hash) return;
var Hash = this.Hash = new Type('Hash', function(object){
if (typeOf(object) == 'hash') object = Object.clone(object.getClean());
for (var key in object) this[key] = object[key];
return this;
});
this.$H = function(object){
return new Hash(object);
};
Hash.implement({
forEach: function(fn, bind){
Object.forEach(this, fn, bind);
},
getClean: function(){
var clean = {};
for (var key in this){
if (this.hasOwnProperty(key)) clean[key] = this[key];
}
return clean;
},
getLength: function(){
var length = 0;
for (var key in this){
if (this.hasOwnProperty(key)) length++;
}
return length;
}
});
Hash.alias('each', 'forEach');
Hash.implement({
has: Object.prototype.hasOwnProperty,
keyOf: function(value){
return Object.keyOf(this, value);
},
hasValue: function(value){
return Object.contains(this, value);
},
extend: function(properties){
Hash.each(properties || {}, function(value, key){
Hash.set(this, key, value);
}, this);
return this;
},
combine: function(properties){
Hash.each(properties || {}, function(value, key){
Hash.include(this, key, value);
}, this);
return this;
},
erase: function(key){
if (this.hasOwnProperty(key)) delete this[key];
return this;
},
get: function(key){
return (this.hasOwnProperty(key)) ? this[key] : null;
},
set: function(key, value){
if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
return this;
},
empty: function(){
Hash.each(this, function(value, key){
delete this[key];
}, this);
return this;
},
include: function(key, value){
if (this[key] == undefined) this[key] = value;
return this;
},
map: function(fn, bind){
return new Hash(Object.map(this, fn, bind));
},
filter: function(fn, bind){
return new Hash(Object.filter(this, fn, bind));
},
every: function(fn, bind){
return Object.every(this, fn, bind);
},
some: function(fn, bind){
return Object.some(this, fn, bind);
},
getKeys: function(){
return Object.keys(this);
},
getValues: function(){
return Object.values(this);
},
toQueryString: function(base){
return Object.toQueryString(this, base);
}
});
Hash.alias({indexOf: 'keyOf', contains: 'hasValue'});
})();
/*
---
script: Hash.Extras.js
name: Hash.Extras
description: Extends the Hash Type to include getFromPath which allows a path notation to child elements.
license: MIT-style license
authors:
- Aaron Newton
requires:
- /Hash
- /Object.Extras
provides: [Hash.Extras]
...
*/
Hash.implement({
getFromPath: function(notation){
return Object.getFromPath(this, notation);
},
cleanValues: function(method){
return new Hash(Object.cleanValues(this, method));
},
run: function(){
Object.run(arguments);
}
});
/*
---
script: Element.Forms.js
name: Element.Forms
description: Extends the Element native object to include methods useful in managing inputs.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Element
- /String.Extras
- /MooTools.More
provides: [Element.Forms]
...
*/
Element.implement({
tidy: function(){
this.set('value', this.get('value').tidy());
},
getTextInRange: function(start, end){
return this.get('value').substring(start, end);
},
getSelectedText: function(){
if (this.setSelectionRange) return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd());
return document.selection.createRange().text;
},
getSelectedRange: function(){
if (this.selectionStart != null){
return {
start: this.selectionStart,
end: this.selectionEnd
};
}
var pos = {
start: 0,
end: 0
};
var range = this.getDocument().selection.createRange();
if (!range || range.parentElement() != this) return pos;
var duplicate = range.duplicate();
if (this.type == 'text'){
pos.start = 0 - duplicate.moveStart('character', -100000);
pos.end = pos.start + range.text.length;
} else {
var value = this.get('value');
var offset = value.length;
duplicate.moveToElementText(this);
duplicate.setEndPoint('StartToEnd', range);
if (duplicate.text.length) offset -= value.match(/[\n\r]*$/)[0].length;
pos.end = offset - duplicate.text.length;
duplicate.setEndPoint('StartToStart', range);
pos.start = offset - duplicate.text.length;
}
return pos;
},
getSelectionStart: function(){
return this.getSelectedRange().start;
},
getSelectionEnd: function(){
return this.getSelectedRange().end;
},
setCaretPosition: function(pos){
if (pos == 'end') pos = this.get('value').length;
this.selectRange(pos, pos);
return this;
},
getCaretPosition: function(){
return this.getSelectedRange().start;
},
selectRange: function(start, end){
if (this.setSelectionRange){
this.focus();
this.setSelectionRange(start, end);
} else {
var value = this.get('value');
var diff = value.substr(start, end - start).replace(/\r/g, '').length;
start = value.substr(0, start).replace(/\r/g, '').length;
var range = this.createTextRange();
range.collapse(true);
range.moveEnd('character', start + diff);
range.moveStart('character', start);
range.select();
}
return this;
},
insertAtCursor: function(value, select){
var pos = this.getSelectedRange();
var text = this.get('value');
this.set('value', text.substring(0, pos.start) + value + text.substring(pos.end, text.length));
if (select !== false) this.selectRange(pos.start, pos.start + value.length);
else this.setCaretPosition(pos.start + value.length);
return this;
},
insertAroundCursor: function(options, select){
options = Object.append({
before: '',
defaultMiddle: '',
after: ''
}, options);
var value = this.getSelectedText() || options.defaultMiddle;
var pos = this.getSelectedRange();
var text = this.get('value');
if (pos.start == pos.end){
this.set('value', text.substring(0, pos.start) + options.before + value + options.after + text.substring(pos.end, text.length));
this.selectRange(pos.start + options.before.length, pos.end + options.before.length + value.length);
} else {
var current = text.substring(pos.start, pos.end);
this.set('value', text.substring(0, pos.start) + options.before + current + options.after + text.substring(pos.end, text.length));
var selStart = pos.start + options.before.length;
if (select !== false) this.selectRange(selStart, selStart + current.length);
else this.setCaretPosition(selStart + text.length);
}
return this;
}
});
/*
---
script: Elements.From.js
name: Elements.From
description: Returns a collection of elements from a string of html.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/String
- Core/Element
- /MooTools.More
provides: [Elements.from, Elements.From]
...
*/
Elements.from = function(text, excludeScripts){
if (excludeScripts || excludeScripts == null) text = text.stripScripts();
var container, match = text.match(/^\s*<(t[dhr]|tbody|tfoot|thead)/i);
if (match){
container = new Element('table');
var tag = match[1].toLowerCase();
if (['td', 'th', 'tr'].contains(tag)){
container = new Element('tbody').inject(container);
if (tag != 'tr') container = new Element('tr').inject(container);
}
}
return (container || new Element('div')).set('html', text).getChildren();
};
/*
---
name: Element.Event.Pseudos
description: Adds the functionality to add pseudo events for Elements
license: MIT-style license
authors:
- Arian Stolwijk
requires: [Core/Element.Event, Events.Pseudos]
provides: [Element.Event.Pseudos]
...
*/
(function(){
var pseudos = {},
copyFromEvents = ['once', 'throttle', 'pause'],
count = copyFromEvents.length;
while (count--) pseudos[copyFromEvents[count]] = Events.lookupPseudo(copyFromEvents[count]);
Event.definePseudo = function(key, listener){
pseudos[key] = Type.isFunction(listener) ? {listener: listener} : listener;
return this;
};
var proto = Element.prototype;
[Element, Window, Document].invoke('implement', Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent));
})();
/*
---
name: Element.Event.Pseudos.Keys
description: Adds functionality fire events if certain keycombinations are pressed
license: MIT-style license
authors:
- Arian Stolwijk
requires: [Element.Event.Pseudos]
provides: [Element.Event.Pseudos.Keys]
...
*/
(function(){
var keysStoreKey = '$moo:keys-pressed',
keysKeyupStoreKey = '$moo:keys-keyup';
Event.definePseudo('keys', function(split, fn, args){
var event = args[0],
keys = [],
pressed = this.retrieve(keysStoreKey, []);
keys.append(split.value.replace('++', function(){
keys.push('+'); // shift++ and shift+++a
return '';
}).split('+'));
pressed.include(event.key);
if (keys.every(function(key){
return pressed.contains(key);
})) fn.apply(this, args);
this.store(keysStoreKey, pressed);
if (!this.retrieve(keysKeyupStoreKey)){
var keyup = function(event){
(function(){
pressed = this.retrieve(keysStoreKey, []).erase(event.key);
this.store(keysStoreKey, pressed);
}).delay(0, this); // Fix for IE
};
this.store(keysKeyupStoreKey, keyup).addEvent('keyup', keyup);
}
});
Object.append(Event.Keys, {
'shift': 16,
'control': 17,
'alt': 18,
'capslock': 20,
'pageup': 33,
'pagedown': 34,
'end': 35,
'home': 36,
'numlock': 144,
'scrolllock': 145,
';': 186,
'=': 187,
',': 188,
'-': Browser.firefox ? 109 : 189,
'.': 190,
'/': 191,
'`': 192,
'[': 219,
'\\': 220,
']': 221,
"'": 222,
'+': 107
});
})();
/*
---
script: Element.Delegation.js
name: Element.Delegation
description: Extends the Element native object to include the delegate method for more efficient event management.
credits:
- "Event checking based on the work of Daniel Steigerwald. License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"
license: MIT-style license
authors:
- Aaron Newton
- Daniel Steigerwald
requires: [/MooTools.More, Element.Event.Pseudos]
provides: [Element.Delegation]
...
*/
(function(){
var eventListenerSupport = !(window.attachEvent && !window.addEventListener),
nativeEvents = Element.NativeEvents;
nativeEvents.focusin = 2;
nativeEvents.focusout = 2;
var check = function(split, target, event){
var elementEvent = Element.Events[split.event], condition;
if (elementEvent) condition = elementEvent.condition;
return Slick.match(target, split.value) && (!condition || condition.call(target, event));
};
var bubbleUp = function(split, event, fn){
for (var target = event.target; target && target != this; target = document.id(target.parentNode)){
if (target && check(split, target, event)) return fn.call(target, event, target);
}
};
var formObserver = function(eventName){
var $delegationKey = '$delegation:';
return {
base: 'focusin',
onRemove: function(element){
element.retrieve($delegationKey + 'forms', []).each(function(el){
el.retrieve($delegationKey + 'listeners', []).each(function(listener){
el.removeEvent(eventName, listener);
});
el.eliminate($delegationKey + eventName + 'listeners')
.eliminate($delegationKey + eventName + 'originalFn');
});
},
listener: function(split, fn, args, monitor, options){
var event = args[0],
forms = this.retrieve($delegationKey + 'forms', []),
target = event.target,
form = (target.get('tag') == 'form') ? target : event.target.getParent('form');
if (!form) return;
var formEvents = form.retrieve($delegationKey + 'originalFn', []),
formListeners = form.retrieve($delegationKey + 'listeners', []),
self = this;
forms.include(form);
this.store($delegationKey + 'forms', forms);
if (!formEvents.contains(fn)){
var formListener = function(event){
bubbleUp.call(self, split, event, fn);
};
form.addEvent(eventName, formListener);
formEvents.push(fn);
formListeners.push(formListener);
form.store($delegationKey + eventName + 'originalFn', formEvents)
.store($delegationKey + eventName + 'listeners', formListeners);
}
}
};
};
var inputObserver = function(eventName){
return {
base: 'focusin',
listener: function(split, fn, args){
var events = {blur: function(){
this.removeEvents(events);
}}, self = this;
events[eventName] = function(event){
bubbleUp.call(self, split, event, fn);
};
args[0].target.addEvents(events);
}
};
};
var eventOptions = {
mouseenter: {
base: 'mouseover'
},
mouseleave: {
base: 'mouseout'
},
focus: {
base: 'focus' + (eventListenerSupport ? '' : 'in'),
args: [true]
},
blur: {
base: eventListenerSupport ? 'blur' : 'focusout',
args: [true]
}
};
if (!eventListenerSupport) Object.append(eventOptions, {
submit: formObserver('submit'),
reset: formObserver('reset'),
change: inputObserver('change'),
select: inputObserver('select')
});
Event.definePseudo('relay', {
listener: function(split, fn, args){
bubbleUp.call(this, split, args[0], fn);
},
options: eventOptions
});
})();
/*
---
script: Element.Measure.js
name: Element.Measure
description: Extends the Element native object to include methods useful in measuring dimensions.
credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Element.Style
- Core/Element.Dimensions
- /MooTools.More
provides: [Element.Measure]
...
*/
(function(){
var getStylesList = function(styles, planes){
var list = [];
Object.each(planes, function(directions){
Object.each(directions, function(edge){
styles.each(function(style){
list.push(style + '-' + edge + (style == 'border' ? '-width' : ''));
});
});
});
return list;
};
var calculateEdgeSize = function(edge, styles){
var total = 0;
Object.each(styles, function(value, style){
if (style.test(edge)) total = total + value.toInt();
});
return total;
};
var isVisible = function(el){
return !!(!el || el.offsetHeight || el.offsetWidth);
};
Element.implement({
measure: function(fn){
if (isVisible(this)) return fn.call(this);
var parent = this.getParent(),
toMeasure = [];
while (!isVisible(parent) && parent != document.body){
toMeasure.push(parent.expose());
parent = parent.getParent();
}
var restore = this.expose(),
result = fn.call(this);
restore();
toMeasure.each(function(restore){
restore();
});
return result;
},
expose: function(){
if (this.getStyle('display') != 'none') return function(){};
var before = this.style.cssText;
this.setStyles({
display: 'block',
position: 'absolute',
visibility: 'hidden'
});
return function(){
this.style.cssText = before;
}.bind(this);
},
getDimensions: function(options){
options = Object.merge({computeSize: false}, options);
var dim = {x: 0, y: 0};
var getSize = function(el, options){
return (options.computeSize) ? el.getComputedSize(options) : el.getSize();
};
var parent = this.getParent('body');
if (parent && this.getStyle('display') == 'none'){
dim = this.measure(function(){
return getSize(this, options);
});
} else if (parent){
try { //safari sometimes crashes here, so catch it
dim = getSize(this, options);
}catch(e){}
}
return Object.append(dim, (dim.x || dim.x === 0) ? {
width: dim.x,
height: dim.y
} : {
x: dim.width,
y: dim.height
}
);
},
getComputedSize: function(options){
//<1.2compat>
//legacy support for my stupid spelling error
if (options && options.plains) options.planes = options.plains;
//</1.2compat>
options = Object.merge({
styles: ['padding','border'],
planes: {
height: ['top','bottom'],
width: ['left','right']
},
mode: 'both'
}, options);
var styles = {},
size = {width: 0, height: 0},
dimensions;
if (options.mode == 'vertical'){
delete size.width;
delete options.planes.width;
} else if (options.mode == 'horizontal'){
delete size.height;
delete options.planes.height;
}
getStylesList(options.styles, options.planes).each(function(style){
styles[style] = this.getStyle(style).toInt();
}, this);
Object.each(options.planes, function(edges, plane){
var capitalized = plane.capitalize(),
style = this.getStyle(plane);
if (style == 'auto' && !dimensions) dimensions = this.getDimensions();
style = styles[plane] = (style == 'auto') ? dimensions[plane] : style.toInt();
size['total' + capitalized] = style;
edges.each(function(edge){
var edgesize = calculateEdgeSize(edge, styles);
size['computed' + edge.capitalize()] = edgesize;
size['total' + capitalized] += edgesize;
});
}, this);
return Object.append(size, styles);
}
});
})();
/*
---
script: Element.Pin.js
name: Element.Pin
description: Extends the Element native object to include the pin method useful for fixed positioning for elements.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Element.Event
- Core/Element.Dimensions
- Core/Element.Style
- /MooTools.More
provides: [Element.Pin]
...
*/
(function(){
var supportsPositionFixed = false,
supportTested = false;
var testPositionFixed = function(){
var test = new Element('div').setStyles({
position: 'fixed',
top: 0,
right: 0
}).inject(document.body);
supportsPositionFixed = (test.offsetTop === 0);
test.dispose();
supportTested = true;
};
Element.implement({
pin: function(enable, forceScroll){
if (!supportTested) testPositionFixed();
if (this.getStyle('display') == 'none') return this;
var pinnedPosition,
scroll = window.getScroll(),
parent,
scrollFixer;
if (enable !== false){
pinnedPosition = this.getPosition(supportsPositionFixed ? document.body : this.getOffsetParent());
if (!this.retrieve('pin:_pinned')){
var currentPosition = {
top: pinnedPosition.y - scroll.y,
left: pinnedPosition.x - scroll.x
};
if (supportsPositionFixed && !forceScroll){
this.setStyle('position', 'fixed').setStyles(currentPosition);
} else {
parent = this.getOffsetParent();
var position = this.getPosition(parent),
styles = this.getStyles('left', 'top');
if (parent && styles.left == 'auto' || styles.top == 'auto') this.setPosition(position);
if (this.getStyle('position') == 'static') this.setStyle('position', 'absolute');
position = {
x: styles.left.toInt() - scroll.x,
y: styles.top.toInt() - scroll.y
};
scrollFixer = function(){
if (!this.retrieve('pin:_pinned')) return;
var scroll = window.getScroll();
this.setStyles({
left: position.x + scroll.x,
top: position.y + scroll.y
});
}.bind(this);
this.store('pin:_scrollFixer', scrollFixer);
window.addEvent('scroll', scrollFixer);
}
this.store('pin:_pinned', true);
}
} else {
if (!this.retrieve('pin:_pinned')) return this;
parent = this.getParent();
var offsetParent = (parent.getComputedStyle('position') != 'static' ? parent : parent.getOffsetParent());
pinnedPosition = this.getPosition(offsetParent);
this.store('pin:_pinned', false);
scrollFixer = this.retrieve('pin:_scrollFixer');
if (!scrollFixer){
this.setStyles({
position: 'absolute',
top: pinnedPosition.y + scroll.y,
left: pinnedPosition.x + scroll.x
});
} else {
this.store('pin:_scrollFixer', null);
window.removeEvent('scroll', scrollFixer);
}
this.removeClass('isPinned');
}
return this;
},
unpin: function(){
return this.pin(false);
},
togglePin: function(){
return this.pin(!this.retrieve('pin:_pinned'));
}
});
//<1.2compat>
Element.alias('togglepin', 'togglePin');
//</1.2compat>
})();
/*
---
script: Element.Position.js
name: Element.Position
description: Extends the Element native object to include methods useful positioning elements relative to others.
license: MIT-style license
authors:
- Aaron Newton
- Jacob Thornton
requires:
- Core/Options
- Core/Element.Dimensions
- Element.Measure
provides: [Element.Position]
...
*/
(function(original){
var local = Element.Position = {
options: {/*
edge: false,
returnPos: false,
minimum: {x: 0, y: 0},
maximum: {x: 0, y: 0},
relFixedPosition: false,
ignoreMargins: false,
ignoreScroll: false,
allowNegative: false,*/
relativeTo: document.body,
position: {
x: 'center', //left, center, right
y: 'center' //top, center, bottom
},
offset: {x: 0, y: 0}
},
getOptions: function(element, options){
options = Object.merge({}, local.options, options);
local.setPositionOption(options);
local.setEdgeOption(options);
local.setOffsetOption(element, options);
local.setDimensionsOption(element, options);
return options;
},
setPositionOption: function(options){
options.position = local.getCoordinateFromValue(options.position);
},
setEdgeOption: function(options){
var edgeOption = local.getCoordinateFromValue(options.edge);
options.edge = edgeOption ? edgeOption :
(options.position.x == 'center' && options.position.y == 'center') ? {x: 'center', y: 'center'} :
{x: 'left', y: 'top'};
},
setOffsetOption: function(element, options){
var parentOffset = {x: 0, y: 0},
offsetParent = element.measure(function(){
return document.id(this.getOffsetParent());
}),
parentScroll = offsetParent.getScroll();
if (!offsetParent || offsetParent == element.getDocument().body) return;
parentOffset = offsetParent.measure(function(){
var position = this.getPosition();
if (this.getStyle('position') == 'fixed'){
var scroll = window.getScroll();
position.x += scroll.x;
position.y += scroll.y;
}
return position;
});
options.offset = {
parentPositioned: offsetParent != document.id(options.relativeTo),
x: options.offset.x - parentOffset.x + parentScroll.x,
y: options.offset.y - parentOffset.y + parentScroll.y
};
},
setDimensionsOption: function(element, options){
options.dimensions = element.getDimensions({
computeSize: true,
styles: ['padding', 'border', 'margin']
});
},
getPosition: function(element, options){
var position = {};
options = local.getOptions(element, options);
var relativeTo = document.id(options.relativeTo) || document.body;
local.setPositionCoordinates(options, position, relativeTo);
if (options.edge) local.toEdge(position, options);
var offset = options.offset;
position.left = ((position.x >= 0 || offset.parentPositioned || options.allowNegative) ? position.x : 0).toInt();
position.top = ((position.y >= 0 || offset.parentPositioned || options.allowNegative) ? position.y : 0).toInt();
local.toMinMax(position, options);
if (options.relFixedPosition || relativeTo.getStyle('position') == 'fixed') local.toRelFixedPosition(relativeTo, position);
if (options.ignoreScroll) local.toIgnoreScroll(relativeTo, position);
if (options.ignoreMargins) local.toIgnoreMargins(position, options);
position.left = Math.ceil(position.left);
position.top = Math.ceil(position.top);
delete position.x;
delete position.y;
return position;
},
setPositionCoordinates: function(options, position, relativeTo){
var offsetY = options.offset.y,
offsetX = options.offset.x,
calc = (relativeTo == document.body) ? window.getScroll() : relativeTo.getPosition(),
top = calc.y,
left = calc.x,
winSize = window.getSize();
switch(options.position.x){
case 'left': position.x = left + offsetX; break;
case 'right': position.x = left + offsetX + relativeTo.offsetWidth; break;
default: position.x = left + ((relativeTo == document.body ? winSize.x : relativeTo.offsetWidth) / 2) + offsetX; break;
}
switch(options.position.y){
case 'top': position.y = top + offsetY; break;
case 'bottom': position.y = top + offsetY + relativeTo.offsetHeight; break;
default: position.y = top + ((relativeTo == document.body ? winSize.y : relativeTo.offsetHeight) / 2) + offsetY; break;
}
},
toMinMax: function(position, options){
var xy = {left: 'x', top: 'y'}, value;
['minimum', 'maximum'].each(function(minmax){
['left', 'top'].each(function(lr){
value = options[minmax] ? options[minmax][xy[lr]] : null;
if (value != null && ((minmax == 'minimum') ? position[lr] < value : position[lr] > value)) position[lr] = value;
});
});
},
toRelFixedPosition: function(relativeTo, position){
var winScroll = window.getScroll();
position.top += winScroll.y;
position.left += winScroll.x;
},
toIgnoreScroll: function(relativeTo, position){
var relScroll = relativeTo.getScroll();
position.top -= relScroll.y;
position.left -= relScroll.x;
},
toIgnoreMargins: function(position, options){
position.left += options.edge.x == 'right'
? options.dimensions['margin-right']
: (options.edge.x != 'center'
? -options.dimensions['margin-left']
: -options.dimensions['margin-left'] + ((options.dimensions['margin-right'] + options.dimensions['margin-left']) / 2));
position.top += options.edge.y == 'bottom'
? options.dimensions['margin-bottom']
: (options.edge.y != 'center'
? -options.dimensions['margin-top']
: -options.dimensions['margin-top'] + ((options.dimensions['margin-bottom'] + options.dimensions['margin-top']) / 2));
},
toEdge: function(position, options){
var edgeOffset = {},
dimensions = options.dimensions,
edge = options.edge;
switch(edge.x){
case 'left': edgeOffset.x = 0; break;
case 'right': edgeOffset.x = -dimensions.x - dimensions.computedRight - dimensions.computedLeft; break;
// center
default: edgeOffset.x = -(Math.round(dimensions.totalWidth / 2)); break;
}
switch(edge.y){
case 'top': edgeOffset.y = 0; break;
case 'bottom': edgeOffset.y = -dimensions.y - dimensions.computedTop - dimensions.computedBottom; break;
// center
default: edgeOffset.y = -(Math.round(dimensions.totalHeight / 2)); break;
}
position.x += edgeOffset.x;
position.y += edgeOffset.y;
},
getCoordinateFromValue: function(option){
if (typeOf(option) != 'string') return option;
option = option.toLowerCase();
return {
x: option.test('left') ? 'left'
: (option.test('right') ? 'right' : 'center'),
y: option.test(/upper|top/) ? 'top'
: (option.test('bottom') ? 'bottom' : 'center')
};
}
};
Element.implement({
position: function(options){
if (options && (options.x != null || options.y != null)) {
return (original ? original.apply(this, arguments) : this);
}
var position = this.setStyle('position', 'absolute').calculatePosition(options);
return (options && options.returnPos) ? position : this.setStyles(position);
},
calculatePosition: function(options){
return local.getPosition(this, options);
}
});
})(Element.prototype.position);
/*
---
script: Element.Shortcuts.js
name: Element.Shortcuts
description: Extends the Element native object to include some shortcut methods.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Element.Style
- /MooTools.More
provides: [Element.Shortcuts]
...
*/
Element.implement({
isDisplayed: function(){
return this.getStyle('display') != 'none';
},
isVisible: function(){
var w = this.offsetWidth,
h = this.offsetHeight;
return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.style.display != 'none';
},
toggle: function(){
return this[this.isDisplayed() ? 'hide' : 'show']();
},
hide: function(){
var d;
try {
//IE fails here if the element is not in the dom
d = this.getStyle('display');
} catch(e){}
if (d == 'none') return this;
return this.store('element:_originalDisplay', d || '').setStyle('display', 'none');
},
show: function(display){
if (!display && this.isDisplayed()) return this;
display = display || this.retrieve('element:_originalDisplay') || 'block';
return this.setStyle('display', (display == 'none') ? 'block' : display);
},
swapClass: function(remove, add){
return this.removeClass(remove).addClass(add);
}
});
Document.implement({
clearSelection: function(){
if (window.getSelection){
var selection = window.getSelection();
if (selection && selection.removeAllRanges) selection.removeAllRanges();
} else if (document.selection && document.selection.empty){
try {
//IE fails here if selected element is not in dom
document.selection.empty();
} catch(e){}
}
}
});
/*
---
script: IframeShim.js
name: IframeShim
description: Defines IframeShim, a class for obscuring select lists and flash objects in IE.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Element.Event
- Core/Element.Style
- Core/Options
- Core/Events
- /Element.Position
- /Class.Occlude
provides: [IframeShim]
...
*/
var IframeShim = new Class({
Implements: [Options, Events, Class.Occlude],
options: {
className: 'iframeShim',
src: 'javascript:false;document.write("");',
display: false,
zIndex: null,
margin: 0,
offset: {x: 0, y: 0},
browsers: (Browser.ie6 || (Browser.firefox && Browser.version < 3 && Browser.Platform.mac))
},
property: 'IframeShim',
initialize: function(element, options){
this.element = document.id(element);
if (this.occlude()) return this.occluded;
this.setOptions(options);
this.makeShim();
return this;
},
makeShim: function(){
if (this.options.browsers){
var zIndex = this.element.getStyle('zIndex').toInt();
if (!zIndex){
zIndex = 1;
var pos = this.element.getStyle('position');
if (pos == 'static' || !pos) this.element.setStyle('position', 'relative');
this.element.setStyle('zIndex', zIndex);
}
zIndex = ((this.options.zIndex != null || this.options.zIndex === 0) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1;
if (zIndex < 0) zIndex = 1;
this.shim = new Element('iframe', {
src: this.options.src,
scrolling: 'no',
frameborder: 0,
styles: {
zIndex: zIndex,
position: 'absolute',
border: 'none',
filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'
},
'class': this.options.className
}).store('IframeShim', this);
var inject = (function(){
this.shim.inject(this.element, 'after');
this[this.options.display ? 'show' : 'hide']();
this.fireEvent('inject');
}).bind(this);
if (!IframeShim.ready) window.addEvent('load', inject);
else inject();
} else {
this.position = this.hide = this.show = this.dispose = Function.from(this);
}
},
position: function(){
if (!IframeShim.ready || !this.shim) return this;
var size = this.element.measure(function(){
return this.getSize();
});
if (this.options.margin != undefined){
size.x = size.x - (this.options.margin * 2);
size.y = size.y - (this.options.margin * 2);
this.options.offset.x += this.options.margin;
this.options.offset.y += this.options.margin;
}
this.shim.set({width: size.x, height: size.y}).position({
relativeTo: this.element,
offset: this.options.offset
});
return this;
},
hide: function(){
if (this.shim) this.shim.setStyle('display', 'none');
return this;
},
show: function(){
if (this.shim) this.shim.setStyle('display', 'block');
return this.position();
},
dispose: function(){
if (this.shim) this.shim.dispose();
return this;
},
destroy: function(){
if (this.shim) this.shim.destroy();
return this;
}
});
window.addEvent('load', function(){
IframeShim.ready = true;
});
/*
---
script: Mask.js
name: Mask
description: Creates a mask element to cover another.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Options
- Core/Events
- Core/Element.Event
- /Class.Binds
- /Element.Position
- /IframeShim
provides: [Mask]
...
*/
var Mask = new Class({
Implements: [Options, Events],
Binds: ['position'],
options: {/*
onShow: function(){},
onHide: function(){},
onDestroy: function(){},
onClick: function(event){},
inject: {
where: 'after',
target: null,
},
hideOnClick: false,
id: null,
destroyOnHide: false,*/
style: {},
'class': 'mask',
maskMargins: false,
useIframeShim: true,
iframeShimOptions: {}
},
initialize: function(target, options){
this.target = document.id(target) || document.id(document.body);
this.target.store('mask', this);
this.setOptions(options);
this.render();
this.inject();
},
render: function(){
this.element = new Element('div', {
'class': this.options['class'],
id: this.options.id || 'mask-' + String.uniqueID(),
styles: Object.merge({}, this.options.style, {
display: 'none'
}),
events: {
click: function(event){
this.fireEvent('click', event);
if (this.options.hideOnClick) this.hide();
}.bind(this)
}
});
this.hidden = true;
},
toElement: function(){
return this.element;
},
inject: function(target, where){
where = where || (this.options.inject ? this.options.inject.where : '') || this.target == document.body ? 'inside' : 'after';
target = target || (this.options.inject && this.options.inject.target) || this.target;
this.element.inject(target, where);
if (this.options.useIframeShim){
this.shim = new IframeShim(this.element, this.options.iframeShimOptions);
this.addEvents({
show: this.shim.show.bind(this.shim),
hide: this.shim.hide.bind(this.shim),
destroy: this.shim.destroy.bind(this.shim)
});
}
},
position: function(){
this.resize(this.options.width, this.options.height);
this.element.position({
relativeTo: this.target,
position: 'topLeft',
ignoreMargins: !this.options.maskMargins,
ignoreScroll: this.target == document.body
});
return this;
},
resize: function(x, y){
var opt = {
styles: ['padding', 'border']
};
if (this.options.maskMargins) opt.styles.push('margin');
var dim = this.target.getComputedSize(opt);
if (this.target == document.body){
this.element.setStyles({width: 0, height: 0});
var win = window.getScrollSize();
if (dim.totalHeight < win.y) dim.totalHeight = win.y;
if (dim.totalWidth < win.x) dim.totalWidth = win.x;
}
this.element.setStyles({
width: Array.pick([x, dim.totalWidth, dim.x]),
height: Array.pick([y, dim.totalHeight, dim.y])
});
return this;
},
show: function(){
if (!this.hidden) return this;
window.addEvent('resize', this.position);
this.position();
this.showMask.apply(this, arguments);
return this;
},
showMask: function(){
this.element.setStyle('display', 'block');
this.hidden = false;
this.fireEvent('show');
},
hide: function(){
if (this.hidden) return this;
window.removeEvent('resize', this.position);
this.hideMask.apply(this, arguments);
if (this.options.destroyOnHide) return this.destroy();
return this;
},
hideMask: function(){
this.element.setStyle('display', 'none');
this.hidden = true;
this.fireEvent('hide');
},
toggle: function(){
this[this.hidden ? 'show' : 'hide']();
},
destroy: function(){
this.hide();
this.element.destroy();
this.fireEvent('destroy');
this.target.eliminate('mask');
}
});
Element.Properties.mask = {
set: function(options){
var mask = this.retrieve('mask');
if (mask) mask.destroy();
return this.eliminate('mask').store('mask:options', options);
},
get: function(){
var mask = this.retrieve('mask');
if (!mask){
mask = new Mask(this, this.retrieve('mask:options'));
this.store('mask', mask);
}
return mask;
}
};
Element.implement({
mask: function(options){
if (options) this.set('mask', options);
this.get('mask').show();
return this;
},
unmask: function(){
this.get('mask').hide();
return this;
}
});
/*
---
script: Spinner.js
name: Spinner
description: Adds a semi-transparent overlay over a dom element with a spinnin ajax icon.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Fx.Tween
- Core/Request
- /Class.refactor
- /Mask
provides: [Spinner]
...
*/
var Spinner = new Class({
Extends: Mask,
Implements: Chain,
options: {/*
message: false,*/
'class': 'spinner',
containerPosition: {},
content: {
'class': 'spinner-content'
},
messageContainer: {
'class': 'spinner-msg'
},
img: {
'class': 'spinner-img'
},
fxOptions: {
link: 'chain'
}
},
initialize: function(target, options){
this.target = document.id(target) || document.id(document.body);
this.target.store('spinner', this);
this.setOptions(options);
this.render();
this.inject();
// Add this to events for when noFx is true; parent methods handle hide/show.
var deactivate = function(){ this.active = false; }.bind(this);
this.addEvents({
hide: deactivate,
show: deactivate
});
},
render: function(){
this.parent();
this.element.set('id', this.options.id || 'spinner-' + String.uniqueID());
this.content = document.id(this.options.content) || new Element('div', this.options.content);
this.content.inject(this.element);
if (this.options.message){
this.msg = document.id(this.options.message) || new Element('p', this.options.messageContainer).appendText(this.options.message);
this.msg.inject(this.content);
}
if (this.options.img){
this.img = document.id(this.options.img) || new Element('div', this.options.img);
this.img.inject(this.content);
}
this.element.set('tween', this.options.fxOptions);
},
show: function(noFx){
if (this.active) return this.chain(this.show.bind(this));
if (!this.hidden){
this.callChain.delay(20, this);
return this;
}
this.active = true;
return this.parent(noFx);
},
showMask: function(noFx){
var pos = function(){
this.content.position(Object.merge({
relativeTo: this.element
}, this.options.containerPosition));
}.bind(this);
if (noFx){
this.parent();
pos();
} else {
if (!this.options.style.opacity) this.options.style.opacity = this.element.getStyle('opacity').toFloat();
this.element.setStyles({
display: 'block',
opacity: 0
}).tween('opacity', this.options.style.opacity);
pos();
this.hidden = false;
this.fireEvent('show');
this.callChain();
}
},
hide: function(noFx){
if (this.active) return this.chain(this.hide.bind(this));
if (this.hidden){
this.callChain.delay(20, this);
return this;
}
this.active = true;
return this.parent(noFx);
},
hideMask: function(noFx){
if (noFx) return this.parent();
this.element.tween('opacity', 0).get('tween').chain(function(){
this.element.setStyle('display', 'none');
this.hidden = true;
this.fireEvent('hide');
this.callChain();
}.bind(this));
},
destroy: function(){
this.content.destroy();
this.parent();
this.target.eliminate('spinner');
}
});
Request = Class.refactor(Request, {
options: {
useSpinner: false,
spinnerOptions: {},
spinnerTarget: false
},
initialize: function(options){
this._send = this.send;
this.send = function(options){
var spinner = this.getSpinner();
if (spinner) spinner.chain(this._send.pass(options, this)).show();
else this._send(options);
return this;
};
this.previous(options);
},
getSpinner: function(){
if (!this.spinner){
var update = document.id(this.options.spinnerTarget) || document.id(this.options.update);
if (this.options.useSpinner && update){
update.set('spinner', this.options.spinnerOptions);
var spinner = this.spinner = update.get('spinner');
['complete', 'exception', 'cancel'].each(function(event){
this.addEvent(event, spinner.hide.bind(spinner));
}, this);
}
}
return this.spinner;
}
});
Element.Properties.spinner = {
set: function(options){
var spinner = this.retrieve('spinner');
if (spinner) spinner.destroy();
return this.eliminate('spinner').store('spinner:options', options);
},
get: function(){
var spinner = this.retrieve('spinner');
if (!spinner){
spinner = new Spinner(this, this.retrieve('spinner:options'));
this.store('spinner', spinner);
}
return spinner;
}
};
Element.implement({
spin: function(options){
if (options) this.set('spinner', options);
this.get('spinner').show();
return this;
},
unspin: function(){
this.get('spinner').hide();
return this;
}
});
/*
---
script: Form.Request.js
name: Form.Request
description: Handles the basic functionality of submitting a form and updating a dom element with the result.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Request.HTML
- /Class.Binds
- /Class.Occlude
- /Spinner
- /String.QueryString
- /Element.Delegation
provides: [Form.Request]
...
*/
if (!window.Form) window.Form = {};
(function(){
Form.Request = new Class({
Binds: ['onSubmit', 'onFormValidate'],
Implements: [Options, Events, Class.Occlude],
options: {/*
onFailure: function(){},
onSuccess: function(){}, // aliased to onComplete,
onSend: function(){}*/
requestOptions: {
evalScripts: true,
useSpinner: true,
emulation: false,
link: 'ignore'
},
sendButtonClicked: true,
extraData: {},
resetForm: true
},
property: 'form.request',
initialize: function(form, target, options){
this.element = document.id(form);
if (this.occlude()) return this.occluded;
this.setOptions(options)
.setTarget(target)
.attach();
},
setTarget: function(target){
this.target = document.id(target);
if (!this.request){
this.makeRequest();
} else {
this.request.setOptions({
update: this.target
});
}
return this;
},
toElement: function(){
return this.element;
},
makeRequest: function(){
var self = this;
this.request = new Request.HTML(Object.merge({
update: this.target,
emulation: false,
spinnerTarget: this.element,
method: this.element.get('method') || 'post'
}, this.options.requestOptions)).addEvents({
success: function(tree, elements, html, javascript){
['complete', 'success'].each(function(evt){
self.fireEvent(evt, [self.target, tree, elements, html, javascript]);
});
},
failure: function(){
self.fireEvent('complete', arguments).fireEvent('failure', arguments);
},
exception: function(){
self.fireEvent('failure', arguments);
}
});
return this.attachReset();
},
attachReset: function(){
if (!this.options.resetForm) return this;
this.request.addEvent('success', function(){
Function.attempt(function(){
this.element.reset();
}.bind(this));
if (window.OverText) OverText.update();
}.bind(this));
return this;
},
attach: function(attach){
var method = (attach != false) ? 'addEvent' : 'removeEvent';
this.element[method]('click:relay(button, input[type=submit])', this.saveClickedButton.bind(this));
var fv = this.element.retrieve('validator');
if (fv) fv[method]('onFormValidate', this.onFormValidate);
else this.element[method]('submit', this.onSubmit);
return this;
},
detach: function(){
return this.attach(false);
},
//public method
enable: function(){
return this.attach();
},
//public method
disable: function(){
return this.detach();
},
onFormValidate: function(valid, form, event){
//if there's no event, then this wasn't a submit event
if (!event) return;
var fv = this.element.retrieve('validator');
if (valid || (fv && !fv.options.stopOnFailure)){
event.stop();
this.send();
}
},
onSubmit: function(event){
var fv = this.element.retrieve('validator');
if (fv){
//form validator was created after Form.Request
this.element.removeEvent('submit', this.onSubmit);
fv.addEvent('onFormValidate', this.onFormValidate);
this.element.validate();
return;
}
if (event) event.stop();
this.send();
},
saveClickedButton: function(event, target){
var targetName = target.get('name');
if (!targetName || !this.options.sendButtonClicked) return;
this.options.extraData[targetName] = target.get('value') || true;
this.clickedCleaner = function(){
delete this.options.extraData[targetName];
this.clickedCleaner = function(){};
}.bind(this);
},
clickedCleaner: function(){},
send: function(){
var str = this.element.toQueryString().trim(),
data = Object.toQueryString(this.options.extraData);
if (str) str += "&" + data;
else str = data;
this.fireEvent('send', [this.element, str.parseQueryString()]);
this.request.send({
data: str,
url: this.options.requestOptions.url || this.element.get('action')
});
this.clickedCleaner();
return this;
}
});
Element.implement('formUpdate', function(update, options){
var fq = this.retrieve('form.request');
if (!fq) {
fq = new Form.Request(this, update, options);
} else {
if (update) fq.setTarget(update);
if (options) fq.setOptions(options).makeRequest();
}
fq.send();
return this;
});
})();
/*
---
script: Fx.Reveal.js
name: Fx.Reveal
description: Defines Fx.Reveal, a class that shows and hides elements with a transition.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Fx.Morph
- /Element.Shortcuts
- /Element.Measure
provides: [Fx.Reveal]
...
*/
(function(){
var hideTheseOf = function(object){
var hideThese = object.options.hideInputs;
if (window.OverText){
var otClasses = [null];
OverText.each(function(ot){
otClasses.include('.' + ot.options.labelClass);
});
if (otClasses) hideThese += otClasses.join(', ');
}
return (hideThese) ? object.element.getElements(hideThese) : null;
};
Fx.Reveal = new Class({
Extends: Fx.Morph,
options: {/*
onShow: function(thisElement){},
onHide: function(thisElement){},
onComplete: function(thisElement){},
heightOverride: null,
widthOverride: null,*/
link: 'cancel',
styles: ['padding', 'border', 'margin'],
transitionOpacity: !Browser.ie6,
mode: 'vertical',
display: function(){
return this.element.get('tag') != 'tr' ? 'block' : 'table-row';
},
opacity: 1,
hideInputs: Browser.ie ? 'select, input, textarea, object, embed' : null
},
dissolve: function(){
if (!this.hiding && !this.showing){
if (this.element.getStyle('display') != 'none'){
this.hiding = true;
this.showing = false;
this.hidden = true;
this.cssText = this.element.style.cssText;
var startStyles = this.element.getComputedSize({
styles: this.options.styles,
mode: this.options.mode
});
if (this.options.transitionOpacity) startStyles.opacity = this.options.opacity;
var zero = {};
Object.each(startStyles, function(style, name){
zero[name] = [style, 0];
});
this.element.setStyles({
display: Function.from(this.options.display).call(this),
overflow: 'hidden'
});
var hideThese = hideTheseOf(this);
if (hideThese) hideThese.setStyle('visibility', 'hidden');
this.$chain.unshift(function(){
if (this.hidden){
this.hiding = false;
this.element.style.cssText = this.cssText;
this.element.setStyle('display', 'none');
if (hideThese) hideThese.setStyle('visibility', 'visible');
}
this.fireEvent('hide', this.element);
this.callChain();
}.bind(this));
this.start(zero);
} else {
this.callChain.delay(10, this);
this.fireEvent('complete', this.element);
this.fireEvent('hide', this.element);
}
} else if (this.options.link == 'chain'){
this.chain(this.dissolve.bind(this));
} else if (this.options.link == 'cancel' && !this.hiding){
this.cancel();
this.dissolve();
}
return this;
},
reveal: function(){
if (!this.showing && !this.hiding){
if (this.element.getStyle('display') == 'none'){
this.hiding = false;
this.showing = true;
this.hidden = false;
this.cssText = this.element.style.cssText;
var startStyles;
this.element.measure(function(){
startStyles = this.element.getComputedSize({
styles: this.options.styles,
mode: this.options.mode
});
}.bind(this));
if (this.options.heightOverride != null) startStyles.height = this.options.heightOverride.toInt();
if (this.options.widthOverride != null) startStyles.width = this.options.widthOverride.toInt();
if (this.options.transitionOpacity){
this.element.setStyle('opacity', 0);
startStyles.opacity = this.options.opacity;
}
var zero = {
height: 0,
display: Function.from(this.options.display).call(this)
};
Object.each(startStyles, function(style, name){
zero[name] = 0;
});
zero.overflow = 'hidden';
this.element.setStyles(zero);
var hideThese = hideTheseOf(this);
if (hideThese) hideThese.setStyle('visibility', 'hidden');
this.$chain.unshift(function(){
this.element.style.cssText = this.cssText;
this.element.setStyle('display', Function.from(this.options.display).call(this));
if (!this.hidden) this.showing = false;
if (hideThese) hideThese.setStyle('visibility', 'visible');
this.callChain();
this.fireEvent('show', this.element);
}.bind(this));
this.start(startStyles);
} else {
this.callChain();
this.fireEvent('complete', this.element);
this.fireEvent('show', this.element);
}
} else if (this.options.link == 'chain'){
this.chain(this.reveal.bind(this));
} else if (this.options.link == 'cancel' && !this.showing){
this.cancel();
this.reveal();
}
return this;
},
toggle: function(){
if (this.element.getStyle('display') == 'none'){
this.reveal();
} else {
this.dissolve();
}
return this;
},
cancel: function(){
this.parent.apply(this, arguments);
if (this.cssText != null) this.element.style.cssText = this.cssText;
this.hiding = false;
this.showing = false;
return this;
}
});
Element.Properties.reveal = {
set: function(options){
this.get('reveal').cancel().setOptions(options);
return this;
},
get: function(){
var reveal = this.retrieve('reveal');
if (!reveal){
reveal = new Fx.Reveal(this);
this.store('reveal', reveal);
}
return reveal;
}
};
Element.Properties.dissolve = Element.Properties.reveal;
Element.implement({
reveal: function(options){
this.get('reveal').setOptions(options).reveal();
return this;
},
dissolve: function(options){
this.get('reveal').setOptions(options).dissolve();
return this;
},
nix: function(options){
var params = Array.link(arguments, {destroy: Type.isBoolean, options: Type.isObject});
this.get('reveal').setOptions(options).dissolve().chain(function(){
this[params.destroy ? 'destroy' : 'dispose']();
}.bind(this));
return this;
},
wink: function(){
var params = Array.link(arguments, {duration: Type.isNumber, options: Type.isObject});
var reveal = this.get('reveal').setOptions(params.options);
reveal.reveal().chain(function(){
(function(){
reveal.dissolve();
}).delay(params.duration || 2000);
});
}
});
})();
/*
---
script: Form.Request.Append.js
name: Form.Request.Append
description: Handles the basic functionality of submitting a form and updating a dom element with the result. The result is appended to the DOM element instead of replacing its contents.
license: MIT-style license
authors:
- Aaron Newton
requires:
- /Form.Request
- /Fx.Reveal
- /Elements.from
provides: [Form.Request.Append]
...
*/
Form.Request.Append = new Class({
Extends: Form.Request,
options: {
//onBeforeEffect: function(){},
useReveal: true,
revealOptions: {},
inject: 'bottom'
},
makeRequest: function(){
this.request = new Request.HTML(Object.merge({
url: this.element.get('action'),
method: this.element.get('method') || 'post',
spinnerTarget: this.element
}, this.options.requestOptions, {
evalScripts: false
})
).addEvents({
success: function(tree, elements, html, javascript){
var container;
var kids = Elements.from(html);
if (kids.length == 1){
container = kids[0];
} else {
container = new Element('div', {
styles: {
display: 'none'
}
}).adopt(kids);
}
container.inject(this.target, this.options.inject);
if (this.options.requestOptions.evalScripts) Browser.exec(javascript);
this.fireEvent('beforeEffect', container);
var finish = function(){
this.fireEvent('success', [container, this.target, tree, elements, html, javascript]);
}.bind(this);
if (this.options.useReveal){
container.set('reveal', this.options.revealOptions).get('reveal').chain(finish);
container.reveal();
} else {
finish();
}
}.bind(this),
failure: function(xhr){
this.fireEvent('failure', xhr);
}.bind(this)
});
this.attachReset();
}
});
/*
---
name: Locale.en-US.Form.Validator
description: Form Validator messages for English.
license: MIT-style license
authors:
- Aaron Newton
requires:
- /Locale
provides: [Locale.en-US.Form.Validator]
...
*/
Locale.define('en-US', 'FormValidator', {
required: 'This field is required.',
minLength: 'Please enter at least {minLength} characters (you entered {length} characters).',
maxLength: 'Please enter no more than {maxLength} characters (you entered {length} characters).',
integer: 'Please enter an integer in this field. Numbers with decimals (e.g. 1.25) are not permitted.',
numeric: 'Please enter only numeric values in this field (i.e. "1" or "1.1" or "-1" or "-1.1").',
digits: 'Please use numbers and punctuation only in this field (for example, a phone number with dashes or dots is permitted).',
alpha: 'Please use only letters (a-z) within this field. No spaces or other characters are allowed.',
alphanum: 'Please use only letters (a-z) or numbers (0-9) in this field. No spaces or other characters are allowed.',
dateSuchAs: 'Please enter a valid date such as {date}',
dateInFormatMDY: 'Please enter a valid date such as MM/DD/YYYY (i.e. "12/31/1999")',
email: 'Please enter a valid email address. For example "fred@domain.com".',
url: 'Please enter a valid URL such as http://www.example.com.',
currencyDollar: 'Please enter a valid $ amount. For example $100.00 .',
oneRequired: 'Please enter something for at least one of these inputs.',
errorPrefix: 'Error: ',
warningPrefix: 'Warning: ',
// Form.Validator.Extras
noSpace: 'There can be no spaces in this input.',
reqChkByNode: 'No items are selected.',
requiredChk: 'This field is required.',
reqChkByName: 'Please select a {label}.',
match: 'This field needs to match the {matchName} field',
startDate: 'the start date',
endDate: 'the end date',
currendDate: 'the current date',
afterDate: 'The date should be the same or after {label}.',
beforeDate: 'The date should be the same or before {label}.',
startMonth: 'Please select a start month',
sameMonth: 'These two dates must be in the same month - you must change one or the other.',
creditcard: 'The credit card number entered is invalid. Please check the number and try again. {length} digits entered.'
});
/*
---
script: Form.Validator.js
name: Form.Validator
description: A css-class based form validation system.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Options
- Core/Events
- Core/Slick.Finder
- Core/Element.Event
- Core/Element.Style
- Core/JSON
- /Locale
- /Class.Binds
- /Date
- /Element.Forms
- /Locale.en-US.Form.Validator
- /Element.Shortcuts
provides: [Form.Validator, InputValidator, FormValidator.BaseValidators]
...
*/
if (!window.Form) window.Form = {};
var InputValidator = this.InputValidator = new Class({
Implements: [Options],
options: {
errorMsg: 'Validation failed.',
test: Function.from(true)
},
initialize: function(className, options){
this.setOptions(options);
this.className = className;
},
test: function(field, props){
field = document.id(field);
return (field) ? this.options.test(field, props || this.getProps(field)) : false;
},
getError: function(field, props){
field = document.id(field);
var err = this.options.errorMsg;
if (typeOf(err) == 'function') err = err(field, props || this.getProps(field));
return err;
},
getProps: function(field){
field = document.id(field);
return (field) ? field.get('validatorProps') : {};
}
});
Element.Properties.validators = {
get: function(){
return (this.get('data-validators') || this.className).clean().split(' ');
}
};
Element.Properties.validatorProps = {
set: function(props){
return this.eliminate('$moo:validatorProps').store('$moo:validatorProps', props);
},
get: function(props){
if (props) this.set(props);
if (this.retrieve('$moo:validatorProps')) return this.retrieve('$moo:validatorProps');
if (this.getProperty('data-validator-properties') || this.getProperty('validatorProps')){
try {
this.store('$moo:validatorProps', JSON.decode(this.getProperty('validatorProps') || this.getProperty('data-validator-properties')));
}catch(e){
return {};
}
} else {
var vals = this.get('validators').filter(function(cls){
return cls.test(':');
});
if (!vals.length){
this.store('$moo:validatorProps', {});
} else {
props = {};
vals.each(function(cls){
var split = cls.split(':');
if (split[1]){
try {
props[split[0]] = JSON.decode(split[1]);
} catch(e){}
}
});
this.store('$moo:validatorProps', props);
}
}
return this.retrieve('$moo:validatorProps');
}
};
Form.Validator = new Class({
Implements: [Options, Events],
Binds: ['onSubmit'],
options: {/*
onFormValidate: function(isValid, form, event){},
onElementValidate: function(isValid, field, className, warn){},
onElementPass: function(field){},
onElementFail: function(field, validatorsFailed){}, */
fieldSelectors: 'input, select, textarea',
ignoreHidden: true,
ignoreDisabled: true,
useTitles: false,
evaluateOnSubmit: true,
evaluateFieldsOnBlur: true,
evaluateFieldsOnChange: true,
serial: true,
stopOnFailure: true,
warningPrefix: function(){
return Form.Validator.getMsg('warningPrefix') || 'Warning: ';
},
errorPrefix: function(){
return Form.Validator.getMsg('errorPrefix') || 'Error: ';
}
},
initialize: function(form, options){
this.setOptions(options);
this.element = document.id(form);
this.element.store('validator', this);
this.warningPrefix = Function.from(this.options.warningPrefix)();
this.errorPrefix = Function.from(this.options.errorPrefix)();
if (this.options.evaluateOnSubmit) this.element.addEvent('submit', this.onSubmit);
if (this.options.evaluateFieldsOnBlur || this.options.evaluateFieldsOnChange) this.watchFields(this.getFields());
},
toElement: function(){
return this.element;
},
getFields: function(){
return (this.fields = this.element.getElements(this.options.fieldSelectors));
},
watchFields: function(fields){
fields.each(function(el){
if (this.options.evaluateFieldsOnBlur)
el.addEvent('blur', this.validationMonitor.pass([el, false], this));
if (this.options.evaluateFieldsOnChange)
el.addEvent('change', this.validationMonitor.pass([el, true], this));
}, this);
},
validationMonitor: function(){
clearTimeout(this.timer);
this.timer = this.validateField.delay(50, this, arguments);
},
onSubmit: function(event){
if (this.validate(event)) this.reset();
},
reset: function(){
this.getFields().each(this.resetField, this);
return this;
},
validate: function(event){
var result = this.getFields().map(function(field){
return this.validateField(field, true);
}, this).every(function(v){
return v;
});
this.fireEvent('formValidate', [result, this.element, event]);
if (this.options.stopOnFailure && !result && event) event.preventDefault();
return result;
},
validateField: function(field, force){
if (this.paused) return true;
field = document.id(field);
var passed = !field.hasClass('validation-failed');
var failed, warned;
if (this.options.serial && !force){
failed = this.element.getElement('.validation-failed');
warned = this.element.getElement('.warning');
}
if (field && (!failed || force || field.hasClass('validation-failed') || (failed && !this.options.serial))){
var validationTypes = field.get('validators');
var validators = validationTypes.some(function(cn){
return this.getValidator(cn);
}, this);
var validatorsFailed = [];
validationTypes.each(function(className){
if (className && !this.test(className, field)) validatorsFailed.include(className);
}, this);
passed = validatorsFailed.length === 0;
if (validators && !this.hasValidator(field, 'warnOnly')){
if (passed){
field.addClass('validation-passed').removeClass('validation-failed');
this.fireEvent('elementPass', [field]);
} else {
field.addClass('validation-failed').removeClass('validation-passed');
this.fireEvent('elementFail', [field, validatorsFailed]);
}
}
if (!warned){
var warnings = validationTypes.some(function(cn){
if (cn.test('^warn'))
return this.getValidator(cn.replace(/^warn-/,''));
else return null;
}, this);
field.removeClass('warning');
var warnResult = validationTypes.map(function(cn){
if (cn.test('^warn'))
return this.test(cn.replace(/^warn-/,''), field, true);
else return null;
}, this);
}
}
return passed;
},
test: function(className, field, warn){
field = document.id(field);
if ((this.options.ignoreHidden && !field.isVisible()) || (this.options.ignoreDisabled && field.get('disabled'))) return true;
var validator = this.getValidator(className);
if (warn != null) warn = false;
if (this.hasValidator(field, 'warnOnly')) warn = true;
var isValid = this.hasValidator(field, 'ignoreValidation') || (validator ? validator.test(field) : true);
if (validator && field.isVisible()) this.fireEvent('elementValidate', [isValid, field, className, warn]);
if (warn) return true;
return isValid;
},
hasValidator: function(field, value){
return field.get('validators').contains(value);
},
resetField: function(field){
field = document.id(field);
if (field){
field.get('validators').each(function(className){
if (className.test('^warn-')) className = className.replace(/^warn-/, '');
field.removeClass('validation-failed');
field.removeClass('warning');
field.removeClass('validation-passed');
}, this);
}
return this;
},
stop: function(){
this.paused = true;
return this;
},
start: function(){
this.paused = false;
return this;
},
ignoreField: function(field, warn){
field = document.id(field);
if (field){
this.enforceField(field);
if (warn) field.addClass('warnOnly');
else field.addClass('ignoreValidation');
}
return this;
},
enforceField: function(field){
field = document.id(field);
if (field) field.removeClass('warnOnly').removeClass('ignoreValidation');
return this;
}
});
Form.Validator.getMsg = function(key){
return Locale.get('FormValidator.' + key);
};
Form.Validator.adders = {
validators:{},
add : function(className, options){
this.validators[className] = new InputValidator(className, options);
//if this is a class (this method is used by instances of Form.Validator and the Form.Validator namespace)
//extend these validators into it
//this allows validators to be global and/or per instance
if (!this.initialize){
this.implement({
validators: this.validators
});
}
},
addAllThese : function(validators){
Array.convert(validators).each(function(validator){
this.add(validator[0], validator[1]);
}, this);
},
getValidator: function(className){
return this.validators[className.split(':')[0]];
}
};
Object.append(Form.Validator, Form.Validator.adders);
Form.Validator.implement(Form.Validator.adders);
Form.Validator.add('IsEmpty', {
errorMsg: false,
test: function(element){
if (element.type == 'select-one' || element.type == 'select')
return !(element.selectedIndex >= 0 && element.options[element.selectedIndex].value != '');
else
return ((element.get('value') == null) || (element.get('value').length == 0));
}
});
Form.Validator.addAllThese([
['required', {
errorMsg: function(){
return Form.Validator.getMsg('required');
},
test: function(element){
return !Form.Validator.getValidator('IsEmpty').test(element);
}
}],
['minLength', {
errorMsg: function(element, props){
if (typeOf(props.minLength) != 'null')
return Form.Validator.getMsg('minLength').substitute({minLength:props.minLength,length:element.get('value').length });
else return '';
},
test: function(element, props){
if (typeOf(props.minLength) != 'null') return (element.get('value').length >= (props.minLength || 0));
else return true;
}
}],
['maxLength', {
errorMsg: function(element, props){
//props is {maxLength:10}
if (typeOf(props.maxLength) != 'null')
return Form.Validator.getMsg('maxLength').substitute({maxLength:props.maxLength,length:element.get('value').length });
else return '';
},
test: function(element, props){
return element.get('value').length <= (props.maxLength || 10000);
}
}],
['validate-integer', {
errorMsg: Form.Validator.getMsg.pass('integer'),
test: function(element){
return Form.Validator.getValidator('IsEmpty').test(element) || (/^(-?[1-9]\d*|0)$/).test(element.get('value'));
}
}],
['validate-numeric', {
errorMsg: Form.Validator.getMsg.pass('numeric'),
test: function(element){
return Form.Validator.getValidator('IsEmpty').test(element) ||
(/^-?(?:0$0(?=\d*\.)|[1-9]|0)\d*(\.\d+)?$/).test(element.get('value'));
}
}],
['validate-digits', {
errorMsg: Form.Validator.getMsg.pass('digits'),
test: function(element){
return Form.Validator.getValidator('IsEmpty').test(element) || (/^[\d() .:\-\+#]+$/.test(element.get('value')));
}
}],
['validate-alpha', {
errorMsg: Form.Validator.getMsg.pass('alpha'),
test: function(element){
return Form.Validator.getValidator('IsEmpty').test(element) || (/^[a-zA-Z]+$/).test(element.get('value'));
}
}],
['validate-alphanum', {
errorMsg: Form.Validator.getMsg.pass('alphanum'),
test: function(element){
return Form.Validator.getValidator('IsEmpty').test(element) || !(/\W/).test(element.get('value'));
}
}],
['validate-date', {
errorMsg: function(element, props){
if (Date.parse){
var format = props.dateFormat || '%x';
return Form.Validator.getMsg('dateSuchAs').substitute({date: new Date().format(format)});
} else {
return Form.Validator.getMsg('dateInFormatMDY');
}
},
test: function(element, props){
if (Form.Validator.getValidator('IsEmpty').test(element)) return true;
var dateLocale = Locale.getCurrent().sets.Date,
dateNouns = new RegExp([dateLocale.days, dateLocale.days_abbr, dateLocale.months, dateLocale.months_abbr].flatten().join('|'), 'i'),
value = element.get('value'),
wordsInValue = value.match(/[a-z]+/gi);
if (wordsInValue && !wordsInValue.every(dateNouns.exec, dateNouns)) return false;
var date = Date.parse(value),
format = props.dateFormat || '%x',
formatted = date.format(format);
if (formatted != 'invalid date') element.set('value', formatted);
return date.isValid();
}
}],
['validate-email', {
errorMsg: Form.Validator.getMsg.pass('email'),
test: function(element){
/*
var chars = "[a-z0-9!#$%&'*+/=?^_`{|}~-]",
local = '(?:' + chars + '\\.?){0,63}' + chars,
label = '[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?',
hostname = '(?:' + label + '\\.)*' + label;
octet = '(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)',
ipv4 = '\\[(?:' + octet + '\\.){3}' + octet + '\\]',
domain = '(?:' + hostname + '|' + ipv4 + ')';
var regex = new RegExp('^' + local + '@' + domain + '$', 'i');
*/
return Form.Validator.getValidator('IsEmpty').test(element) || (/^(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]\.?){0,63}[a-z0-9!#$%&'*+\/=?^_`{|}~-]@(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\])$/i).test(element.get('value'));
}
}],
['validate-url', {
errorMsg: Form.Validator.getMsg.pass('url'),
test: function(element){
return Form.Validator.getValidator('IsEmpty').test(element) || (/^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i).test(element.get('value'));
}
}],
['validate-currency-dollar', {
errorMsg: Form.Validator.getMsg.pass('currencyDollar'),
test: function(element){
return Form.Validator.getValidator('IsEmpty').test(element) || (/^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(element.get('value'));
}
}],
['validate-one-required', {
errorMsg: Form.Validator.getMsg.pass('oneRequired'),
test: function(element, props){
var p = document.id(props['validate-one-required']) || element.getParent(props['validate-one-required']);
return p.getElements('input').some(function(el){
if (['checkbox', 'radio'].contains(el.get('type'))) return el.get('checked');
return el.get('value');
});
}
}]
]);
Element.Properties.validator = {
set: function(options){
this.get('validator').setOptions(options);
},
get: function(){
var validator = this.retrieve('validator');
if (!validator){
validator = new Form.Validator(this);
this.store('validator', validator);
}
return validator;
}
};
Element.implement({
validate: function(options){
if (options) this.set('validator', options);
return this.get('validator').validate();
}
});
//<1.2compat>
//legacy
var FormValidator = Form.Validator;
//</1.2compat>
/*
---
script: Form.Validator.Inline.js
name: Form.Validator.Inline
description: Extends Form.Validator to add inline messages.
license: MIT-style license
authors:
- Aaron Newton
requires:
- /Form.Validator
provides: [Form.Validator.Inline]
...
*/
Form.Validator.Inline = new Class({
Extends: Form.Validator,
options: {
showError: function(errorElement){
if (errorElement.reveal) errorElement.reveal();
else errorElement.setStyle('display', 'block');
},
hideError: function(errorElement){
if (errorElement.dissolve) errorElement.dissolve();
else errorElement.setStyle('display', 'none');
},
scrollToErrorsOnSubmit: true,
scrollToErrorsOnBlur: false,
scrollToErrorsOnChange: false,
scrollFxOptions: {
transition: 'quad:out',
offset: {
y: -20
}
}
},
initialize: function(form, options){
this.parent(form, options);
this.addEvent('onElementValidate', function(isValid, field, className, warn){
var validator = this.getValidator(className);
if (!isValid && validator.getError(field)){
if (warn) field.addClass('warning');
var advice = this.makeAdvice(className, field, validator.getError(field), warn);
this.insertAdvice(advice, field);
this.showAdvice(className, field);
} else {
this.hideAdvice(className, field);
}
});
},
makeAdvice: function(className, field, error, warn){
var errorMsg = (warn) ? this.warningPrefix : this.errorPrefix;
errorMsg += (this.options.useTitles) ? field.title || error:error;
var cssClass = (warn) ? 'warning-advice' : 'validation-advice';
var advice = this.getAdvice(className, field);
if (advice){
advice = advice.set('html', errorMsg);
} else {
advice = new Element('div', {
html: errorMsg,
styles: { display: 'none' },
id: 'advice-' + className.split(':')[0] + '-' + this.getFieldId(field)
}).addClass(cssClass);
}
field.store('$moo:advice-' + className, advice);
return advice;
},
getFieldId : function(field){
return field.id ? field.id : field.id = 'input_' + field.name;
},
showAdvice: function(className, field){
var advice = this.getAdvice(className, field);
if (
advice &&
!field.retrieve('$moo:' + this.getPropName(className)) &&
(
advice.getStyle('display') == 'none' ||
advice.getStyle('visiblity') == 'hidden' ||
advice.getStyle('opacity') == 0
)
){
field.store('$moo:' + this.getPropName(className), true);
this.options.showError(advice);
this.fireEvent('showAdvice', [field, advice, className]);
}
},
hideAdvice: function(className, field){
var advice = this.getAdvice(className, field);
if (advice && field.retrieve('$moo:' + this.getPropName(className))){
field.store('$moo:' + this.getPropName(className), false);
this.options.hideError(advice);
this.fireEvent('hideAdvice', [field, advice, className]);
}
},
getPropName: function(className){
return 'advice' + className;
},
resetField: function(field){
field = document.id(field);
if (!field) return this;
this.parent(field);
field.get('validators').each(function(className){
this.hideAdvice(className, field);
}, this);
return this;
},
getAllAdviceMessages: function(field, force){
var advice = [];
if (field.hasClass('ignoreValidation') && !force) return advice;
var validators = field.get('validators').some(function(cn){
var warner = cn.test('^warn-') || field.hasClass('warnOnly');
if (warner) cn = cn.replace(/^warn-/, '');
var validator = this.getValidator(cn);
if (!validator) return;
advice.push({
message: validator.getError(field),
warnOnly: warner,
passed: validator.test(),
validator: validator
});
}, this);
return advice;
},
getAdvice: function(className, field){
return field.retrieve('$moo:advice-' + className);
},
insertAdvice: function(advice, field){
//Check for error position prop
var props = field.get('validatorProps');
//Build advice
if (!props.msgPos || !document.id(props.msgPos)){
if (field.type && field.type.toLowerCase() == 'radio') field.getParent().adopt(advice);
else advice.inject(document.id(field), 'after');
} else {
document.id(props.msgPos).grab(advice);
}
},
validateField: function(field, force, scroll){
var result = this.parent(field, force);
if (((this.options.scrollToErrorsOnSubmit && scroll == null) || scroll) && !result){
var failed = document.id(this).getElement('.validation-failed');
var par = document.id(this).getParent();
while (par != document.body && par.getScrollSize().y == par.getSize().y){
par = par.getParent();
}
var fx = par.retrieve('$moo:fvScroller');
if (!fx && window.Fx && Fx.Scroll){
fx = new Fx.Scroll(par, this.options.scrollFxOptions);
par.store('$moo:fvScroller', fx);
}
if (failed){
if (fx) fx.toElement(failed);
else par.scrollTo(par.getScroll().x, failed.getPosition(par).y - 20);
}
}
return result;
},
watchFields: function(fields){
fields.each(function(el){
if (this.options.evaluateFieldsOnBlur){
el.addEvent('blur', this.validationMonitor.pass([el, false, this.options.scrollToErrorsOnBlur], this));
}
if (this.options.evaluateFieldsOnChange){
el.addEvent('change', this.validationMonitor.pass([el, true, this.options.scrollToErrorsOnChange], this));
}
}, this);
}
});
/*
---
script: Form.Validator.Extras.js
name: Form.Validator.Extras
description: Additional validators for the Form.Validator class.
license: MIT-style license
authors:
- Aaron Newton
requires:
- /Form.Validator
provides: [Form.Validator.Extras]
...
*/
Form.Validator.addAllThese([
['validate-enforce-oncheck', {
test: function(element, props){
var fv = element.getParent('form').retrieve('validator');
if (!fv) return true;
(props.toEnforce || document.id(props.enforceChildrenOf).getElements('input, select, textarea')).map(function(item){
if (element.checked){
fv.enforceField(item);
} else {
fv.ignoreField(item);
fv.resetField(item);
}
});
return true;
}
}],
['validate-ignore-oncheck', {
test: function(element, props){
var fv = element.getParent('form').retrieve('validator');
if (!fv) return true;
(props.toIgnore || document.id(props.ignoreChildrenOf).getElements('input, select, textarea')).each(function(item){
if (element.checked){
fv.ignoreField(item);
fv.resetField(item);
} else {
fv.enforceField(item);
}
});
return true;
}
}],
['validate-nospace', {
errorMsg: function(){
return Form.Validator.getMsg('noSpace');
},
test: function(element, props){
return !element.get('value').test(/\s/);
}
}],
['validate-toggle-oncheck', {
test: function(element, props){
var fv = element.getParent('form').retrieve('validator');
if (!fv) return true;
var eleArr = props.toToggle || document.id(props.toToggleChildrenOf).getElements('input, select, textarea');
if (!element.checked){
eleArr.each(function(item){
fv.ignoreField(item);
fv.resetField(item);
});
} else {
eleArr.each(function(item){
fv.enforceField(item);
});
}
return true;
}
}],
['validate-reqchk-bynode', {
errorMsg: function(){
return Form.Validator.getMsg('reqChkByNode');
},
test: function(element, props){
return (document.id(props.nodeId).getElements(props.selector || 'input[type=checkbox], input[type=radio]')).some(function(item){
return item.checked;
});
}
}],
['validate-required-check', {
errorMsg: function(element, props){
return props.useTitle ? element.get('title') : Form.Validator.getMsg('requiredChk');
},
test: function(element, props){
return !!element.checked;
}
}],
['validate-reqchk-byname', {
errorMsg: function(element, props){
return Form.Validator.getMsg('reqChkByName').substitute({label: props.label || element.get('type')});
},
test: function(element, props){
var grpName = props.groupName || element.get('name');
var oneCheckedItem = $$(document.getElementsByName(grpName)).some(function(item, index){
return item.checked;
});
var fv = element.getParent('form').retrieve('validator');
if (oneCheckedItem && fv) fv.resetField(element);
return oneCheckedItem;
}
}],
['validate-match', {
errorMsg: function(element, props){
return Form.Validator.getMsg('match').substitute({matchName: props.matchName || document.id(props.matchInput).get('name')});
},
test: function(element, props){
var eleVal = element.get('value');
var matchVal = document.id(props.matchInput) && document.id(props.matchInput).get('value');
return eleVal && matchVal ? eleVal == matchVal : true;
}
}],
['validate-after-date', {
errorMsg: function(element, props){
return Form.Validator.getMsg('afterDate').substitute({
label: props.afterLabel || (props.afterElement ? Form.Validator.getMsg('startDate') : Form.Validator.getMsg('currentDate'))
});
},
test: function(element, props){
var start = document.id(props.afterElement) ? Date.parse(document.id(props.afterElement).get('value')) : new Date();
var end = Date.parse(element.get('value'));
return end && start ? end >= start : true;
}
}],
['validate-before-date', {
errorMsg: function(element, props){
return Form.Validator.getMsg('beforeDate').substitute({
label: props.beforeLabel || (props.beforeElement ? Form.Validator.getMsg('endDate') : Form.Validator.getMsg('currentDate'))
});
},
test: function(element, props){
var start = Date.parse(element.get('value'));
var end = document.id(props.beforeElement) ? Date.parse(document.id(props.beforeElement).get('value')) : new Date();
return end && start ? end >= start : true;
}
}],
['validate-custom-required', {
errorMsg: function(){
return Form.Validator.getMsg('required');
},
test: function(element, props){
return element.get('value') != props.emptyValue;
}
}],
['validate-same-month', {
errorMsg: function(element, props){
var startMo = document.id(props.sameMonthAs) && document.id(props.sameMonthAs).get('value');
var eleVal = element.get('value');
if (eleVal != '') return Form.Validator.getMsg(startMo ? 'sameMonth' : 'startMonth');
},
test: function(element, props){
var d1 = Date.parse(element.get('value'));
var d2 = Date.parse(document.id(props.sameMonthAs) && document.id(props.sameMonthAs).get('value'));
return d1 && d2 ? d1.format('%B') == d2.format('%B') : true;
}
}],
['validate-cc-num', {
errorMsg: function(element){
var ccNum = element.get('value').replace(/[^0-9]/g, '');
return Form.Validator.getMsg('creditcard').substitute({length: ccNum.length});
},
test: function(element){
// required is a different test
if (Form.Validator.getValidator('IsEmpty').test(element)) return true;
// Clean number value
var ccNum = element.get('value');
ccNum = ccNum.replace(/[^0-9]/g, '');
var valid_type = false;
if (ccNum.test(/^4[0-9]{12}([0-9]{3})?$/)) valid_type = 'Visa';
else if (ccNum.test(/^5[1-5]([0-9]{14})$/)) valid_type = 'Master Card';
else if (ccNum.test(/^3[47][0-9]{13}$/)) valid_type = 'American Express';
else if (ccNum.test(/^6011[0-9]{12}$/)) valid_type = 'Discover';
if (valid_type){
var sum = 0;
var cur = 0;
for (var i=ccNum.length-1; i>=0; --i){
cur = ccNum.charAt(i).toInt();
if (cur == 0) continue;
if ((ccNum.length-i) % 2 == 0) cur += cur;
if (cur > 9){
cur = cur.toString().charAt(0).toInt() + cur.toString().charAt(1).toInt();
}
sum += cur;
}
if ((sum % 10) == 0) return true;
}
var chunks = '';
while (ccNum != ''){
chunks += ' ' + ccNum.substr(0,4);
ccNum = ccNum.substr(4);
}
element.getParent('form').retrieve('validator').ignoreField(element);
element.set('value', chunks.clean());
element.getParent('form').retrieve('validator').enforceField(element);
return false;
}
}]
]);
/*
---
script: OverText.js
name: OverText
description: Shows text over an input that disappears when the user clicks into it. The text remains hidden if the user adds a value.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Options
- Core/Events
- Core/Element.Event
- Class.Binds
- Class.Occlude
- Element.Position
- Element.Shortcuts
provides: [OverText]
...
*/
var OverText = new Class({
Implements: [Options, Events, Class.Occlude],
Binds: ['reposition', 'assert', 'focus', 'hide'],
options: {/*
textOverride: null,
onFocus: function(){},
onTextHide: function(textEl, inputEl){},
onTextShow: function(textEl, inputEl){}, */
element: 'label',
labelClass: 'overTxtLabel',
positionOptions: {
position: 'upperLeft',
edge: 'upperLeft',
offset: {
x: 4,
y: 2
}
},
poll: false,
pollInterval: 250,
wrap: false
},
property: 'OverText',
initialize: function(element, options){
element = this.element = document.id(element);
if (this.occlude()) return this.occluded;
this.setOptions(options);
this.attach(element);
OverText.instances.push(this);
if (this.options.poll) this.poll();
},
toElement: function(){
return this.element;
},
attach: function(){
var element = this.element,
options = this.options,
value = options.textOverride || element.get('alt') || element.get('title');
if (!value) return this;
var text = this.text = new Element(options.element, {
'class': options.labelClass,
styles: {
lineHeight: 'normal',
position: 'absolute',
cursor: 'text'
},
html: value,
events: {
click: this.hide.pass(options.element == 'label', this)
}
}).inject(element, 'after');
if (options.element == 'label'){
if (!element.get('id')) element.set('id', 'input_' + String.uniqueID());
text.set('for', element.get('id'));
}
if (options.wrap){
this.textHolder = new Element('div.overTxtWrapper', {
styles: {
lineHeight: 'normal',
position: 'relative'
}
}).grab(text).inject(element, 'before');
}
return this.enable();
},
destroy: function(){
this.element.eliminate(this.property); // Class.Occlude storage
this.disable();
if (this.text) this.text.destroy();
if (this.textHolder) this.textHolder.destroy();
return this;
},
disable: function(){
this.element.removeEvents({
focus: this.focus,
blur: this.assert,
change: this.assert
});
window.removeEvent('resize', this.reposition);
this.hide(true, true);
return this;
},
enable: function(){
this.element.addEvents({
focus: this.focus,
blur: this.assert,
change: this.assert
});
window.addEvent('resize', this.reposition);
this.assert(true);
this.reposition();
return this;
},
wrap: function(){
if (this.options.element == 'label'){
if (!this.element.get('id')) this.element.set('id', 'input_' + String.uniqueID());
this.text.set('for', this.element.get('id'));
}
},
startPolling: function(){
this.pollingPaused = false;
return this.poll();
},
poll: function(stop){
//start immediately
//pause on focus
//resumeon blur
if (this.poller && !stop) return this;
if (stop){
clearInterval(this.poller);
} else {
this.poller = (function(){
if (!this.pollingPaused) this.assert(true);
}).periodical(this.options.pollInterval, this);
}
return this;
},
stopPolling: function(){
this.pollingPaused = true;
return this.poll(true);
},
focus: function(){
if (this.text && (!this.text.isDisplayed() || this.element.get('disabled'))) return this;
return this.hide();
},
hide: function(suppressFocus, force){
if (this.text && (this.text.isDisplayed() && (!this.element.get('disabled') || force))){
this.text.hide();
this.fireEvent('textHide', [this.text, this.element]);
this.pollingPaused = true;
if (!suppressFocus){
try {
this.element.fireEvent('focus');
this.element.focus();
} catch(e){} //IE barfs if you call focus on hidden elements
}
}
return this;
},
show: function(){
if (this.text && !this.text.isDisplayed()){
this.text.show();
this.reposition();
this.fireEvent('textShow', [this.text, this.element]);
this.pollingPaused = false;
}
return this;
},
test: function(){
return !this.element.get('value');
},
assert: function(suppressFocus){
return this[this.test() ? 'show' : 'hide'](suppressFocus);
},
reposition: function(){
this.assert(true);
if (!this.element.isVisible()) return this.stopPolling().hide();
if (this.text && this.test()){
this.text.position(Object.merge(this.options.positionOptions, {
relativeTo: this.element
}));
}
return this;
}
});
OverText.instances = [];
Object.append(OverText, {
each: function(fn){
return OverText.instances.each(function(ot, i){
if (ot.element && ot.text) fn.call(OverText, ot, i);
});
},
update: function(){
return OverText.each(function(ot){
return ot.reposition();
});
},
hideAll: function(){
return OverText.each(function(ot){
return ot.hide(true, true);
});
},
showAll: function(){
return OverText.each(function(ot){
return ot.show();
});
}
});
/*
---
script: Fx.Elements.js
name: Fx.Elements
description: Effect to change any number of CSS properties of any number of Elements.
license: MIT-style license
authors:
- Valerio Proietti
requires:
- Core/Fx.CSS
- /MooTools.More
provides: [Fx.Elements]
...
*/
Fx.Elements = new Class({
Extends: Fx.CSS,
initialize: function(elements, options){
this.elements = this.subject = $$(elements);
this.parent(options);
},
compute: function(from, to, delta){
var now = {};
for (var i in from){
var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
}
return now;
},
set: function(now){
for (var i in now){
if (!this.elements[i]) continue;
var iNow = now[i];
for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
}
return this;
},
start: function(obj){
if (!this.check(obj)) return this;
var from = {}, to = {};
for (var i in obj){
if (!this.elements[i]) continue;
var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
for (var p in iProps){
var parsed = this.prepare(this.elements[i], p, iProps[p]);
iFrom[p] = parsed.from;
iTo[p] = parsed.to;
}
}
return this.parent(from, to);
}
});
/*
---
script: Fx.Accordion.js
name: Fx.Accordion
description: An Fx.Elements extension which allows you to easily create accordion type controls.
license: MIT-style license
authors:
- Valerio Proietti
requires:
- Core/Element.Event
- /Fx.Elements
provides: [Fx.Accordion]
...
*/
Fx.Accordion = new Class({
Extends: Fx.Elements,
options: {/*
onActive: function(toggler, section){},
onBackground: function(toggler, section){},*/
fixedHeight: false,
fixedWidth: false,
display: 0,
show: false,
height: true,
width: false,
opacity: true,
alwaysHide: false,
trigger: 'click',
initialDisplayFx: true,
resetHeight: true
},
initialize: function(){
var defined = function(obj){
return obj != null;
};
var params = Array.link(arguments, {
'container': Type.isElement, //deprecated
'options': Type.isObject,
'togglers': defined,
'elements': defined
});
this.parent(params.elements, params.options);
var options = this.options,
togglers = this.togglers = $$(params.togglers);
this.previous = -1;
this.internalChain = new Chain();
if (options.alwaysHide) this.options.link = 'chain';
if (options.show || this.options.show === 0){
options.display = false;
this.previous = options.show;
}
if (options.start){
options.display = false;
options.show = false;
}
var effects = this.effects = {};
if (options.opacity) effects.opacity = 'fullOpacity';
if (options.width) effects.width = options.fixedWidth ? 'fullWidth' : 'offsetWidth';
if (options.height) effects.height = options.fixedHeight ? 'fullHeight' : 'scrollHeight';
for (var i = 0, l = togglers.length; i < l; i++) this.addSection(togglers[i], this.elements[i]);
this.elements.each(function(el, i){
if (options.show === i){
this.fireEvent('active', [togglers[i], el]);
} else {
for (var fx in effects) el.setStyle(fx, 0);
}
}, this);
if (options.display || options.display === 0 || options.initialDisplayFx === false){
this.display(options.display, options.initialDisplayFx);
}
if (options.fixedHeight !== false) options.resetHeight = false;
this.addEvent('complete', this.internalChain.callChain.bind(this.internalChain));
},
addSection: function(toggler, element){
toggler = document.id(toggler);
element = document.id(element);
this.togglers.include(toggler);
this.elements.include(element);
var togglers = this.togglers,
options = this.options,
test = togglers.contains(toggler),
idx = togglers.indexOf(toggler),
displayer = this.display.pass(idx, this);
toggler.store('accordion:display', displayer)
.addEvent(options.trigger, displayer);
if (options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
if (options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
element.fullOpacity = 1;
if (options.fixedWidth) element.fullWidth = options.fixedWidth;
if (options.fixedHeight) element.fullHeight = options.fixedHeight;
element.setStyle('overflow', 'hidden');
if (!test) for (var fx in this.effects){
element.setStyle(fx, 0);
}
return this;
},
removeSection: function(toggler, displayIndex){
var togglers = this.togglers,
idx = togglers.indexOf(toggler),
element = this.elements[idx];
var remover = function(){
togglers.erase(toggler);
this.elements.erase(element);
this.detach(toggler);
}.bind(this);
if (this.now == idx || displayIndex != null){
this.display(displayIndex != null ? displayIndex : (idx - 1 >= 0 ? idx - 1 : 0)).chain(remover);
} else {
remover();
}
return this;
},
detach: function(toggler){
var remove = function(toggler){
toggler.removeEvent(this.options.trigger, toggler.retrieve('accordion:display'));
}.bind(this);
if (!toggler) this.togglers.each(remove);
else remove(toggler);
return this;
},
display: function(index, useFx){
if (!this.check(index, useFx)) return this;
var obj = {},
elements = this.elements,
options = this.options,
effects = this.effects;
if (useFx == null) useFx = true;
if (typeOf(index) == 'element') index = elements.indexOf(index);
if (index == this.previous && !options.alwaysHide) return this;
if (options.resetHeight){
var prev = elements[this.previous];
if (prev && !this.selfHidden){
for (var fx in effects) prev.setStyle(fx, prev[effects[fx]]);
}
}
if ((this.timer && options.link == 'chain') || (index === this.previous && !options.alwaysHide)) return this;
this.previous = index;
this.selfHidden = false;
elements.each(function(el, i){
obj[i] = {};
var hide;
if (i != index){
hide = true;
} else if (options.alwaysHide && ((el.offsetHeight > 0 && options.height) || el.offsetWidth > 0 && options.width)){
hide = true;
this.selfHidden = true;
}
this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
for (var fx in effects) obj[i][fx] = hide ? 0 : el[effects[fx]];
if (!useFx && !hide && options.resetHeight) obj[i].height = 'auto';
}, this);
this.internalChain.clearChain();
this.internalChain.chain(function(){
if (options.resetHeight && !this.selfHidden){
var el = elements[index];
if (el) el.setStyle('height', 'auto');
}
}.bind(this));
return useFx ? this.start(obj) : this.set(obj).internalChain.callChain();
}
});
/*<1.2compat>*/
/*
Compatibility with 1.2.0
*/
var Accordion = new Class({
Extends: Fx.Accordion,
initialize: function(){
this.parent.apply(this, arguments);
var params = Array.link(arguments, {'container': Type.isElement});
this.container = params.container;
},
addSection: function(toggler, element, pos){
toggler = document.id(toggler);
element = document.id(element);
var test = this.togglers.contains(toggler);
var len = this.togglers.length;
if (len && (!test || pos)){
pos = pos != null ? pos : len - 1;
toggler.inject(this.togglers[pos], 'before');
element.inject(toggler, 'after');
} else if (this.container && !test){
toggler.inject(this.container);
element.inject(this.container);
}
return this.parent.apply(this, arguments);
}
});
/*</1.2compat>*/
/*
---
script: Fx.Move.js
name: Fx.Move
description: Defines Fx.Move, a class that works with Element.Position.js to transition an element from one location to another.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Fx.Morph
- /Element.Position
provides: [Fx.Move]
...
*/
Fx.Move = new Class({
Extends: Fx.Morph,
options: {
relativeTo: document.body,
position: 'center',
edge: false,
offset: {x: 0, y: 0}
},
start: function(destination){
var element = this.element,
topLeft = element.getStyles('top', 'left');
if (topLeft.top == 'auto' || topLeft.left == 'auto'){
element.setPosition(element.getPosition(element.getOffsetParent()));
}
return this.parent(element.position(Object.merge({}, this.options, destination, {returnPos: true})));
}
});
Element.Properties.move = {
set: function(options){
this.get('move').cancel().setOptions(options);
return this;
},
get: function(){
var move = this.retrieve('move');
if (!move){
move = new Fx.Move(this, {link: 'cancel'});
this.store('move', move);
}
return move;
}
};
Element.implement({
move: function(options){
this.get('move').start(options);
return this;
}
});
/*
---
script: Fx.Scroll.js
name: Fx.Scroll
description: Effect to smoothly scroll any element, including the window.
license: MIT-style license
authors:
- Valerio Proietti
requires:
- Core/Fx
- Core/Element.Event
- Core/Element.Dimensions
- /MooTools.More
provides: [Fx.Scroll]
...
*/
(function(){
Fx.Scroll = new Class({
Extends: Fx,
options: {
offset: {x: 0, y: 0},
wheelStops: true
},
initialize: function(element, options){
this.element = this.subject = document.id(element);
this.parent(options);
if (typeOf(this.element) != 'element') this.element = document.id(this.element.getDocument().body);
if (this.options.wheelStops){
var stopper = this.element,
cancel = this.cancel.pass(false, this);
this.addEvent('start', function(){
stopper.addEvent('mousewheel', cancel);
}, true);
this.addEvent('complete', function(){
stopper.removeEvent('mousewheel', cancel);
}, true);
}
},
set: function(){
var now = Array.flatten(arguments);
if (Browser.firefox) now = [Math.round(now[0]), Math.round(now[1])]; // not needed anymore in newer firefox versions
this.element.scrollTo(now[0], now[1]);
return this;
},
compute: function(from, to, delta){
return [0, 1].map(function(i){
return Fx.compute(from[i], to[i], delta);
});
},
start: function(x, y){
if (!this.check(x, y)) return this;
var scroll = this.element.getScroll();
return this.parent([scroll.x, scroll.y], [x, y]);
},
calculateScroll: function(x, y){
var element = this.element,
scrollSize = element.getScrollSize(),
scroll = element.getScroll(),
size = element.getSize(),
offset = this.options.offset,
values = {x: x, y: y};
for (var z in values){
if (!values[z] && values[z] !== 0) values[z] = scroll[z];
if (typeOf(values[z]) != 'number') values[z] = scrollSize[z] - size[z];
values[z] += offset[z];
}
return [values.x, values.y];
},
toTop: function(){
return this.start.apply(this, this.calculateScroll(false, 0));
},
toLeft: function(){
return this.start.apply(this, this.calculateScroll(0, false));
},
toRight: function(){
return this.start.apply(this, this.calculateScroll('right', false));
},
toBottom: function(){
return this.start.apply(this, this.calculateScroll(false, 'bottom'));
},
toElement: function(el, axes){
axes = axes ? Array.convert(axes) : ['x', 'y'];
var scroll = isBody(this.element) ? {x: 0, y: 0} : this.element.getScroll();
var position = Object.map(document.id(el).getPosition(this.element), function(value, axis){
return axes.contains(axis) ? value + scroll[axis] : false;
});
return this.start.apply(this, this.calculateScroll(position.x, position.y));
},
toElementEdge: function(el, axes, offset){
axes = axes ? Array.convert(axes) : ['x', 'y'];
el = document.id(el);
var to = {},
position = el.getPosition(this.element),
size = el.getSize(),
scroll = this.element.getScroll(),
containerSize = this.element.getSize(),
edge = {
x: position.x + size.x,
y: position.y + size.y
};
['x', 'y'].each(function(axis){
if (axes.contains(axis)){
if (edge[axis] > scroll[axis] + containerSize[axis]) to[axis] = edge[axis] - containerSize[axis];
if (position[axis] < scroll[axis]) to[axis] = position[axis];
}
if (to[axis] == null) to[axis] = scroll[axis];
if (offset && offset[axis]) to[axis] = to[axis] + offset[axis];
}, this);
if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y);
return this;
},
toElementCenter: function(el, axes, offset){
axes = axes ? Array.convert(axes) : ['x', 'y'];
el = document.id(el);
var to = {},
position = el.getPosition(this.element),
size = el.getSize(),
scroll = this.element.getScroll(),
containerSize = this.element.getSize();
['x', 'y'].each(function(axis){
if (axes.contains(axis)){
to[axis] = position[axis] - (containerSize[axis] - size[axis]) / 2;
}
if (to[axis] == null) to[axis] = scroll[axis];
if (offset && offset[axis]) to[axis] = to[axis] + offset[axis];
}, this);
if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y);
return this;
}
});
//<1.2compat>
Fx.Scroll.implement({
scrollToCenter: function(){
return this.toElementCenter.apply(this, arguments);
},
scrollIntoView: function(){
return this.toElementEdge.apply(this, arguments);
}
});
//</1.2compat>
function isBody(element){
return (/^(?:body|html)$/i).test(element.tagName);
}
})();
/*
---
script: Fx.Slide.js
name: Fx.Slide
description: Effect to slide an element in and out of view.
license: MIT-style license
authors:
- Valerio Proietti
requires:
- Core/Fx
- Core/Element.Style
- /MooTools.More
provides: [Fx.Slide]
...
*/
Fx.Slide = new Class({
Extends: Fx,
options: {
mode: 'vertical',
wrapper: false,
hideOverflow: true,
resetHeight: false
},
initialize: function(element, options){
element = this.element = this.subject = document.id(element);
this.parent(options);
options = this.options;
var wrapper = element.retrieve('wrapper'),
styles = element.getStyles('margin', 'position', 'overflow');
if (options.hideOverflow) styles = Object.append(styles, {overflow: 'hidden'});
if (options.wrapper) wrapper = document.id(options.wrapper).setStyles(styles);
if (!wrapper) wrapper = new Element('div', {
styles: styles
}).wraps(element);
element.store('wrapper', wrapper).setStyle('margin', 0);
if (element.getStyle('overflow') == 'visible') element.setStyle('overflow', 'hidden');
this.now = [];
this.open = true;
this.wrapper = wrapper;
this.addEvent('complete', function(){
this.open = (wrapper['offset' + this.layout.capitalize()] != 0);
if (this.open && this.options.resetHeight) wrapper.setStyle('height', '');
}, true);
},
vertical: function(){
this.margin = 'margin-top';
this.layout = 'height';
this.offset = this.element.offsetHeight;
},
horizontal: function(){
this.margin = 'margin-left';
this.layout = 'width';
this.offset = this.element.offsetWidth;
},
set: function(now){
this.element.setStyle(this.margin, now[0]);
this.wrapper.setStyle(this.layout, now[1]);
return this;
},
compute: function(from, to, delta){
return [0, 1].map(function(i){
return Fx.compute(from[i], to[i], delta);
});
},
start: function(how, mode){
if (!this.check(how, mode)) return this;
this[mode || this.options.mode]();
var margin = this.element.getStyle(this.margin).toInt(),
layout = this.wrapper.getStyle(this.layout).toInt(),
caseIn = [[margin, layout], [0, this.offset]],
caseOut = [[margin, layout], [-this.offset, 0]],
start;
switch (how){
case 'in': start = caseIn; break;
case 'out': start = caseOut; break;
case 'toggle': start = (layout == 0) ? caseIn : caseOut;
}
return this.parent(start[0], start[1]);
},
slideIn: function(mode){
return this.start('in', mode);
},
slideOut: function(mode){
return this.start('out', mode);
},
hide: function(mode){
this[mode || this.options.mode]();
this.open = false;
return this.set([-this.offset, 0]);
},
show: function(mode){
this[mode || this.options.mode]();
this.open = true;
return this.set([0, this.offset]);
},
toggle: function(mode){
return this.start('toggle', mode);
}
});
Element.Properties.slide = {
set: function(options){
this.get('slide').cancel().setOptions(options);
return this;
},
get: function(){
var slide = this.retrieve('slide');
if (!slide){
slide = new Fx.Slide(this, {link: 'cancel'});
this.store('slide', slide);
}
return slide;
}
};
Element.implement({
slide: function(how, mode){
how = how || 'toggle';
var slide = this.get('slide'), toggle;
switch (how){
case 'hide': slide.hide(mode); break;
case 'show': slide.show(mode); break;
case 'toggle':
var flag = this.retrieve('slide:flag', slide.open);
slide[flag ? 'slideOut' : 'slideIn'](mode);
this.store('slide:flag', !flag);
toggle = true;
break;
default: slide.start(how, mode);
}
if (!toggle) this.eliminate('slide:flag');
return this;
}
});
/*
---
script: Fx.SmoothScroll.js
name: Fx.SmoothScroll
description: Class for creating a smooth scrolling effect to all internal links on the page.
license: MIT-style license
authors:
- Valerio Proietti
requires:
- Core/Slick.Finder
- /Fx.Scroll
provides: [Fx.SmoothScroll]
...
*/
/*<1.2compat>*/var SmoothScroll = /*</1.2compat>*/Fx.SmoothScroll = new Class({
Extends: Fx.Scroll,
options: {
axes: ['x', 'y']
},
initialize: function(options, context){
context = context || document;
this.doc = context.getDocument();
this.parent(this.doc, options);
var win = context.getWindow(),
location = win.location.href.match(/^[^#]*/)[0] + '#',
links = $$(this.options.links || this.doc.links);
links.each(function(link){
if (link.href.indexOf(location) != 0) return;
var anchor = link.href.substr(location.length);
if (anchor) this.useLink(link, anchor);
}, this);
this.addEvent('complete', function(){
win.location.hash = this.anchor;
this.element.scrollTo(this.to[0], this.to[1]);
}, true);
},
useLink: function(link, anchor){
link.addEvent('click', function(event){
var el = document.id(anchor) || this.doc.getElement('a[name=' + anchor + ']');
if (!el) return;
event.preventDefault();
this.toElement(el, this.options.axes).chain(function(){
this.fireEvent('scrolledTo', [link, el]);
}.bind(this));
this.anchor = anchor;
}.bind(this));
return this;
}
});
/*
---
script: Fx.Sort.js
name: Fx.Sort
description: Defines Fx.Sort, a class that reorders lists with a transition.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Element.Dimensions
- /Fx.Elements
- /Element.Measure
provides: [Fx.Sort]
...
*/
Fx.Sort = new Class({
Extends: Fx.Elements,
options: {
mode: 'vertical'
},
initialize: function(elements, options){
this.parent(elements, options);
this.elements.each(function(el){
if (el.getStyle('position') == 'static') el.setStyle('position', 'relative');
});
this.setDefaultOrder();
},
setDefaultOrder: function(){
this.currentOrder = this.elements.map(function(el, index){
return index;
});
},
sort: function(){
if (!this.check(arguments)) return this;
var newOrder = Array.flatten(arguments);
var top = 0,
left = 0,
next = {},
zero = {},
vert = this.options.mode == 'vertical';
var current = this.elements.map(function(el, index){
var size = el.getComputedSize({styles: ['border', 'padding', 'margin']});
var val;
if (vert){
val = {
top: top,
margin: size['margin-top'],
height: size.totalHeight
};
top += val.height - size['margin-top'];
} else {
val = {
left: left,
margin: size['margin-left'],
width: size.totalWidth
};
left += val.width;
}
var plane = vert ? 'top' : 'left';
zero[index] = {};
var start = el.getStyle(plane).toInt();
zero[index][plane] = start || 0;
return val;
}, this);
this.set(zero);
newOrder = newOrder.map(function(i){ return i.toInt(); });
if (newOrder.length != this.elements.length){
this.currentOrder.each(function(index){
if (!newOrder.contains(index)) newOrder.push(index);
});
if (newOrder.length > this.elements.length)
newOrder.splice(this.elements.length-1, newOrder.length - this.elements.length);
}
var margin = 0;
top = left = 0;
newOrder.each(function(item){
var newPos = {};
if (vert){
newPos.top = top - current[item].top - margin;
top += current[item].height;
} else {
newPos.left = left - current[item].left;
left += current[item].width;
}
margin = margin + current[item].margin;
next[item]=newPos;
}, this);
var mapped = {};
Array.clone(newOrder).sort().each(function(index){
mapped[index] = next[index];
});
this.start(mapped);
this.currentOrder = newOrder;
return this;
},
rearrangeDOM: function(newOrder){
newOrder = newOrder || this.currentOrder;
var parent = this.elements[0].getParent();
var rearranged = [];
this.elements.setStyle('opacity', 0);
//move each element and store the new default order
newOrder.each(function(index){
rearranged.push(this.elements[index].inject(parent).setStyles({
top: 0,
left: 0
}));
}, this);
this.elements.setStyle('opacity', 1);
this.elements = $$(rearranged);
this.setDefaultOrder();
return this;
},
getDefaultOrder: function(){
return this.elements.map(function(el, index){
return index;
});
},
getCurrentOrder: function(){
return this.currentOrder;
},
forward: function(){
return this.sort(this.getDefaultOrder());
},
backward: function(){
return this.sort(this.getDefaultOrder().reverse());
},
reverse: function(){
return this.sort(this.currentOrder.reverse());
},
sortByElements: function(elements){
return this.sort(elements.map(function(el){
return this.elements.indexOf(el);
}, this));
},
swap: function(one, two){
if (typeOf(one) == 'element') one = this.elements.indexOf(one);
if (typeOf(two) == 'element') two = this.elements.indexOf(two);
var newOrder = Array.clone(this.currentOrder);
newOrder[this.currentOrder.indexOf(one)] = two;
newOrder[this.currentOrder.indexOf(two)] = one;
return this.sort(newOrder);
}
});
/*
---
script: Drag.js
name: Drag
description: The base Drag Class. Can be used to drag and resize Elements using mouse events.
license: MIT-style license
authors:
- Valerio Proietti
- Tom Occhinno
- Jan Kassens
requires:
- Core/Events
- Core/Options
- Core/Element.Event
- Core/Element.Style
- Core/Element.Dimensions
- /MooTools.More
provides: [Drag]
...
*/
var Drag = new Class({
Implements: [Events, Options],
options: {/*
onBeforeStart: function(thisElement){},
onStart: function(thisElement, event){},
onSnap: function(thisElement){},
onDrag: function(thisElement, event){},
onCancel: function(thisElement){},
onComplete: function(thisElement, event){},*/
snap: 6,
unit: 'px',
grid: false,
style: true,
limit: false,
handle: false,
invert: false,
preventDefault: false,
stopPropagation: false,
modifiers: {x: 'left', y: 'top'}
},
initialize: function(){
var params = Array.link(arguments, {
'options': Type.isObject,
'element': function(obj){
return obj != null;
}
});
this.element = document.id(params.element);
this.document = this.element.getDocument();
this.setOptions(params.options || {});
var htype = typeOf(this.options.handle);
this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element;
this.mouse = {'now': {}, 'pos': {}};
this.value = {'start': {}, 'now': {}};
this.selection = (Browser.ie) ? 'selectstart' : 'mousedown';
if (Browser.ie && !Drag.ondragstartFixed){
document.ondragstart = Function.from(false);
Drag.ondragstartFixed = true;
}
this.bound = {
start: this.start.bind(this),
check: this.check.bind(this),
drag: this.drag.bind(this),
stop: this.stop.bind(this),
cancel: this.cancel.bind(this),
eventStop: Function.from(false)
};
this.attach();
},
attach: function(){
this.handles.addEvent('mousedown', this.bound.start);
return this;
},
detach: function(){
this.handles.removeEvent('mousedown', this.bound.start);
return this;
},
start: function(event){
var options = this.options;
if (event.rightClick) return;
if (options.preventDefault) event.preventDefault();
if (options.stopPropagation) event.stopPropagation();
this.mouse.start = event.page;
this.fireEvent('beforeStart', this.element);
var limit = options.limit;
this.limit = {x: [], y: []};
var z, coordinates;
for (z in options.modifiers){
if (!options.modifiers[z]) continue;
var style = this.element.getStyle(options.modifiers[z]);
// Some browsers (IE and Opera) don't always return pixels.
if (style && !style.match(/px$/)){
if (!coordinates) coordinates = this.element.getCoordinates(this.element.getOffsetParent());
style = coordinates[options.modifiers[z]];
}
if (options.style) this.value.now[z] = (style || 0).toInt();
else this.value.now[z] = this.element[options.modifiers[z]];
if (options.invert) this.value.now[z] *= -1;
this.mouse.pos[z] = event.page[z] - this.value.now[z];
if (limit && limit[z]){
var i = 2;
while (i--){
var limitZI = limit[z][i];
if (limitZI || limitZI === 0) this.limit[z][i] = (typeof limitZI == 'function') ? limitZI() : limitZI;
}
}
}
if (typeOf(this.options.grid) == 'number') this.options.grid = {
x: this.options.grid,
y: this.options.grid
};
var events = {
mousemove: this.bound.check,
mouseup: this.bound.cancel
};
events[this.selection] = this.bound.eventStop;
this.document.addEvents(events);
},
check: function(event){
if (this.options.preventDefault) event.preventDefault();
var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
if (distance > this.options.snap){
this.cancel();
this.document.addEvents({
mousemove: this.bound.drag,
mouseup: this.bound.stop
});
this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element);
}
},
drag: function(event){
var options = this.options;
if (options.preventDefault) event.preventDefault();
this.mouse.now = event.page;
for (var z in options.modifiers){
if (!options.modifiers[z]) continue;
this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
if (options.invert) this.value.now[z] *= -1;
if (options.limit && this.limit[z]){
if ((this.limit[z][1] || this.limit[z][1] === 0) && (this.value.now[z] > this.limit[z][1])){
this.value.now[z] = this.limit[z][1];
} else if ((this.limit[z][0] || this.limit[z][0] === 0) && (this.value.now[z] < this.limit[z][0])){
this.value.now[z] = this.limit[z][0];
}
}
if (options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % options.grid[z]);
if (options.style) this.element.setStyle(options.modifiers[z], this.value.now[z] + options.unit);
else this.element[options.modifiers[z]] = this.value.now[z];
}
this.fireEvent('drag', [this.element, event]);
},
cancel: function(event){
this.document.removeEvents({
mousemove: this.bound.check,
mouseup: this.bound.cancel
});
if (event){
this.document.removeEvent(this.selection, this.bound.eventStop);
this.fireEvent('cancel', this.element);
}
},
stop: function(event){
var events = {
mousemove: this.bound.drag,
mouseup: this.bound.stop
};
events[this.selection] = this.bound.eventStop;
this.document.removeEvents(events);
if (event) this.fireEvent('complete', [this.element, event]);
}
});
Element.implement({
makeResizable: function(options){
var drag = new Drag(this, Object.merge({
modifiers: {
x: 'width',
y: 'height'
}
}, options));
this.store('resizer', drag);
return drag.addEvent('drag', function(){
this.fireEvent('resize', drag);
}.bind(this));
}
});
/*
---
script: Drag.Move.js
name: Drag.Move
description: A Drag extension that provides support for the constraining of draggables to containers and droppables.
license: MIT-style license
authors:
- Valerio Proietti
- Tom Occhinno
- Jan Kassens
- Aaron Newton
- Scott Kyle
requires:
- Core/Element.Dimensions
- /Drag
provides: [Drag.Move]
...
*/
Drag.Move = new Class({
Extends: Drag,
options: {/*
onEnter: function(thisElement, overed){},
onLeave: function(thisElement, overed){},
onDrop: function(thisElement, overed, event){},*/
droppables: [],
container: false,
precalculate: false,
includeMargins: true,
checkDroppables: true
},
initialize: function(element, options){
this.parent(element, options);
element = this.element;
this.droppables = $$(this.options.droppables);
this.container = document.id(this.options.container);
if (this.container && typeOf(this.container) != 'element')
this.container = document.id(this.container.getDocument().body);
if (this.options.style){
if (this.options.modifiers.x == 'left' && this.options.modifiers.y == 'top'){
var parent = element.getOffsetParent(),
styles = element.getStyles('left', 'top');
if (parent && (styles.left == 'auto' || styles.top == 'auto')){
element.setPosition(element.getPosition(parent));
}
}
if (element.getStyle('position') == 'static') element.setStyle('position', 'absolute');
}
this.addEvent('start', this.checkDroppables, true);
this.overed = null;
},
start: function(event){
if (this.container) this.options.limit = this.calculateLimit();
if (this.options.precalculate){
this.positions = this.droppables.map(function(el){
return el.getCoordinates();
});
}
this.parent(event);
},
calculateLimit: function(){
var element = this.element,
container = this.container,
offsetParent = document.id(element.getOffsetParent()) || document.body,
containerCoordinates = container.getCoordinates(offsetParent),
elementMargin = {},
elementBorder = {},
containerMargin = {},
containerBorder = {},
offsetParentPadding = {};
['top', 'right', 'bottom', 'left'].each(function(pad){
elementMargin[pad] = element.getStyle('margin-' + pad).toInt();
elementBorder[pad] = element.getStyle('border-' + pad).toInt();
containerMargin[pad] = container.getStyle('margin-' + pad).toInt();
containerBorder[pad] = container.getStyle('border-' + pad).toInt();
offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt();
}, this);
var width = element.offsetWidth + elementMargin.left + elementMargin.right,
height = element.offsetHeight + elementMargin.top + elementMargin.bottom,
left = 0,
top = 0,
right = containerCoordinates.right - containerBorder.right - width,
bottom = containerCoordinates.bottom - containerBorder.bottom - height;
if (this.options.includeMargins){
left += elementMargin.left;
top += elementMargin.top;
} else {
right += elementMargin.right;
bottom += elementMargin.bottom;
}
if (element.getStyle('position') == 'relative'){
var coords = element.getCoordinates(offsetParent);
coords.left -= element.getStyle('left').toInt();
coords.top -= element.getStyle('top').toInt();
left -= coords.left;
top -= coords.top;
if (container.getStyle('position') != 'relative'){
left += containerBorder.left;
top += containerBorder.top;
}
right += elementMargin.left - coords.left;
bottom += elementMargin.top - coords.top;
if (container != offsetParent){
left += containerMargin.left + offsetParentPadding.left;
top += ((Browser.ie6 || Browser.ie7) ? 0 : containerMargin.top) + offsetParentPadding.top;
}
} else {
left -= elementMargin.left;
top -= elementMargin.top;
if (container != offsetParent){
left += containerCoordinates.left + containerBorder.left;
top += containerCoordinates.top + containerBorder.top;
}
}
return {
x: [left, right],
y: [top, bottom]
};
},
getDroppableCoordinates: function(element){
var position = element.getCoordinates();
if (element.getStyle('position') == 'fixed'){
var scroll = window.getScroll();
position.left += scroll.x;
position.right += scroll.x;
position.top += scroll.y;
position.bottom += scroll.y;
}
return position;
},
checkDroppables: function(){
var overed = this.droppables.filter(function(el, i){
el = this.positions ? this.positions[i] : this.getDroppableCoordinates(el);
var now = this.mouse.now;
return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
}, this).getLast();
if (this.overed != overed){
if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
if (overed) this.fireEvent('enter', [this.element, overed]);
this.overed = overed;
}
},
drag: function(event){
this.parent(event);
if (this.options.checkDroppables && this.droppables.length) this.checkDroppables();
},
stop: function(event){
this.checkDroppables();
this.fireEvent('drop', [this.element, this.overed, event]);
this.overed = null;
return this.parent(event);
}
});
Element.implement({
makeDraggable: function(options){
var drag = new Drag.Move(this, options);
this.store('dragger', drag);
return drag;
}
});
/*
---
script: Slider.js
name: Slider
description: Class for creating horizontal and vertical slider controls.
license: MIT-style license
authors:
- Valerio Proietti
requires:
- Core/Element.Dimensions
- /Class.Binds
- /Drag
- /Element.Measure
provides: [Slider]
...
*/
var Slider = new Class({
Implements: [Events, Options],
Binds: ['clickedElement', 'draggedKnob', 'scrolledElement'],
options: {/*
onTick: function(intPosition){},
onChange: function(intStep){},
onComplete: function(strStep){},*/
onTick: function(position){
this.setKnobPosition(position);
},
initialStep: 0,
snap: false,
offset: 0,
range: false,
wheel: false,
steps: 100,
mode: 'horizontal'
},
initialize: function(element, knob, options){
this.setOptions(options);
options = this.options;
this.element = document.id(element);
knob = this.knob = document.id(knob);
this.previousChange = this.previousEnd = this.step = -1;
var limit = {},
modifiers = {x: false, y: false};
switch (options.mode){
case 'vertical':
this.axis = 'y';
this.property = 'top';
this.offset = 'offsetHeight';
break;
case 'horizontal':
this.axis = 'x';
this.property = 'left';
this.offset = 'offsetWidth';
}
this.setSliderDimensions();
this.setRange(options.range);
if (knob.getStyle('position') == 'static') knob.setStyle('position', 'relative');
knob.setStyle(this.property, -options.offset);
modifiers[this.axis] = this.property;
limit[this.axis] = [-options.offset, this.full - options.offset];
var dragOptions = {
snap: 0,
limit: limit,
modifiers: modifiers,
onDrag: this.draggedKnob,
onStart: this.draggedKnob,
onBeforeStart: (function(){
this.isDragging = true;
}).bind(this),
onCancel: function(){
this.isDragging = false;
}.bind(this),
onComplete: function(){
this.isDragging = false;
this.draggedKnob();
this.end();
}.bind(this)
};
if (options.snap) this.setSnap(dragOptions);
this.drag = new Drag(knob, dragOptions);
this.attach();
if (options.initialStep != null) this.set(options.initialStep);
},
attach: function(){
this.element.addEvent('mousedown', this.clickedElement);
if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement);
this.drag.attach();
return this;
},
detach: function(){
this.element.removeEvent('mousedown', this.clickedElement)
.removeEvent('mousewheel', this.scrolledElement);
this.drag.detach();
return this;
},
autosize: function(){
this.setSliderDimensions()
.setKnobPosition(this.toPosition(this.step));
this.drag.options.limit[this.axis] = [-this.options.offset, this.full - this.options.offset];
if (this.options.snap) this.setSnap();
return this;
},
setSnap: function(options){
if (!options) options = this.drag.options;
options.grid = Math.ceil(this.stepWidth);
options.limit[this.axis][1] = this.full;
return this;
},
setKnobPosition: function(position){
if (this.options.snap) position = this.toPosition(this.step);
this.knob.setStyle(this.property, position);
return this;
},
setSliderDimensions: function(){
this.full = this.element.measure(function(){
this.half = this.knob[this.offset] / 2;
return this.element[this.offset] - this.knob[this.offset] + (this.options.offset * 2);
}.bind(this));
return this;
},
set: function(step){
if (!((this.range > 0) ^ (step < this.min))) step = this.min;
if (!((this.range > 0) ^ (step > this.max))) step = this.max;
this.step = Math.round(step);
return this.checkStep()
.fireEvent('tick', this.toPosition(this.step))
.end();
},
setRange: function(range, pos){
this.min = Array.pick([range[0], 0]);
this.max = Array.pick([range[1], this.options.steps]);
this.range = this.max - this.min;
this.steps = this.options.steps || this.full;
this.stepSize = Math.abs(this.range) / this.steps;
this.stepWidth = this.stepSize * this.full / Math.abs(this.range);
if (range) this.set(Array.pick([pos, this.step]).floor(this.min).max(this.max));
return this;
},
clickedElement: function(event){
if (this.isDragging || event.target == this.knob) return;
var dir = this.range < 0 ? -1 : 1,
position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
position = position.limit(-this.options.offset, this.full - this.options.offset);
this.step = Math.round(this.min + dir * this.toStep(position));
this.checkStep()
.fireEvent('tick', position)
.end();
},
scrolledElement: function(event){
var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
this.set(this.step + (mode ? -1 : 1) * this.stepSize);
event.stop();
},
draggedKnob: function(){
var dir = this.range < 0 ? -1 : 1,
position = this.drag.value.now[this.axis];
position = position.limit(-this.options.offset, this.full -this.options.offset);
this.step = Math.round(this.min + dir * this.toStep(position));
this.checkStep();
},
checkStep: function(){
var step = this.step;
if (this.previousChange != step){
this.previousChange = step;
this.fireEvent('change', step);
}
return this;
},
end: function(){
var step = this.step;
if (this.previousEnd !== step){
this.previousEnd = step;
this.fireEvent('complete', step + '');
}
return this;
},
toStep: function(position){
var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
},
toPosition: function(step){
return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
}
});
/*
---
script: Sortables.js
name: Sortables
description: Class for creating a drag and drop sorting interface for lists of items.
license: MIT-style license
authors:
- Tom Occhino
requires:
- Core/Fx.Morph
- /Drag.Move
provides: [Sortables]
...
*/
var Sortables = new Class({
Implements: [Events, Options],
options: {/*
onSort: function(element, clone){},
onStart: function(element, clone){},
onComplete: function(element){},*/
opacity: 1,
clone: false,
revert: false,
handle: false,
dragOptions: {}/*<1.2compat>*/,
snap: 4,
constrain: false,
preventDefault: false
/*</1.2compat>*/
},
initialize: function(lists, options){
this.setOptions(options);
this.elements = [];
this.lists = [];
this.idle = true;
this.addLists($$(document.id(lists) || lists));
if (!this.options.clone) this.options.revert = false;
if (this.options.revert) this.effect = new Fx.Morph(null, Object.merge({
duration: 250,
link: 'cancel'
}, this.options.revert));
},
attach: function(){
this.addLists(this.lists);
return this;
},
detach: function(){
this.lists = this.removeLists(this.lists);
return this;
},
addItems: function(){
Array.flatten(arguments).each(function(element){
this.elements.push(element);
var start = element.retrieve('sortables:start', function(event){
this.start.call(this, event, element);
}.bind(this));
(this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
}, this);
return this;
},
addLists: function(){
Array.flatten(arguments).each(function(list){
this.lists.include(list);
this.addItems(list.getChildren());
}, this);
return this;
},
removeItems: function(){
return $$(Array.flatten(arguments).map(function(element){
this.elements.erase(element);
var start = element.retrieve('sortables:start');
(this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
return element;
}, this));
},
removeLists: function(){
return $$(Array.flatten(arguments).map(function(list){
this.lists.erase(list);
this.removeItems(list.getChildren());
return list;
}, this));
},
getClone: function(event, element){
if (!this.options.clone) return new Element(element.tagName).inject(document.body);
if (typeOf(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
var clone = element.clone(true).setStyles({
margin: 0,
position: 'absolute',
visibility: 'hidden',
width: element.getStyle('width')
}).addEvent('mousedown', function(event){
element.fireEvent('mousedown', event);
});
//prevent the duplicated radio inputs from unchecking the real one
if (clone.get('html').test('radio')){
clone.getElements('input[type=radio]').each(function(input, i){
input.set('name', 'clone_' + i);
if (input.get('checked')) element.getElements('input[type=radio]')[i].set('checked', true);
});
}
return clone.inject(this.list).setPosition(element.getPosition(element.getOffsetParent()));
},
getDroppables: function(){
var droppables = this.list.getChildren().erase(this.clone).erase(this.element);
if (!this.options.constrain) droppables.append(this.lists).erase(this.list);
return droppables;
},
insert: function(dragging, element){
var where = 'inside';
if (this.lists.contains(element)){
this.list = element;
this.drag.droppables = this.getDroppables();
} else {
where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
}
this.element.inject(element, where);
this.fireEvent('sort', [this.element, this.clone]);
},
start: function(event, element){
if (
!this.idle ||
event.rightClick ||
['button', 'input', 'a'].contains(event.target.get('tag'))
) return;
this.idle = false;
this.element = element;
this.opacity = element.get('opacity');
this.list = element.getParent();
this.clone = this.getClone(event, element);
this.drag = new Drag.Move(this.clone, Object.merge({
/*<1.2compat>*/
preventDefault: this.options.preventDefault,
snap: this.options.snap,
container: this.options.constrain && this.element.getParent(),
/*</1.2compat>*/
droppables: this.getDroppables()
}, this.options.dragOptions)).addEvents({
onSnap: function(){
event.stop();
this.clone.setStyle('visibility', 'visible');
this.element.set('opacity', this.options.opacity || 0);
this.fireEvent('start', [this.element, this.clone]);
}.bind(this),
onEnter: this.insert.bind(this),
onCancel: this.end.bind(this),
onComplete: this.end.bind(this)
});
this.clone.inject(this.element, 'before');
this.drag.start(event);
},
end: function(){
this.drag.detach();
this.element.set('opacity', this.opacity);
if (this.effect){
var dim = this.element.getStyles('width', 'height'),
clone = this.clone,
pos = clone.computePosition(this.element.getPosition(this.clone.getOffsetParent()));
var destroy = function(){
this.removeEvent('cancel', destroy);
clone.destroy();
};
this.effect.element = clone;
this.effect.start({
top: pos.top,
left: pos.left,
width: dim.width,
height: dim.height,
opacity: 0.25
}).addEvent('cancel', destroy).chain(destroy);
} else {
this.clone.destroy();
}
this.reset();
},
reset: function(){
this.idle = true;
this.fireEvent('complete', this.element);
},
serialize: function(){
var params = Array.link(arguments, {
modifier: Type.isFunction,
index: function(obj){
return obj != null;
}
});
var serial = this.lists.map(function(list){
return list.getChildren().map(params.modifier || function(element){
return element.get('id');
}, this);
}, this);
var index = params.index;
if (this.lists.length == 1) index = 0;
return (index || index === 0) && index >= 0 && index < this.lists.length ? serial[index] : serial;
}
});
/*
---
script: Request.JSONP.js
name: Request.JSONP
description: Defines Request.JSONP, a class for cross domain javascript via script injection.
license: MIT-style license
authors:
- Aaron Newton
- Guillermo Rauch
- Arian Stolwijk
requires:
- Core/Element
- Core/Request
- MooTools.More
provides: [Request.JSONP]
...
*/
Request.JSONP = new Class({
Implements: [Chain, Events, Options],
options: {/*
onRequest: function(src, scriptElement){},
onComplete: function(data){},
onSuccess: function(data){},
onCancel: function(){},
onTimeout: function(){},
onError: function(){}, */
onRequest: function(src){
if (this.options.log && window.console && console.log){
console.log('JSONP retrieving script with url:' + src);
}
},
onError: function(src){
if (this.options.log && window.console && console.warn){
console.warn('JSONP '+ src +' will fail in Internet Explorer, which enforces a 2083 bytes length limit on URIs');
}
},
url: '',
callbackKey: 'callback',
injectScript: document.head,
data: '',
link: 'ignore',
timeout: 0,
log: false
},
initialize: function(options){
this.setOptions(options);
},
send: function(options){
if (!Request.prototype.check.call(this, options)) return this;
this.running = true;
var type = typeOf(options);
if (type == 'string' || type == 'element') options = {data: options};
options = Object.merge(this.options, options || {});
var data = options.data;
switch (typeOf(data)){
case 'element': data = document.id(data).toQueryString(); break;
case 'object': case 'hash': data = Object.toQueryString(data);
}
var index = this.index = Request.JSONP.counter++;
var src = options.url +
(options.url.test('\\?') ? '&' :'?') +
(options.callbackKey) +
'=Request.JSONP.request_map.request_'+ index +
(data ? '&' + data : '');
if (src.length > 2083) this.fireEvent('error', src);
Request.JSONP.request_map['request_' + index] = function(){
this.success(arguments, index);
}.bind(this);
var script = this.getScript(src).inject(options.injectScript);
this.fireEvent('request', [src, script]);
if (options.timeout) this.timeout.delay(options.timeout, this);
return this;
},
getScript: function(src){
if (!this.script) this.script = new Element('script', {
type: 'text/javascript',
async: true,
src: src
});
return this.script;
},
success: function(args, index){
if (!this.running) return;
this.clear()
.fireEvent('complete', args).fireEvent('success', args)
.callChain();
},
cancel: function(){
if (this.running) this.clear().fireEvent('cancel');
return this;
},
isRunning: function(){
return !!this.running;
},
clear: function(){
this.running = false;
if (this.script){
this.script.destroy();
this.script = null;
}
return this;
},
timeout: function(){
if (this.running){
this.running = false;
this.fireEvent('timeout', [this.script.get('src'), this.script]).fireEvent('failure').cancel();
}
return this;
}
});
Request.JSONP.counter = 0;
Request.JSONP.request_map = {};
/*
---
script: Request.Queue.js
name: Request.Queue
description: Controls several instances of Request and its variants to run only one request at a time.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Element
- Core/Request
- /Class.Binds
provides: [Request.Queue]
...
*/
Request.Queue = new Class({
Implements: [Options, Events],
Binds: ['attach', 'request', 'complete', 'cancel', 'success', 'failure', 'exception'],
options: {/*
onRequest: function(argsPassedToOnRequest){},
onSuccess: function(argsPassedToOnSuccess){},
onComplete: function(argsPassedToOnComplete){},
onCancel: function(argsPassedToOnCancel){},
onException: function(argsPassedToOnException){},
onFailure: function(argsPassedToOnFailure){},
onEnd: function(){},
*/
stopOnFailure: true,
autoAdvance: true,
concurrent: 1,
requests: {}
},
initialize: function(options){
var requests;
if (options){
requests = options.requests;
delete options.requests;
}
this.setOptions(options);
this.requests = {};
this.queue = [];
this.reqBinders = {};
if (requests) this.addRequests(requests);
},
addRequest: function(name, request){
this.requests[name] = request;
this.attach(name, request);
return this;
},
addRequests: function(obj){
Object.each(obj, function(req, name){
this.addRequest(name, req);
}, this);
return this;
},
getName: function(req){
return Object.keyOf(this.requests, req);
},
attach: function(name, req){
if (req._groupSend) return this;
['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){
if (!this.reqBinders[name]) this.reqBinders[name] = {};
this.reqBinders[name][evt] = function(){
this['on' + evt.capitalize()].apply(this, [name, req].append(arguments));
}.bind(this);
req.addEvent(evt, this.reqBinders[name][evt]);
}, this);
req._groupSend = req.send;
req.send = function(options){
this.send(name, options);
return req;
}.bind(this);
return this;
},
removeRequest: function(req){
var name = typeOf(req) == 'object' ? this.getName(req) : req;
if (!name && typeOf(name) != 'string') return this;
req = this.requests[name];
if (!req) return this;
['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){
req.removeEvent(evt, this.reqBinders[name][evt]);
}, this);
req.send = req._groupSend;
delete req._groupSend;
return this;
},
getRunning: function(){
return Object.filter(this.requests, function(r){
return r.running;
});
},
isRunning: function(){
return !!(Object.keys(this.getRunning()).length);
},
send: function(name, options){
var q = function(){
this.requests[name]._groupSend(options);
this.queue.erase(q);
}.bind(this);
q.name = name;
if (Object.keys(this.getRunning()).length >= this.options.concurrent || (this.error && this.options.stopOnFailure)) this.queue.push(q);
else q();
return this;
},
hasNext: function(name){
return (!name) ? !!this.queue.length : !!this.queue.filter(function(q){ return q.name == name; }).length;
},
resume: function(){
this.error = false;
(this.options.concurrent - Object.keys(this.getRunning()).length).times(this.runNext, this);
return this;
},
runNext: function(name){
if (!this.queue.length) return this;
if (!name){
this.queue[0]();
} else {
var found;
this.queue.each(function(q){
if (!found && q.name == name){
found = true;
q();
}
});
}
return this;
},
runAll: function(){
this.queue.each(function(q){
q();
});
return this;
},
clear: function(name){
if (!name){
this.queue.empty();
} else {
this.queue = this.queue.map(function(q){
if (q.name != name) return q;
else return false;
}).filter(function(q){
return q;
});
}
return this;
},
cancel: function(name){
this.requests[name].cancel();
return this;
},
onRequest: function(){
this.fireEvent('request', arguments);
},
onComplete: function(){
this.fireEvent('complete', arguments);
if (!this.queue.length) this.fireEvent('end');
},
onCancel: function(){
if (this.options.autoAdvance && !this.error) this.runNext();
this.fireEvent('cancel', arguments);
},
onSuccess: function(){
if (this.options.autoAdvance && !this.error) this.runNext();
this.fireEvent('success', arguments);
},
onFailure: function(){
this.error = true;
if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext();
this.fireEvent('failure', arguments);
},
onException: function(){
this.error = true;
if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext();
this.fireEvent('exception', arguments);
}
});
/*
---
script: Request.Periodical.js
name: Request.Periodical
description: Requests the same URL to pull data from a server but increases the intervals if no data is returned to reduce the load
license: MIT-style license
authors:
- Christoph Pojer
requires:
- Core/Request
- /MooTools.More
provides: [Request.Periodical]
...
*/
Request.implement({
options: {
initialDelay: 5000,
delay: 5000,
limit: 60000
},
startTimer: function(data){
var fn = function(){
if (!this.running) this.send({data: data});
};
this.lastDelay = this.options.initialDelay;
this.timer = fn.delay(this.lastDelay, this);
this.completeCheck = function(response){
clearTimeout(this.timer);
this.lastDelay = (response) ? this.options.delay : (this.lastDelay + this.options.delay).min(this.options.limit);
this.timer = fn.delay(this.lastDelay, this);
};
return this.addEvent('complete', this.completeCheck);
},
stopTimer: function(){
clearTimeout(this.timer);
return this.removeEvent('complete', this.completeCheck);
}
});
/*
---
script: Assets.js
name: Assets
description: Provides methods to dynamically load JavaScript, CSS, and Image files into the document.
license: MIT-style license
authors:
- Valerio Proietti
requires:
- Core/Element.Event
- /MooTools.More
provides: [Assets]
...
*/
var Asset = {
javascript: function(source, properties){
if (!properties) properties = {};
var script = new Element('script', {src: source, type: 'text/javascript'}),
doc = properties.document || document,
loaded = 0,
loadEvent = properties.onload || properties.onLoad;
var load = loadEvent ? function(){ // make sure we only call the event once
if (++loaded == 1) loadEvent.call(this);
} : function(){};
delete properties.onload;
delete properties.onLoad;
delete properties.document;
return script.addEvents({
load: load,
readystatechange: function(){
if (['loaded', 'complete'].contains(this.readyState)) load.call(this);
}
}).set(properties).inject(doc.head);
},
css: function(source, properties){
if (!properties) properties = {};
var link = new Element('link', {
rel: 'stylesheet',
media: 'screen',
type: 'text/css',
href: source
});
var load = properties.onload || properties.onLoad,
doc = properties.document || document;
delete properties.onload;
delete properties.onLoad;
delete properties.document;
if (load) link.addEvent('load', load);
return link.set(properties).inject(doc.head);
},
image: function(source, properties){
if (!properties) properties = {};
var image = new Image(),
element = document.id(image) || new Element('img');
['load', 'abort', 'error'].each(function(name){
var type = 'on' + name,
cap = 'on' + name.capitalize(),
event = properties[type] || properties[cap] || function(){};
delete properties[cap];
delete properties[type];
image[type] = function(){
if (!image) return;
if (!element.parentNode){
element.width = image.width;
element.height = image.height;
}
image = image.onload = image.onabort = image.onerror = null;
event.delay(1, element, element);
element.fireEvent(name, element, 1);
};
});
image.src = element.src = source;
if (image && image.complete) image.onload.delay(1);
return element.set(properties);
},
images: function(sources, options){
sources = Array.convert(sources);
var fn = function(){},
counter = 0;
options = Object.merge({
onComplete: fn,
onProgress: fn,
onError: fn,
properties: {}
}, options);
return new Elements(sources.map(function(source, index){
return Asset.image(source, Object.append(options.properties, {
onload: function(){
counter++;
options.onProgress.call(this, counter, index, source);
if (counter == sources.length) options.onComplete();
},
onerror: function(){
counter++;
options.onError.call(this, counter, index, source);
if (counter == sources.length) options.onComplete();
}
}));
}));
}
};
/*
---
script: Color.js
name: Color
description: Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.
license: MIT-style license
authors:
- Valerio Proietti
requires:
- Core/Array
- Core/String
- Core/Number
- Core/Hash
- Core/Function
- MooTools.More
provides: [Color]
...
*/
(function(){
var Color = this.Color = new Type('Color', function(color, type){
if (arguments.length >= 3){
type = 'rgb'; color = Array.slice(arguments, 0, 3);
} else if (typeof color == 'string'){
if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true);
else if (color.match(/hsb/)) color = color.hsbToRgb();
else color = color.hexToRgb(true);
}
type = type || 'rgb';
switch (type){
case 'hsb':
var old = color;
color = color.hsbToRgb();
color.hsb = old;
break;
case 'hex': color = color.hexToRgb(true); break;
}
color.rgb = color.slice(0, 3);
color.hsb = color.hsb || color.rgbToHsb();
color.hex = color.rgbToHex();
return Object.append(color, this);
});
Color.implement({
mix: function(){
var colors = Array.slice(arguments);
var alpha = (typeOf(colors.getLast()) == 'number') ? colors.pop() : 50;
var rgb = this.slice();
colors.each(function(color){
color = new Color(color);
for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha));
});
return new Color(rgb, 'rgb');
},
invert: function(){
return new Color(this.map(function(value){
return 255 - value;
}));
},
setHue: function(value){
return new Color([value, this.hsb[1], this.hsb[2]], 'hsb');
},
setSaturation: function(percent){
return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb');
},
setBrightness: function(percent){
return new Color([this.hsb[0], this.hsb[1], percent], 'hsb');
}
});
this.$RGB = function(r, g, b){
return new Color([r, g, b], 'rgb');
};
this.$HSB = function(h, s, b){
return new Color([h, s, b], 'hsb');
};
this.$HEX = function(hex){
return new Color(hex, 'hex');
};
Array.implement({
rgbToHsb: function(){
var red = this[0],
green = this[1],
blue = this[2],
hue = 0;
var max = Math.max(red, green, blue),
min = Math.min(red, green, blue);
var delta = max - min;
var brightness = max / 255,
saturation = (max != 0) ? delta / max : 0;
if (saturation != 0){
var rr = (max - red) / delta;
var gr = (max - green) / delta;
var br = (max - blue) / delta;
if (red == max) hue = br - gr;
else if (green == max) hue = 2 + rr - br;
else hue = 4 + gr - rr;
hue /= 6;
if (hue < 0) hue++;
}
return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)];
},
hsbToRgb: function(){
var br = Math.round(this[2] / 100 * 255);
if (this[1] == 0){
return [br, br, br];
} else {
var hue = this[0] % 360;
var f = hue % 60;
var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255);
var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255);
switch (Math.floor(hue / 60)){
case 0: return [br, t, p];
case 1: return [q, br, p];
case 2: return [p, br, t];
case 3: return [p, q, br];
case 4: return [t, p, br];
case 5: return [br, p, q];
}
}
return false;
}
});
String.implement({
rgbToHsb: function(){
var rgb = this.match(/\d{1,3}/g);
return (rgb) ? rgb.rgbToHsb() : null;
},
hsbToRgb: function(){
var hsb = this.match(/\d{1,3}/g);
return (hsb) ? hsb.hsbToRgb() : null;
}
});
})();
/*
---
script: Group.js
name: Group
description: Class for monitoring collections of events
license: MIT-style license
authors:
- Valerio Proietti
requires:
- Core/Events
- /MooTools.More
provides: [Group]
...
*/
(function(){
this.Group = new Class({
initialize: function(){
this.instances = Array.flatten(arguments);
this.events = {};
this.checker = {};
},
addEvent: function(type, fn){
this.checker[type] = this.checker[type] || {};
this.events[type] = this.events[type] || [];
if (this.events[type].contains(fn)) return false;
else this.events[type].push(fn);
this.instances.each(function(instance, i){
instance.addEvent(type, this.check.pass([type, instance, i], this));
}, this);
return this;
},
check: function(type, instance, i){
this.checker[type][i] = true;
var every = this.instances.every(function(current, j){
return this.checker[type][j] || false;
}, this);
if (!every) return;
this.checker[type] = {};
this.events[type].each(function(event){
event.call(this, this.instances, instance);
}, this);
}
});
})();
/*
---
script: Hash.Cookie.js
name: Hash.Cookie
description: Class for creating, reading, and deleting Cookies in JSON format.
license: MIT-style license
authors:
- Valerio Proietti
- Aaron Newton
requires:
- Core/Cookie
- Core/JSON
- /MooTools.More
- /Hash
provides: [Hash.Cookie]
...
*/
Hash.Cookie = new Class({
Extends: Cookie,
options: {
autoSave: true
},
initialize: function(name, options){
this.parent(name, options);
this.load();
},
save: function(){
var value = JSON.encode(this.hash);
if (!value || value.length > 4096) return false; //cookie would be truncated!
if (value == '{}') this.dispose();
else this.write(value);
return true;
},
load: function(){
this.hash = new Hash(JSON.decode(this.read(), true));
return this;
}
});
Hash.each(Hash.prototype, function(method, name){
if (typeof method == 'function') Hash.Cookie.implement(name, function(){
var value = method.apply(this.hash, arguments);
if (this.options.autoSave) this.save();
return value;
});
});
/*
---
name: Table
description: LUA-Style table implementation.
license: MIT-style license
authors:
- Valerio Proietti
requires: [Core/Array]
provides: [Table]
...
*/
(function(){
var Table = this.Table = function(){
this.length = 0;
var keys = [],
values = [];
this.set = function(key, value){
var index = keys.indexOf(key);
if (index == -1){
var length = keys.length;
keys[length] = key;
values[length] = value;
this.length++;
} else {
values[index] = value;
}
return this;
};
this.get = function(key){
var index = keys.indexOf(key);
return (index == -1) ? null : values[index];
};
this.erase = function(key){
var index = keys.indexOf(key);
if (index != -1){
this.length--;
keys.splice(index, 1);
return values.splice(index, 1)[0];
}
return null;
};
this.each = this.forEach = function(fn, bind){
for (var i = 0, l = this.length; i < l; i++) fn.call(bind, keys[i], values[i], this);
};
};
if (this.Type) new Type('Table', Table);
})();
/*
---
script: HtmlTable.js
name: HtmlTable
description: Builds table elements with methods to add rows.
license: MIT-style license
authors:
- Aaron Newton
requires:
- Core/Options
- Core/Events
- /Class.Occlude
provides: [HtmlTable]
...
*/
var HtmlTable = new Class({
Implements: [Options, Events, Class.Occlude],
options: {
properties: {
cellpadding: 0,
cellspacing: 0,
border: 0
},
rows: [],
headers: [],
footers: []
},
property: 'HtmlTable',
initialize: function(){
var params = Array.link(arguments, {options: Type.isObject, table: Type.isElement, id: Type.isString});
this.setOptions(params.options);
if (!params.table && params.id) params.table = document.id(params.id);
this.element = params.table || new Element('table', this.options.properties);
if (this.occlude()) return this.occluded;
this.build();
},
build: function(){
this.element.store('HtmlTable', this);
this.body = document.id(this.element.tBodies[0]) || new Element('tbody').inject(this.element);
$$(this.body.rows);
if (this.options.headers.length) this.setHeaders(this.options.headers);
else this.thead = document.id(this.element.tHead);
if (this.thead) this.head = this.getHead();
if (this.options.footers.length) this.setFooters(this.options.footers);
this.tfoot = document.id(this.element.tFoot);
if (this.tfoot) this.foot = document.id(this.tfoot.rows[0]);
this.options.rows.each(function(row){
this.push(row);
}, this);
},
toElement: function(){
return this.element;
},
empty: function(){
this.body.empty();
return this;
},
set: function(what, items){
var target = (what == 'headers') ? 'tHead' : 'tFoot',
lower = target.toLowerCase();
this[lower] = (document.id(this.element[target]) || new Element(lower).inject(this.element, 'top')).empty();
var data = this.push(items, {}, this[lower], what == 'headers' ? 'th' : 'td');
if (what == 'headers') this.head = this.getHead();
else this.foot = this.getHead();
return data;
},
getHead: function(){
var rows = this.thead.rows;
return rows.length > 1 ? $$(rows) : rows.length ? document.id(rows[0]) : false;
},
setHeaders: function(headers){
this.set('headers', headers);
return this;
},
setFooters: function(footers){
this.set('footers', footers);
return this;
},
push: function(row, rowProperties, target, tag, where){
if (typeOf(row) == 'element' && row.get('tag') == 'tr'){
row.inject(target || this.body, where);
return {
tr: row,
tds: row.getChildren('td')
};
}
var tds = row.map(function(data){
var td = new Element(tag || 'td', data ? data.properties : {}),
content = (data ? data.content : '') || data,
type = typeOf(content);
if (['element', 'array', 'collection', 'elements'].contains(type)) td.adopt(content);
else td.set('html', content);
return td;
});
return {
tr: new Element('tr', rowProperties).inject(target || this.body, where).adopt(tds),
tds: tds
};
}
});
['adopt', 'inject', 'wraps', 'grab', 'replaces', 'dispose'].each(function(method){
HtmlTable.implement(method, function(){
this.element[method].apply(this.element, arguments);
return this;
});
});
/*
---
script: HtmlTable.Zebra.js
name: HtmlTable.Zebra
description: Builds a stripy table with methods to add rows.
license: MIT-style license
authors:
- Harald Kirschner
- Aaron Newton
requires:
- /HtmlTable
- /Class.refactor
provides: [HtmlTable.Zebra]
...
*/
HtmlTable = Class.refactor(HtmlTable, {
options: {
classZebra: 'table-tr-odd',
zebra: true
},
initialize: function(){
this.previous.apply(this, arguments);
if (this.occluded) return this.occluded;
if (this.options.zebra) this.updateZebras();
},
updateZebras: function(){
Array.each(this.body.rows, this.zebra, this);
},
setRowStyle: function(row, i){
if (this.previous) this.previous(row, i);
this.zebra(row, i);
},
zebra: function(row, i){
return row[((i % 2) ? 'remove' : 'add')+'Class'](this.options.classZebra);
},
push: function(){
var pushed = this.previous.apply(this, arguments);
if (this.options.zebra) this.updateZebras();
return pushed;
}
});
/*
---
script: HtmlTable.Sort.js
name: HtmlTable.Sort
description: Builds a stripy, sortable table with methods to add rows.
license: MIT-style license
authors:
- Harald Kirschner
- Aaron Newton
- Jacob Thornton
requires:
- Core/Hash
- /HtmlTable
- /Class.refactor
- /Element.Delegation
- /String.Extras
- /Date
provides: [HtmlTable.Sort]
...
*/
HtmlTable = Class.refactor(HtmlTable, {
options: {/*
onSort: function(){}, */
sortIndex: 0,
sortReverse: false,
parsers: [],
defaultParser: 'string',
classSortable: 'table-sortable',
classHeadSort: 'table-th-sort',
classHeadSortRev: 'table-th-sort-rev',
classNoSort: 'table-th-nosort',
classGroupHead: 'table-tr-group-head',
classGroup: 'table-tr-group',
classCellSort: 'table-td-sort',
classSortSpan: 'table-th-sort-span',
sortable: false,
thSelector: 'th'
},
initialize: function (){
this.previous.apply(this, arguments);
if (this.occluded) return this.occluded;
this.sorted = {index: null, dir: 1};
this.bound = {
headClick: this.headClick.bind(this)
};
this.sortSpans = new Elements();
if (this.options.sortable){
this.enableSort();
if (this.options.sortIndex != null) this.sort(this.options.sortIndex, this.options.sortReverse);
}
},
attachSorts: function(attach){
this.detachSorts();
if (attach !== false) this.element.addEvent('click:relay(' + this.options.thSelector + ')', this.bound.headClick);
},
detachSorts: function(){
this.element.removeEvents('click:relay(' + this.options.thSelector + ')');
},
setHeaders: function(){
this.previous.apply(this, arguments);
if (this.sortEnabled) this.setParsers();
},
setParsers: function(){
this.parsers = this.detectParsers();
},
detectParsers: function(){
return this.head && this.head.getElements(this.options.thSelector).flatten().map(this.detectParser, this);
},
detectParser: function(cell, index){
if (cell.hasClass(this.options.classNoSort) || cell.retrieve('htmltable-parser')) return cell.retrieve('htmltable-parser');
var thDiv = new Element('div');
thDiv.adopt(cell.childNodes).inject(cell);
var sortSpan = new Element('span', {'class': this.options.classSortSpan}).inject(thDiv, 'top');
this.sortSpans.push(sortSpan);
var parser = this.options.parsers[index],
rows = this.body.rows,
cancel;
switch (typeOf(parser)){
case 'function': parser = {convert: parser}; cancel = true; break;
case 'string': parser = parser; cancel = true; break;
}
if (!cancel){
HtmlTable.ParserPriority.some(function(parserName){
var current = HtmlTable.Parsers[parserName],
match = current.match;
if (!match) return false;
for (var i = 0, j = rows.length; i < j; i++){
var cell = document.id(rows[i].cells[index]),
text = cell ? cell.get('html').clean() : '';
if (text && match.test(text)){
parser = current;
return true;
}
}
});
}
if (!parser) parser = this.options.defaultParser;
cell.store('htmltable-parser', parser);
return parser;
},
headClick: function(event, el){
if (!this.head || el.hasClass(this.options.classNoSort)) return;
return this.sort(Array.indexOf(this.head.getElements(this.options.thSelector).flatten(), el) % this.body.rows[0].cells.length);
},
serialize: function() {
var previousSerialization = this.previous.apply(this, arguments) || {};
if (this.options.sortable) {
previousSerialization.sortIndex = this.sorted.index;
previousSerialization.sortReverse = this.sorted.reverse;
}
return previousSerialization;
},
restore: function(tableState) {
if(this.options.sortable && tableState.sortIndex) {
this.sort(tableState.sortIndex, tableState.sortReverse);
}
this.previous.apply(this, arguments);
},
setSortedState: function(index, reverse){
if (reverse != null) this.sorted.reverse = reverse;
else if (this.sorted.index == index) this.sorted.reverse = !this.sorted.reverse;
else this.sorted.reverse = this.sorted.index == null;
if (index != null) this.sorted.index = index;
},
setHeadSort: function(sorted){
var head = $$(!this.head.length ? this.head.cells[this.sorted.index] : this.head.map(function(row){
return row.getElements(this.options.thSelector)[this.sorted.index];
}, this).clean());
if (!head.length) return;
if (sorted){
head.addClass(this.options.classHeadSort);
if (this.sorted.reverse) head.addClass(this.options.classHeadSortRev);
else head.removeClass(this.options.classHeadSortRev);
} else {
head.removeClass(this.options.classHeadSort).removeClass(this.options.classHeadSortRev);
}
},
setRowSort: function(data, pre){
var count = data.length,
body = this.body,
group,
rowIndex;
while (count){
var item = data[--count],
position = item.position,
row = body.rows[position];
if (row.disabled) continue;
if (!pre){
group = this.setGroupSort(group, row, item);
this.setRowStyle(row, count);
}
body.appendChild(row);
for (rowIndex = 0; rowIndex < count; rowIndex++){
if (data[rowIndex].position > position) data[rowIndex].position--;
}
}
},
setRowStyle: function(row, i){
this.previous(row, i);
row.cells[this.sorted.index].addClass(this.options.classCellSort);
},
setGroupSort: function(group, row, item){
if (group == item.value) row.removeClass(this.options.classGroupHead).addClass(this.options.classGroup);
else row.removeClass(this.options.classGroup).addClass(this.options.classGroupHead);
return item.value;
},
getParser: function(){
var parser = this.parsers[this.sorted.index];
return typeOf(parser) == 'string' ? HtmlTable.Parsers[parser] : parser;
},
sort: function(index, reverse, pre){
if (!this.head) return;
if (!pre){
this.clearSort();
this.setSortedState(index, reverse);
this.setHeadSort(true);
}
var parser = this.getParser();
if (!parser) return;
var rel;
if (!Browser.ie){
rel = this.body.getParent();
this.body.dispose();
}
var data = this.parseData(parser).sort(function(a, b){
if (a.value === b.value) return 0;
return a.value > b.value ? 1 : -1;
});
if (this.sorted.reverse == (parser == HtmlTable.Parsers['input-checked'])) data.reverse(true);
this.setRowSort(data, pre);
if (rel) rel.grab(this.body);
this.fireEvent('stateChanged');
return this.fireEvent('sort', [this.body, this.sorted.index]);
},
parseData: function(parser){
return Array.map(this.body.rows, function(row, i){
var value = parser.convert.call(document.id(row.cells[this.sorted.index]));
return {
position: i,
value: value
};
}, this);
},
clearSort: function(){
this.setHeadSort(false);
this.body.getElements('td').removeClass(this.options.classCellSort);
},
reSort: function(){
if (this.sortEnabled) this.sort.call(this, this.sorted.index, this.sorted.reverse);
return this;
},
enableSort: function(){
this.element.addClass(this.options.classSortable);
this.attachSorts(true);
this.setParsers();
this.sortEnabled = true;
return this;
},
disableSort: function(){
this.element.removeClass(this.options.classSortable);
this.attachSorts(false);
this.sortSpans.each(function(span){
span.destroy();
});
this.sortSpans.empty();
this.sortEnabled = false;
return this;
}
});
HtmlTable.ParserPriority = ['date', 'input-checked', 'input-value', 'float', 'number'];
HtmlTable.Parsers = {
'date': {
match: /^\d{2}[-\/ ]\d{2}[-\/ ]\d{2,4}$/,
convert: function(){
var d = Date.parse(this.get('text').stripTags());
return (typeOf(d) == 'date') ? d.format('db') : '';
},
type: 'date'
},
'input-checked': {
match: / type="(radio|checkbox)" /,
convert: function(){
return this.getElement('input').checked;
}
},
'input-value': {
match: /<input/,
convert: function(){
return this.getElement('input').value;
}
},
'number': {
match: /^\d+[^\d.,]*$/,
convert: function(){
return this.get('text').stripTags().toInt();
},
number: true
},
'numberLax': {
match: /^[^\d]+\d+$/,
convert: function(){
return this.get('text').replace(/[^-?^0-9]/, '').stripTags().toInt();
},
number: true
},
'float': {
match: /^[\d]+\.[\d]+/,
convert: function(){
return this.get('text').replace(/[^-?^\d.]/, '').stripTags().toFloat();
},
number: true
},
'floatLax': {
match: /^[^\d]+[\d]+\.[\d]+$/,
convert: function(){
return this.get('text').replace(/[^-?^\d.]/, '').stripTags();
},
number: true
},
'string': {
match: null,
convert: function(){
return this.get('text').stripTags().toLowerCase();
}
},
'title': {
match: null,
convert: function(){
return this.title;
}
}
};
//<1.2compat>
HtmlTable.Parsers = new Hash(HtmlTable.Parsers);
//</1.2compat>
HtmlTable.defineParsers = function(parsers){
HtmlTable.Parsers = Object.append(HtmlTable.Parsers, parsers);
for (var parser in parsers){
HtmlTable.ParserPriority.unshift(parser);
}
};
/*
---
script: Keyboard.js
name: Keyboard
description: KeyboardEvents used to intercept events on a class for keyboard and format modifiers in a specific order so as to make alt+shift+c the same as shift+alt+c.
license: MIT-style license
authors:
- Perrin Westrich
- Aaron Newton
- Scott Kyle
requires:
- Core/Events
- Core/Options
- Core/Element.Event
- Element.Event.Pseudos.Keys
provides: [Keyboard]
...
*/
(function(){
var Keyboard = this.Keyboard = new Class({
Extends: Events,
Implements: [Options],
options: {/*
onActivate: function(){},
onDeactivate: function(){},*/
defaultEventType: 'keydown',
active: false,
manager: null,
events: {},
nonParsedEvents: ['activate', 'deactivate', 'onactivate', 'ondeactivate', 'changed', 'onchanged']
},
initialize: function(options){
if (options && options.manager){
this._manager = options.manager;
delete options.manager;
}
this.setOptions(options);
this._setup();
},
addEvent: function(type, fn, internal){
return this.parent(Keyboard.parse(type, this.options.defaultEventType, this.options.nonParsedEvents), fn, internal);
},
removeEvent: function(type, fn){
return this.parent(Keyboard.parse(type, this.options.defaultEventType, this.options.nonParsedEvents), fn);
},
toggleActive: function(){
return this[this.isActive() ? 'deactivate' : 'activate']();
},
activate: function(instance){
if (instance){
if (instance.isActive()) return this;
//if we're stealing focus, store the last keyboard to have it so the relinquish command works
if (this._activeKB && instance != this._activeKB){
this.previous = this._activeKB;
this.previous.fireEvent('deactivate');
}
//if we're enabling a child, assign it so that events are now passed to it
this._activeKB = instance.fireEvent('activate');
Keyboard.manager.fireEvent('changed');
} else if (this._manager){
//else we're enabling ourselves, we must ask our parent to do it for us
this._manager.activate(this);
}
return this;
},
isActive: function(){
return this._manager ? (this._manager._activeKB == this) : (Keyboard.manager == this);
},
deactivate: function(instance){
if (instance){
if (instance === this._activeKB){
this._activeKB = null;
instance.fireEvent('deactivate');
Keyboard.manager.fireEvent('changed');
}
} else if (this._manager){
this._manager.deactivate(this);
}
return this;
},
relinquish: function(){
if (this.isActive() && this._manager && this._manager.previous) this._manager.activate(this._manager.previous);
else this.deactivate();
return this;
},
//management logic
manage: function(instance){
if (instance._manager) instance._manager.drop(instance);
this._instances.push(instance);
instance._manager = this;
if (!this._activeKB) this.activate(instance);
return this;
},
drop: function(instance){
instance.relinquish();
this._instances.erase(instance);
if (this._activeKB == instance){
if (this.previous && this._instances.contains(this.previous)) this.activate(this.previous);
else this._activeKB = this._instances[0];
}
return this;
},
trace: function(){
Keyboard.trace(this);
},
each: function(fn){
Keyboard.each(this, fn);
},
/*
PRIVATE METHODS
*/
_instances: [],
_disable: function(instance){
if (this._activeKB == instance) this._activeKB = null;
},
_setup: function(){
this.addEvents(this.options.events);
//if this is the root manager, nothing manages it
if (Keyboard.manager && !this._manager) Keyboard.manager.manage(this);
if (this.options.active) this.activate();
else this.relinquish();
},
_handle: function(event, type){
//Keyboard.stop(event) prevents key propagation
if (event.preventKeyboardPropagation) return;
var bubbles = !!this._manager;
if (bubbles && this._activeKB){
this._activeKB._handle(event, type);
if (event.preventKeyboardPropagation) return;
}
this.fireEvent(type, event);
if (!bubbles && this._activeKB) this._activeKB._handle(event, type);
}
});
var parsed = {};
var modifiers = ['shift', 'control', 'alt', 'meta'];
var regex = /^(?:shift|control|ctrl|alt|meta)$/;
Keyboard.parse = function(type, eventType, ignore){
if (ignore && ignore.contains(type.toLowerCase())) return type;
type = type.toLowerCase().replace(/^(keyup|keydown):/, function($0, $1){
eventType = $1;
return '';
});
if (!parsed[type]){
var key, mods = {};
type.split('+').each(function(part){
if (regex.test(part)) mods[part] = true;
else key = part;
});
mods.control = mods.control || mods.ctrl; // allow both control and ctrl
var keys = [];
modifiers.each(function(mod){
if (mods[mod]) keys.push(mod);
});
if (key) keys.push(key);
parsed[type] = keys.join('+');
}
return eventType + ':keys(' + parsed[type] + ')';
};
Keyboard.each = function(keyboard, fn){
var current = keyboard || Keyboard.manager;
while (current){
fn.run(current);
current = current._activeKB;
}
};
Keyboard.stop = function(event){
event.preventKeyboardPropagation = true;
};
Keyboard.manager = new Keyboard({
active: true
});
Keyboard.trace = function(keyboard){
keyboard = keyboard || Keyboard.manager;
var hasConsole = window.console && console.log;
if (hasConsole) console.log('the following items have focus: ');
Keyboard.each(keyboard, function(current){
if (hasConsole) console.log(document.id(current.widget) || current.wiget || current);
});
};
var handler = function(event){
var keys = [];
modifiers.each(function(mod){
if (event[mod]) keys.push(mod);
});
if (!regex.test(event.key)) keys.push(event.key);
Keyboard.manager._handle(event, event.type + ':keys(' + keys.join('+') + ')');
};
document.addEvents({
'keyup': handler,
'keydown': handler
});
})();
/*
---
script: Keyboard.Extras.js
name: Keyboard.Extras
description: Enhances Keyboard by adding the ability to name and describe keyboard shortcuts, and the ability to grab shortcuts by name and bind the shortcut to different keys.
license: MIT-style license
authors:
- Perrin Westrich
requires:
- /Keyboard
- /MooTools.More
provides: [Keyboard.Extras]
...
*/
Keyboard.prototype.options.nonParsedEvents.combine(['rebound', 'onrebound']);
Keyboard.implement({
/*
shortcut should be in the format of:
{
'keys': 'shift+s', // the default to add as an event.
'description': 'blah blah blah', // a brief description of the functionality.
'handler': function(){} // the event handler to run when keys are pressed.
}
*/
addShortcut: function(name, shortcut){
this._shortcuts = this._shortcuts || [];
this._shortcutIndex = this._shortcutIndex || {};
shortcut.getKeyboard = Function.from(this);
shortcut.name = name;
this._shortcutIndex[name] = shortcut;
this._shortcuts.push(shortcut);
if (shortcut.keys) this.addEvent(shortcut.keys, shortcut.handler);
return this;
},
addShortcuts: function(obj){
for (var name in obj) this.addShortcut(name, obj[name]);
return this;
},
removeShortcut: function(name){
var shortcut = this.getShortcut(name);
if (shortcut && shortcut.keys){
this.removeEvent(shortcut.keys, shortcut.handler);
delete this._shortcutIndex[name];
this._shortcuts.erase(shortcut);
}
return this;
},
removeShortcuts: function(names){
names.each(this.removeShortcut, this);
return this;
},
getShortcuts: function(){
return this._shortcuts || [];
},
getShortcut: function(name){
return (this._shortcutIndex || {})[name];
}
});
Keyboard.rebind = function(newKeys, shortcuts){
Array.convert(shortcuts).each(function(shortcut){
shortcut.getKeyboard().removeEvent(shortcut.keys, shortcut.handler);
shortcut.getKeyboard().addEvent(newKeys, shortcut.handler);
shortcut.keys = newKeys;
shortcut.getKeyboard().fireEvent('rebound');
});
};
Keyboard.getActiveShortcuts = function(keyboard){
var activeKBS = [], activeSCS = [];
Keyboard.each(keyboard, [].push.bind(activeKBS));
activeKBS.each(function(kb){ activeSCS.extend(kb.getShortcuts()); });
return activeSCS;
};
Keyboard.getShortcut = function(name, keyboard, opts){
opts = opts || {};
var shortcuts = opts.many ? [] : null,
set = opts.many ? function(kb){
var shortcut = kb.getShortcut(name);
if (shortcut) shortcuts.push(shortcut);
} : function(kb){
if (!shortcuts) shortcuts = kb.getShortcut(name);
};
Keyboard.each(keyboard, set);
return shortcuts;
};
Keyboard.getShortcuts = function(name, keyboard){
return Keyboard.getShortcut(name, keyboard, { many: true });
};
/*
---
script: HtmlTable.Select.js
name: HtmlTable.Select
description: Builds a stripy, sortable table with methods to add rows. Rows can be selected with the mouse or keyboard navigation.
license: MIT-style license
authors:
- Harald Kirschner
- Aaron Newton
requires:
- /Keyboard
- /Keyboard.Extras
- /HtmlTable
- /Class.refactor
- /Element.Delegation
- /Element.Shortcuts
provides: [HtmlTable.Select]
...
*/
HtmlTable = Class.refactor(HtmlTable, {
options: {
/*onRowFocus: function(){},
onRowUnfocus: function(){},*/
useKeyboard: true,
classRowSelected: 'table-tr-selected',
classRowHovered: 'table-tr-hovered',
classSelectable: 'table-selectable',
shiftForMultiSelect: true,
allowMultiSelect: true,
selectable: false
},
initialize: function(){
this.previous.apply(this, arguments);
if (this.occluded) return this.occluded;
this.selectedRows = new Elements();
this.bound = {
mouseleave: this.mouseleave.bind(this),
clickRow: this.clickRow.bind(this),
activateKeyboard: function() {
if (this.keyboard && this.selectEnabled) this.keyboard.activate();
}.bind(this)
};
if (this.options.selectable) this.enableSelect();
},
empty: function(){
this.selectNone();
return this.previous();
},
enableSelect: function(){
this.selectEnabled = true;
this.attachSelects();
this.element.addClass(this.options.classSelectable);
return this;
},
disableSelect: function(){
this.selectEnabled = false;
this.attachSelects(false);
this.element.removeClass(this.options.classSelectable);
return this;
},
push: function(){
var ret = this.previous.apply(this, arguments);
this.updateSelects();
return ret;
},
isSelected: function(row){
return this.selectedRows.contains(row);
},
toggleRow: function(row){
return this[(this.isSelected(row) ? 'de' : '') + 'selectRow'](row);
},
selectRow: function(row, _nocheck){
//private variable _nocheck: boolean whether or not to confirm the row is in the table body
//added here for optimization when selecting ranges
if (this.isSelected(row) || (!_nocheck && !this.body.getChildren().contains(row))) return;
if (!this.options.allowMultiSelect) this.selectNone();
if (!this.isSelected(row)){
this.selectedRows.push(row);
row.addClass(this.options.classRowSelected);
this.fireEvent('rowFocus', [row, this.selectedRows]);
this.fireEvent('stateChanged');
}
this.focused = row;
document.clearSelection();
return this;
},
getSelected: function(){
return this.selectedRows;
},
serialize: function() {
var previousSerialization = this.previous.apply(this, arguments) || {};
if (this.options.selectable) {
previousSerialization.selectedRows = this.selectedRows.map(function(row) {
return Array.indexOf(this.body.rows, row);
}.bind(this));
}
return previousSerialization;
},
restore: function(tableState) {
if(this.options.selectable && tableState.selectedRows) {
tableState.selectedRows.each(function(index) {
this.selectRow(this.body.rows[index]);
}.bind(this));
}
this.previous.apply(this, arguments);
},
deselectRow: function(row, _nocheck){
if (!this.isSelected(row) || (!_nocheck && !this.body.getChildren().contains(row))) return;
this.selectedRows = new Elements(Array.convert(this.selectedRows).erase(row));
row.removeClass(this.options.classRowSelected);
this.fireEvent('rowUnfocus', [row, this.selectedRows]);
this.fireEvent('stateChanged');
return this;
},
selectAll: function(selectNone){
if (!selectNone && !this.options.allowMultiSelect) return;
this.selectRange(0, this.body.rows.length, selectNone);
return this;
},
selectNone: function(){
return this.selectAll(true);
},
selectRange: function(startRow, endRow, _deselect){
if (!this.options.allowMultiSelect && !_deselect) return;
var method = _deselect ? 'deselectRow' : 'selectRow',
rows = Array.clone(this.body.rows);
if (typeOf(startRow) == 'element') startRow = rows.indexOf(startRow);
if (typeOf(endRow) == 'element') endRow = rows.indexOf(endRow);
endRow = endRow < rows.length - 1 ? endRow : rows.length - 1;
if (endRow < startRow){
var tmp = startRow;
startRow = endRow;
endRow = tmp;
}
for (var i = startRow; i <= endRow; i++) this[method](rows[i], true);
return this;
},
deselectRange: function(startRow, endRow){
this.selectRange(startRow, endRow, true);
},
getSelected: function(){
return this.selectedRows;
},
/*
Private methods:
*/
enterRow: function(row){
if (this.hovered) this.hovered = this.leaveRow(this.hovered);
this.hovered = row.addClass(this.options.classRowHovered);
},
leaveRow: function(row){
row.removeClass(this.options.classRowHovered);
},
updateSelects: function(){
Array.each(this.body.rows, function(row){
var binders = row.retrieve('binders');
if (!binders && !this.selectEnabled) return;
if (!binders){
binders = {
mouseenter: this.enterRow.pass([row], this),
mouseleave: this.leaveRow.pass([row], this)
};
row.store('binders', binders);
}
if (this.selectEnabled) row.addEvents(binders);
else row.removeEvents(binders);
}, this);
},
shiftFocus: function(offset, event){
if (!this.focused) return this.selectRow(this.body.rows[0], event);
var to = this.getRowByOffset(offset);
if (to === null || this.focused == this.body.rows[to]) return this;
this.toggleRow(this.body.rows[to], event);
},
clickRow: function(event, row){
var selecting = (event.shift || event.meta || event.control) && this.options.shiftForMultiSelect;
if (!selecting && !(event.rightClick && this.isSelected(row) && this.options.allowMultiSelect)) this.selectNone();
if (event.rightClick) this.selectRow(row);
else this.toggleRow(row);
if (event.shift){
this.selectRange(this.rangeStart || this.body.rows[0], row, this.rangeStart ? !this.isSelected(row) : true);
this.focused = row;
}
this.rangeStart = row;
},
getRowByOffset: function(offset){
if (!this.focused) return 0;
var rows = Array.clone(this.body.rows),
index = rows.indexOf(this.focused) + offset;
if (index < 0) index = null;
if (index >= rows.length) index = null;
return index;
},
attachSelects: function(attach){
attach = attach != null ? attach : true;
var method = attach ? 'addEvents' : 'removeEvents';
this.element[method]({
mouseleave: this.bound.mouseleave,
click: this.bound.activateKeyboard
});
this.body[method]({
'click:relay(tr)': this.bound.clickRow,
'contextmenu:relay(tr)': this.bound.clickRow
});
if (this.options.useKeyboard || this.keyboard){
if (!this.keyboard) this.keyboard = new Keyboard();
if (!this.selectKeysDefined) {
this.selectKeysDefined = true;
var timer, held;
var move = function(offset){
var mover = function(e){
clearTimeout(timer);
e.preventDefault();
var to = this.body.rows[this.getRowByOffset(offset)];
if (e.shift && to && this.isSelected(to)){
this.deselectRow(this.focused);
this.focused = to;
} else {
if (to && (!this.options.allowMultiSelect || !e.shift)){
this.selectNone();
}
this.shiftFocus(offset, e);
}
if (held){
timer = mover.delay(100, this, e);
} else {
timer = (function(){
held = true;
mover(e);
}).delay(400);
}
}.bind(this);
return mover;
}.bind(this);
var clear = function(){
clearTimeout(timer);
held = false;
};
this.keyboard.addEvents({
'keydown:shift+up': move(-1),
'keydown:shift+down': move(1),
'keyup:shift+up': clear,
'keyup:shift+down': clear,
'keyup:up': clear,
'keyup:down': clear
});
var shiftHint = '';
if (this.options.allowMultiSelect && this.options.shiftForMultiSelect && this.options.useKeyboard){
shiftHint = " (Shift multi-selects).";
}
this.keyboard.addShortcuts({
'Select Previous Row': {
keys: 'up',
shortcut: 'up arrow',
handler: move(-1),
description: 'Select the previous row in the table.' + shiftHint
},
'Select Next Row': {
keys: 'down',
shortcut: 'down arrow',
handler: move(1),
description: 'Select the next row in the table.' + shiftHint
}
});
}
this.keyboard[attach ? 'activate' : 'deactivate']();
}
this.updateSelects();
},
mouseleave: function(){
if (this.hovered) this.leaveRow(this.hovered);
}
});
/*
---
script: Scroller.js
name: Scroller
description: Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries.
license: MIT-style license
authors:
- Valerio Proietti
requires:
- Core/Events
- Core/Options
- Core/Element.Event
- Core/Element.Dimensions
- MooTools.More
provides: [Scroller]
...
*/
var Scroller = new Class({
Implements: [Events, Options],
options: {
area: 20,
velocity: 1,
onChange: function(x, y){
this.element.scrollTo(x, y);
},
fps: 50
},
initialize: function(element, options){
this.setOptions(options);
this.element = document.id(element);
this.docBody = document.id(this.element.getDocument().body);
this.listener = (typeOf(this.element) != 'element') ? this.docBody : this.element;
this.timer = null;
this.bound = {
attach: this.attach.bind(this),
detach: this.detach.bind(this),
getCoords: this.getCoords.bind(this)
};
},
start: function(){
this.listener.addEvents({
mouseover: this.bound.attach,
mouseleave: this.bound.detach
});
return this;
},
stop: function(){
this.listener.removeEvents({
mouseover: this.bound.attach,
mouseleave: this.bound.detach
});
this.detach();
this.timer = clearInterval(this.timer);
return this;
},
attach: function(){
this.listener.addEvent('mousemove', this.bound.getCoords);
},
detach: function(){
this.listener.removeEvent('mousemove', this.bound.getCoords);
this.timer = clearInterval(this.timer);
},
getCoords: function(event){
this.page = (this.listener.get('tag') == 'body') ? event.client : event.page;
if (!this.timer) this.timer = this.scroll.periodical(Math.round(1000 / this.options.fps), this);
},
scroll: function(){
var size = this.element.getSize(),
scroll = this.element.getScroll(),
pos = this.element != this.docBody ? this.element.getOffsets() : {x: 0, y:0},
scrollSize = this.element.getScrollSize(),
change = {x: 0, y: 0},
top = this.options.area.top || this.options.area,
bottom = this.options.area.bottom || this.options.area;
for (var z in this.page){
if (this.page[z] < (top + pos[z]) && scroll[z] != 0){
change[z] = (this.page[z] - top - pos[z]) * this.options.velocity;
} else if (this.page[z] + bottom > (size[z] + pos[z]) && scroll[z] + size[z] != scrollSize[z]){
change[z] = (this.page[z] - size[z] + bottom - pos[z]) * this.options.velocity;
}
change[z] = change[z].round();
}
if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x, scroll.y + change.y]);
}
});
/*
---
script: Tips.js
name: Tips
description: Class for creating nice tips that follow the mouse cursor when hovering an element.
license: MIT-style license
authors:
- Valerio Proietti
- Christoph Pojer
- Luis Merino
requires:
- Core/Options
- Core/Events
- Core/Element.Event
- Core/Element.Style
- Core/Element.Dimensions
- /MooTools.More
provides: [Tips]
...
*/
(function(){
var read = function(option, element){
return (option) ? (typeOf(option) == 'function' ? option(element) : element.get(option)) : '';
};
this.Tips = new Class({
Implements: [Events, Options],
options: {/*
onAttach: function(element){},
onDetach: function(element){},
onBound: function(coords){},*/
onShow: function(){
this.tip.setStyle('display', 'block');
},
onHide: function(){
this.tip.setStyle('display', 'none');
},
title: 'title',
text: function(element){
return element.get('rel') || element.get('href');
},
showDelay: 100,
hideDelay: 100,
className: 'tip-wrap',
offset: {x: 16, y: 16},
windowPadding: {x:0, y:0},
fixed: false
},
initialize: function(){
var params = Array.link(arguments, {
options: Type.isObject,
elements: function(obj){
return obj != null;
}
});
this.setOptions(params.options);
if (params.elements) this.attach(params.elements);
this.container = new Element('div', {'class': 'tip'});
},
toElement: function(){
if (this.tip) return this.tip;
this.tip = new Element('div', {
'class': this.options.className,
styles: {
position: 'absolute',
top: 0,
left: 0
}
}).adopt(
new Element('div', {'class': 'tip-top'}),
this.container,
new Element('div', {'class': 'tip-bottom'})
);
return this.tip;
},
attach: function(elements){
$$(elements).each(function(element){
var title = read(this.options.title, element),
text = read(this.options.text, element);
element.set('title', '').store('tip:native', title).retrieve('tip:title', title);
element.retrieve('tip:text', text);
this.fireEvent('attach', [element]);
var events = ['enter', 'leave'];
if (!this.options.fixed) events.push('move');
events.each(function(value){
var event = element.retrieve('tip:' + value);
if (!event) event = function(event){
this['element' + value.capitalize()].apply(this, [event, element]);
}.bind(this);
element.store('tip:' + value, event).addEvent('mouse' + value, event);
}, this);
}, this);
return this;
},
detach: function(elements){
$$(elements).each(function(element){
['enter', 'leave', 'move'].each(function(value){
element.removeEvent('mouse' + value, element.retrieve('tip:' + value)).eliminate('tip:' + value);
});
this.fireEvent('detach', [element]);
if (this.options.title == 'title'){ // This is necessary to check if we can revert the title
var original = element.retrieve('tip:native');
if (original) element.set('title', original);
}
}, this);
return this;
},
elementEnter: function(event, element){
clearTimeout(this.timer);
this.timer = (function(){
this.container.empty();
['title', 'text'].each(function(value){
var content = element.retrieve('tip:' + value);
var div = this['_' + value + 'Element'] = new Element('div', {
'class': 'tip-' + value
}).inject(this.container);
if (content) this.fill(div, content);
}, this);
this.show(element);
this.position((this.options.fixed) ? {page: element.getPosition()} : event);
}).delay(this.options.showDelay, this);
},
elementLeave: function(event, element){
clearTimeout(this.timer);
this.timer = this.hide.delay(this.options.hideDelay, this, element);
this.fireForParent(event, element);
},
setTitle: function(title){
if (this._titleElement){
this._titleElement.empty();
this.fill(this._titleElement, title);
}
return this;
},
setText: function(text){
if (this._textElement){
this._textElement.empty();
this.fill(this._textElement, text);
}
return this;
},
fireForParent: function(event, element){
element = element.getParent();
if (!element || element == document.body) return;
if (element.retrieve('tip:enter')) element.fireEvent('mouseenter', event);
else this.fireForParent(event, element);
},
elementMove: function(event, element){
this.position(event);
},
position: function(event){
if (!this.tip) document.id(this);
var size = window.getSize(), scroll = window.getScroll(),
tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight},
props = {x: 'left', y: 'top'},
bounds = {y: false, x2: false, y2: false, x: false},
obj = {};
for (var z in props){
obj[props[z]] = event.page[z] + this.options.offset[z];
if (obj[props[z]] < 0) bounds[z] = true;
if ((obj[props[z]] + tip[z] - scroll[z]) > size[z] - this.options.windowPadding[z]){
obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z];
bounds[z+'2'] = true;
}
}
this.fireEvent('bound', bounds);
this.tip.setStyles(obj);
},
fill: function(element, contents){
if (typeof contents == 'string') element.set('html', contents);
else element.adopt(contents);
},
show: function(element){
if (!this.tip) document.id(this);
if (!this.tip.getParent()) this.tip.inject(document.body);
this.fireEvent('show', [this.tip, element]);
},
hide: function(element){
if (!this.tip) document.id(this);
this.fireEvent('hide', [this.tip, element]);
}
});
})();
window.addEvent('domready', function()
{
if (navigator.userAgent.indexOf('Mac') != -1)
{
$$('body')[0].addClass('Plateform-Mac');
}
else if (navigator.userAgent.indexOf('Win') != -1)
{
$$('body')[0].addClass('Plateform-Windows');
}
var ua = navigator.userAgent,
M  = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
if(/trident/i.test(M[1])){
$$('body')[0].addClass('Browser-Ie');
} else if(M[1]=== 'Chrome' && ua.match(/\bOPR\/(\d+)/)){
$$('body')[0].addClass('Browser-Opera');
} else {
$$('body')[0].addClass('Browser-' + M[1]);
}
});/**
* This is a small fix to correct the png transparency problem under IE under version 7
*/
window.addEvent('load', function(e) {
var arVersion = navigator.appVersion.split("MSIE");
var version = parseFloat(arVersion[1]);
if ((version >= 5.5) && (version < 7) && (document.body.filters)) {
for(var i=0; i<document.images.length; i++) {
var img = document.images[i];
var imgName = img.src.toUpperCase();
if ((imgName.substring(imgName.length-3, imgName.length) == "PNG" || imgName.indexOf('FILETYPE=PNG') != -1) && (img.height && img.width)) {
var imgID = (img.id) ? "id='" + img.id + "' " : "";
var imgClass = (img.className) ? "class='" + img.className + "' " : "";
var imgTitle = (img.title) ? "title='" + img.title + "' " : "title='" + img.alt + "' ";
var imgStyle = "display:inline-block;" + img.style.cssText;
if (img.align == "left") imgStyle = "float:left;" + imgStyle;
if (img.align == "right") imgStyle = "float:right;" + imgStyle;
if (img.parentElement.href) imgStyle = "cursor:hand;" + imgStyle;
var strNewHTML = "<span " + imgID + imgClass + imgTitle
+ " style=\"" + "width:" + img.width + "px; height:" + img.height + "px;" + imgStyle + ";"
+ "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"
+ "(src=\'" + img.src + "\', sizingMethod='scale');\"></span>";
img.outerHTML = strNewHTML;
i = i-1;
}
}
}
});
var PrintingWindow = new Class({
options: {
container: document.getElement('body'),
containerClass : 'Synergee-Web-Page-Component-PrintingWindow',
outputId: 'printingWindowFrame',
width : 750,
height : 370,
button : null
},
initialize: function(options) {
this.setOptions(options);
this.output =  new Element('iframe').inject(document.getElement('body'));
this.output.setStyle('display','none');
this.output.setAttribute('id', this.options.outputId);
if(this.options.button)this.options.button.addEvent('click', this.displayPrintWindow.bind(this));
},
displayPrintWindow : function(){
Lightbox.show('{hiddenId|' + this.output.id + '}', '', 'width=' + this.options.width + ', height=' + this.options.height);
if (/msie/i.test(navigator.userAgent)) {
// IE
// seems to be a bug in IE that makes contentWindow raise an error in some cases
try {
var w = this.output.contentWindow;
} catch(ex) {
}
var d = w ? w.document : null;
} else {
try {
var d = this.output.contentDocument;
}
catch (ex) {
}	// in case of cross domain scripting
var w = d ? d.defaultView : null;
}
if (d) {
try {
var tempContainer = new Element('div');
var child = this.options.container.clone();
child.inject(tempContainer);
child.getElements('script').each(function(item, index){
item.innerHTML = '';
}.bind(this));
d.open();
d.write('<html><head/><body >'+ tempContainer.innerHTML +'</body></html>');
d.close();
var body = d.getElementsByTagName("body")[0];
body.style.textAlign = 'left';
body.setAttribute('class', this.options.containerClass);
//                    if (this.options.button && this.options.button.id && d.getElementById(this.options.button.id)) {
//                        d.getElementById(this.options.button.id).removeChild(d.getElementById(this.options.button.id).childNodes[0]);
//                    }
var styleSheets = document.getElementsByTagName("LINK");
var head = d.getElementsByTagName("HEAD")[0];
if (!head) {
head = d.createElement("HEAD");
d.getElementsByTagName("HTML")[0].insertBefore(head, d.getElementsByTagName("BODY")[0]);
}
for (var i = 0; i < styleSheets.length; i++) {
if (styleSheets[i].getAttribute("rel") == 'stylesheet') {
var linkElement = d.createElement('LINK');
linkElement.setAttribute('href', styleSheets[i].getAttribute("href"));
linkElement.setAttribute('rel', 'stylesheet');
linkElement.setAttribute('type', 'text/css');
head.appendChild(linkElement);
}
}
w.focus();
w.print();
} catch (ex) {
}
}
}
});
PrintingWindow.implement(new Events);
PrintingWindow.implement(new Options);
var ResultHelper = new Class( {
Extends : Events,
initialize: function (resultsPerPage, countResults) {
this.resultsPerPage = resultsPerPage;
this.countResults = countResults;
if (this.resultsPerPage == 0)
this.resultsPerPage = 1;
this.countPages = Math.ceil(this.countResults / this.resultsPerPage);
this.currentPage = 0;
},
getCountPages : function () {
return this.countPages;
},
getCurrentPage : function () {
return this.currentPage;
},
injectNavBar : function(toInjectNavBarClass, tagName, navClassBaseName, separator) {
var s = "";
for (var i=0; i<this.countPages; i++) {
var className = navClassBaseName + i;
s += "<" + (tagName == 'button' ? tagName + ' type="button"' : tagName) + " class='" + className + "'>" + (i+1) + "</" + tagName + ">";
if (i != this.countPages - 1) {
s += separator;
}
}
$$('.'+toInjectNavBarClass).set("html", s);
},
nextPage : function () {
if (this.currentPage < this.countPages - 1) {
this.fireEvent('onPageChangeBefore');
this.currentPage++;
this.fireEvent('onPageChangeAfter');
if (this.currentPage == this.countPages - 1) {
this.fireEvent('onPageChangeUpperLimit');
}
}
},
previousPage : function () {
if (this.currentPage > 0) {
this.fireEvent('onPageChangeBefore');
this.currentPage--;
this.fireEvent('onPageChangeAfter');
if (this.currentPage == 0) {
this.fireEvent('onPageChangeLowerLimit');
}
}
},
selectPage : function (page) {
if (page >=0 && page < this.countPages) {
this.fireEvent('onPageChangeBefore');
this.currentPage = page;
this.fireEvent('onPageChangeAfter');
if (this.currentPage == 0) {
this.fireEvent('onPageChangeLowerLimit');
}
if (this.currentPage == this.countPages - 1) {
this.fireEvent('onPageChangeUpperLimit');
}
}
},
firstPage : function () {
this.selectPage(0);
},
lastPage : function () {
this.selectPage(this.countPages - 1);
}
});
/****************************************************************
*                                                              *
*  curvyCorners                                                *
*  ------------                                                *
*                                                              *
*  This script generates rounded corners for your divs.        *
*                                                              *
*  Version 1.2.9                                               *
*  Copyright (c) 2006 Cameron Cooke                            *
*  By: Cameron Cooke and Tim Hutchison.                        *
*                                                              *
*                                                              *
*  Website: http://www.curvycorners.net                        *
*  Email:   info@totalinfinity.com                             *
*  Forum:   http://www.curvycorners.net/forum/                 *
*                                                              *
*                                                              *
*  This library is free software; you can redistribute         *
*  it and/or modify it under the terms of the GNU              *
*  Lesser General Public License as published by the           *
*  Free Software Foundation; either version 2.1 of the         *
*  License, or (at your option) any later version.             *
*                                                              *
*  This library is distributed in the hope that it will        *
*  be useful, but WITHOUT ANY WARRANTY; without even the       *
*  implied warranty of MERCHANTABILITY or FITNESS FOR A        *
*  PARTICULAR PURPOSE. See the GNU Lesser General Public       *
*  License for more details.                                   *
*                                                              *
*  You should have received a copy of the GNU Lesser           *
*  General Public License along with this library;             *
*  Inc., 59 Temple Place, Suite 330, Boston,                   *
*  MA 02111-1307 USA                                           *
*                                                              *
****************************************************************/
var isIE = navigator.userAgent.toLowerCase().indexOf("msie") > -1; var isMoz = document.implementation && document.implementation.createDocument; var isSafari = ((navigator.userAgent.toLowerCase().indexOf('safari')!=-1)&&(navigator.userAgent.toLowerCase().indexOf('mac')!=-1))?true:false; function curvyCorners()
{ if(typeof(arguments[0]) != "object") throw newCurvyError("First parameter of curvyCorners() must be an object."); if(typeof(arguments[1]) != "object" && typeof(arguments[1]) != "string") throw newCurvyError("Second parameter of curvyCorners() must be an object or a class name."); if(typeof(arguments[1]) == "string")
{ var startIndex = 0; var boxCol = getElementsByClass(arguments[1]);}
else
{ var startIndex = 1; var boxCol = arguments;}
var curvyCornersCol = new Array(); if(arguments[0].validTags)
var validElements = arguments[0].validTags; else
var validElements = ["div"]; for(var i = startIndex, j = boxCol.length; i < j; i++)
{ var currentTag = boxCol[i].tagName.toLowerCase(); if(inArray(validElements, currentTag) !== false)
{ curvyCornersCol[curvyCornersCol.length] = new curvyObject(arguments[0], boxCol[i]);}
}
this.objects = curvyCornersCol; this.applyCornersToAll = function()
{ for(var x = 0, k = this.objects.length; x < k; x++)
{ this.objects[x].applyCorners();}
}
}
function curvyObject()
{ this.box = arguments[1]; this.settings = arguments[0]; this.topContainer = null; this.bottomContainer = null; this.masterCorners = new Array(); this.contentDIV = null; var boxHeight = get_style(this.box, "height", "height"); var boxWidth = get_style(this.box, "width", "width"); var borderWidth = get_style(this.box, "borderBottomWidth", "border-bottom-width"); var borderColour = get_style(this.box, "borderBottomColor", "border-bottom-color"); var boxColour = get_style(this.box, "backgroundColor", "background-color"); var backgroundImage = get_style(this.box, "backgroundImage", "background-image"); var boxPosition = get_style(this.box, "position", "position"); var boxPadding = get_style(this.box, "paddingBottom", "padding-bottom"); this.boxHeight = parseInt(((boxHeight != "" && boxHeight != "auto" && boxHeight.indexOf("%") == -1)? boxHeight.substring(0, boxHeight.indexOf("px")) : this.box.scrollHeight)); this.boxWidth = parseInt(((boxWidth != "" && boxWidth != "auto" && boxWidth.indexOf("%") == -1)? boxWidth.substring(0, boxWidth.indexOf("px")) : this.box.scrollWidth)); this.borderWidth = parseInt(((borderWidth != "" && borderWidth.indexOf("px") !== -1)? borderWidth.slice(0, borderWidth.indexOf("px")) : 0)); this.boxColour = format_colour(boxColour); this.boxPadding = parseInt(((boxPadding != "" && boxPadding.indexOf("px") !== -1)? boxPadding.slice(0, boxPadding.indexOf("px")) : 0)); this.borderColour = format_colour(borderColour); this.borderString = this.borderWidth + "px" + " solid " + this.borderColour; this.backgroundImage = ((backgroundImage != "none")? backgroundImage : ""); this.boxContent = this.box.innerHTML; if(boxPosition != "absolute") this.box.style.position = "relative"; this.box.style.padding = "0px"; if(isIE && boxWidth == "auto" && boxHeight == "auto") this.box.style.width = "100%"; if(this.settings.autoPad == true && this.boxPadding > 0)
this.box.innerHTML = ""; this.applyCorners = function()
{ for(var t = 0; t < 2; t++)
{ switch(t)
{ case 0:
if(this.settings.tl || this.settings.tr)
{ var newMainContainer = document.createElement("DIV"); newMainContainer.style.width = "100%"; newMainContainer.style.fontSize = "1px"; newMainContainer.style.overflow = "hidden"; newMainContainer.style.position = "absolute"; newMainContainer.style.paddingLeft = this.borderWidth + "px"; newMainContainer.style.paddingRight = this.borderWidth + "px"; var topMaxRadius = Math.max(this.settings.tl ? this.settings.tl.radius : 0, this.settings.tr ? this.settings.tr.radius : 0); newMainContainer.style.height = topMaxRadius + "px"; newMainContainer.style.top = 0 - topMaxRadius + "px"; newMainContainer.style.left = 0 - this.borderWidth + "px"; this.topContainer = this.box.appendChild(newMainContainer);}
break; case 1:
if(this.settings.bl || this.settings.br)
{ var newMainContainer = document.createElement("DIV"); newMainContainer.style.width = "100%"; newMainContainer.style.fontSize = "1px"; newMainContainer.style.overflow = "hidden"; newMainContainer.style.position = "absolute"; newMainContainer.style.paddingLeft = this.borderWidth + "px"; newMainContainer.style.paddingRight = this.borderWidth + "px"; var botMaxRadius = Math.max(this.settings.bl ? this.settings.bl.radius : 0, this.settings.br ? this.settings.br.radius : 0); newMainContainer.style.height = botMaxRadius + "px"; newMainContainer.style.bottom = 0 - botMaxRadius + "px"; newMainContainer.style.left = 0 - this.borderWidth + "px"; this.bottomContainer = this.box.appendChild(newMainContainer);}
break;}
}
if(this.topContainer) this.box.style.borderTopWidth = "0px"; if(this.bottomContainer) this.box.style.borderBottomWidth = "0px"; var corners = ["tr", "tl", "br", "bl"]; for(var i in corners)
{ if(i > -1 < 4)
{ var cc = corners[i]; if(!this.settings[cc])
{ if(((cc == "tr" || cc == "tl") && this.topContainer != null) || ((cc == "br" || cc == "bl") && this.bottomContainer != null))
{ var newCorner = document.createElement("DIV"); newCorner.style.position = "relative"; newCorner.style.fontSize = "1px"; newCorner.style.overflow = "hidden"; if(this.backgroundImage == "")
newCorner.style.backgroundColor = this.boxColour; else
newCorner.style.backgroundImage = this.backgroundImage; switch(cc)
{ case "tl":
newCorner.style.height = topMaxRadius - this.borderWidth + "px"; newCorner.style.marginRight = this.settings.tr.radius - (this.borderWidth*2) + "px"; newCorner.style.borderLeft = this.borderString; newCorner.style.borderTop = this.borderString; newCorner.style.left = -this.borderWidth + "px"; break; case "tr":
newCorner.style.height = topMaxRadius - this.borderWidth + "px"; newCorner.style.marginLeft = this.settings.tl.radius - (this.borderWidth*2) + "px"; newCorner.style.borderRight = this.borderString; newCorner.style.borderTop = this.borderString; newCorner.style.backgroundPosition = "-" + (topMaxRadius + this.borderWidth) + "px 0px"; newCorner.style.left = this.borderWidth + "px"; break; case "bl":
newCorner.style.height = botMaxRadius - this.borderWidth + "px"; newCorner.style.marginRight = this.settings.br.radius - (this.borderWidth*2) + "px"; newCorner.style.borderLeft = this.borderString; newCorner.style.borderBottom = this.borderString; newCorner.style.left = -this.borderWidth + "px"; newCorner.style.backgroundPosition = "-" + (this.borderWidth) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidth)) + "px"; break; case "br":
newCorner.style.height = botMaxRadius - this.borderWidth + "px"; newCorner.style.marginLeft = this.settings.bl.radius - (this.borderWidth*2) + "px"; newCorner.style.borderRight = this.borderString; newCorner.style.borderBottom = this.borderString; newCorner.style.left = this.borderWidth + "px"
newCorner.style.backgroundPosition = "-" + (botMaxRadius + this.borderWidth) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidth)) + "px"; break;}
}
}
else
{ if(this.masterCorners[this.settings[cc].radius])
{ var newCorner = this.masterCorners[this.settings[cc].radius].cloneNode(true);}
else
{ var newCorner = document.createElement("DIV"); newCorner.style.height = this.settings[cc].radius + "px"; newCorner.style.width = this.settings[cc].radius + "px"; newCorner.style.position = "absolute"; newCorner.style.fontSize = "1px"; newCorner.style.overflow = "hidden"; var borderRadius = parseInt(this.settings[cc].radius - this.borderWidth); for(var intx = 0, j = this.settings[cc].radius; intx < j; intx++)
{ if((intx +1) >= borderRadius)
var y1 = -1; else
var y1 = (Math.floor(Math.sqrt(Math.pow(borderRadius, 2) - Math.pow((intx+1), 2))) - 1); if(borderRadius != j)
{ if((intx) >= borderRadius)
var y2 = -1; else
var y2 = Math.ceil(Math.sqrt(Math.pow(borderRadius,2) - Math.pow(intx, 2))); if((intx+1) >= j)
var y3 = -1; else
var y3 = (Math.floor(Math.sqrt(Math.pow(j ,2) - Math.pow((intx+1), 2))) - 1);}
if((intx) >= j)
var y4 = -1; else
var y4 = Math.ceil(Math.sqrt(Math.pow(j ,2) - Math.pow(intx, 2))); if(y1 > -1) this.drawPixel(intx, 0, this.boxColour, 100, (y1+1), newCorner, -1, this.settings[cc].radius); if(borderRadius != j)
{ for(var inty = (y1 + 1); inty < y2; inty++)
{ if(this.settings.antiAlias)
{ if(this.backgroundImage != "")
{ var borderFract = (pixelFraction(intx, inty, borderRadius) * 100); if(borderFract < 30)
{ this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, 0, this.settings[cc].radius);}
else
{ this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, -1, this.settings[cc].radius);}
}
else
{ var pixelcolour = BlendColour(this.boxColour, this.borderColour, pixelFraction(intx, inty, borderRadius)); this.drawPixel(intx, inty, pixelcolour, 100, 1, newCorner, 0, this.settings[cc].radius, cc);}
}
}
if(this.settings.antiAlias)
{ if(y3 >= y2)
{ if (y2 == -1) y2 = 0; this.drawPixel(intx, y2, this.borderColour, 100, (y3 - y2 + 1), newCorner, 0, 0);}
}
else
{ if(y3 >= y1)
{ this.drawPixel(intx, (y1 + 1), this.borderColour, 100, (y3 - y1), newCorner, 0, 0);}
}
var outsideColour = this.borderColour;}
else
{ var outsideColour = this.boxColour; var y3 = y1;}
if(this.settings.antiAlias)
{ for(var inty = (y3 + 1); inty < y4; inty++)
{ this.drawPixel(intx, inty, outsideColour, (pixelFraction(intx, inty , j) * 100), 1, newCorner, ((this.borderWidth > 0)? 0 : -1), this.settings[cc].radius);}
}
}
this.masterCorners[this.settings[cc].radius] = newCorner.cloneNode(true);}
if(cc != "br")
{ for(var t = 0, k = newCorner.childNodes.length; t < k; t++)
{ var pixelBar = newCorner.childNodes[t]; var pixelBarTop = parseInt(pixelBar.style.top.substring(0, pixelBar.style.top.indexOf("px"))); var pixelBarLeft = parseInt(pixelBar.style.left.substring(0, pixelBar.style.left.indexOf("px"))); var pixelBarHeight = parseInt(pixelBar.style.height.substring(0, pixelBar.style.height.indexOf("px"))); if(cc == "tl" || cc == "bl"){ pixelBar.style.left = this.settings[cc].radius -pixelBarLeft -1 + "px";}
if(cc == "tr" || cc == "tl"){ pixelBar.style.top = this.settings[cc].radius -pixelBarHeight -pixelBarTop + "px";}
switch(cc)
{ case "tr":
pixelBar.style.backgroundPosition = "-" + Math.abs((this.boxWidth - this.settings[cc].radius + this.borderWidth) + pixelBarLeft) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px"; break; case "tl":
pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px"; break; case "bl":
pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) + "px -" + Math.abs((this.boxHeight + this.settings[cc].radius + pixelBarTop) -this.borderWidth) + "px"; break;}
}
}
}
if(newCorner)
{ switch(cc)
{ case "tl":
if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; if(this.topContainer) this.topContainer.appendChild(newCorner); break; case "tr":
if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; if(this.topContainer) this.topContainer.appendChild(newCorner); break; case "bl":
if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); break; case "br":
if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); break;}
}
}
}
var radiusDiff = new Array(); radiusDiff["t"] = Math.abs(this.settings.tl.radius - this.settings.tr.radius)
radiusDiff["b"] = Math.abs(this.settings.bl.radius - this.settings.br.radius); for(z in radiusDiff)
{ if(z == "t" || z == "b")
{ if(radiusDiff[z])
{ var smallerCornerType = ((this.settings[z + "l"].radius < this.settings[z + "r"].radius)? z +"l" : z +"r"); var newFiller = document.createElement("DIV"); newFiller.style.height = radiusDiff[z] + "px"; newFiller.style.width = this.settings[smallerCornerType].radius+ "px"
newFiller.style.position = "absolute"; newFiller.style.fontSize = "1px"; newFiller.style.overflow = "hidden"; newFiller.style.backgroundColor = this.boxColour; switch(smallerCornerType)
{ case "tl":
newFiller.style.bottom = "0px"; newFiller.style.left = "0px"; newFiller.style.borderLeft = this.borderString; this.topContainer.appendChild(newFiller); break; case "tr":
newFiller.style.bottom = "0px"; newFiller.style.right = "0px"; newFiller.style.borderRight = this.borderString; this.topContainer.appendChild(newFiller); break; case "bl":
newFiller.style.top = "0px"; newFiller.style.left = "0px"; newFiller.style.borderLeft = this.borderString; this.bottomContainer.appendChild(newFiller); break; case "br":
newFiller.style.top = "0px"; newFiller.style.right = "0px"; newFiller.style.borderRight = this.borderString; this.bottomContainer.appendChild(newFiller); break;}
}
var newFillerBar = document.createElement("DIV"); newFillerBar.style.position = "relative"; newFillerBar.style.fontSize = "1px"; newFillerBar.style.overflow = "hidden"; newFillerBar.style.backgroundColor = this.boxColour; newFillerBar.style.backgroundImage = this.backgroundImage; switch(z)
{ case "t":
if(this.topContainer)
{ if(this.settings.tl.radius && this.settings.tr.radius)
{ newFillerBar.style.height = topMaxRadius - this.borderWidth + "px"; newFillerBar.style.marginLeft = this.settings.tl.radius - this.borderWidth + "px"; newFillerBar.style.marginRight = this.settings.tr.radius - this.borderWidth + "px"; newFillerBar.style.borderTop = this.borderString; if(this.backgroundImage != "")
newFillerBar.style.backgroundPosition = "-" + (topMaxRadius + this.borderWidth) + "px 0px"; this.topContainer.appendChild(newFillerBar);}
this.box.style.backgroundPosition = "0px -" + (topMaxRadius - this.borderWidth) + "px";}
break; case "b":
if(this.bottomContainer)
{ if(this.settings.bl.radius && this.settings.br.radius)
{ newFillerBar.style.height = botMaxRadius - this.borderWidth + "px"; newFillerBar.style.marginLeft = this.settings.bl.radius - this.borderWidth + "px"; newFillerBar.style.marginRight = this.settings.br.radius - this.borderWidth + "px"; newFillerBar.style.borderBottom = this.borderString; if(this.backgroundImage != "")
newFillerBar.style.backgroundPosition = "-" + (botMaxRadius + this.borderWidth) + "px -" + (this.boxHeight + (topMaxRadius + this.borderWidth)) + "px"; this.bottomContainer.appendChild(newFillerBar);}
}
break;}
}
}
if(this.settings.autoPad == true && this.boxPadding > 0)
{ var contentContainer = document.createElement("DIV"); contentContainer.style.position = "relative"; contentContainer.innerHTML = this.boxContent; contentContainer.className = "autoPadDiv"; var topPadding = Math.abs(topMaxRadius - this.boxPadding); var botPadding = Math.abs(botMaxRadius - this.boxPadding); if(topMaxRadius < this.boxPadding)
contentContainer.style.paddingTop = topPadding + "px"; if(botMaxRadius < this.boxPadding)
contentContainer.style.paddingBottom = botMaxRadius + "px"; contentContainer.style.paddingLeft = this.boxPadding + "px"; contentContainer.style.paddingRight = this.boxPadding + "px"; this.contentDIV = this.box.appendChild(contentContainer);}
}
this.drawPixel = function(intx, inty, colour, transAmount, height, newCorner, image, cornerRadius)
{ var pixel = document.createElement("DIV"); pixel.style.height = height + "px"; pixel.style.width = "1px"; pixel.style.position = "absolute"; pixel.style.fontSize = "1px"; pixel.style.overflow = "hidden"; var topMaxRadius = Math.max(this.settings["tr"].radius, this.settings["tl"].radius); if(image == -1 && this.backgroundImage != "")
{ pixel.style.backgroundImage = this.backgroundImage; pixel.style.backgroundPosition = "-" + (this.boxWidth - (cornerRadius - intx) + this.borderWidth) + "px -" + ((this.boxHeight + topMaxRadius + inty) -this.borderWidth) + "px";}
else
{ pixel.style.backgroundColor = colour;}
if (transAmount != 100)
setOpacity(pixel, transAmount); pixel.style.top = inty + "px"; pixel.style.left = intx + "px"; newCorner.appendChild(pixel);}
}
function insertAfter(parent, node, referenceNode)
{ parent.insertBefore(node, referenceNode.nextSibling);}
function BlendColour(Col1, Col2, Col1Fraction)
{ var red1 = parseInt(Col1.substr(1,2),16); var green1 = parseInt(Col1.substr(3,2),16); var blue1 = parseInt(Col1.substr(5,2),16); var red2 = parseInt(Col2.substr(1,2),16); var green2 = parseInt(Col2.substr(3,2),16); var blue2 = parseInt(Col2.substr(5,2),16); if(Col1Fraction > 1 || Col1Fraction < 0) Col1Fraction = 1; var endRed = Math.round((red1 * Col1Fraction) + (red2 * (1 - Col1Fraction))); if(endRed > 255) endRed = 255; if(endRed < 0) endRed = 0; var endGreen = Math.round((green1 * Col1Fraction) + (green2 * (1 - Col1Fraction))); if(endGreen > 255) endGreen = 255; if(endGreen < 0) endGreen = 0; var endBlue = Math.round((blue1 * Col1Fraction) + (blue2 * (1 - Col1Fraction))); if(endBlue > 255) endBlue = 255; if(endBlue < 0) endBlue = 0; return "#" + IntToHex(endRed)+ IntToHex(endGreen)+ IntToHex(endBlue);}
function IntToHex(strNum)
{ base = strNum / 16; rem = strNum % 16; base = base - (rem / 16); baseS = MakeHex(base); remS = MakeHex(rem); return baseS + '' + remS;}
function MakeHex(x)
{ if((x >= 0) && (x <= 9))
{ return x;}
else
{ switch(x)
{ case 10: return "A"; case 11: return "B"; case 12: return "C"; case 13: return "D"; case 14: return "E"; case 15: return "F";}
}
}
function pixelFraction(x, y, r)
{ var pixelfraction = 0; var xvalues = new Array(1); var yvalues = new Array(1); var point = 0; var whatsides = ""; var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x,2))); if ((intersect >= y) && (intersect < (y+1)))
{ whatsides = "Left"; xvalues[point] = 0; yvalues[point] = intersect - y; point = point + 1;}
var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y+1,2))); if ((intersect >= x) && (intersect < (x+1)))
{ whatsides = whatsides + "Top"; xvalues[point] = intersect - x; yvalues[point] = 1; point = point + 1;}
var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x+1,2))); if ((intersect >= y) && (intersect < (y+1)))
{ whatsides = whatsides + "Right"; xvalues[point] = 1; yvalues[point] = intersect - y; point = point + 1;}
var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y,2))); if ((intersect >= x) && (intersect < (x+1)))
{ whatsides = whatsides + "Bottom"; xvalues[point] = intersect - x; yvalues[point] = 0;}
switch (whatsides)
{ case "LeftRight":
pixelfraction = Math.min(yvalues[0],yvalues[1]) + ((Math.max(yvalues[0],yvalues[1]) - Math.min(yvalues[0],yvalues[1]))/2); break; case "TopRight":
pixelfraction = 1-(((1-xvalues[0])*(1-yvalues[1]))/2); break; case "TopBottom":
pixelfraction = Math.min(xvalues[0],xvalues[1]) + ((Math.max(xvalues[0],xvalues[1]) - Math.min(xvalues[0],xvalues[1]))/2); break; case "LeftBottom":
pixelfraction = (yvalues[0]*xvalues[1])/2; break; default:
pixelfraction = 1;}
return pixelfraction;}
function rgb2Hex(rgbColour)
{ try{ var rgbArray = rgb2Array(rgbColour); var red = parseInt(rgbArray[0]); var green = parseInt(rgbArray[1]); var blue = parseInt(rgbArray[2]); var hexColour = "#" + IntToHex(red) + IntToHex(green) + IntToHex(blue);}
catch(e){ alert("There was an error converting the RGB value to Hexadecimal in function rgb2Hex");}
return hexColour;}
function rgb2Array(rgbColour)
{ var rgbValues = rgbColour.substring(4, rgbColour.indexOf(")")); var rgbArray = rgbValues.split(", "); return rgbArray;}
function setOpacity(obj, opacity)
{ opacity = (opacity == 100)?99.999:opacity; if(isSafari && obj.tagName != "IFRAME")
{ var rgbArray = rgb2Array(obj.style.backgroundColor); var red = parseInt(rgbArray[0]); var green = parseInt(rgbArray[1]); var blue = parseInt(rgbArray[2]); obj.style.backgroundColor = "rgba(" + red + ", " + green + ", " + blue + ", " + opacity/100 + ")";}
else if(typeof(obj.style.opacity) != "undefined")
{ obj.style.opacity = opacity/100;}
else if(typeof(obj.style.MozOpacity) != "undefined")
{ obj.style.MozOpacity = opacity/100;}
else if(typeof(obj.style.filter) != "undefined")
{ obj.style.filter = "alpha(opacity:" + opacity + ")";}
else if(typeof(obj.style.KHTMLOpacity) != "undefined")
{ obj.style.KHTMLOpacity = opacity/100;}
}
function inArray(array, value)
{ for(var i = 0; i < array.length; i++){ if (array[i] === value) return i;}
return false;}
function inArrayKey(array, value)
{ for(key in array){ if(key === value) return true;}
return false;}
function addEvent(elm, evType, fn, useCapture) { if (elm.addEventListener) { elm.addEventListener(evType, fn, useCapture); return true;}
else if (elm.attachEvent) { var r = elm.attachEvent('on' + evType, fn); return r;}
else { elm['on' + evType] = fn;}
}
function removeEvent(obj, evType, fn, useCapture){ if (obj.removeEventListener){ obj.removeEventListener(evType, fn, useCapture); return true;} else if (obj.detachEvent){ var r = obj.detachEvent("on"+evType, fn); return r;} else { alert("Handler could not be removed");}
}
function format_colour(colour)
{ var returnColour = "#ffffff"; if(colour != "" && colour != "transparent")
{ if(colour.substr(0, 3) == "rgb")
{ returnColour = rgb2Hex(colour);}
else if(colour.length == 4)
{ returnColour = "#" + colour.substring(1, 2) + colour.substring(1, 2) + colour.substring(2, 3) + colour.substring(2, 3) + colour.substring(3, 4) + colour.substring(3, 4);}
else
{ returnColour = colour;}
}
return returnColour;}
function get_style(obj, property, propertyNS)
{ try
{ if(obj.currentStyle)
{ var returnVal = eval("obj.currentStyle." + property);}
else
{ if(isSafari && obj.style.display == "none")
{ obj.style.display = ""; var wasHidden = true;}
var returnVal = document.defaultView.getComputedStyle(obj, '').getPropertyValue(propertyNS); if(isSafari && wasHidden)
{ obj.style.display = "none";}
}
}
catch(e)
{ }
return returnVal;}
function getElementsByClass(searchClass, node, tag)
{ var classElements = new Array(); if(node == null)
node = document; if(tag == null)
tag = '*'; var els = node.getElementsByTagName(tag); var elsLen = els.length; var pattern = new RegExp("(^|\s)"+searchClass+"(\s|$)"); for (i = 0, j = 0; i < elsLen; i++)
{ if(pattern.test(els[i].className))
{ classElements[j] = els[i]; j++;}
}
return classElements;}
function newCurvyError(errorMessage)
{ return new Error("curvyCorners Error:\n" + errorMessage)
}
/*	Slimbox (Extended Version 1.3.1, 2007-02-21)
by Yukio Arita (http://homepage.mac.com/yukikun/software/slimbox_ex/)
- 	Support to show external content using iframe.
- 	Support to set content size. You can add width/height parameters
in rev attribute of the anchor url.
ex1) <a href="image.jpg" rev="width=50%, height=50%" rel="lightbox">
<img src="image_thumb.jpg" alt="image"></a>
ex2) <a href="text.html" rev="width=500, height=300" rel="lightbox">
some text here</a>
- 	Some rendering problem with IE6 is fixed. Now you can use Slimbox in
valid XHTML document with XML prolog.
- Of course, license is same as original.
Based on:
Slimbox v1.3 - The ultimate lightweight Lightbox clone
by Christophe Beyls (http://www.digitalia.be) - MIT-style license.
Inspired by the original Lightbox v2 by Lokesh Dhakar.*/
var Lightbox = {
init: function(options) {
this.options = Object.extend({
resizeDuration: 400,
resizeTransition: Fx.Transitions.sineInOut,
initialWidth: 250,
initialHeight: 250,
animateCaption: true,
defaultIframeWidth : 500,
defaultIframeHeight: 300
}, options || {});
// IE 6 - XML prolog problem
if (Browser.ie6 && document.compatMode == "BackCompat") {
this.options.animateCaption = false;
}
this.anchors = [];
Array.each(document.links, function(el) {
if (el.rel && el.rel.test(/^lightbox/i)) {
el.onclick = this.click.pass(el, this);
this.anchors.push(el);
}
}, this);
//        this.eventKeyDown = this.keyboardListener.bindWithEvent(this);
this.eventPosition = this.position.bind(this);
this.overlay = new Element('div').setProperty('id', 'lbOverlay').inject(document.body);
this.center = new Element('div').setProperty('id', 'lbCenter').setStyles({width: this.options.initialWidth + 'px', height: this.options.initialHeight + 'px', marginLeft: '-' + (this.options.initialWidth / 2) + 'px', display: 'none'}).inject(document.body);
this.canvas = new Element('div').setProperty('id', 'lbCanvas').inject(this.center);
this.prevLink = new Element('a').setProperties({id: 'lbPrevLink', href: '#'}).setStyle('display', 'none').inject(this.canvas);
this.nextLink = this.prevLink.clone().setProperty('id', 'lbNextLink').inject(this.canvas);
this.prevLink.onclick = this.previous.bind(this);
this.nextLink.onclick = this.next.bind(this);
this.bottomContainer = new Element('div').setProperty('id', 'lbBottomContainer').setStyle('display', 'none').inject(document.body);
this.bottom = new Element('div').setProperty('id', 'lbBottom').inject(this.bottomContainer);
new Element('a').setProperties({id: 'lbCloseLink', href: '#'}).inject(this.bottom).onclick = this.overlay.onclick = this.close.bind(this);
this.caption = new Element('div').setProperty('id', 'lbCaption').inject(this.bottom);
this.number = new Element('div').setProperty('id', 'lbNumber').inject(this.bottom);
new Element('div').setStyle('clear', 'both').inject(this.bottom);
// Build effects
var nextEffect = this.nextEffect.bind(this);
this.fx = {
overlay: new Fx.Tween(this.overlay , {
duration: 500,
property: 'opacity'
}),
resizeCenter: new Fx.Morph(this.center , {
duration: this.options.resizeDuration,
transition: this.options.resizeTransition,
onComplete: nextEffect
}),
image: new Fx.Tween(this.canvas , {
duration: 500,
onComplete: nextEffect,
property: 'opacity'
}),
bottom:  new Fx.Tween(this.bottomContainer, {
duration: 400,
onComplete: nextEffect,
property: 'opacity'
})
};
this.fx.overlay.set(0);
this.preloadPrev = new Image();
this.preloadNext = new Image();
},
click: function(link) {
if (link.rel.length == 8) return this.show(link.href, link.title, link.rev);
var j, itemNumber, items = [];
this.anchors.each(function(el) {
if (el.rel == link.rel) {
for (j = 0; j < items.length; j++) if (items[j][0] == el.href && items[j][2] == el.rev) break;
if (j == items.length) {
items.push([el.href, el.title, el.rev]);
if (el.href == link.href && el.rev == link.rev) itemNumber = j;
}
}
}, this);
return this.open(items, itemNumber);
},
show: function(url, title, rev) {
return this.open([[url, title, rev]], 0);
},
open: function(items, itemNumber) {
this.items = items;
this.position();
this.setup(true);
var wh = (window.getHeight() == 0) ? window.getScrollHeight() : window.getHeight();
var st = document.body.scrollTop || document.documentElement.scrollTop;
var rev = this.items[0][2];
var height = this.matchOrDefault(rev, new RegExp("height=(\\d+)", "i"), 600);
this.top = st + ((wh - (height)) / 2) - 30;
this.center.setStyles({top: this.top + 'px', display: ''});
this.fx.overlay.start(0.8);
return this.changeItem(itemNumber);
},
position: function() {
//IE6 - XML prolog problem.
var ww = (window.getWidth() == 0) ? window.getScrollWidth() - 22 : window.getWidth();
var wh = (window.getHeight() == 0) ? window.getScrollHeight() : window.getHeight();
var st = document.body.scrollTop || document.documentElement.scrollTop;
this.overlay.setStyles({top: st + 'px', height: wh + 'px', width:ww + 'px'});
},
setup: function(open) {
var elements = Array.from(document.getElementsByTagName('object'));
if (Browser.ie6) elements.extend(document.getElementsByTagName('select'));
elements.each(function(el) {
el.style.visibility = open ? 'hidden' : '';
});
var fn = open ? 'addEvent' : 'removeEvent';
window[fn]('scroll', this.eventPosition)[fn]('resize', this.eventPosition);
document[fn]('keydown', this.keyboardListener.bind(this));
this.step = 0;
},
keyboardListener: function(event) {
switch (event.keyCode) {
case 27: case 88: case 67: this.close(); break;
case 37: case 80: this.previous(); break;
case 39: case 78: this.next();
}
},
previous: function() {
return this.changeItem(this.activeItem - 1);
},
next: function() {
return this.changeItem(this.activeItem + 1);
},
changeItem: function(itemNumber) {
if (this.step || (itemNumber < 0) || (itemNumber >= this.items.length)) return false;
this.step = 1;
this.activeItem = itemNumber;
this.bottomContainer.style.display = this.prevLink.style.display = this.nextLink.style.display = 'none';
this.fx.image.set(0);
this.center.className = 'lbLoading';
// discard previous content by clicking
this.removeCurrentItem();
// check item type
var url = this.items[this.activeItem][0];
var rev = this.items[this.activeItem][2];
var re_imageURL = /\.(jpe?g|png|gif|bmp)/i;
var re_flashURL = /\.(swf)/i;
var re_youtubeURL = /https?\:\/\/(?:www\.){0,1}youtube\.com\/watch\?v=(.*)?/i;
var re_vimeoURL = /https?\:\/\/(?:www\.){0,1}vimeo\.com\/(.*)?/i;
var re_dailymotionURL = /^https?\:\/\/(?:www\.){0,1}dailymotion\.com\/video\/(.*?)\_/i;
var re_streamURL = /stream\.(php)/i;
var re_externalStreamURL = /^https?\:\/\/(.*).(mp4|flv)/i;
var re_downloadURL = /download\.php\?ressource=(.*)\&filename=(.*)/i;
var re_hiddenDivURL = /\{hiddenId\|(\w+)\}/i;
var re_googleMapsUrl = /^https?:\/\/maps.google.(\w+)/i;
var matches = null;
if (url.match(re_imageURL)) {
this.preload = new Image();	// JavaScript native Object
this.preload.datatype = 'image';
this.preload.w = this.matchOrDefault(rev, new RegExp("width=(\\d+%?)", "i"), -1); //-1 if use original size.
this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+%?)", "i"), -1);
this.preload.onload = this.nextEffect.bind(this);
this.preload.src = url;
} else if (url.match(re_flashURL)) {
this.preload = new Object();	// JavaScript native Object
this.preload.datatype = 'flash';
this.preload.w = this.matchOrDefault(rev, new RegExp("width=(\\d+)", "i"), 425);
this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+)", "i"), 355);
this.preload.src = url;
this.nextEffect(); //asynchronous loading
} else if (url.match(re_streamURL)) {
this.preload = new Object();	// JavaScript native Object
this.preload.datatype = 'stream';
this.preload.w = this.matchOrDefault(rev, new RegExp("width=(\\d+)", "i"), 425);
this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+)", "i"), 355);
this.preload.src = url;
this.nextEffect(); //asynchronous loading
} else if (matches = url.match(re_downloadURL)) {
this.preload = new Object();	// JavaScript native Object
if (matches[2].match(/\.flv/) || matches[2].match(/\.mp4/)) {
this.preload.src = 'stream.php?media=' + matches[2];
this.preload.datatype = 'stream';
} else {
this.preload.src = 'ressource.php?media=' + matches[2];
this.preload.datatype = 'iframe';
}
this.preload.w = this.matchOrDefault(rev, new RegExp("width=(\\d+)", "i"), 425);
this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+)", "i"), 355);
this.nextEffect(); //asynchronous loading
} else if (matches = url.match(re_youtubeURL)) {
this.preload = new Object();	// JavaScript native Object
this.preload.datatype = 'youtube';
this.preload.w = this.matchOrDefault(rev, new RegExp("width=(\\d+)", "i"), this.options.defaultIframeWidth);
this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+)", "i"), this.options.defaultIframeHeight);
this.preload.src = matches[1];
this.nextEffect(); //asynchronous loading
} else if (matches = url.match(re_vimeoURL)) {
this.preload = new Object();	// JavaScript native Object
this.preload.datatype = 'vimeo';
this.preload.w = this.matchOrDefault(rev, new RegExp("width=(\\d+)", "i"), this.options.defaultIframeWidth);
this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+)", "i"), this.options.defaultIframeHeight);
this.preload.src = matches[1];
this.nextEffect(); //asynchronous loading
} else if (matches = url.match(re_dailymotionURL)) {
this.preload = new Object();	// JavaScript native Object
this.preload.datatype = 'dailymotion';
this.preload.w = this.matchOrDefault(rev, new RegExp("width=(\\d+)", "i"), this.options.defaultIframeWidth);
this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+)", "i"), this.options.defaultIframeHeight);
this.preload.src = matches[1];
this.nextEffect();
} else if (matches = url.match(re_hiddenDivURL)) {
this.preload = new Object();	// JavaScript native Object
this.preload.datatype = 'hidden';
this.preload.src = matches[1];
this.preload.w = this.matchOrDefault(rev, new RegExp("width=(\\d+)", "i"), this.options.defaultIframeWidth);
this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+)", "i"), this.options.defaultIframeHeight);
this.nextEffect(); //asynchronous loading
}else if(matches = url.match(re_externalStreamURL)){
this.preload = new Object();	// JavaScript native Object
this.preload.src = url;
this.preload.datatype = 'stream';
this.preload.w = this.matchOrDefault(rev, new RegExp("width=(\\d+)", "i"), 425);
this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+)", "i"), 355);
this.nextEffect(); //asynchronous loading
} else if(matches = url.match(re_googleMapsUrl)) {
this.preload = new Object();	// JavaScript native Object
this.preload.datatype = 'iframe';
this.preload.w = this.matchOrDefault(rev, new RegExp("width=(\\d+)", "i"), this.options.defaultIframeWidth);
this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+)", "i"), this.options.defaultIframeHeight);
this.preload.src = url + '&output=embed';
this.nextEffect(); //asynchronous loading
} else {
this.preload = new Object();	// JavaScript native Object
this.preload.datatype = 'iframe';
this.preload.w = this.matchOrDefault(rev, new RegExp("width=(\\d+)", "i"), this.options.defaultIframeWidth);
this.preload.h = this.matchOrDefault(rev, new RegExp("height=(\\d+)", "i"), this.options.defaultIframeHeight);
this.preload.src = url;
this.nextEffect(); //asynchronous loading
}
return false;
},
nextEffect: function() {
switch (this.step++) {
case 1:
this.center.className = '';
var playerContainer;
// create HTML element
if (this.preload.datatype == 'image') {
var ws = (this.preload.w == -1) ? this.preload.width.toString() : this.preload.w.toString();
var hs = (this.preload.h == -1) ? this.preload.height.toString() : this.preload.h.toString();
this.p_width = ( q = ws.match(/(\d+)%/) ) ? q[1] * this.preload.width * 0.01 : ws;
this.p_height = ( q = hs.match(/(\d+)%/) ) ? q[1] * this.preload.height * 0.01 : hs;
new Element('img').setProperties({id: 'lbImage', src:this.preload.src, width:this.p_width, height:this.p_height}).inject(this.canvas);
this.nextLink.style.right = '';
} else if (this.preload.datatype == 'youtube') {
this.p_width = (this.preload.w >= window.getWidth()) ? window.getWidth() - 60 : this.preload.w;
this.p_height = (this.preload.h >= window.getHeight()) ? window.getHeight() - 60 : this.preload.h;
// Safari would not update iframe content that has static id.
this.iframeId = "lbFrame_" + new Date().getTime();
try {
new Element('iframe').setProperties({id: this.iframeId, width: this.p_width, height: this.p_height, frameBorder:0, scrolling:'auto', src:'//www.youtube.com/embed/' + this.preload.src}).inject(this.canvas);
} catch (ex) {
}
this.nextLink.style.right = '25px';
//                    this.p_width = this.preload.w;
//                    this.p_height = this.preload.h;
//                    playerContainer =  new Element('div').setProperties({id:'lbYoutube'}).inject(this.canvas);
//                    new Element('div').setProperties({id:'lbYoutubePlayer'}).inject(playerContainer);
//                    swfobject.embedSWF('http://www.youtube.com/embed/' + this.preload.src + '&rel=0&autoplay=1&enablejsapi=1&playerapiid=lbYoutube', "lbYoutubePlayer", this.p_width, this.p_height, "8.0.0", "Themes/Common/Components/Video/Flash/expressInstall.swf", '', '', '');
} else if (this.preload.datatype == 'vimeo') {
this.p_width = (this.preload.w >= window.getWidth()) ? window.getWidth() - 60 : this.preload.w;
this.p_height = (this.preload.h >= window.getHeight()) ? window.getHeight() - 60 : this.preload.h;
// Safari would not update iframe content that has static id.
this.iframeId = "lbFrame_" + new Date().getTime();
try {
new Element('iframe').setProperties({id: this.iframeId, width: this.p_width, height: this.p_height, frameBorder:'0', allow:'fullscreen', src:'https://player.vimeo.com/video/' + this.preload.src}).inject(this.canvas);
} catch (ex) {
}
this.nextLink.style.right = '25px';
}
else if (this.preload.datatype == 'dailymotion') {
this.p_width = this.preload.w;
this.p_height = this.preload.h;
playerContainer =  new Element('div').setProperties({id:'lbDailymotion'}).inject(this.canvas);
new Element('div').setProperties({id:'lbDailymotionPlayer'}).inject(playerContainer);
swfobject.embedSWF('http://www.dailymotion.com/swf/' + this.preload.src + '&v3=1&related=0&autoplay=1', "lbDailymotionPlayer", this.p_width, this.p_height, "8.0.0", "Themes/Common/Components/Video/Flash/expressInstall.swf", '', '', '');
} else if (this.preload.datatype == 'stream') {
this.p_width = this.preload.w;
this.p_height = this.preload.h;
var flashvars = {
"type":"flv",
"displayheight":this.p_height,
"file":this.preload.src,
"enablejs":"true",
"overstretch":"true"
};
var params = {
"wMode":"transparent"
};
playerContainer =  new Element('div').setProperties({id:'lbStream'}).inject(this.canvas);
new Element('div').setProperties({id:'lbStreamPlayer'}).inject(playerContainer);
swfobject.embedSWF(window.getCurrentPageLocation() + 'Themes/Default/Components/Video/Flash/mediaplayer.swf', "lbStreamPlayer", this.p_width, this.p_height, "8.0.0", "Themes/Common/Components/Video/Flash/expressInstall.swf", flashvars, params, '');
} else if (this.preload.datatype == 'flash') {
this.p_width = this.preload.w;
this.p_height = this.preload.h;
playerContainer =  new Element('div').setProperties({id:'lbFlash'}).inject(this.canvas);
new Element('div').setProperties({id:'lbFlashPlayer'}).inject(playerContainer);
swfobject.embedSWF(this.preload.src, "lbFlashPlayer", this.p_width, this.p_height, "8.0.0", "Themes/Common/Components/Video/Flash/expressInstall.swf", '', '', '');
} else if (this.preload.datatype == 'googleMap') {
this.p_width = this.preload.w;
this.p_height = this.preload.h;
// Safari would not update iframe content that has static id.
this.iframeId = "mapContainer";
new Element('iframe').setProperties({id: this.iframeId, width: this.p_width, height: this.p_height, frameBorder:0, scrolling:'auto', src:this.preload.src + '&output=embed&s=AARTsJrrJfAezwFwvFA6u8OWSecjpwNIVw'}).inject(this.canvas);
this.nextLinkscr.style.right = '25px';
} else if (this.preload.datatype == 'hidden') {
this.p_width = this.preload.w;
this.p_height = this.preload.h;
this.iframeId = this.preload.src;
$(this.iframeId).setStyles({left:'0px',top:'0px',height:this.p_height,width:this.p_width,display:'block'});
$(this.preload.src).setProperties({width: this.p_width, height: this.p_height, frameBorder:0, scrolling:'auto'}).inject(this.canvas);
this.nextLink.style.right = '25px';
} else {
this.p_width = this.preload.w;
this.p_height = this.preload.h;
// Safari would not update iframe content that has static id.
this.iframeId = "lbFrame_" + new Date().getTime();
try {
new Element('iframe').setProperties({id: this.iframeId, width: this.p_width, height: this.p_height, frameBorder:0, scrolling:'auto', src:this.preload.src}).inject(this.canvas);
} catch (ex) {
}
this.nextLink.style.right = '25px';
}
this.canvas.style.width = this.bottom.style.width = this.p_width + 'px';
this.canvas.style.height = this.prevLink.style.height = this.nextLink.style.height = this.p_height + 'px';
this.caption.set('html',this.items[this.activeItem][1] || '');
this.number.set('html',(this.items.length == 1) ? '' : 'Page ' + (this.activeItem + 1) + ' of ' + this.items.length);
if (this.activeItem) this.preloadPrev.src = this.items[this.activeItem - 1][0];
if (this.activeItem != (this.items.length - 1)) this.preloadNext.src = this.items[this.activeItem + 1][0];
if (this.center.clientHeight != this.canvas.offsetHeight) {
var oh = (this.p_height == this.canvas.clientHeight) ? this.canvas.offsetHeight : eval(this.p_height) + 18; // fix for ie
this.fx.resizeCenter.start({height: oh});
break;
}
this.step++;
case 2:
if (this.center.clientWidth != this.canvas.offsetWidth) {
var ow = (this.p_width == this.canvas.clientWidth) ? this.canvas.offsetWidth : eval(this.p_width) + 18; // fix for ie
this.fx.resizeCenter.start({width: ow, marginLeft: -ow / 2});
break;
}
this.step++;
case 3:
this.bottomContainer.setStyles({top: (this.top + this.center.clientHeight) + 'px', height:'30px', marginLeft: this.center.style.marginLeft, width:this.center.style.width, display: ''});
this.fx.image.start(1);
break;
case 4:
if (this.options.animateCaption) {
// This is not smooth animation in IE 6 with XML prolog.
// If your site is XHTML strict with XML prolog, disable this option.
this.fx.bottom.start(0, this.bottom.offsetHeight + 10);
break;
}
this.bottomContainer.style.height = (this.bottom.offsetHeight + 10) + 'px';
case 5:
if (this.activeItem) {
this.prevLink.style.display = '';
}
if (this.activeItem != (this.items.length - 1)) {
this.nextLink.style.display = '';
}
// we try to start video players
if (this.preload.datatype == 'stream') {
try {
$("lbStreamPlayer").sendEvent('playpause');
} catch(ex) {
}
} else if (this.preload.datatype == 'youtube') {
try {
//                        $("lbYoutube").playVideo();
} catch(ex) {
}
}
window.fireEvent('slimboxready');
this.step = 0;
}
},
close: function() {
if (this.step < 0) return;
this.step = -1;
this.removeCurrentItem();	// discard content
for (var f in this.fx) this.fx[f].cancel();
this.center.style.display = this.bottomContainer.style.display = 'none';
this.center.style.width = this.options.initialWidth + 'px';
this.center.style.height = this.options.initialHeight + 'px';
this.center.style.marginLeft = '-' + (this.options.initialWidth / 2) + 'px';
this.fx.overlay.chain(this.setup.pass(false, this)).start(0);
return false;
},
removeCurrentItem: function() {
if (this.preload) {
try {
if (this.preload.datatype == 'image') {
$('lbImage').dispose();
this.preload.onload = Class.empty;
//                this.preload = null;
} else if (this.preload.datatype == 'youtube') {
$(this.iframeId).dispose();
//$('lbYoutubePlayer').parentNode.removeChild($('lbYoutubePlayer'));
//                this.preload = null;
} else if (this.preload.datatype == 'vimeo') {
$(this.iframeId).dispose();
} else if (this.preload.datatype == 'dailymotion') {
$('lbDailymotionPlayer').parentNode.removeChild($('lbDailymotionPlayer'));
//                this.preload = null;
} else if (this.preload.datatype == 'flash') {
$('lbFlashPlayer').parentNode.removeChild($('lbFlashPlayer'));
} else if (this.preload.datatype == 'stream') {
$('lbStreamPlayer').parentNode.removeChild($('lbStreamPlayer'));
} else if (this.preload.datatype == 'googleMap') {
$(this.iframeId).dispose();
} else if (this.preload.datatype == 'hidden') {
$(this.iframeId).setStyle('display', 'none');
} else {
$(this.iframeId).dispose();
}
this.preload = null;
} catch (ex) {}
}
},
matchOrDefault: function(str, re, val) {
if (!str) {
return val;
}
var hasQuery = str.match(re);
return hasQuery ? hasQuery[1] : val;
}
};
window.addEvent('domready', Lightbox.init.bind(Lightbox));
if(Browser.ie6)
window.addEvent("load",Lightbox.init.bind(Lightbox));
/*	SWFObject v2.2 <http://code.google.com/p/swfobject/>
is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
var swfobject = function()
{
var D = "undefined",r = "object",S = "Shockwave Flash",W = "ShockwaveFlash.ShockwaveFlash",q = "application/x-shockwave-flash",R = "SWFObjectExprInst",x = "onreadystatechange",O = window,j = document,t = navigator,T = false,U = [h],o = [],N = [],I = [],l,Q,E,B,J = false,a = false,n,G,m = true,M = function()
{
var aa = typeof j.getElementById != D && typeof j.getElementsByTagName != D && typeof j.createElement != D,ah = t.userAgent.toLowerCase(),Y = t.platform.toLowerCase(),ae = Y ? /win/.test(Y) : /win/.test(ah),ac = Y ? /mac/.test(Y) : /mac/.test(ah),af = /webkit/.test(ah) ? parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false,X = !+"\v1",ag = [0,0,0],ab = null;
if (typeof t.plugins != D && typeof t.plugins[S] == r)
{
ab = t.plugins[S].description;
if (ab && !(typeof t.mimeTypes != D && t.mimeTypes[q] && !t.mimeTypes[q].enabledPlugin))
{
T = true;
X = false;
ab = ab.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
ag[0] = parseInt(ab.replace(/^(.*)\..*$/, "$1"), 10);
ag[1] = parseInt(ab.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
ag[2] = /[a-zA-Z]/.test(ab) ? parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0
}
}
else
{
if (typeof O.ActiveXObject != D)
{
try
{
var ad = new ActiveXObject(W);
if (ad)
{
ab = ad.GetVariable("$version");
if (ab)
{
X = true;
ab = ab.split(" ")[1].split(",");
ag = [parseInt(ab[0], 10),parseInt(ab[1], 10),parseInt(ab[2], 10)]
}
}
}
catch(Z)
{}
}
}
return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}
}(),k = function()
{
if (!M.w3)
{return}
if ((typeof j.readyState != D && j.readyState == "complete") || (typeof j.readyState == D && (j.getElementsByTagName("body")[0] || j.body)))
{f()}
if (!J)
{
if (typeof j.addEventListener != D)
{j.addEventListener("DOMContentLoaded", f, false)}
if (M.ie && M.win)
{
j.attachEvent(x, function()
{
if (j.readyState == "complete")
{
j.detachEvent(x, arguments.callee);
f()
}
});
if (O == top)
{
(function()
{
if (J)
{return}
try
{j.documentElement.doScroll("left")}
catch(X)
{
setTimeout(arguments.callee, 0);
return
}
f()
})()
}
}
if (M.wk)
{
(function()
{
if (J)
{return}
if (!/loaded|complete/.test(j.readyState))
{
setTimeout(arguments.callee, 0);
return
}
f()
})()
}
s(f)
}
}();
function f()
{
if (J)
{return}
try
{
var Z = j.getElementsByTagName("body")[0].appendChild(C("span"));
Z.parentNode.removeChild(Z)
}
catch(aa)
{return}
J = true;
var X = U.length;
for (var Y = 0; Y < X; Y++)
{U[Y]()}
}
function K(X)
{
if (J)
{X()}
else
{U[U.length] = X}
}
function s(Y)
{
if (typeof O.addEventListener != D)
{O.addEventListener("load", Y, false)}
else
{
if (typeof j.addEventListener != D)
{j.addEventListener("load", Y, false)}
else
{
if (typeof O.attachEvent != D)
{i(O, "onload", Y)}
else
{
if (typeof O.onload == "function")
{
var X = O.onload;
O.onload = function()
{
X();
Y()
}
}
else
{O.onload = Y}
}
}
}
}
function h()
{
if (T)
{V()}
else
{H()}
}
function V()
{
var X = j.getElementsByTagName("body")[0];
var aa = C(r);
aa.setAttribute("type", q);
var Z = X.appendChild(aa);
if (Z)
{
var Y = 0;
(function()
{
if (typeof Z.GetVariable != D)
{
var ab = Z.GetVariable("$version");
if (ab)
{
ab = ab.split(" ")[1].split(",");
M.pv = [parseInt(ab[0], 10),parseInt(ab[1], 10),parseInt(ab[2], 10)]
}
}
else
{
if (Y < 10)
{
Y++;
setTimeout(arguments.callee, 10);
return
}
}
X.removeChild(aa);
Z = null;
H()
})()
}
else
{H()}
}
function H()
{
var ag = o.length;
if (ag > 0)
{
for (var af = 0; af < ag; af++)
{
var Y = o[af].id;
var ab = o[af].callbackFn;
var aa = {success:false,id:Y};
if (M.pv[0] > 0)
{
var ae = c(Y);
if (ae)
{
if (F(o[af].swfVersion) && !(M.wk && M.wk < 312))
{
w(Y, true);
if (ab)
{
aa.success = true;
aa.ref = z(Y);
ab(aa)
}
}
else
{
if (o[af].expressInstall && A())
{
var ai = {};
ai.data = o[af].expressInstall;
ai.width = ae.getAttribute("width") || "0";
ai.height = ae.getAttribute("height") || "0";
if (ae.getAttribute("class"))
{ai.styleclass = ae.getAttribute("class")}
if (ae.getAttribute("align"))
{ai.align = ae.getAttribute("align")}
var ah = {};
var X = ae.getElementsByTagName("param");
var ac = X.length;
for (var ad = 0; ad < ac; ad++)
{
if (X[ad].getAttribute("name").toLowerCase() != "movie")
{ah[X[ad].getAttribute("name")] = X[ad].getAttribute("value")}
}
P(ai, ah, Y, ab)
}
else
{
p(ae);
if (ab)
{ab(aa)}
}
}
}
}
else
{
w(Y, true);
if (ab)
{
var Z = z(Y);
if (Z && typeof Z.SetVariable != D)
{
aa.success = true;
aa.ref = Z
}
ab(aa)
}
}
}
}
}
function z(aa)
{
var X = null;
var Y = c(aa);
if (Y && Y.nodeName == "OBJECT")
{
if (typeof Y.SetVariable != D)
{X = Y}
else
{
var Z = Y.getElementsByTagName(r)[0];
if (Z)
{X = Z}
}
}
return X
}
function A()
{return !a && F("6.0.65") && (M.win || M.mac) && !(M.wk && M.wk < 312)}
function P(aa, ab, X, Z)
{
a = true;
E = Z || null;
B = {success:false,id:X};
var ae = c(X);
if (ae)
{
if (ae.nodeName == "OBJECT")
{
l = g(ae);
Q = null
}
else
{
l = ae;
Q = X
}
aa.id = R;
if (typeof aa.width == D || (!/%$/.test(aa.width) && parseInt(aa.width, 10) < 310))
{aa.width = "310"}
if (typeof aa.height == D || (!/%$/.test(aa.height) && parseInt(aa.height, 10) < 137))
{aa.height = "137"}
j.title = j.title.slice(0, 47) + " - Flash Player Installation";
var ad = M.ie && M.win ? "ActiveX" : "PlugIn",ac = "MMredirectURL=" + O.location.toString().replace(/&/g, "%26") + "&MMplayerType=" + ad + "&MMdoctitle=" + j.title;
if (typeof ab.flashvars != D)
{ab.flashvars += "&" + ac}
else
{ab.flashvars = ac}
if (M.ie && M.win && ae.readyState != 4)
{
var Y = C("div");
X += "SWFObjectNew";
Y.setAttribute("id", X);
ae.parentNode.insertBefore(Y, ae);
ae.style.display = "none";
(function()
{
if (ae.readyState == 4)
{ae.parentNode.removeChild(ae)}
else
{setTimeout(arguments.callee, 10)}
})()
}
u(aa, ab, X)
}
}
function p(Y)
{
if (M.ie && M.win && Y.readyState != 4)
{
var X = C("div");
Y.parentNode.insertBefore(X, Y);
X.parentNode.replaceChild(g(Y), X);
Y.style.display = "none";
(function()
{
if (Y.readyState == 4)
{Y.parentNode.removeChild(Y)}
else
{setTimeout(arguments.callee, 10)}
})()
}
else
{Y.parentNode.replaceChild(g(Y), Y)}
}
function g(ab)
{
var aa = C("div");
if (M.win && M.ie)
{aa.innerHTML = ab.innerHTML}
else
{
var Y = ab.getElementsByTagName(r)[0];
if (Y)
{
var ad = Y.childNodes;
if (ad)
{
var X = ad.length;
for (var Z = 0; Z < X; Z++)
{
if (!(ad[Z].nodeType == 1 && ad[Z].nodeName == "PARAM") && !(ad[Z].nodeType == 8))
{aa.appendChild(ad[Z].cloneNode(true))}
}
}
}
}
return aa
}
function u(ai, ag, Y)
{
var X,aa = c(Y);
if (M.wk && M.wk < 312)
{return X}
if (aa)
{
if (typeof ai.id == D)
{ai.id = Y}
if (M.ie && M.win)
{
var ah = "";
for (var ae in ai)
{
if (ai[ae] != Object.prototype[ae])
{
if (ae.toLowerCase() == "data")
{ag.movie = ai[ae]}
else
{
if (ae.toLowerCase() == "styleclass")
{ah += ' class="' + ai[ae] + '"'}
else
{
if (ae.toLowerCase() != "classid")
{ah += " " + ae + '="' + ai[ae] + '"'}
}
}
}
}
var af = "";
for (var ad in ag)
{
if (ag[ad] != Object.prototype[ad])
{af += '<param name="' + ad + '" value="' + ag[ad] + '" />'}
}
aa.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + ah + ">" + af + "</object>";
N[N.length] = ai.id;
X = c(ai.id)
}
else
{
var Z = C(r);
Z.setAttribute("type", q);
for (var ac in ai)
{
if (ai[ac] != Object.prototype[ac])
{
if (ac.toLowerCase() == "styleclass")
{Z.setAttribute("class", ai[ac])}
else
{
if (ac.toLowerCase() != "classid")
{Z.setAttribute(ac, ai[ac])}
}
}
}
for (var ab in ag)
{
if (ag[ab] != Object.prototype[ab] && ab.toLowerCase() != "movie")
{e(Z, ab, ag[ab])}
}
aa.parentNode.replaceChild(Z, aa);
X = Z
}
}
return X
}
function e(Z, X, Y)
{
var aa = C("param");
aa.setAttribute("name", X);
aa.setAttribute("value", Y);
Z.appendChild(aa)
}
function y(Y)
{
var X = c(Y);
if (X && X.nodeName == "OBJECT")
{
if (M.ie && M.win)
{
X.style.display = "none";
(function()
{
if (X.readyState == 4)
{b(Y)}
else
{setTimeout(arguments.callee, 10)}
})()
}
else
{X.parentNode.removeChild(X)}
}
}
function b(Z)
{
var Y = c(Z);
if (Y)
{
for (var X in Y)
{
if (typeof Y[X] == "function")
{Y[X] = null}
}
Y.parentNode.removeChild(Y)
}
}
function c(Z)
{
var X = null;
try
{X = j.getElementById(Z)}
catch(Y)
{}
return X
}
function C(X)
{return j.createElement(X)}
function i(Z, X, Y)
{
Z.attachEvent(X, Y);
I[I.length] = [Z,X,Y]
}
function F(Z)
{
var Y = M.pv,X = Z.split(".");
X[0] = parseInt(X[0], 10);
X[1] = parseInt(X[1], 10) || 0;
X[2] = parseInt(X[2], 10) || 0;
return(Y[0] > X[0] || (Y[0] == X[0] && Y[1] > X[1]) || (Y[0] == X[0] && Y[1] == X[1] && Y[2] >= X[2])) ? true : false
}
function v(ac, Y, ad, ab)
{
if (M.ie && M.mac)
{return}
var aa = j.getElementsByTagName("head")[0];
if (!aa)
{return}
var X = (ad && typeof ad == "string") ? ad : "screen";
if (ab)
{
n = null;
G = null
}
if (!n || G != X)
{
var Z = C("style");
Z.setAttribute("type", "text/css");
Z.setAttribute("media", X);
n = aa.appendChild(Z);
if (M.ie && M.win && typeof j.styleSheets != D && j.styleSheets.length > 0)
{n = j.styleSheets[j.styleSheets.length - 1]}
G = X
}
if (M.ie && M.win)
{
if (n && typeof n.addRule == r)
{n.addRule(ac, Y)}
}
else
{
if (n && typeof j.createTextNode != D)
{n.appendChild(j.createTextNode(ac + " {" + Y + "}"))}
}
}
function w(Z, X)
{
if (!m)
{return}
var Y = X ? "visible" : "hidden";
if (J && c(Z))
{c(Z).style.visibility = Y}
else
{v("#" + Z, "visibility:" + Y)}
}
function L(Y)
{
var Z = /[\\\"<>\.;]/;
var X = Z.exec(Y) != null;
return X && typeof encodeURIComponent != D ? encodeURIComponent(Y) : Y
}
var d = function()
{
if (M.ie && M.win)
{
window.attachEvent("onunload", function()
{
var ac = I.length;
for (var ab = 0; ab < ac; ab++)
{I[ab][0].detachEvent(I[ab][1], I[ab][2])}
var Z = N.length;
for (var aa = 0; aa < Z; aa++)
{y(N[aa])}
for (var Y in M)
{M[Y] = null}
M = null;
for (var X in swfobject)
{swfobject[X] = null}
swfobject = null
})
}
}();
return{registerObject:function(ab, X, aa, Z)
{
if (M.w3 && ab && X)
{
var Y = {};
Y.id = ab;
Y.swfVersion = X;
Y.expressInstall = aa;
Y.callbackFn = Z;
o[o.length] = Y;
w(ab, false)
}
else
{
if (Z)
{Z({success:false,id:ab})}
}
},getObjectById:function(X)
{
if (M.w3)
{return z(X)}
},embedSWF:function(ab, ah, ae, ag, Y, aa, Z, ad, af, ac)
{
var X = {success:false,id:ah};
if (M.w3 && !(M.wk && M.wk < 312) && ab && ah && ae && ag && Y)
{
w(ah, false);
K(function()
{
ae += "";
ag += "";
var aj = {};
if (af && typeof af === r)
{
for (var al in af)
{aj[al] = af[al]}
}
aj.data = ab;
aj.width = ae;
aj.height = ag;
var am = {};
if (ad && typeof ad === r)
{
for (var ak in ad)
{am[ak] = ad[ak]}
}
if (Z && typeof Z === r)
{
for (var ai in Z)
{
if (typeof am.flashvars != D)
{am.flashvars += "&" + ai + "=" + Z[ai]}
else
{am.flashvars = ai + "=" + Z[ai]}
}
}
if (F(Y))
{
var an = u(aj, am, ah);
if (aj.id == ah)
{w(ah, true)}
X.success = true;
X.ref = an
}
else
{
if (aa && A())
{
aj.data = aa;
P(aj, am, ah, ac);
return
}
else
{w(ah, true)}
}
if (ac)
{ac(X)}
})
}
else
{
if (ac)
{ac(X)}
}
},switchOffAutoHideShow:function()
{m = false},ua:M,getFlashPlayerVersion:function()
{return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z, Y, X)
{
if (M.w3)
{return u(Z, Y, X)}
else
{return undefined}
},showExpressInstall:function(Z, aa, X, Y)
{
if (M.w3 && A())
{P(Z, aa, X, Y)}
},removeSWF:function(X)
{
if (M.w3)
{y(X)}
},createCSS:function(aa, Z, Y, X)
{
if (M.w3)
{v(aa, Z, Y, X)}
},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa)
{
var Z = j.location.search || j.location.hash;
if (Z)
{
if (/\?/.test(Z))
{Z = Z.split("?")[1]}
if (aa == null)
{return L(Z)}
var Y = Z.split("&");
for (var X = 0; X < Y.length; X++)
{
if (Y[X].substring(0, Y[X].indexOf("=")) == aa)
{return L(Y[X].substring((Y[X].indexOf("=") + 1)))}
}
}
return""
},expressInstallCallback:function()
{
if (a)
{
var X = c(R);
if (X && l)
{
X.parentNode.replaceChild(l, X);
if (Q)
{
w(Q, true);
if (M.ie && M.win)
{l.style.display = "block"}
}
if (E)
{E(B)}
}
a = false
}
}}
}();// Calendar: a Javascript class for Mootools that adds accessible and unobtrusive date pickers to your form elements <http://electricprism.com/aeron/calendar>
// Calendar RC4, Copyright (c) 2007 Aeron Glemann <http://electricprism.com/aeron>, MIT Style License.
var Calendar = new Class({
options: {
blocked: [], // blocked dates
classes: [], // ['calendar', 'prev', 'next', 'month', 'year', 'today', 'invalid', 'valid', 'inactive', 'active', 'hover', 'hilite']
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // days of the week starting at sunday
direction: 0, // -1 past, 0 past + future, 1 future
draggable: true,
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
navigation: 1, // 0 = no nav; 1 = single nav for month; 2 = dual nav for month and year
offset: 0, // first day of the week: 0 = sunday, 1 = monday, etc..
onHideStart: Class.empty,
onHideComplete: Class.empty,
onShowStart: Class.empty,
onShowComplete: Class.empty,
maxYear : new Date().getFullYear(),
pad: 1, // padding between multiple calendars
tweak: {x: 0, y: 0} // tweak calendar positioning
},
// initialize: calendar constructor
// @param obj (obj) a js object containing the form elements and format strings { id: 'format', id: 'format' etc }
// @param props (obj) optional properties
initialize: function(obj, options) {
// basic error checking
if (!obj) { return false; }
this.setOptions(options);
// create our classes array
var keys = ['calendar', 'prev', 'next', 'month', 'year', 'today', 'invalid', 'valid', 'inactive', 'active', 'hover', 'hilite'];
var values = keys.map(function(key, i) {
if (this.options.classes[i]) {
if (this.options.classes[i].length) { key = this.options.classes[i]; }
}
return key;
}, this);
this.classes = values.associate(keys);
// create cal element with css styles required for proper cal functioning
this.calendar = new Element('div').addClass(this.classes.calendar).inject(document.body);
this.calendar.setStyles({ left: '-1000px', opacity: 0, position: 'absolute', top: '-1000px', zIndex: 1000 });
// iex 6 needs a transparent iframe underneath the calendar in order to not allow select elements to render through
if (Browser.ie6) {
this.iframe = new Element('iframe').inject(document.body);
this.calendar.setStyles({ left: '-1000px', position: 'absolute', top: '-1000px', zIndex: 999 });
this.iframe.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';
}
// initialize fade method
this.fx = new Fx.Tween(this.calendar,  { property : 'opacity',
onStart: function() {
if (this.calendar.getStyle('opacity') == 0) { // show
if (Browser.ie6) { this.iframe.setStyle('display', 'block'); }
this.calendar.setStyle('display', 'block');
this.fireEvent('onShowStart', this.element);
}
else { // hide
this.fireEvent('onHideStart', this.element);
}
}.bind(this),
onComplete: function() {
if (this.calendar.getStyle('opacity') == 0) { // hidden
this.calendar.setStyle('display', 'none');
if (Browser.ie6) { this.iframe.setStyle('display', 'none'); }
this.fireEvent('onHideComplete', this.element);
}
else { // shown
this.fireEvent('onShowComplete', this.element);
}
}.bind(this)
});
// initialize drag method
if (window.Drag && this.options.draggable) {
this.drag = new Drag.Move(this.calendar, {
onDrag: function() {
if (Browser.ie6) { this.iframe.setStyles({ left: this.calendar.style.left, top: this.calendar.style.top }); }
}.bind(this)
});
}
// create calendars array
this.calendars = [];
this._id = 0;
var d = new Date(); // today
d.setDate(d.getDate() + this.options.direction.toInt()); // correct today for directional offset
for (var i in obj) {
var cal = {
button: new Element('button').setProperties({'type': 'button'}),
el: $(i),
els: [],
id: this._id++,
month: d.getMonth(),
visible: false,
year: d.getFullYear()
};
// fix for bad element (naughty, naughty element!)
if (!this.element(i, obj[i], cal)) { continue; }
cal.el.addClass(this.classes.calendar);
// create cal button
cal.button.addClass(this.classes.calendar).addEvent('click', function(cal) { this.toggle(cal); }.pass(cal, this)).inject(cal.el, 'after');
// read in default value
cal.val = this.read(cal);
Object.append(cal, this.bounds(cal)); // abs bounds of calendar
Object.append(cal, this.values(cal)); // valid days, months, years
if(cal.val)
this.rebuild(cal);
this.calendars.push(cal); // add to cals array
}
},
addElement: function(obj){
var d = new Date(); // today
d.setDate(d.getDate() + this.options.direction.toInt()); // correct today for directional offset
for (var i in obj) {
var cal = {
button: new Element('button').setProperties({'type': 'button'}),
el: $(i),
els: [],
id: this._id++,
month: d.getMonth(),
visible: false,
year: d.getFullYear()
};
// fix for bad element (naughty, naughty element!)
if (!this.element(i, obj[i], cal)) { continue; }
cal.el.addClass(this.classes.calendar);
// create cal button
cal.button.addClass(this.classes.calendar).addEvent('click', function(cal) { this.toggle(cal); }.pass(cal, this)).inject(cal.el, 'after');
// read in default value
//	cal.val = this.read(cal);
Object.append(cal, this.bounds(cal)); // abs bounds of calendar
Object.append(cal, this.values(cal)); // valid days, months, years
//this.rebuild(cal);
this.calendars.push(cal); // add to cals array
}
},
// blocked: returns an array of blocked days for the month / year
// @param cal (obj)
// @returns blocked days (array)
blocked: function(cal) {
var blocked = [];
var offset = new Date(cal.year, cal.month, 1).getDay(); // day of the week (offset)
var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of this month
this.options.blocked.each(function(date){
var values = date.split(' ');
// preparation
for (var i = 0; i <= 3; i++){
if (!values[i]){ values[i] = (i == 3) ? '' : '*'; } // make sure blocked date contains values for at least d, m and y
values[i] = values[i].contains(',') ? values[i].split(',') : new Array(values[i]); // split multiple values
var count = values[i].length - 1;
for (var j = count; j >= 0; j--){
if (values[i][j].contains('-')){ // a range
var val = values[i][j].split('-');
for (var k = val[0]; k <= val[1]; k++){
if (!values[i].contains(k)){ values[i].push(k + ''); }
}
values[i].splice(j, 1);
}
}
}
// execution
if (values[2].contains(cal.year + '') || values[2].contains('*')){
if (values[1].contains(cal.month + 1 + '') || values[1].contains('*')){
values[0].each(function(val){ // if blocked value indicates this month / year
if (val > 0){ blocked.push(val.toInt()); } // add date to blocked array
});
if (values[3]){ // optional value for day of week
for (var i = 0; i < last; i++){
var day = (i + offset) % 7;
if (values[3].contains(day + '')){
blocked.push(i + 1); // add every date that corresponds to the blocked day of the week to the blocked array
}
}
}
}
}
}, this);
return blocked;
},
// bounds: returns the start / end bounds of the calendar
// @param cal (obj)
// @returns obj
bounds: function(cal) {
// 1. first we assume the calendar has no bounds (or a thousand years in either direction)
// by default the calendar will accept a millennium in either direction
var start = new Date(1000, 0, 1); // jan 1, 1000
var end = new Date(2999, 11, 31); // dec 31, 2999
// 2. but if the cal is one directional we adjust accordingly
var date = new Date().getDate() + this.options.direction.toInt();
if (this.options.direction > 0) {
start = new Date();
start.setDate(date + this.options.pad * cal.id);
}
if (this.options.direction < 0) {
end = new Date();
end.setDate(date - this.options.pad * (this.calendars.length - cal.id - 1));
}
// 3. then we can further filter the limits by using the pre-existing values in the selects
cal.els.each(function(el) {
if (el.get('tag') == 'select') {
if (el.format.test('(y|Y)')) { // search for a year select
var years = [];
el.getChildren().each(function(option) { // get options
var values = this.unformat(option.value, el.format);
if (!years.contains(values[0])) { years.push(values[0]); } // add to years array
}, this);
years.sort(this.sort);
if (years[0] > start.getFullYear()) {
d = new Date(years[0], start.getMonth() + 1, 0); // last day of new month
if (start.getDate() > d.getDate()) { start.setDate(d.getDate()); }
start.setYear(years[0]);
}
if (years.getLast() < end.getFullYear()) {
d = new Date(years.getLast(), end.getMonth() + 1, 0); // last day of new month
if (end.getDate() > d.getDate()) { end.setDate(d.getDate()); }
end.setYear(years.getLast());
}
}
if (el.format.test('(F|m|M|n)')) { // search for a month select
var months_start = [];
var months_end = [];
el.getChildren().each(function(option) { // get options
var values = this.unformat(option.value, el.format);
if (typeOf(values[0]) != 'number' || values[0] == years[0]) { // if it's a year / month combo for curr year, or simply a month select
if (!months_start.contains(values[1])) { months_start.push(values[1]); } // add to months array
}
if (typeOf(values[0]) != 'number' || values[0] == years.getLast()) { // if it's a year / month combo for curr year, or simply a month select
if (!months_end.contains(values[1])) { months_end.push(values[1]); } // add to months array
}
}, this);
months_start.sort(this.sort);
months_end.sort(this.sort);
if (months_start[0] > start.getMonth()) {
d = new Date(start.getFullYear(), months_start[0] + 1, 0); // last day of new month
if (start.getDate() > d.getDate()) { start.setDate(d.getDate()); }
start.setMonth(months_start[0]);
}
if (months_end.getLast() < end.getMonth()) {
d = new Date(start.getFullYear(), months_end.getLast() + 1, 0); // last day of new month
if (end.getDate() > d.getDate()) { end.setDate(d.getDate()); }
end.setMonth(months_end.getLast());
}
}
}
}, this);
return { 'start': start, 'end': end };
},
// caption: returns the caption element with header and navigation
// @param cal (obj)
// @returns caption (element)
caption: function(cal) {
// start by assuming navigation is allowed
var navigation = {
prev: { 'month': true, 'year': true },
next: { 'month': true, 'year': true }
};
// if we're in an out of bounds year
if (cal.year == cal.start.getFullYear()) {
navigation.prev.year = false;
if (cal.month == cal.start.getMonth() && this.options.navigation == 1) {
navigation.prev.month = false;
}
}
if (cal.year == cal.end.getFullYear()) {
navigation.next.year = false;
if (cal.month == cal.end.getMonth() && this.options.navigation == 1) {
navigation.next.month = false;
}
}
// special case of improved navigation but months array with only 1 month we can disable all month navigation
if (typeOf(cal.months) == 'array') {
if (cal.months.length == 1 && this.options.navigation == 2) {
navigation.prev.month = navigation.next.month = false;
}
}
var caption = new Element('caption');
//		var prev = new Element('a').addClass(this.classes.prev).appendText('\x3c'); // <
//		var next = new Element('a').addClass(this.classes.next).appendText('\x3e'); // >
var prev = new Element('a').addClass(this.classes.prev).set("html", '\x3c'); // <
var next = new Element('a').addClass(this.classes.next).set("html", '\x3e'); // >
if (this.options.navigation == 2) {
var month = new Element('span').addClass(this.classes.month).inject(caption);
if (navigation.prev.month) { prev.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', -1); }.pass(cal, this)).inject(month); }
var monthSpan = new Element('span').set("html", this.options.months[cal.month]);
monthSpan.setStyle('cursor','pointer');
monthSpan.addEvent('mouseenter', function(e){
monthSpan.setStyle('text-decoration', 'underline');
});
monthSpan.addEvent('mouseleave', function(e){
monthSpan.setStyle('text-decoration', 'none');
});
// create a list of month select menu
var monthsList = new Element('select', {'style': 'width:60px; font-size:11px;'});
for (var i = 0; i < this.options.months.length; i++) {
var option = new Element('option', {'style': 'font-size:11px;','value':i}).set("html", this.options.months[i]);
if(cal.month == i){
option.setAttribute('selected', 'selected');
}
option.inject(monthsList);
}
monthsList.addEvent('change', function(e){
this.navigate(cal, 'm', monthsList.value-cal.month);
}.bind(this));
monthSpan.addEvent('click', function(e){
monthSpan.removeEvents('click');
monthSpan.empty();
monthsList.inject(monthSpan);
}.bind(this));
month.adopt(monthSpan);
if (navigation.next.month) { next.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', 1); }.pass(cal, this)).inject(month); }
var year = new Element('span').addClass(this.classes.year).inject(caption);
if (navigation.prev.year) { prev.clone().addEvent('click', function(cal) { this.navigate(cal, 'y', -1); }.pass(cal, this)).inject(year); }
var yearSpan = new Element('span').appendText(cal.year);
yearSpan.setStyle('cursor','pointer');
yearSpan.addEvent('mouseenter', function(e){
yearSpan.setStyle('text-decoration', 'underline');
});
yearSpan.addEvent('mouseleave', function(e){
yearSpan.setStyle('text-decoration', 'none');
});
// create a list of years select menu
var yearsList = new Element('select', {'style': 'font-size:11px;'});
var currentYear = new Date().getFullYear();
for (var i = currentYear - 100; i < currentYear+1; i++) {
var option = new Element('option', {'style': 'font-size:11px;','value':i}).appendText(i);
if(cal.year == i){
option.setAttribute('selected', 'selected');
}
option.inject(yearsList);
}
yearsList.addEvent('change', function(e){
this.navigate(cal, 'y', yearsList.value-cal.year);
}.bind(this));
yearSpan.addEvent('click', function(e){
yearSpan.removeEvents('click');
yearSpan.empty();
yearsList.inject(yearSpan);
}.bind(this));
year.adopt(yearSpan);
if (navigation.next.year) { next.clone().addEvent('click', function(cal) { this.navigate(cal, 'y', 1); }.pass(cal, this)).inject(year); }
}
else { // 1 or 0
if (navigation.prev.month && this.options.navigation) { prev.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', -1); }.pass(cal, this)).inject(caption); }
caption.adopt(new Element('span').addClass(this.classes.month).appendText(this.options.months[cal.month]));
caption.adopt(new Element('span').addClass(this.classes.year).appendText(cal.year));
if (navigation.next.month && this.options.navigation) { next.clone().addEvent('click', function(cal) { this.navigate(cal, 'm', 1); }.pass(cal, this)).inject(caption); }
}
return caption;
},
// changed: run when a select value is changed
// @param cal (obj)
changed: function(cal) {
cal.val = this.read(cal); // update calendar val from inputs
Object.append(cal, this.values(cal)); // update bounds - based on curr month
this.rebuild(cal); // rebuild days select
if (!cal.val) { return; } // in case the same date was clicked the cal has no set date we should exit
if (cal.val.getDate() < cal.days[0]) { cal.val.setDate(cal.days[0]); }
if (cal.val.getDate() > cal.days.getLast()) { cal.val.setDate(cal.days.getLast()); }
cal.els.each(function(el) {	// then we can set the value to the field
el.value = this.format(cal.val, el.format);
}, this);
this.check(cal); // checks other cals
this.calendars.each(function(kal) { // update cal graphic if visible
if (kal.visible) { this.display(kal); }
}, this);
},
// check: checks other calendars to make sure no overlapping values
// @param cal (obj)
check: function(cal) {
/** this.calendars.each(function(kal, i) {
if (kal.val) { // if calendar has value set
var change = false;
if (i < cal.id) { // preceding calendar
var bound = new Date(Date.parse(cal.val));
bound.setDate(bound.getDate() - (this.options.pad * (cal.id - i)));
if (bound < kal.val) { change = true; }
}
if (i > cal.id) { // following calendar
var bound = new Date(Date.parse(cal.val));
bound.setDate(bound.getDate() + (this.options.pad * (i - cal.id)));
if (bound > kal.val) { change = true; }
}
if (change) {
if (kal.start > bound) { bound = kal.start; }
if (kal.end < bound) { bound = kal.end; }
kal.month = bound.getMonth();
kal.year = bound.getFullYear();
Object.append(kal, this.values(kal));
// TODO - IN THE CASE OF SELECT MOVE TO NEAREST VALID VALUE
// IN THE CASE OF INPUT DISABLE
// if new date is not valid better unset cal value
// otherwise it would mean incrementally checking to find the nearest valid date which could be months / years away
kal.val = kal.days.contains(bound.getDate()) ? bound : null;
this.write(kal);
if (kal.visible) { this.display(kal); } // update cal graphic if visible
}
}
else {
kal.month = cal.month;
kal.year = cal.year;
}
}, this); **/
},
// clicked: run when a valid day is clicked in the calendar
// @param cal (obj)
clicked: function(td, day, cal) {
cal.val = (this.value(cal) == day) ? null : new Date(cal.year, cal.month, day); // set new value - if same then disable
this.write(cal);
// ok - in the special case that it's all selects and there's always a date no matter what (at least as far as the form is concerned)
// we can't let the calendar undo a date selection - it's just not possible!!
if (!cal.val) { cal.val = this.read(cal); }
if (cal.val) {
this.check(cal); // checks other cals
this.toggle(cal); // hide cal
}
else { // remove active class and replace with valid
td.addClass(this.classes.valid);
td.removeClass(this.classes.active);
}
},
// display: create calendar element
// @param cal (obj)
display: function(cal) {
// 1. header and navigation
this.calendar.empty(); // init div
this.calendar.className = this.classes.calendar + ' ' + this.options.months[cal.month].toLowerCase();
var div = new Element('div').inject(this.calendar); // a wrapper div to help correct browser css problems with the caption element
var table = new Element('table').inject(div).adopt(this.caption(cal));
// 2. day names
var thead = new Element('thead').inject(table);
var tr = new Element('tr').inject(thead);
for (var i = 0; i <= 6; i++) {
var th = this.options.days[(i + this.options.offset) % 7];
tr.adopt(new Element('th', { 'title': th }).appendText(th.substr(0, 1)));
}
// 3. day numbers
var tbody = new Element('tbody').inject(table);
//		var tr = new Element('tr').inject(tbody);
var d = new Date(cal.year, cal.month, 1);
var offset = ((d.getDay() - this.options.offset) + 7) % 7; // day of the week (offset)
var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of this month
var prev = new Date(cal.year, cal.month, 0).getDate(); // last day of previous month
var active = this.value(cal); // active date (if set and within curr month)
var valid = cal.days; // valid days for curr month
var inactive = []; // active dates set by other calendars
var hilited = [];
/**	this.calendars.each(function(kal, i) {
if (kal != cal && kal.val) {
if (cal.year == kal.val.getFullYear() && cal.month == kal.val.getMonth()) { inactive.push(kal.val.getDate()); }
if (cal.val) {
for (var day = 1; day <= last; day++) {
d.setDate(day);
if ((i < cal.id && d > kal.val && d < cal.val) || (i > cal.id && d > cal.val && d < kal.val)) {
if (!hilited.contains(day)) { hilited.push(day); }
}
}
}
}
}, this);**/
var d = new Date();
var today = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime(); // today obv
for (var i = 1; i < 43; i++) { // 1 to 42 (6 x 7 or 6 weeks)
if ((i - 1) % 7 == 0) { tr = new Element('tr').inject(tbody); } // each week is it's own table row
var td = new Element('td').inject(tr);
var day = i - offset;
var date = new Date(cal.year, cal.month, day);
var cls = '';
if (day === active) { cls = this.classes.active; } // active
else if (inactive.contains(day)) { cls = this.classes.inactive; } // inactive
else if (valid.contains(day)) { cls = this.classes.valid; } // valid
else if (day >= 1 && day <= last) { cls = this.classes.invalid; } // invalid
if (date.getTime() == today) { cls = cls + ' ' + this.classes.today; } // adds class for today
if (hilited.contains(day)) { cls = cls + ' ' + this.classes.hilite; } // adds class if hilited
td.addClass(cls);
if (valid.contains(day)) { // if it's a valid - clickable - day we add interaction
td.setProperty('title', this.format(date, 'D M jS Y'));
td.addEvents({
'click': function(td, day, cal) {
this.clicked(td, day, cal);
}.pass([td, day, cal], this),
'mouseover': function(td, cls) {
td.addClass(cls);
}.pass([td, this.classes.hover]),
'mouseout': function(td, cls) {
td.removeClass(cls);
}.pass([td, this.classes.hover])
});
}
// pad calendar with last days of prev month and first days of next month
if (day < 1) { day = prev + day; }
else if (day > last) { day = day - last; }
td.appendText(day);
}
},
// element: helper function
// @param el (string) element id
// @param f (string) format string
// @param cal (obj)
element: function(el, f, cal) {
if (typeOf(f) == 'object') { // in the case of multiple inputs per calendar
for (var i in f) {
if (!this.element(i, f[i], cal)) { return false; }
}
return true;
}
el = $(el);
if (!el) { return false; }
el.format = f;
if (el.get('tag') == 'select') { // select elements allow the user to manually set the date via select option
el.addEvent('change', function(cal) { this.changed(cal); }.pass(cal, this));
}
else { // input (type text) elements restrict the user to only setting the date via the calendar
el.readOnly = true;
el.addEvent('focus', function(cal) { this.toggle(cal); }.pass(cal, this));
}
cal.els.push(el);
return true;
},
// format: formats a date object according to passed in instructions
// @param date (obj)
// @param f (string) any combination of punctuation / separators and d, j, D, l, S, m, n, F, M, y, Y
// @returns string
format: function(date, format) {
var str = '';
if (date) {
var j = date.getDate(); // 1 - 31
var w = date.getDay(); // 0 - 6
var l = this.options.days[w]; // Sunday - Saturday
var n = date.getMonth() + 1; // 1 - 12
var f = this.options.months[n - 1]; // January - December
var y = date.getFullYear() + ''; // 19xx - 20xx
for (var i = 0, len = format.length; i < len; i++) {
var cha = format.charAt(i); // format char
switch(cha) {
// year cases
case 'y': // xx - xx
y = y.substr(2);
case 'Y': // 19xx - 20xx
str += y;
break;
// month cases
case 'm': // 01 - 12
if (n < 10) { n = '0' + n; }
case 'n': // 1 - 12
str += n;
break;
case 'M': // Jan - Dec
f = f.substr(0, 3);
case 'F': // January - December
str += f;
break;
// day cases
case 'd': // 01 - 31
if (j < 10) { j = '0' + j; }
case 'j': // 1 - 31
str += j;
break;
case 'D': // Sun - Sat
l = l.substr(0, 3);
case 'l': // Sunday - Saturday
str += l;
break;
case 'N': // 1 - 7
w += 1;
case 'w': // 0 - 6
str += w;
break;
case 'S': // st, nd, rd or th (works well with j)
if (j % 10 == 1 && j != '11') { str += 'st'; }
else if (j % 10 == 2 && j != '12') { str += 'nd'; }
else if (j % 10 == 3 && j != '13') { str += 'rd'; }
else { str += 'th'; }
break;
default:
str += cha;
}
}
}
return str; //  return format with values replaced
},
// navigate: calendar navigation
// @param cal (obj)
// @param type (str) m or y for month or year
// @param n (int) + or - for next or prev
navigate: function(cal, type, n) {
switch (type) {
case 'm': // month
if (typeOf(cal.months) == 'array') {
var i = cal.months.indexOf(cal.month) + n; // index of current month
if (i < 0 || i == cal.months.length) { // out of range
if (this.options.navigation == 1) { // if type 1 nav we'll need to increment the year
this.navigate(cal, 'y', n);
}
i = (i < 0) ? cal.months.length - 1 : 0;
}
cal.month = cal.months[i];
}
else {
var i = cal.month + n;
if (i < 0 || i == 12) {
if (this.options.navigation == 1) {
this.navigate(cal, 'y', n);
}
i = (i < 0) ? 11 : 0;
}
cal.month = i;
}
break;
case 'y': // year
if (typeOf(cal.years) == 'array') {
var i = cal.years.indexOf(cal.year) + n;
cal.year = cal.years[i];
}
else {
cal.year += n;
}
break;
}
Object.append(cal, this.values(cal));
if (typeOf(cal.months) == 'array') { // if the calendar has a months select
var i = cal.months.indexOf(cal.month); // and make sure the curr months exists for the new year
if (i < 0) { cal.month = cal.months[0]; } // otherwise we'll reset the month
}
this.display(cal);
},
// read: compiles cal value based on array of inputs passed in
// @param cal (obj)
// @returns date (obj) or (null)
read: function(cal) {
var arr = [null, null, null];
cal.els.each(function(el) {
// returns an array which may contain empty values
var values = this.unformat(el.value, el.format);
values.each(function(val, i) {
if (typeOf(val) == 'number') { arr[i] = val; }
});
}, this);
// we can update the cals month and year values
if (typeOf(arr[0]) == 'number') { cal.year = arr[0]; }
if (typeOf(arr[1]) == 'number') { cal.month = arr[1]; }
var val = null;
if (arr.every(function(i) { return typeOf(i) == 'number'; })) { // if valid date
var last = new Date(arr[0], arr[1] + 1, 0).getDate(); // last day of month
if (arr[2] > last) { arr[2] = last; } // make sure we stay within the month (ex in case default day of select is 31 and month is feb)
val = new Date(arr[0], arr[1], arr[2]);
}
return (cal.val == val) ? null : val; // if new date matches old return null (same date clicked twice = disable)
},
// rebuild: rebuilds days + months selects
// @param cal (obj)
rebuild: function(cal) {
cal.els.each(function(el) {
/*
if (el.get('tag') == 'select' && el.format.test('^(F|m|M|n)$')) { // special case for months-only select
if (!cal.options) { cal.options = el.clone(); } // clone a copy of months select
var val = (cal.val) ? cal.val.getMonth() : el.value.toInt();
el.empty(); // initialize select
cal.months.each(function(month) {
// create an option element
var option = new Element('option', {
'selected': (val == month),
'value': this.format(new Date(1, month, 1), el.format);
}).appendText(day).inject(el);
}, this);
}
*/
if (el.get('tag') == 'select' && el.format.test('^(d|j)$')) { // special case for days-only select
var d = this.value(cal);
if (!d) { d = el.value.toInt(); } // if the calendar doesn't have a set value, try to use value from select
el.empty(); // initialize select
var emptyOption = new Element('option', {
'selected':'true',
'value': '0'
}).inject(el);
cal.days.each(function(day) {
// create an option element
var option = new Element('option', {
'selected': (d == day),
'value': ((el.format == 'd' && day < 10) ? '0' + day : day)
}).appendText(day).inject(el);
}, this);
}
}, this);
},
// sort: helper function for numerical sorting
sort: function(a, b) {
return a - b;
},
// toggle: show / hide calendar
// @param cal (obj)
toggle: function(cal) {
document.removeEvent('mousedown', this.fn); // always remove the current mousedown script first
if (cal.visible) { // simply hide curr cal
cal.visible = false;
cal.button.removeClass(this.classes.active); // active
this.fx.start(1, 0);
}
else { // otherwise show (may have to hide others)
// hide cal on out-of-bounds click
this.fn = function(e, cal) {
var e = new Event(e);
var el = e.target;
var stop = false;
while (el != document.body && el.nodeType == 1) {
if (el == this.calendar) { stop = true; }
this.calendars.each(function(kal) {
if (kal.button == el || kal.els.contains(el)) { stop = true; }
});
if (stop) {
e.stop();
return false;
}
else { el = el.parentNode; }
}
if(stop || cal.visible)
this.toggle(cal);
}.apply(this, { 'arguments': cal, 'bind': this, 'event': true });
//			};
document.addEvent('mousedown', function(e){
//                this.fn.apply(this, [e, cal] );
}.bind(this));
this.calendars.each(function(kal) {
if (kal == cal) {
kal.visible = true;
kal.button.addClass(this.classes.active); // css c-icon-active
}
else {
kal.visible = false;
kal.button.removeClass(this.classes.active); // css c-icon-active
}
}, this);
var size = window.getScrollSize();
var coord = cal.button.getCoordinates();
var x = coord.right + this.options.tweak.x;
var y = coord.top + this.options.tweak.y;
// make sure the calendar doesn't open off screen
if (!this.calendar.coord) { this.calendar.coord = this.calendar.getCoordinates(); }
if (x + this.calendar.coord.width > size.x) { x -= (x + this.calendar.coord.width - size.x); }
if (y + this.calendar.coord.height > size.y) { y -= (y + this.calendar.coord.height - size.y); }
this.calendar.setStyles({ left: x + 'px', top: y + 'px' });
if (Browser.ie6) {
this.iframe.setStyles({ height: this.calendar.coord.height + 'px', left: x + 'px', top: y + 'px', width: this.calendar.coord.width + 'px' });
}
this.display(cal);
this.fx.start(0, 1);
}
},
// unformat: takes a value from an input and parses the d, m and y elements
// @param val (string)
// @param f (string) any combination of punctuation / separators and d, j, D, l, S, m, n, F, M, y, Y
// @returns array
unformat: function(val, f) {
f = f.escapeRegExp();
var re = {
d: '([0-9]{2})',
j: '([0-9]{1,2})',
D: '(' + this.options.days.map(function(day) { return day.substr(0, 3); }).join('|') + ')',
l: '(' + this.options.days.join('|') + ')',
S: '(st|nd|rd|th)',
F: '(' + this.options.months.join('|') + ')',
m: '([0-9]{2})',
M: '(' + this.options.months.map(function(month) { return month.substr(0, 3); }).join('|') + ')',
n: '([0-9]{1,2})',
Y: '([0-9]{4})',
y: '([0-9]{2})'
};
var arr = []; // array of indexes
var g = '';
// convert our format string to regexp
for (var i = 0; i < f.length; i++) {
var c = f.charAt(i);
if (re[c]) {
arr.push(c);
g += re[c];
}
else {
g += c;
}
}
// match against date
var matches = val.match('^' + g + '$');
var dates = new Array(3);
if (matches) {
matches = matches.slice(1); // remove first match which is the date
arr.each(function(c, i) {
i = matches[i];
switch(c) {
// year cases
case 'y':
i = '19' + i; // 2 digit year assumes 19th century (same as JS)
case 'Y':
dates[0] = i.toInt();
break;
// month cases
case 'F':
i = i.substr(0, 3);
case 'M':
i = this.options.months.map(function(month) { return month.substr(0, 3); }).indexOf(i) + 1;
case 'm':
case 'n':
dates[1] = i.toInt() - 1;
break;
// day cases
case 'd':
case 'j':
dates[2] = i.toInt();
break;
}
}, this);
}
return dates;
},
// value: returns day value of calendar if set
// @param cal (obj)
// @returns day (int) or null
value: function(cal) {
var day = null;
if (cal.val) {
if (cal.year == cal.val.getFullYear() && cal.month == cal.val.getMonth()) { day = cal.val.getDate(); }
}
return day;
},
// values: returns the years, months (for curr year) and days (for curr month and year) for the calendar
// @param cal (obj)
// @returns obj
values: function(cal) {
var years, months, days;
cal.els.each(function(el) {
if (el.get('tag') == 'select') {
if (el.format.test('(y|Y)')) { // search for a year select
years = [];
el.getChildren().each(function(option) { // get options
var values = this.unformat(option.value, el.format);
if (!years.contains(values[0])) { years.push(values[0]); } // add to years array
}, this);
years.sort(this.sort);
}
if (el.format.test('(F|m|M|n)')) { // search for a month select
months = []; // 0 - 11 should be
el.getChildren().each(function(option) { // get options
var values = this.unformat(option.value, el.format);
if (typeOf(values[0]) != 'number' || values[0] == cal.year) { // if it's a year / month combo for curr year, or simply a month select
if (!months.contains(values[1])) { months.push(values[1]); } // add to months array
}
}, this);
months.sort(this.sort);
}
if (el.format.test('(d|j)') && !el.format.test('^(d|j)$')) { // search for a day select, but NOT a days only select
days = []; // 1 - 31
el.getChildren().each(function(option) { // get options
var values = this.unformat(option.value, el.format);
// in the special case of days we dont want the value if its a days only select
// otherwise that will screw up the options rebuilding
// we will take the values if they are exact dates though
if (values[0] == cal.year && values[1] == cal.month) {
if (!days.contains(values[2])) { days.push(values[2]); } // add to days array
}
}, this);
}
}
}, this);
// we start with what would be the first and last days were there no restrictions
var first = 1;
var last = new Date(cal.year, cal.month + 1, 0).getDate(); // last day of the month
// if we're in an out of bounds year
if (cal.year == cal.start.getFullYear()) {
// in the special case of improved navigation but no months array, we'll need to construct one
if (months == null && this.options.navigation == 2) {
months = [];
for (var i = 0; i < 12; i ++) {
if (i >= cal.start.getMonth()) { months.push(i); }
}
}
// if we're in an out of bounds month
if (cal.month == cal.start.getMonth()) {
first = cal.start.getDate(); // first day equals day of bound
}
}
if (cal.year == cal.end.getFullYear()) {
// in the special case of improved navigation but no months array, we'll need to construct one
if (months == null && this.options.navigation == 2) {
months = [];
for (var i = 0; i < 12; i ++) {
if (i <= cal.end.getMonth()) { months.push(i); }
}
}
if (cal.month == cal.end.getMonth()) {
last = cal.end.getDate(); // last day equals day of bound
}
}
// let's get our invalid days
var blocked = this.blocked(cal);
// finally we can prepare all the valid days in a neat little array
if (typeOf(days) == 'array') { // somewhere there was a days select
days = days.filter(function(day) {
if (day >= first && day <= last && !blocked.contains(day)) { return day; }
});
}
else { // no days select we'll need to construct a valid days array
days = [];
for (var i = first; i <= last; i++) {
if (!blocked.contains(i)) { days.push(i); }
}
}
days.sort(this.sort); // sorting our days will give us first and last of month
return { 'days': days, 'months': months, 'years': years };
},
// write: sets calendars value to form elements
// @param cal (obj)
write: function(cal) {
this.rebuild(cal);	 // in the case of options, we'll need to make sure we have the correct number of days available
cal.els.each(function(el) {	// then we can set the value to the field
el.value = this.format(cal.val, el.format);
}, this);
}
});
Calendar.implement(new Events);
Calendar.implement(new Options);/**
*  Create a drop-down menu for navigation
*
*/
var DropDownMenu = new Class({
Implements : Options,
options: {
onComplete: Class.empty,
onStart: Class.empty,
delay:500
},
initialize: function(el, options) {
this.el = $(el);
this.setOptions(options);
var elementToHide;
this.el.getElements('li').each(function(li, index) {
if(li.getElements('a')[0])li.getElements('a')[0].removeAttribute('title');
if(li.getElements('ul').length){
li.addClass('HasChild');
}
li.menu = this;
li.addEvent('mouseenter', function() {
this.addClass('iehover');
clearTimeout(this._myTimer);
if(elementToHide && !this.getElements('ul').contains(elementToHide)){
elementToHide.removeClass('ulhover');
}
elementToHide = null;
if(this.getElements('ul')[0]) {
this.getElements('ul')[0].addClass('ulhover');
}
});
li.addEvent('mouseleave', function() {
this.removeClass('iehover');
if(this.getElements('ul')[0]) {
elementToHide = this.getElements('ul')[0];
this._myTimer = this.getElements('ul')[0].removeClass.delay(this.menu.options.delay ,this.getElements('ul')[0], 'ulhover');
}
});
}.bind(this));
},
hide : function(){
this.el.getElements('ul').each(function(item,index){
item.removeClass('ulhover');
});
}
});
var SynergeeNews = new Class({
initialize: function(el, elementClass, timer, className, navContainer) {
this.el = el;
this.elementClass = elementClass;
this._timer = timer;
this._className = ' ';
if(className){
this._className += className;
}
if(navContainer){
this._navContainer = navContainer;
}else {
this._navContainer = this.el.getParent();
}
this.items = this.el.getElements(this.elementClass);
// create the nav div
var nav = new Element('ul', {'class': 'Synergee-Web-Page-TextTicker-Nav-Container' + this._className});
this.navButtons = new Array();
for (var i = 0; i < this.items.length; i++) {
this.navButtons[i] = new Element('li', {'class': 'Synergee-Web-Page-TextTicker-NavButton' + this._className});
this.navButtons[i].position = i;
nav.appendChild(this.navButtons[i]);
this.navButtons[i].addEvent('click', function(event) {
event = new Event(event);
this.goto(event.target.position);
}.bind(this));
}
this._navContainer.appendChild(nav);
if (this.items.length) {
var w = 0;
this.items.each(function(item, index) {
w += item.getSize().x;
item.addEvent('mouseenter', function() {
clearTimeout(this.delayedFunction);
}.bind(this));
item.addEvent('mouseleave', function() {
clearTimeout(this.delayedFunction);
this.delayedFunction = this.next.bind(this).delay(this._timer);
}.bind(this));
}.bind(this));
this.el.setStyles({
position: 'absolute',
top: 0,
left: 0/*,
width: w*/
});
var me = this;
this.fx = new Fx.Morph(this.el, {duration:800, transition: Fx.Transitions.Quint.easeInOut,
onStart: function(){
var i = (me.current == 0) ? me.items.length : me.current;
me.navButtons[me.current].addClass('Active');
for (var j = 0; j < me.navButtons.length; j ++) {
if (j != me.current)me.navButtons[j].removeClass('Active');
}
me.el.fireEvent('onstart', me.items[me.current]);
}.bind(this),
onCancel :function() {
me.el.fireEvent('oncancel', me.items[me.current]);
},
onComplete:function() {
me.el.fireEvent('itemchanged', me.items[me.current]);
}.bind(this)});
this.current = this.items.length;
this.next();
}
},
next : function() {
this.goto(++this.current);
},
back : function() {
this.goto(--this.current);
},
goto : function(to) {
clearTimeout(this.delayedFunction);
if (to >= this.items.length) to = 0;
if (to < 0) to = this.items.length - 1;
this.current = to;
this.fx.cancel();
this.fx.start({
top: -this.items[this.current].offsetTop,
left: -this.items[this.current].offsetLeft
});
this.delayedFunction = this.next.bind(this).delay(this._timer);
}
});var TextSplitter = new Class({
initialize: function(component, splitterElements, titleElement, accordion) {
this.blocks = new Array();
this.titles = new Array();
if (component.getElement) {
this.el = component;
if (component.getElement('.Synergee-Web-Page-Component-Text-Container')) {
this.el = component.getElement('.Synergee-Web-Page-Component-Text-Container');
}
var elements = new Array();
if (!(splitterElements instanceof Array)) {
splitterElements = new Array(splitterElements);
}
for (var i = 0; i < splitterElements.length; i++) {
elements = this.el.getElement(splitterElements[i]);
if (elements)break;
}
// retrieve the blocks
if (elements) {
var children = this.el.childNodes;
this.blocks.push(new Element('div', {"class": "Synergee-Web-Page-TextSplitter-Content"}));
for (var i = 0; i < children.length; i++) {
if (typeOf(children[i]) == 'element' && splitterElements.contains(children[i].tagName.toLowerCase())) {
this.blocks.push(new Element('div', {"class": "Synergee-Web-Page-TextSplitter-Content"}));
} else if (this.blocks.length) {
if (typeOf(children[i]) == 'element') {
this.blocks[this.blocks.length - 1].appendChild(children[i]);
} else {
this.blocks[this.blocks.length - 1].appendChild(children[i]);
}
i--;
}
}
this.el.empty();
if (titleElement) {
for (var i = 0; i < this.blocks.length; i++) {
if (this.blocks[i].getElement(titleElement)) {
this.titles[i] = this.blocks[i].getElement(titleElement);
this.titles[i].addClass('Synergee-Web-Page-TextSplitter-Title');
} else {
this.titles[i] = new Element(titleElement, {'class': 'Synergee-Web-Page-TextSplitter-Title'});
this.titles[i].set("html", '"' + titleElement + '" title missing');
}
}
if (this.titles.length) {
this.titles[this.titles.length - 1].addClass('Synergee-Web-Page-TextSplitter-Title-Last');
}
this.titlesContainer = new Element('div', {"class": "Synergee-Web-Page-TextSplitter-Title-Container"});
if (!accordion || this.titles.length != this.blocks.length)
this.el.appendChild(this.titlesContainer);
}
this.contentsContainer = new Element('div', {"class": "Synergee-Web-Page-TextSplitter-Content-Container"});
this.el.appendChild(this.contentsContainer);
if (accordion && this.titles.length == this.blocks.length) {
for (var i = 0; i < this.blocks.length; i ++) {
this.contentsContainer.appendChild(this.titles[i]);
this.contentsContainer.appendChild(this.blocks[i]);
}
} else {
for (var i = 0; i < this.titles.length; i ++) {
this.titlesContainer.appendChild(this.titles[i]);
}
for (var i = 0; i < this.blocks.length; i ++) {
this.contentsContainer.appendChild(this.blocks[i]);
}
}
}
}
},
getContentContainer : function() {
return this.contentsContainer;
},
getTitleContainer :  function() {
if (this.titlesContainer)return this.titlesContainer;
return null;
},
getTitles : function() {
return this.titles;
},
getContents : function() {
return this.blocks;
}
});var SynergeeTips = new Class({
Extends: Tips,
options: {
onShow: function(tip){
tip.addClass('tool-tip-visible Component-No-Print');
},
onHide: function(tip){
tip.removeClass('tool-tip-visible');
},
maxTitleChars: 30,
showDelay: 100,
hideDelay: 100,
className: 'Tool-tip Tool-tip tool',
offset: {'x': 16, 'y': 16},
fixed: false,
maxHeight : 0,
closeButton : false,
eventType : 'click'
},
build: function(el){
el.$tmp.myTitle = (el.href && el.get('tag') == 'a') ? el.href.replace('http://', '') : (el.rel || false);
if (el.title){
var dual = el.title.split('::');
if (dual.length > 1) {
el.$tmp.myTitle = dual[0].trim();
el.$tmp.myText = dual[1].trim();
} else {
el.$tmp.myText = el.title;
}
el.removeAttribute('title');
} else {
el.$tmp.myText = false;
}
if (el.$tmp.myTitle && el.$tmp.myTitle.length > this.options.maxTitleChars) el.$tmp.myTitle = el.$tmp.myTitle.substr(0, this.options.maxTitleChars - 1) + "&hellip;";
el.addEvent(this.options.eventType, function(event){
this.start(el);
if (this.options.maxHeight) {
if(this.text && this.options.maxHeight < this.text.getParent().getCoordinates().height){
this.text.getParent().setStyle('max-height', this.options.maxHeight);
this.text.getParent().setStyle('overflowY', 'scroll');
}
}
if (!this.options.fixed) this.locate(event);
else this.position(el);
}.bind(this));
if (!this.options.fixed) el.addEvent('mousemove', this.locate.bindWithEvent(this));
var end = this.end.bind(this);
el.addEvent('mouseleave', end);
el.addEvent('trash', end);
this.toolTip.addEvent('mouseleave', end);
this.toolTip.addEvent('mouseenter', function(event){
clearTimeout(this.timer);
}.bind(this));
},
start: function(el){
this.wrapper.empty();
if (el.$tmp.myTitle){
this.title = new Element('span').inject(
new Element('div', {'class': this.options.className + '-title'}).inject(this.wrapper)
).set("html", el.$tmp.myTitle);
if(this.options.closeButton){
this.closeButton = new Element('div',{'class':this.options.className + '-Close'});
var end = this.hide.bind(this);
this.closeButton.inject(this.title.getParent());
this.closeButton.addEvent('click',end);
}
}
if (el.$tmp.myText){
this.text = new Element('div').inject(
new Element('div', {'class': this.options.className + '-text Synergee-Web-Page-Component-Text'}).inject(this.wrapper)
).set("html", el.$tmp.myText);
}
clearTimeout(this.timer);
this.timer = this.show.delay(this.options.showDelay, this);
}
});
/**
* The synergee form javascript code.
* This file contains all the necessary code used by the form widget.
*
* Copyright (c) 2007 Pyrameed all right reserved (http://www.pyrameed.com)
*/
/**
* The field validator.
* This class is used to validate a form field
*/
var SynergeeFieldValidator = new Class({
/**
* The validation method
*
* @param Element field The field element to validate
* @return boolean True if the field is valid
*/
isValid : function(field) {
return true;
},
/**
* Return the vaidator name
*
* @return string The validator name
*/
getValidatorName : function() {
throw Error('The method "getValidatorName" has to be defined for the field validator classes.');
}
});
/**
* The not empty field validator
*/
var SynergeeFieldValidatorNotEmpty = new Class({
Extends : SynergeeFieldValidator,
/**
* The validation method
*
* @param Element field The field element to validate
* @return boolean True if the field is valid
*/
isValid : function(field) {
return (field.tagName.toLowerCase() == 'div' || field.get('value') != '');
},
/**
* Return the vaidator name
*
* @return string The validator name
*/
getValidatorName : function() {
return 'SynergeeFieldValidatorNotEmpty';
}
});
/**
* The email field validator
*/
var SynergeeFieldValidatorEmail = new Class({
Extends : SynergeeFieldValidator,
_emailRegex : new RegExp(/^([\w-\._\+%]+@(?:[\w-]+\.)+[\w]{2,6})?$/),
/**
* The validation method
*
* @param Element field The field element to validate
* @return boolean True if the field is valid
*/
isValid : function(field) {
return this._emailRegex.test(field.get('value'));
},
/**
* Return the vaidator name
*
* @return string The validator name
*/
getValidatorName : function() {
return 'SynergeeFieldValidatorEmail';
}
});
/**
* The list field validator
*/
var SynergeeFieldValidatorList = new Class({
Extends : SynergeeFieldValidator,
/**
* The validation method
*
* @param Element field The field element to validate
* @return boolean True if the field is valid
*/
isValid : function(field) {
var inputElements = field.getElementsByTagName('input');
for (var i = 0; i < inputElements.length; i++) {
if (inputElements[i].checked) {
return true;
}
}
return false;
},
/**
* Return the vaidator name
*
* @return string The validator name
*/
getValidatorName : function() {
return 'SynergeeFieldValidatorList';
}
});
/**
* The alpha field validator
*/
var SynergeeFieldValidatorAlpha = new Class({
Extends : SynergeeFieldValidator,
_alphaRegex : new RegExp("^([a-zA-Z])*?$"),
/**
* The validation method
*
* @param Element field The field element to validate
* @return boolean True if the field is valid
*/
isValid : function(field) {
return this._alphaRegex.test(field.get('value'));
},
/**
* Return the vaidator name
*
* @return string The validator name
*/
getValidatorName : function() {
return 'SynergeeFieldValidatorAlpha';
}
});
/**
* The numeric field validator
*/
var SynergeeFieldValidatorNum = new Class({
Extends : SynergeeFieldValidator,
_numRegex : new RegExp("^([0-9])*?$"),
/**
* The validation method
*
* @param Element field The field element to validate
* @return boolean True if the field is valid
*/
isValid : function(field) {
return this._numRegex.test(field.get('value'));
},
/**
* Return the vaidator name
*
* @return string The validator name
*/
getValidatorName : function() {
return 'SynergeeFieldValidatorNum';
}
});
/**
* The alphanumeric field validator
*/
var SynergeeFieldValidatorAlnum = new Class({
Extends : SynergeeFieldValidator,
_alnumRegex : new RegExp("^([a-zA-Z0-9])*?$"),
/**
* The validation method
*
* @param Element field The field element to validate
* @return boolean True if the field is valid
*/
isValid : function(field) {
return this._alnumRegex.test(field.get('value'));
},
/**
* Return the vaidator name
*
* @return string The validator name
*/
getValidatorName : function() {
return 'SynergeeFieldValidatorAlnum';
}
});
/**
* The date field validator
*/
var SynergeeFieldValidatorDate = new Class({
Extends : SynergeeFieldValidator,
/**
* The validation method
*
* @param Element field The field element to validate
* @return boolean True if the field is valid
*/
isValid : function(field) {
return (field.tagName.toLowerCase() == 'div' || field.get('value') != '');
},
/**
* Return the vaidator name
*
* @return string The validator name
*/
getValidatorName : function() {
return 'SynergeeFieldValidatorDate';
}
});
/**
* The Birthdate field validator
*/
var SynergeeFieldValidatorBirthdate = new Class({
Extends : SynergeeFieldValidator,
initialize : function(minDate, maxDate, format) {
if(minDate){
this._minDate = (new Date()).fromIso(minDate);
}   else{
this._minDate = null;
}
if(maxDate){
this._maxDate = (new Date()).fromIso(maxDate);
}   else{
this._maxDate = null;
}
if(format) this._format = format;
},
/**
* The validation method
*
* @param Element field The field element to validate
* @return boolean True if the field is valid
*/
isValid : function(field) {
// always return true, the backend check the right dates
////
//        var fieldValue = field.get('value');
//        if(fieldValue != '' && (new Date()).fromIso(fieldValue)){
//            fieldValue = (new Date()).fromIso(fieldValue);
//            if(this._maxDate && this._maxDate.getTime() < fieldValue){
//                return false;
//
//            }
//            if(this._minDate && this._minDate.getTime() > fieldValue){
//                return false;
//            }
//        }
return true;
},
/**
* Return the vaidator name
*
* @return string The validator name
*/
getValidatorName : function() {
return 'SynergeeFieldValidatorBirthdate';
}
});
/**
* The Birthdate field validator
*/
var SynergeeFieldValidatorLevel = new Class({
Extends : SynergeeFieldValidator,
initialize : function(minimumLevel, maximumLevel) {
if(minimumLevel){
this._minimumLevel = minimumLevel;
}   else{
this._minimumLevel = 1;
}
if(maximumLevel){
this._maximumLevel = maximumLevel;
}   else{
this._maximumLevel = 7;
}
},
/**
* The validation method
*
* @param Element field The field element to validate
* @return boolean True if the field is valid
*/
isValid : function(field) {
var selIndex = field.selectedIndex;
var comboValue = field.options[selIndex].value;
if(comboValue){
if(comboValue >= this._minimumLevel && comboValue <= this._maximumLevel){
return true;
}
return false;
}
return true;
},
/**
* Return the validator name
*
* @return string The validator name
*/
getValidatorName : function() {
return 'SynergeeFieldValidatorLevel';
}
});
/**
* The form validator.
* This class is used to validate the fields of a specific form
*/
var SynergeeFormValidator = new Class({
_formElement : null,
_onComplete : null,
_fields : [],
_submitButton : null,
/**
* The constructor
*
* @param Element formElement The form element
* @param function onComplete The function called once the form has been submitted (this function is called by the ajax component)
*/
initialize: function(formElement, onComplete) {
this._fields = [];
this._formElement = formElement;
this._scroller = new Fx.Scroll(window, {offset: {'x': 0, 'y': -50}});
if (typeof(onComplete) == 'function') {
this._onComplete = onComplete;
} else {
this._onComplete = eval(onComplete);
}
this._formElement.addEvent('submit', function(e) {
new Event(e).stop();
});
// The error message functions are added to the form
this._formElement._errorMessage = new Fx.Slide(this._formElement.getElementsByTagName('div')[0]);
this._formElement.getElementsByTagName('div')[0].setStyle('clear', 'both');
this._formElement._errorMessage.hide();
this._formElement.displayErrorMessage = function() {
if (this._errorMessage) {
this._errorMessage.cancel();
this._errorMessage.slideIn();
}
};
this._formElement.hideErrorMessage = function() {
if (this._errorMessage) {
this._errorMessage.cancel();
this._errorMessage.slideOut();
}
};
},
/**
* This method is called when the response of the form has been received.
* The goal of this method is to analyse the response and if some fields were
* invalid the error message is displayed again.
* If no invalidate fields are returned, the user defined function (set on the onComplete
* attribute of the form tag) is called with the response as parameter.
*
* @param string response The response returned by the server
*/
onComplete : function(response) {
// The submit button is ungrayed
if (this._submitButton) {
this._submitButton.removeClass('WaitingButton');
this._submitButton.removeProperty('disabled');
}
// We check if the invalidateFields is empty or not
try {
var response = JSON.decode(response);
if (response.invalidFields && response.invalidFields.length) {
try {
if ($(response.invalidFields[0])) {
$(response.invalidFields[0]).focus();
} else {
$('id_' + response.invalidFields[0]).focus();
}
}catch(ex){
// if the element is not focusable (it's hidden or something)
}
response.invalidFields.each(function(fieldName) {
if ($(fieldName)) {
$(fieldName).displayErrorMessage();
} else {
$('id_' + fieldName).displayErrorMessage();
}
});
try {
if(response.invalidFields[0]){
if ($(response.invalidFields[0])) {
if($(response.invalidFields[0]).getStyle('display') == 'none'){
var element = $(response.invalidFields[0]).getParent();
}else {
var element = $(response.invalidFields[0]);
}
this._scroller.toElement(element);
} else {
if($('id_' + response.invalidFields[0]).getStyle('display') == 'none'){
var element = $('id_' + response.invalidFields[0]).getParent();
}else {
var element = $('id_' + response.invalidFields[0]);
}
this._scroller.toElement(element);
}
}
}catch (ex){}
this._formElement.fireEvent('invalidfield');
} else if (response.isValid) {
// The user defined on complete method is called with the response as parameter
this._onComplete(response.response);
} else {
// The request wasn't valid, then a global message has to be send
this._formElement.displayErrorMessage();
}
} catch(ex) {
// The response is not a valid JSON encoded one
this._formElement.displayErrorMessage();
this._formElement.fireEvent('invalidfield');
}
},
/**
* Validate the fields of the form
*
* @param boolean ajaxCall True if an ajax call has to be done
*/
validate : function(ajaxCall) {
// The submit button is grayed
if (this._submitButton) {
this._submitButton.addClass('WaitingButton');
this._submitButton.setProperty('disabled', 'disabled');
}
// The error message of the form is hidden
this._formElement.hideErrorMessage();
var valid = true;
var invalidFields = new Array();
for (var i = 0; i < this._fields.length; i++) {
for (var j = 0; j < this._fields[i].getValidators().length; j++) {
if (!this._fields[i].getValidators()[j].isValid(this._fields[i])) {
invalidFields.push(this._fields[i]);
}
}
}
// The valid fields error message are hidden
for (var i = 0; i < this._fields.length; i++) {
if (!invalidFields.contains(this._fields[i])) {
this._fields[i].hideErrorMessage();
}
}
// The invalidate fields error message are displayed
if (invalidFields.length) {
invalidFields[0].focus();
// The page is scrolled to the first error message
this._scroller.toElement(invalidFields[0]);
for (var i = 0; i < invalidFields.length; i++) {
invalidFields[i].displayErrorMessage();
}
// The submit button is ungrayed
if (this._submitButton) {
this._submitButton.removeClass('WaitingButton');
this._submitButton.removeProperty('disabled');
}
return false;
} else {
if (ajaxCall) {
// The right page location is set to the action attribute of the form
this._formElement.setProperty('action', window.getCurrentPageUrl());
// The componentName and componentId fields are populated
if (!this._formElement.getChildren().contains($('id_componentName'))) {
if ($('id_componentName')) {
$('id_componentName').dispose();
}
(new Element('input').setProperties({name:'componentName',id:'id_componentName',type:'hidden'})).inject(this._formElement.getChildren()[0], 'before');
}
$('id_componentName').setProperty('value', this._formElement.getComponentName());
if (!this._formElement.getChildren().contains($('id_componentId'))) {
if ($('id_componentId')) {
$('id_componentId').dispose();
}
(new Element('input').setProperties({name:'componentId',id:'id_componentId',type:'hidden'})).inject(this._formElement.getChildren()[1], 'before');
}
$('id_componentId').setProperty('value', this._formElement.getComponentId());
// The validationInformation field is populated with all the validation information
if (!this._formElement.getChildren().contains($('id_validationInformation'))) {
if ($('id_validationInformation')) {
$('id_validationInformation').dispose();
}
(new Element('input').setProperties({name:'validationInformation',id:'id_validationInformation',type:'hidden'})).inject(this._formElement.getChildren()[2], 'before');
}
$('id_validationInformation').setProperty('value', this.getValidationInformation());
// All the fields are ok, then the form is sent
this._formElement.fireEvent('ajaxcall');
this._formElement.set('send',{onComplete:this.onComplete.bind(this)});
this._formElement.send('');
return false;
} else {
return true;
}
}
},
createTips : function(field) {
},
/**
* Add a field to the validator
*
* @param Element field The field to validate
* @optionalParam string[] validators The list of validators to apply to the field
*/
addField : function(field, validators) {
var validatorObjects = [];
if (validators && validators.length) {
for (var i = 0; i < validators.length; i++) {
validatorObjects.push(validators[i]);
}
}
if (field.getStyle('position') == "absolute") {
if (field.parentNode.getElementsByTagName('div').length) {
field._errorImage = field.getParent().getElementsByTagName('div')[0];
field._errorMessage = new Tips(field._errorImage);
}
} else {
if (field.parentNode.getElementsByTagName('div').length) {
if (field.parentNode.getElementsByTagName('div').length) {
// The error fx object is created
if($(field.parentNode.getElementsByTagName('div')[0]).hasClass('Synergee-Web-Page-Form-FieldErrorMessage')){
field._errorMessage = new Fx.Slide(field.parentNode.getElementsByTagName('div')[0]);
$(field.getParent().getElementsByTagName('div')[0]).addClass('Synergee-Web-Page-Form-FieldErrorMessage-Container');
field._errorMessage.hide();
}
}
}
}
field._validators = validatorObjects;
field.getValidators = function() {
return this._validators;
};
field.displayErrorMessage = function() {
if (this._errorMessage) {
if (this.getStyle('position') == "absolute") {
this._errorImage.setStyle('display', 'block');
} else {
this.getParent().addClass('InvalidField')
this._errorMessage.cancel();
//                    this.setStyle('margin-top', '2px');
this._errorMessage.slideIn();
}
}
};
field.hideErrorMessage = function() {
if (this._errorMessage) {
if (this.getStyle('position') == "absolute") {
this._errorImage.setStyle('display', 'none');
} else {
this.getParent().removeClass('InvalidField')
this._errorMessage.cancel();
//                    this.setStyle('margin-top', '0px');
this._errorMessage.slideOut();
}
}
};
this._fields.push(field);
// A check to determine if this is the submit button
if (field && field.getProperty('type') && field.getProperty('type').toLowerCase() == 'submit') {
this._submitButton = field;
}
},
/**
* Return the validation information encoded in JSON.
* This is a hash table that have the field name as key and a coma separated validator name as values.
*
* @return string
*/
getValidationInformation : function() {
var validationInformation = {};
this._fields.each(function(field) {
var validators = new Array();
field.getValidators().each(function(validator) {
validators.push(validator.getValidatorName());
});
validationInformation[field.getProperty('name')] = validators.join(',');
});
return JSON.encode(validationInformation);
}
});
/**
* The synergee general javascript code.
* This file contains all the necessary code used by the form widget.
*
* Copyright (c) 2007 Pyrameed all right reserved (http://www.pyrameed.com)
*/
/**
* Add two usefull method to the Element objects in order to determine
* its synergee web page parent component name and id.
* Those informations are usefull when dealing with AJAX requests.
*/
Element.implement('getComponentName', function() {
var currentElement = this;
while (!(currentElement.className && currentElement.className.test('Synergee-Web-Page-Component-(.*)-Container', 'i'))) {
currentElement = currentElement.getParent();
}
var regex = new RegExp('Synergee-Web-Page-Component-(.*)-Container', 'i');
var values = regex.exec(currentElement.className);
if (values && values.length == 2) {
return values[1];
} else {
return null;
}
});
Element.implement('getComponentId', function() {
var currentElement = this;
while (!(currentElement.className && currentElement.className.test('Synergee-Web-Page-Component-(.*)-Container', 'i'))) {
currentElement = currentElement.getParent();
}
var regex = new RegExp('Synergee-Web-Page-Component-(.*)-Container', 'i');
var values = regex.exec(currentElement.className);
if (values && values.length == 2) {
return currentElement.getParent().id;
} else {
return null;
}
});
/**
* Bookmark the current web page
*
* @param string title The page title
* @param string url The web page url
*/
window.bookmark = function(title, url) {
if (document.all) {
window.external.AddFavorite(url, title);
} else if (window.sidebar) {
window.sidebar.addPanel(title, url, "");
}
};
/**
* Return the current web page url
*
* @return string The current web page url
*/
window.getCurrentPageUrl = function() {
var currentPageUrl = window.location.href;
if(currentPageUrl.indexOf('#') != -1){
currentPageUrl = currentPageUrl.slice(0, currentPageUrl.indexOf('#'));
}
if (currentPageUrl.indexOf('?') != -1) {
return currentPageUrl.substr(0, currentPageUrl.indexOf('?'));
}
return currentPageUrl;
};
/**
* Return the current JSON url.
* This url is used by all the JSON invokations
*
* @param Element element The component element
* @return string The current JSON url
*/
window.getCurrentJsonUrl = function(element) {
var htmlElement = $(document).getElement('html');
var windowUrl = window.getCurrentPageUrl();
if (htmlElement.getAttribute('designmode') == 'on') {
var webPageId = /webPageId=([0-9]*)/.exec(window.location.href);
return windowUrl + '?webPageId=' + webPageId[1] + '&componentName=' + element.getComponentName() + '&componentId=' + element.getComponentId();
} else {
return windowUrl + '?componentName=' + element.getComponentName() + '&componentId=' + element.getComponentId();
}
};
/**
* Return the currrent page location (the url without the page name)
*
* @return string The current page location
*/
window.getCurrentPageLocation = function() {
return window.getCurrentPageUrl().substr(0, window.getCurrentPageUrl().lastIndexOf('/')) + '/';
};
document.isInDesignMode = function() {
var htmlElement = $(document).getElement('html');
return (htmlElement.getProperty('designmode') == 'on');
};
/**
* Decode the html entities
*
* @param String sa
* @return String
*/
function decodeHtmlEntities(sa) {
var ta=document.createElement("textarea");
ta.innerHTML=sa.replace(/</g,"&lt;").replace(/>/g,"&gt;");
return ta.value;
};
/**
* Pad a number regarding the number passed to the pad. By default 2
* @param number valueToPad The value to pad
* @param number pad The pad value
* @return string the padded number
*/
Number.padTo = function(valueToPad, pad) {
pad = pad ? pad : 2;
pad = Math.pow(10, pad-1);
return (valueToPad < pad) ? ("0" + valueToPad) : valueToPad;
};
/**
* Populate the date from an iso formatted string that represent a date
*
* @param string The iso formatted date
*/
Date.prototype.fromIso = function(isoFormattedDate) {
var dateTimeRegex = new RegExp('^([0-9]{4})-([0-9]{2})-([0-9]{2})[T ]([0-9]{2}):([0-9]{2}):([0-9]{2})(([+-])([0-9]{2})(:([0-9]{2}))?)?$');
var dateRegex = new RegExp('^([0-9]{4})-([0-9]{2})-([0-9]{2})$');
var timeRegex = new RegExp('^([0-9]{2}):([0-9]{2}):([0-9]{2})$');
if (dateTimeRegex.test(isoFormattedDate)) {
var values = dateTimeRegex.exec(isoFormattedDate);
var tmpDate = new Date(values[1], values[2] - 1, values[3], values[4], values[5], values[6]);
this.setTime(tmpDate.getTime());
//this.setTime(Date.parse(values[1] + ' ' + values[2] + ' ' + values[3] + ' ' + values[4] + ':' + values[5] + ':' + values[6]));
// The time zone is calculated
if(values[9]){
var timeZoneOffset = -1 * (values[9]*60 + (values[11]?values[11]*1:0)) * (values[8]=='-'?-1:1);
var currentTimeZoneOffset = this.getTimezoneOffset();
this.setTime(this.getTime() + (timeZoneOffset - currentTimeZoneOffset) * 60000);
}
} else if (dateRegex.test(isoFormattedDate)) {
var values = dateRegex.exec(isoFormattedDate);
var tmpDate = new Date(values[1], values[2] - 1, values[3]);
this.setTime(tmpDate.getTime());
//this.setTime(Date.parse(values[1] + ' ' + values[2] + ' ' + values[3]));
} else if (timeRegex.test(isoFormattedDate)) {
var values = timeRegex.exec(isoFormattedDate);
this.setHours(values[1]);
this.setMinutes(values[2]);
this.setSeconds(values[3]);
}
return this;
};
/**
* Return the iso formatted representation of the date
*
* @return string The iso formatted date
*/
Date.prototype.toIso = function() {
var formattedDate = this.getFullYear() + '-' + Number.padTo(this.getMonth()+1) + '-' + Number.padTo(this.getDate());
formattedDate = formattedDate + 'T' + Number.padTo(this.getHours()) + ':' + Number.padTo(this.getMinutes()) + ':' + Number.padTo(this.getSeconds());
return formattedDate;
};
/**
* Return formatted date
*
* @param string format The date output format
* @optionalparam string[] months The translated months
* @return string The formatted date
*/
Date.prototype.format = function(format, months) {
// The date is formatted
format = format.replace('dddd','dd');
format = format.replace('ddd', 'dd');
format = format.replace('dd', Number.padTo(this.getDate()));
format = format.replace('d', this.getDate());
format = format.replace('yyyy', this.getFullYear());
format = format.replace('yy', (new String(this.getFullYear())).substr(2,2));
format = format.replace('y', (new String(this.getFullYear())).substr(2,2));
// The time is formatted
format = format.replace('HH', Number.padTo(this.getHours()));
format = format.replace('H', this.getHours());
format = format.replace('mm', Number.padTo(this.getMinutes()));
format = format.replace('m', this.getMinutes());
format = format.replace('ss', Number.padTo(this.getSeconds()));
format = format.replace('s', this.getSeconds());
if(format.indexOf('MMMM') != -1 && months && months.length && months[this.getMonth()]){
format = format.replace('MMMM', months[this.getMonth()]);
}else{
format = format.replace('MMMM','MM');
format = format.replace('MMM', 'MM');
format = format.replace('MM', Number.padTo(this.getMonth()+1));
format = format.replace('M', this.getMonth()+1);
}
format = format.replace(/['"]/g,'');
return format;
};
/**
* Returns the week number for this date.
* @return int the week number
*/
Date.prototype.getWeek = function() {
var onejan = new Date(this.getFullYear(),0,1);
return Math.ceil((((this - onejan) / 86400000) + onejan.getDay())/7);
};
//annet-pk 2011.04.26 messages part
function addslashes(str) {
str = str.replace(/\\/g, '\\\\');
str = str.replace(/\'/g, '\\\'');
str = str.replace(/\"/g, '\\"');
str = str.replace(/\0/g, '\\0');
return str;
}
function stripslashes(str) {
str = str.replace(/\\'/g, '\'');
str = str.replace(/\\"/g, '"');
str = str.replace(/\\0/g, '\0');
str = str.replace(/\\\\/g, '\\');
return str;
}
function get_html_translation_table(table, quote_style) {
// http://kevin.vanzonneveld.net
// +   original by: Philip Peterson
// +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// +   bugfixed by: noname
// +   bugfixed by: Alex
// +   bugfixed by: Marco
// +   bugfixed by: madipta
// +   improved by: KELAN
// +   improved by: Brett Zamir (http://brett-zamir.me)
// +   bugfixed by: Brett Zamir (http://brett-zamir.me)
// +      input by: Frank Forte
// +   bugfixed by: T.Wild
// +      input by: Ratheous
// %          note: It has been decided that we're not going to add global
// %          note: dependencies to php.js, meaning the constants are not
// %          note: real constants, but strings instead. Integers are also supported if someone
// %          note: chooses to create the constants themselves.
// *     example 1: get_html_translation_table('HTML_SPECIALCHARS');
// *     returns 1: {'"': '&quot;', '&': '&amp;', '<': '&lt;', '>': '&gt;'}
var entities = {},
hash_map = {},
decimal = 0,
symbol = '';
var constMappingTable = {},
constMappingQuoteStyle = {};
var useTable = {},
useQuoteStyle = {};
// Translate arguments
constMappingTable[0] = 'HTML_SPECIALCHARS';
constMappingTable[1] = 'HTML_ENTITIES';
constMappingQuoteStyle[0] = 'ENT_NOQUOTES';
constMappingQuoteStyle[2] = 'ENT_COMPAT';
constMappingQuoteStyle[3] = 'ENT_QUOTES';
useTable = !isNaN(table) ? constMappingTable[table] : table ? table.toUpperCase() : 'HTML_SPECIALCHARS';
useQuoteStyle = !isNaN(quote_style) ? constMappingQuoteStyle[quote_style] : quote_style ? quote_style.toUpperCase() : 'ENT_COMPAT';
if (useTable !== 'HTML_SPECIALCHARS' && useTable !== 'HTML_ENTITIES') {
throw new Error("Table: " + useTable + ' not supported');
// return false;
}
entities['38'] = '&amp;';
if (useTable === 'HTML_ENTITIES') {
entities['160'] = '&nbsp;';
entities['161'] = '&iexcl;';
entities['162'] = '&cent;';
entities['163'] = '&pound;';
entities['164'] = '&curren;';
entities['165'] = '&yen;';
entities['166'] = '&brvbar;';
entities['167'] = '&sect;';
entities['168'] = '&uml;';
entities['169'] = '&copy;';
entities['170'] = '&ordf;';
entities['171'] = '&laquo;';
entities['172'] = '&not;';
entities['173'] = '&shy;';
entities['174'] = '&reg;';
entities['175'] = '&macr;';
entities['176'] = '&deg;';
entities['177'] = '&plusmn;';
entities['178'] = '&sup2;';
entities['179'] = '&sup3;';
entities['180'] = '&acute;';
entities['181'] = '&micro;';
entities['182'] = '&para;';
entities['183'] = '&middot;';
entities['184'] = '&cedil;';
entities['185'] = '&sup1;';
entities['186'] = '&ordm;';
entities['187'] = '&raquo;';
entities['188'] = '&frac14;';
entities['189'] = '&frac12;';
entities['190'] = '&frac34;';
entities['191'] = '&iquest;';
entities['192'] = '&Agrave;';
entities['193'] = '&Aacute;';
entities['194'] = '&Acirc;';
entities['195'] = '&Atilde;';
entities['196'] = '&Auml;';
entities['197'] = '&Aring;';
entities['198'] = '&AElig;';
entities['199'] = '&Ccedil;';
entities['200'] = '&Egrave;';
entities['201'] = '&Eacute;';
entities['202'] = '&Ecirc;';
entities['203'] = '&Euml;';
entities['204'] = '&Igrave;';
entities['205'] = '&Iacute;';
entities['206'] = '&Icirc;';
entities['207'] = '&Iuml;';
entities['208'] = '&ETH;';
entities['209'] = '&Ntilde;';
entities['210'] = '&Ograve;';
entities['211'] = '&Oacute;';
entities['212'] = '&Ocirc;';
entities['213'] = '&Otilde;';
entities['214'] = '&Ouml;';
entities['215'] = '&times;';
entities['216'] = '&Oslash;';
entities['217'] = '&Ugrave;';
entities['218'] = '&Uacute;';
entities['219'] = '&Ucirc;';
entities['220'] = '&Uuml;';
entities['221'] = '&Yacute;';
entities['222'] = '&THORN;';
entities['223'] = '&szlig;';
entities['224'] = '&agrave;';
entities['225'] = '&aacute;';
entities['226'] = '&acirc;';
entities['227'] = '&atilde;';
entities['228'] = '&auml;';
entities['229'] = '&aring;';
entities['230'] = '&aelig;';
entities['231'] = '&ccedil;';
entities['232'] = '&egrave;';
entities['233'] = '&eacute;';
entities['234'] = '&ecirc;';
entities['235'] = '&euml;';
entities['236'] = '&igrave;';
entities['237'] = '&iacute;';
entities['238'] = '&icirc;';
entities['239'] = '&iuml;';
entities['240'] = '&eth;';
entities['241'] = '&ntilde;';
entities['242'] = '&ograve;';
entities['243'] = '&oacute;';
entities['244'] = '&ocirc;';
entities['245'] = '&otilde;';
entities['246'] = '&ouml;';
entities['247'] = '&divide;';
entities['248'] = '&oslash;';
entities['249'] = '&ugrave;';
entities['250'] = '&uacute;';
entities['251'] = '&ucirc;';
entities['252'] = '&uuml;';
entities['253'] = '&yacute;';
entities['254'] = '&thorn;';
entities['255'] = '&yuml;';
}
if (useQuoteStyle !== 'ENT_NOQUOTES') {
entities['34'] = '&quot;';
}
if (useQuoteStyle === 'ENT_QUOTES') {
entities['39'] = '&#39;';
}
entities['60'] = '&lt;';
entities['62'] = '&gt;';
// ascii decimals to real symbols
for (decimal in entities) {
symbol = String.fromCharCode(decimal);
hash_map[symbol] = entities[decimal];
}
return hash_map;
}
function html_entity_decode(string, quote_style) {
// Convert all HTML entities to their applicable characters
//
// version: 901.714
// discuss at: http://phpjs.org/functions/html_entity_decode
var histogram = {}, symbol = '', tmp_str = '', entity = '';
tmp_str = string.toString();
if (false === (histogram = get_html_translation_table('HTML_ENTITIES', quote_style))) {
return false;
}
// &amp; must be the last character when decoding!
delete(histogram['&']);
histogram['&'] = '&amp;';
for (symbol in histogram) {
entity = histogram[symbol];
tmp_str = tmp_str.split(entity).join(symbol);
}
return tmp_str;
}
//annet-pk 2011.04.26 messages part ends
var Ticker = new Class({
setOptions: function(options) {
this.options = Object.extend({
speed: 1000,
delay: 5000,
onComplete: Class.empty,
onStart: Class.empty
}, options || {});
},
initialize: function(el,options){
this.setOptions(options);
this.el = $(el).getElement('div');
if (this.el.getElement('hr')) {
var children = this.el.childNodes;
var news = new Array();
news.push(new Element('div'));
for (var i = 0; i < children.length; i++) {
if (typeOf(children[i]) == 'element' && children[i].tagName.toLowerCase() == 'hr') {
news.push(new Element('div'));
} else if (news.length) {
if (typeOf(children[i]) == 'element') {
news[news.length - 1].appendChild(children[i]);
} else {
news[news.length - 1].appendChild(children[i]);
}
i--;
}
}
this.el.empty();
for(var i = 0; i < news.length; i ++){
this.el.appendChild(news[i]);
}
this.items = this.el.getElements('div');
if (this.items.length) {
var w = 0;
this.items.each(function(item, index) {
w += item.getSize().x;
item.addEvent('mouseenter', function(){
clearTimeout(this.delayedFunction);
}.bind(this));
item.addEvent('mouseleave', function(){
this.next();
}.bind(this));
}.bind(this));
this.el.setStyles({
position: 'absolute',
top: 0,
left: 0,
width: w
});
this.periodical =
this.fx = new Fx.Morph(this.el, {duration:this.options.speed,onComplete:function() {
var i = (this.current == 0) ? this.items.length : this.current;
this.items[i - 1].inject(this.el);
this.el.setStyle('left', 0);
}.bind(this)});
this.current = this.items.length;
this.next();
}
}
},
next: function() {
this.current++;
if (this.current >= this.items.length) this.current = 0;
this.fx.start({
top: this.items[this.current].offsetTop,
left: -this.items[this.current].offsetLeft
});
this.delayedFunction = this.next.bind(this).delay(this.options.delay);
}
});/**
*  Create a waiting panel object
*
*/
var WaitingPanel = new Class({
Implements: Events,
initialize: function(el) {
if($(el).waitingPanel){
return $(el).waitingPanel;
}
this.el = $(el);
this.el.waitingPanel = this;
},
buildObjects : function() {
// the waiting panel layer and fx style are created
this._waitingPanelLayer = new Element('div', {'class': 'WaitingPanel-AjaxLoading','id':'waitingPanel'});
//        this._waitingFxStyle = new Fx.Style(this._waitingPanelLayer, 'opacity', {duration:100});
this._waitingFxStyle = new Fx.Tween(this._waitingPanelLayer, {
duration:100,
property : 'opacity'
});
this._waitingPanelLayer.inject(document.getElementsByTagName('div')[0], 'after');
this._waitingPanelLayer.setStyle('position', 'absolute');
this._waitingPanelLayer.setStyles({
opacity : 0
});
this._waitingImageLayer = new Element('div', {'class': 'WaitingPanel-AjaxLoadingImage','id':'waitingPanel'});
//        this._waitingImageFxStyle = new Fx.Style(this._waitingImageLayer, 'opacity', {duration:100});
this._waitingImageFxStyle = new Fx.Tween(this._waitingImageLayer, {
duration:100,
property : 'opacity'
});
this._waitingImageLayer.inject(this._waitingPanelLayer, 'after');
this._waitingImageLayer.setStyle('position', 'absolute');
this._waitingImageLayer.setStyles({
opacity : 0
});
},
/**
*  Display a waiting panel layer over the element
*/
display : function() {
if (!this._waitingPanelLayer) {
this.buildObjects();
}
// refresh the height of the waiting panel layer
var coordinates = this.el.getCoordinates();
this._waitingPanelLayer.elementTo = this.el;
this._waitingPanelLayer.setStyles({
height : (coordinates.height + 2),
width : (coordinates.width + 2),
left : (coordinates.left - 2),
top : (coordinates.top - 2)
});
this._waitingImageLayer.setStyles({
height : (coordinates.height + 2),
width : (coordinates.width + 2),
left : (coordinates.left - 2),
top : (coordinates.top - 2)
});
this._waitingFxStyle.start(0, 0.5);
this._waitingImageFxStyle.start(0, 1);
},
/**
* Hide the waiting panel
*/
hide : function() {
if (this._waitingPanelLayer && this._waitingPanelLayer.getStyle('opacity') != 0) {
this._waitingFxStyle.start(0.5, 0);
this._waitingImageFxStyle.start(1, 0);
}
}
});/**
*  Create a message panel object
*
*/
var WindowMessagePanel = new Class({
Implements: Events,
initialize: function(el) {
if ($(el).messagePanel) {
return $(el).messagePanel;
}
this.el = $(el);
this.el.messagePanel = this;
},
/**
* Display a given message
* @param string message The message
* @param integer autoClose The autoclose timer in milliseconds
*/
displayMessage : function(message, title ,autoClose) {
clearTimeout(this.autoClose);
if (!this._messageContainer) {
this._container = new Element('div',{'class': 'Component-MessagePanel'});
this._messageContainer = new Element('div', {'class': 'Component-MessagePanel-Content'});
this._messageTitle = new Element('div', {'class': 'Component-MessagePanel-Title'});
this._messageContainerClose = new Element('div', {'class': 'Component-MessagePanel-Close'});
this._messageContainerClose.addEvent('click', this.hideMessage.bind(this));
this._messageContainerClose.set("html", 'x');
this._messageTitle.set("html", title);
//            this._messageContainerFx = new Fx.Style(this._container, 'opacity', {duration:300});
this._messageContainerFx = new Fx.Tween(this._container, {
duration:300,
property : 'opacity'
});
this._container.inject(this.el);
this._messageTitle.inject(this._container);
this._messageContainer.inject(this._container);
this._messageContainerClose.inject(this._container);
this._container.setStyle('position', 'absolute');
this._container.setStyle('opacity', 0);
}
this._messageContainer.set("html", message);
// the waiting panel layer and fx style are created
var containerCoord = this.el.getCoordinates();
this._messageContainerFx.cancel();
this._container.setStyles({
'top': (this.el.getCoordinates().height - this._container.getCoordinates().height ) / 2,
'left': (containerCoord.width - 300) / 2,
'width':300,
'min-height':20,
'z-index':10000
});
this._messageContainerFx.start(0, 1);
if (autoClose) {
this.autoClose =  this.hideMessage.delay(autoClose, this);
}
},
getContent : function(){
return this._messageContainer;
},
hideMessage : function() {
clearTimeout(this.autoClose);
this._messageContainerFx.cancel();
this._messageContainerFx.start(1, 0);
}
});
window.addEvent('domready', function()
{
if ($("temp-warning"))
{
if (document.isInDesignMode())
{
$("temp-warning").setStyle("display", "block");
$("temp-warning").setStyle("left", "300px");
}
else
{
var contentWarning = $("temp-warning").getElement(".Synergee-Web-Page-Component-Text-Container").innerHTML.clean();
var tempWarningCookie = Cookie.read("temp-warning");
if ('' != contentWarning && tempWarningCookie === null)
{
$("temp-warning").setStyle("display", "block");
$("temp-warning-close").addEvent("click", function(event)
{
$("temp-warning").remove();
Cookie.write("temp-warning", "1", {duration: 1});
});
}
}
}
});/*
* Lazy Load - jQuery plugin for lazy loading images
*
* Copyright (c) 2007-2013 Mika Tuupola
*
* Licensed under the MIT license:
*   http://www.opensource.org/licenses/mit-license.php
*
* Project home:
*   http://www.appelsiini.net/projects/lazyload
*
* Version:  1.8.4
*
*/
(function(a,b,c,d){var e=a(b);a.fn.lazyload=function(c){function i(){var b=0;f.each(function(){var c=a(this);if(h.skip_invisible&&!c.is(":visible"))return;if(!a.abovethetop(this,h)&&!a.leftofbegin(this,h))if(!a.belowthefold(this,h)&&!a.rightoffold(this,h))c.trigger("appear"),b=0;else if(++b>h.failure_limit)return!1})}var f=this,g,h={threshold:0,failure_limit:0,event:"scroll",effect:"show",container:b,data_attribute:"original",skip_invisible:!0,appear:null,load:null};return c&&(d!==c.failurelimit&&(c.failure_limit=c.failurelimit,delete c.failurelimit),d!==c.effectspeed&&(c.effect_speed=c.effectspeed,delete c.effectspeed),a.extend(h,c)),g=h.container===d||h.container===b?e:a(h.container),0===h.event.indexOf("scroll")&&g.bind(h.event,function(a){return i()}),this.each(function(){var b=this,c=a(b);b.loaded=!1,c.one("appear",function(){if(!this.loaded){if(h.appear){var d=f.length;h.appear.call(b,d,h)}a("<img />").bind("load",function(){c.hide().attr("src",c.data(h.data_attribute))[h.effect](h.effect_speed),b.loaded=!0;var d=a.grep(f,function(a){return!a.loaded});f=a(d);if(h.load){var e=f.length;h.load.call(b,e,h)}}).attr("src",c.data(h.data_attribute))}}),0!==h.event.indexOf("scroll")&&c.bind(h.event,function(a){b.loaded||c.trigger("appear")})}),e.bind("resize",function(a){i()}),/iphone|ipod|ipad.*os 5/gi.test(navigator.appVersion)&&e.bind("pageshow",function(b){b.originalEvent.persisted&&f.each(function(){a(this).trigger("appear")})}),a(b).load(function(){i()}),this},a.belowthefold=function(c,f){var g;return f.container===d||f.container===b?g=e.height()+e.scrollTop():g=a(f.container).offset().top+a(f.container).height(),g<=a(c).offset().top-f.threshold},a.rightoffold=function(c,f){var g;return f.container===d||f.container===b?g=e.width()+e.scrollLeft():g=a(f.container).offset().left+a(f.container).width(),g<=a(c).offset().left-f.threshold},a.abovethetop=function(c,f){var g;return f.container===d||f.container===b?g=e.scrollTop():g=a(f.container).offset().top,g>=a(c).offset().top+f.threshold+a(c).height()},a.leftofbegin=function(c,f){var g;return f.container===d||f.container===b?g=e.scrollLeft():g=a(f.container).offset().left,g>=a(c).offset().left+f.threshold+a(c).width()},a.inviewport=function(b,c){return!a.rightoffold(b,c)&&!a.leftofbegin(b,c)&&!a.belowthefold(b,c)&&!a.abovethetop(b,c)},a.extend(a.expr[":"],{"below-the-fold":function(b){return a.belowthefold(b,{threshold:0})},"above-the-top":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-screen":function(b){return a.rightoffold(b,{threshold:0})},"left-of-screen":function(b){return!a.rightoffold(b,{threshold:0})},"in-viewport":function(b){return a.inviewport(b,{threshold:0})},"above-the-fold":function(b){return!a.belowthefold(b,{threshold:0})},"right-of-fold":function(b){return a.rightoffold(b,{threshold:0})},"left-of-fold":function(b){return!a.rightoffold(b,{threshold:0})}})})(jQuery,window,document)
var WebsiteSelector = new Class({
Implements : Options,
initialize: function(componentId) {
this._componentId = componentId;
this._websiteList = $(this._componentId + '_siteSelectorLanguageListId');
this._height = this._websiteList.getCoordinates().height - this._websiteList.getStyle('padding-top').toInt() - this._websiteList.getStyle('padding-bottom').toInt();
if (!document.isInDesignMode())
{
this._fxTime = 300;
this.listFx = new Fx.Tween($(this._componentId + '_siteSelectorLanguageListId'),{ property :  'height', wait:false, duration:this._fxTime});
$(this._componentId + '_siteSelectorId').addEvent('mouseover', this.displaySiteSelector.bind(this));
$(this._componentId + '_siteSelectorId').addEvent('mouseout', this.hideSiteSelector.bind(this));
this.listFx.set(0);
this._websiteList.setStyle('visibility', 'visible');
}
},
hideSiteSelector : function(e) {
this.listFx.cancel();
this.listFx.start(this._height, 0);
},
/**
* Set the height
*/
setHeight : function(height) {
this._height = height;
},
displaySiteSelector : function(e) {
this.listFx.cancel();
var height = (this._height < this._websiteList.getCoordinates().height) ? this._height : this._websiteList.getCoordinates().height;
this.listFx.start(height, this._height);
}
});var QuickSearch = new Class({
Implements : Events,
initialize : function(resultContainer, containerType, targetUrl) {
this._targetUrl = null;
if (targetUrl) {
this._targetUrl = targetUrl;
}
if (containerType) {
this._resultContainer = $(resultContainer);
} else {
if ($$('.' + resultContainer) && $$('.' + resultContainer).length) {
this._resultContainer = $$('.' + resultContainer)[0];
}
}
if (this._resultContainer) {
var defaultValue = $('idQuickSearchTextField').getProperty('value');
function onQuickSearchTextFieldClick() {
if ($('idQuickSearchTextField').getProperty('value') == defaultValue)
$('idQuickSearchTextField').setProperty('value', '');
}
function onQuickSearchTextFieldClickOut() {
if ($('idQuickSearchTextField').getProperty('value') == '')
$('idQuickSearchTextField').setProperty('value', defaultValue);
}
$('idQuickSearchTextField').addEvent('focus', onQuickSearchTextFieldClick);
$('idQuickSearchTextField').addEvent('blur', onQuickSearchTextFieldClickOut);
$('idQuickSearchTextField').addClass('QuickSearchText');
this._waitingPanel = new WaitingPanel(this._resultContainer);
if (this._targetUrl) {
$('id_QuickSearchForm').removeProperty('onsubmit');
}
// check the window location search
if (window.location.search) {
var search = window.location.search;
var value;
if(search && search.indexOf('query=') != -1){
search = search.substr(1);
var split = search.split('&');
for(var i = 0 ; i < split.length; i++){
if(split[i].indexOf('query=') != -1){
value = split[i].replace('query=', '');
}
}
if(value){
$('idQuickSearchTextField').setProperty('value', value);
}
}
$('id_QuickSearchForm').removeProperty('onsubmit');
}
$('id_QuickSearchForm').addEvent('submit', function (e) {
if (this._targetUrl) {
window.location.href = this._targetUrl + '?query=' + $('idQuickSearchTextField').getProperty('value');
}
this._waitingPanel.display();
}.bind(this));
}
},
highlight : function (oElements, sSearch) {
if (sSearch) {
var searchParts = sSearch.split(' ');
if (oElements && oElements.length) {
for (var i = 0; i < oElements.length; i++) {
for (var j = 0; j < searchParts.length; j++) {
oElements[i].set("html", oElements[i].innerHTML.replace(new RegExp('(' + searchParts[j] + ')', 'ig'), '<span class="highlighted">$1</span>'));
}
}
}
}
},
handleResponse : function (response) {
if(this._resultContainer){
var recherche = $("idQuickSearchTextField").value;
this._resultContainer.set("html", response);
$('closeButtonSearch').addEvent('click', function(event)
{
$$('.Synergee-Web-Page-Component-QuickSearch-Results-Content')[0].destroy();
});
this.highlight(this._resultContainer.getElements(".Synergee-Web-Page-Component-QuickSearch-Results-Page-Title a"), recherche);
this.highlight(this._resultContainer.getElements(".Synergee-Web-Page-Component-QuickSearch-Results-Page-Description"), recherche);
this.highlight(this._resultContainer.getElements(".Synergee-Web-Page-Component-QuickSearch-Results-Page-Url a"), recherche);
this._waitingPanel.hide();
var nbResults = $$('.Synergee-Web-Page-Component-QuickSearch-Item').length;
if (nbResults > 10 && nbResults <= 100) {
var resultsHelper = new ResultHelper(10, nbResults);
resultsHelper.injectNavBar('Synergee-Web-Page-Component-QuickSearch-NavigationBars', 'button', 'QuickSearchNav', '');
$$('.QuickSearchNavNext').addEvent('click', function (e) {
resultsHelper.nextPage();
if (e.target) e.target.blur();
else e.srcElement.blur();
});
$$('.QuickSearchNavPrevious').addEvent('click', function (e) {
resultsHelper.previousPage();
if (e.target) e.target.blur();
else e.srcElement.blur();
});
resultsHelper.addEvent('onPageChangeBefore', function () {
$$('.Synergee-Web-Page-Component-QuickSearch-Page' + resultsHelper.getCurrentPage()).setStyle('display','none');
$$('.QuickSearchNav' + resultsHelper.getCurrentPage()).removeClass('Synergee-Web-Page-Component-QuickSearch-NavigationBars-Selected');
$$('.QuickSearchNav' + resultsHelper.getCurrentPage()).addClass('Synergee-Web-Page-Component-QuickSearch-NavigationBars-Default');
$$('.QuickSearchNav' + resultsHelper.getCurrentPage()).removeClass('QuickSearchNavButtonSelectedColor');
$$('.QuickSearchNav' + resultsHelper.getCurrentPage()).addClass('QuickSearchNavButtonDefaultColor');
$$('.QuickSearchNavNext').removeClass('QuickSearchNavButtonSelectedColor');
});
resultsHelper.addEvent('onPageChangeAfter', function () {
$$('.Synergee-Web-Page-Component-QuickSearch-Page' + resultsHelper.getCurrentPage()).setStyle('display','block');
$$('.QuickSearchNav' + resultsHelper.getCurrentPage()).removeClass('Synergee-Web-Page-Component-QuickSearch-NavigationBars-Default');
$$('.QuickSearchNav' + resultsHelper.getCurrentPage()).addClass('Synergee-Web-Page-Component-QuickSearch-NavigationBars-Selected');
$$('.QuickSearchNav' + resultsHelper.getCurrentPage()).removeClass('QuickSearchNavButtonDefaultColor');
$$('.QuickSearchNav' + resultsHelper.getCurrentPage()).addClass('QuickSearchNavButtonSelectedColor');
$$('.QuickSearchNavPrevious').removeClass('QuickSearchNavButtonSelectedColor');
});
resultsHelper.addEvent('onPageChangeLowerLimit', function () {
$$('.QuickSearchNavPrevious').addClass('QuickSearchNavButtonSelectedColor');
});
resultsHelper.addEvent('onPageChangeUpperLimit', function () {
$$('.QuickSearchNavNext').addClass('QuickSearchNavButtonSelectedColor');
});
for (var i=0; i < resultsHelper.getCountPages(); i++) {
$$('.QuickSearchNav'+i).each(function (e) { e.pageNumber = i; });
$$('.QuickSearchNav'+i).addClass('Synergee-Web-Page-Component-QuickSearch-NavigationBars-Default');
$$('.QuickSearchNav'+i).addClass('QuickSearchNavButtonDefaultColor');
$$('.QuickSearchNav'+i).addEvent('click', function (e) {
if (e.target) {
resultsHelper.selectPage(e.target.pageNumber);
e.target.blur();
}
else {
resultsHelper.selectPage(e.srcElement.pageNumber);
e.srcElement.blur();
}
});
}
$$('.QuickSearchNav0').removeClass('Synergee-Web-Page-Component-QuickSearch-NavigationBars-Default');
$$('.QuickSearchNav0').removeClass('QuickSearchNavButtonDefaultColor');
$$('.QuickSearchNav0').addClass('Synergee-Web-Page-Component-QuickSearch-NavigationBars-Selected');
$$('.QuickSearchNav0').addClass('QuickSearchNavButtonSelectedColor');
$$('.QuickSearchNavPrevious').addClass('QuickSearchNavButtonSelectedColor');
}
}
}
});
window.addEvent('domready', function()
{
if ($('ExtendedMenuSlider'))
{
var width = $('ExtendedMenuSlider').getWidth();
var minHeight = $('ExtendedMenuSlider').getParent('.Synergee-Web-Page-Component-Menu').getHeight();
$('ExtendedMenuSlider').fx = new Fx.Tween($('ExtendedMenuSlider'), {property: 'margin-left', duration:500});
$('ExtendedMenuSlider').fxHeight = new Fx.Tween($('ExtendedMenuSlider').getParent(), {property : 'height',duration:500});
$('ExtendedMenuSlider').getDepth = function(element)
{
var depth = 0;
if ($('ExtendedMenuSlider') !== element && $('ExtendedMenuSlider').contains(element))
{
var parentEl = element;
while (parentEl != $('ExtendedMenuSlider'))
{
parentEl = parentEl.getParent();
if (parentEl.get('tag') == 'ul')depth++;
}
}
return depth;
};
$('ExtendedMenuSlider').toRoot = function()
{
var height = ($('ExtendedMenuSlider').getCoordinates().height > minHeight ? $('ExtendedMenuSlider').getCoordinates().height : minHeight);
$('ExtendedMenuSlider').fxHeight.start($('ExtendedMenuSlider').getParent().getStyle('height'), height);
$('ExtendedMenuSlider').fx.start($('ExtendedMenuSlider').getStyle('margin-left'), -( ($('ExtendedMenuSlider').getDepth($('ExtendedMenuSlider'))) * width));
$('ExtendedMenuSlider').fx.start($('ExtendedMenuSlider').getStyle('margin-left'), 0);
}
var height = ($('ExtendedMenuSlider').getCoordinates().height > minHeight ? $('ExtendedMenuSlider').getCoordinates().height : minHeight);
$('ExtendedMenuSlider').fxHeight.start($('ExtendedMenuSlider').getParent().getStyle('height'), height);
$('ExtendedMenuSlider').getElements('ul').setStyle('display', 'none');
$('ExtendedMenuSlider').getElements('li').each(function(item, index)
{
if (item.getElement('ul'))
{
item.addEvent('mouseenter', function()
{
item.addClass('hover');
});
item.addEvent('mouseleave', function()
{
item.removeClass('hover');
});
item.addEvent('click', function(event)
{
// if the user click on the link, the animation is stopped
if (event)
{
event = new Event(event);
if (event.target && event.target.get && event.target.get('tag') == 'a')
{
item.removeEvents('mouseleave');
item.addClass('hover');
return;
}
event.stop();
}
item.getParent().getElements('ul').setStyle('display', 'none');
var childUL = item.getElement('ul');
childUL.setStyle('display', '');
// check if the parents are not hidden
if ($('ExtendedMenuSlider') !== item && $('ExtendedMenuSlider').contains(item))
{
var parentEl = item;
while (parentEl != $('ExtendedMenuSlider'))
{
parentEl = parentEl.getParent();
if (parentEl.get('tag') == 'ul')parentEl.setStyle('display', '');
}
}
// set the height
var height = (childUL.getCoordinates().height > minHeight ? childUL.getCoordinates().height : minHeight);
$('ExtendedMenuSlider').fxHeight.stop();
$('ExtendedMenuSlider').fxHeight.start($('ExtendedMenuSlider').getParent().getStyle('height'), height);
// slide to the right item
$('ExtendedMenuSlider').fx.start($('ExtendedMenuSlider').getStyle('margin-left'), - ($('ExtendedMenuSlider').getDepth(item) * width));
});
}
else if (item.hasClass('BackLinkContainer'))
{
item.addEvent('click', function(event)
{
if (event)
{
event = new Event(event);
event.stop();
}
var parent = item.getParent('ul').getParent('ul');
var height = (parent.getCoordinates().height > minHeight ? parent.getCoordinates().height : minHeight);
$('ExtendedMenuSlider').fxHeight.start($('ExtendedMenuSlider').getParent().getStyle('height'), height);
$('ExtendedMenuSlider').fx.start($('ExtendedMenuSlider').getStyle('margin-left'), -(($('ExtendedMenuSlider').getDepth(parent)) * width));
});
}
});
if ($$('.CurrentPage') && $$('.CurrentPage').length)
{
if ($$('.CurrentPage')[0].getElements('li').length)
{
$$('.CurrentPage')[0].fireEvent('click');
}
else
{
//                $$('.CurrentPage')[0].getParent().getParent().getElement('h2').fireEvent('click');
}
}
}
});/*
jQuery ColorBox v1.3.32 - 2013-01-31
(c) 2013 Jack Moore - jacklmoore.com/colorbox
license: http://www.opensource.org/licenses/mit-license.php
*/
(function ($, document, window) {
var
// Default settings object.
// See http://jacklmoore.com/colorbox for details.
defaults = {
transition: "elastic",
speed: 300,
width: false,
initialWidth: "600",
innerWidth: false,
maxWidth: false,
height: false,
initialHeight: "450",
innerHeight: false,
maxHeight: false,
scalePhotos: true,
scrolling: true,
inline: false,
html: false,
iframe: false,
fastIframe: true,
photo: false,
href: false,
title: false,
rel: false,
opacity: 0.9,
preloading: true,
className: false,
current: "image {current} of {total}",
previous: "previous",
next: "next",
close: "close",
xhrError: "This content failed to load.",
imgError: "This image failed to load.",
open: false,
returnFocus: true,
reposition: true,
loop: true,
slideshow: false,
slideshowAuto: true,
slideshowSpeed: 2500,
slideshowStart: "start slideshow",
slideshowStop: "stop slideshow",
onOpen: false,
onLoad: false,
onComplete: false,
onCleanup: false,
onClosed: false,
overlayClose: true,
escKey: true,
arrowKey: true,
top: false,
bottom: false,
left: false,
right: false,
fixed: false,
data: undefined
},
// Abstracting the HTML and event identifiers for easy rebranding
colorbox = 'colorbox',
prefix = 'cbox',
boxElement = prefix + 'Element',
// Events
event_open = prefix + '_open',
event_load = prefix + '_load',
event_complete = prefix + '_complete',
event_cleanup = prefix + '_cleanup',
event_closed = prefix + '_closed',
event_purge = prefix + '_purge',
// Special Handling for IE
isIE = !$.support.leadingWhitespace, // IE6 to IE8
isIE6 = isIE && !window.XMLHttpRequest, // IE6
event_ie6 = prefix + '_IE6',
// Cached jQuery Object Variables
$overlay,
$box,
$wrap,
$content,
$topBorder,
$leftBorder,
$rightBorder,
$bottomBorder,
$related,
$window,
$loaded,
$loadingBay,
$loadingOverlay,
$title,
$current,
$slideshow,
$next,
$prev,
$close,
$groupControls,
$events = $({}),
// Variables for cached values or use across multiple functions
settings,
interfaceHeight,
interfaceWidth,
loadedHeight,
loadedWidth,
element,
index,
photo,
open,
active,
closing,
loadingTimer,
publicMethod,
div = "div",
className,
init;
// ****************
// HELPER FUNCTIONS
// ****************
// Convience function for creating new jQuery objects
function $tag(tag, id, css) {
var element = document.createElement(tag);
if (id) {
element.id = prefix + id;
}
if (css) {
element.style.cssText = css;
}
return $(element);
}
// Determine the next and previous members in a group.
function getIndex(increment) {
var
max = $related.length,
newIndex = (index + increment) % max;
return (newIndex < 0) ? max + newIndex : newIndex;
}
// Convert '%' and 'px' values to integers
function setSize(size, dimension) {
return Math.round((/%/.test(size) ? ((dimension === 'x' ? $window.width() : $window.height()) / 100) : 1) * parseInt(size, 10));
}
// Checks an href to see if it is a photo.
// There is a force photo option (photo: true) for hrefs that cannot be matched by this regex.
function isImage(url) {
return settings.photo || /\.(gif|png|jp(e|g|eg)|bmp|ico)((#|\?).*)?$/i.test(url);
}
// Assigns function results to their respective properties
function makeSettings() {
var i,
data = $.data(element, colorbox);
if (data == null) {
settings = $.extend({}, defaults);
if (console && console.log) {
console.log('Error: cboxElement missing settings object');
}
} else {
settings = $.extend({}, data);
}
for (i in settings) {
if ($.isFunction(settings[i]) && i.slice(0, 2) !== 'on') { // checks to make sure the function isn't one of the callbacks, they will be handled at the appropriate time.
settings[i] = settings[i].call(element);
}
}
settings.rel = settings.rel || element.rel || $(element).data('rel') || 'nofollow';
settings.href = settings.href || $(element).attr('href');
settings.title = settings.title || element.title;
if (typeof settings.href === "string") {
settings.href = $.trim(settings.href);
}
}
function trigger(event, callback) {
// for external use
$(document).trigger(event);
// for internal use
$events.trigger(event);
if ($.isFunction(callback)) {
callback.call(element);
}
}
// Slideshow functionality
function slideshow() {
var
timeOut,
className = prefix + "Slideshow_",
click = "click." + prefix,
clear,
set,
start,
stop;
if (settings.slideshow && $related[1]) {
clear = function () {
clearTimeout(timeOut);
};
set = function () {
if (settings.loop || $related[index + 1]) {
timeOut = setTimeout(publicMethod.next, settings.slideshowSpeed);
}
};
start = function () {
$slideshow
.html(settings.slideshowStop)
.unbind(click)
.one(click, stop);
$events
.bind(event_complete, set)
.bind(event_load, clear)
.bind(event_cleanup, stop);
$box.removeClass(className + "off").addClass(className + "on");
};
stop = function () {
clear();
$events
.unbind(event_complete, set)
.unbind(event_load, clear)
.unbind(event_cleanup, stop);
$slideshow
.html(settings.slideshowStart)
.unbind(click)
.one(click, function () {
publicMethod.next();
start();
});
$box.removeClass(className + "on").addClass(className + "off");
};
if (settings.slideshowAuto) {
start();
} else {
stop();
}
} else {
$box.removeClass(className + "off " + className + "on");
}
}
function launch(target) {
if (!closing) {
element = target;
makeSettings();
$related = $(element);
index = 0;
if (settings.rel !== 'nofollow') {
$related = $('.' + boxElement).filter(function () {
var data = $.data(this, colorbox),
relRelated;
if (data) {
relRelated =  $(this).data('rel') || data.rel || this.rel;
}
return (relRelated === settings.rel);
});
index = $related.index(element);
// Check direct calls to ColorBox.
if (index === -1) {
$related = $related.add(element);
index = $related.length - 1;
}
}
if (!open) {
open = active = true; // Prevents the page-change action from queuing up if the visitor holds down the left or right keys.
// Show colorbox so the sizes can be calculated in older versions of jQuery
$box.css({visibility:'hidden', display:'block'});
$loaded = $tag(div, 'LoadedContent', 'width:0; height:0; overflow:hidden').appendTo($content);
// Cache values needed for size calculations
interfaceHeight = $topBorder.height() + $bottomBorder.height() + $content.outerHeight(true) - $content.height();//Subtraction needed for IE6
interfaceWidth = $leftBorder.width() + $rightBorder.width() + $content.outerWidth(true) - $content.width();
loadedHeight = $loaded.outerHeight(true);
loadedWidth = $loaded.outerWidth(true);
if (settings.returnFocus) {
$(element).blur();
$events.one(event_closed, function () {
$(element).focus();
});
}
$overlay.css({
opacity: parseFloat(settings.opacity),
cursor: settings.overlayClose ? "pointer" : "auto",
visibility: 'visible'
}).show();
// Opens inital empty ColorBox prior to content being loaded.
settings.w = setSize(settings.initialWidth, 'x');
settings.h = setSize(settings.initialHeight, 'y');
publicMethod.position();
if (isIE6) {
$window.bind('resize.' + event_ie6 + ' scroll.' + event_ie6, function () {
$overlay.css({width: $window.width(), height: $window.height(), top: $window.scrollTop(), left: $window.scrollLeft()});
}).trigger('resize.' + event_ie6);
}
slideshow();
trigger(event_open, settings.onOpen);
$groupControls.add($title).hide();
$close.html(settings.close).show();
}
publicMethod.load(true);
}
}
// ColorBox's markup needs to be added to the DOM prior to being called
// so that the browser will go ahead and load the CSS background images.
function appendHTML() {
if (!$box && document.body) {
init = false;
$window = $(window);
$box = $tag(div).attr({id: colorbox, 'class': isIE ? prefix + (isIE6 ? 'IE6' : 'IE') : ''}).hide();
$overlay = $tag(div, "Overlay", isIE6 ? 'position:absolute' : '').hide();
$loadingOverlay = $tag(div, "LoadingOverlay").add($tag(div, "LoadingGraphic"));
$wrap = $tag(div, "Wrapper");
$content = $tag(div, "Content").append(
$title = $tag(div, "Title"),
$current = $tag(div, "Current"),
$next = $tag(div, "Next"),
$prev = $tag(div, "Previous"),
$slideshow = $tag(div, "Slideshow"),
$close = $tag(div, "Close")
);
$wrap.append( // The 3x3 Grid that makes up ColorBox
$tag(div).append(
$tag(div, "TopLeft"),
$topBorder = $tag(div, "TopCenter"),
$tag(div, "TopRight")
),
$tag(div, false, 'clear:left').append(
$leftBorder = $tag(div, "MiddleLeft"),
$content,
$rightBorder = $tag(div, "MiddleRight")
),
$tag(div, false, 'clear:left').append(
$tag(div, "BottomLeft"),
$bottomBorder = $tag(div, "BottomCenter"),
$tag(div, "BottomRight")
)
).find('div div').css({'float': 'left'});
$loadingBay = $tag(div, false, 'position:absolute; width:9999px; visibility:hidden; display:none');
$groupControls = $next.add($prev).add($current).add($slideshow);
$(document.body).append($overlay, $box.append($wrap, $loadingBay));
}
}
// Add ColorBox's event bindings
function addBindings() {
function clickHandler(e) {
// ignore non-left-mouse-clicks and clicks modified with ctrl / command, shift, or alt.
// See: http://jacklmoore.com/notes/click-events/
if (!(e.which > 1 || e.shiftKey || e.altKey || e.metaKey)) {
e.preventDefault();
launch(this);
}
}
if ($box) {
if (!init) {
init = true;
// Anonymous functions here keep the public method from being cached, thereby allowing them to be redefined on the fly.
$next.click(function () {
publicMethod.next();
});
$prev.click(function () {
publicMethod.prev();
});
$close.click(function () {
publicMethod.close();
});
$overlay.click(function () {
if (settings.overlayClose) {
publicMethod.close();
}
});
// Key Bindings
$(document).bind('keydown.' + prefix, function (e) {
var key = e.keyCode;
if (open && settings.escKey && key === 27) {
e.preventDefault();
publicMethod.close();
}
if (open && settings.arrowKey && $related[1]) {
if (key === 37) {
e.preventDefault();
$prev.click();
} else if (key === 39) {
e.preventDefault();
$next.click();
}
}
});
if ($.isFunction($.fn.on)) {
$(document).on('click.'+prefix, '.'+boxElement, clickHandler);
} else { // For jQuery 1.3.x -> 1.6.x
$('.'+boxElement).live('click.'+prefix, clickHandler);
}
}
return true;
}
return false;
}
// Don't do anything if ColorBox already exists.
if ($.colorbox) {
return;
}
// Append the HTML when the DOM loads
$(appendHTML);
// ****************
// PUBLIC FUNCTIONS
// Usage format: $.fn.colorbox.close();
// Usage from within an iframe: parent.$.fn.colorbox.close();
// ****************
publicMethod = $.fn[colorbox] = $[colorbox] = function (options, callback) {
var $this = this;
options = options || {};
appendHTML();
if (addBindings()) {
if ($.isFunction($this)) { // assume a call to $.colorbox
$this = $('<a/>');
options.open = true;
} else if (!$this[0]) { // colorbox being applied to empty collection
return $this;
}
if (callback) {
options.onComplete = callback;
}
$this.each(function () {
$.data(this, colorbox, $.extend({}, $.data(this, colorbox) || defaults, options));
}).addClass(boxElement);
if (($.isFunction(options.open) && options.open.call($this)) || options.open) {
launch($this[0]);
}
}
return $this;
};
publicMethod.position = function (speed, loadedCallback) {
var
css,
top = 0,
left = 0,
offset = $box.offset(),
scrollTop,
scrollLeft;
$window.unbind('resize.' + prefix);
// remove the modal so that it doesn't influence the document width/height
$box.css({top: -9e4, left: -9e4});
scrollTop = $window.scrollTop();
scrollLeft = $window.scrollLeft();
if (settings.fixed && !isIE6) {
offset.top -= scrollTop;
offset.left -= scrollLeft;
$box.css({position: 'fixed'});
} else {
top = scrollTop;
left = scrollLeft;
$box.css({position: 'absolute'});
}
// keeps the top and left positions within the browser's viewport.
if (settings.right !== false) {
left += Math.max($window.width() - settings.w - loadedWidth - interfaceWidth - setSize(settings.right, 'x'), 0);
} else if (settings.left !== false) {
left += setSize(settings.left, 'x');
} else {
left += Math.round(Math.max($window.width() - settings.w - loadedWidth - interfaceWidth, 0) / 2);
}
if (settings.bottom !== false) {
top += Math.max($window.height() - settings.h - loadedHeight - interfaceHeight - setSize(settings.bottom, 'y'), 0);
} else if (settings.top !== false) {
top += setSize(settings.top, 'y');
} else {
top += Math.round(Math.max($window.height() - settings.h - loadedHeight - interfaceHeight, 0) / 2);
}
$box.css({top: offset.top, left: offset.left, visibility:'visible'});
// setting the speed to 0 to reduce the delay between same-sized content.
speed = ($box.width() === settings.w + loadedWidth && $box.height() === settings.h + loadedHeight) ? 0 : speed || 0;
// this gives the wrapper plenty of breathing room so it's floated contents can move around smoothly,
// but it has to be shrank down around the size of div#colorbox when it's done.  If not,
// it can invoke an obscure IE bug when using iframes.
$wrap[0].style.width = $wrap[0].style.height = "9999px";
function modalDimensions(that) {
$topBorder[0].style.width = $bottomBorder[0].style.width = $content[0].style.width = (parseInt(that.style.width,10) - interfaceWidth)+'px';
$content[0].style.height = $leftBorder[0].style.height = $rightBorder[0].style.height = (parseInt(that.style.height,10) - interfaceHeight)+'px';
}
css = {width: settings.w + loadedWidth + interfaceWidth, height: settings.h + loadedHeight + interfaceHeight, top: top, left: left};
if(speed===0){ // temporary workaround to side-step jQuery-UI 1.8 bug (http://bugs.jquery.com/ticket/12273)
$box.css(css);
}
$box.dequeue().animate(css, {
duration: speed,
complete: function () {
modalDimensions(this);
active = false;
// shrink the wrapper down to exactly the size of colorbox to avoid a bug in IE's iframe implementation.
$wrap[0].style.width = (settings.w + loadedWidth + interfaceWidth) + "px";
$wrap[0].style.height = (settings.h + loadedHeight + interfaceHeight) + "px";
if (settings.reposition) {
setTimeout(function () {  // small delay before binding onresize due to an IE8 bug.
$window.bind('resize.' + prefix, publicMethod.position);
}, 1);
}
if (loadedCallback) {
loadedCallback();
}
},
step: function () {
modalDimensions(this);
}
});
};
publicMethod.resize = function (options) {
if (open) {
options = options || {};
if (options.width) {
settings.w = setSize(options.width, 'x') - loadedWidth - interfaceWidth;
}
if (options.innerWidth) {
settings.w = setSize(options.innerWidth, 'x');
}
$loaded.css({width: settings.w});
if (options.height) {
settings.h = setSize(options.height, 'y') - loadedHeight - interfaceHeight;
}
if (options.innerHeight) {
settings.h = setSize(options.innerHeight, 'y');
}
if (!options.innerHeight && !options.height) {
$loaded.css({height: "auto"});
settings.h = $loaded.height();
}
$loaded.css({height: settings.h});
publicMethod.position(settings.transition === "none" ? 0 : settings.speed);
}
};
publicMethod.prep = function (object) {
if (!open) {
return;
}
var callback, speed = settings.transition === "none" ? 0 : settings.speed;
$loaded.empty().remove(); // Using empty first may prevent some IE7 issues.
$loaded = $tag(div, 'LoadedContent').append(object);
function getWidth() {
settings.w = settings.w || $loaded.width();
settings.w = settings.mw && settings.mw < settings.w ? settings.mw : settings.w;
return settings.w;
}
function getHeight() {
settings.h = settings.h || $loaded.height();
settings.h = settings.mh && settings.mh < settings.h ? settings.mh : settings.h;
return settings.h;
}
$loaded.hide()
.appendTo($loadingBay.show())// content has to be appended to the DOM for accurate size calculations.
.css({width: getWidth(), overflow: settings.scrolling ? 'auto' : 'hidden'})
.css({height: getHeight()})// sets the height independently from the width in case the new width influences the value of height.
.prependTo($content);
$loadingBay.hide();
// floating the IMG removes the bottom line-height and fixed a problem where IE miscalculates the width of the parent element as 100% of the document width.
//$(photo).css({'float': 'none', marginLeft: 'auto', marginRight: 'auto'});
$(photo).css({'float': 'none'});
callback = function () {
var total = $related.length,
iframe,
frameBorder = 'frameBorder',
allowTransparency = 'allowTransparency',
complete;
if (!open) {
return;
}
function removeFilter() {
if (isIE) {
$box[0].style.removeAttribute('filter');
}
}
complete = function () {
clearTimeout(loadingTimer);
$loadingOverlay.remove();
trigger(event_complete, settings.onComplete);
};
if (isIE) {
//This fadeIn helps the bicubic resampling to kick-in.
if (photo) {
$loaded.fadeIn(100);
}
}
$title.html(settings.title).add($loaded).show();
if (total > 1) { // handle grouping
if (typeof settings.current === "string") {
$current.html(settings.current.replace('{current}', index + 1).replace('{total}', total)).show();
}
$next[(settings.loop || index < total - 1) ? "show" : "hide"]().html(settings.next);
$prev[(settings.loop || index) ? "show" : "hide"]().html(settings.previous);
if (settings.slideshow) {
$slideshow.show();
}
// Preloads images within a rel group
if (settings.preloading) {
$.each([getIndex(-1), getIndex(1)], function(){
var src,
img,
i = $related[this],
data = $.data(i, colorbox);
if (data && data.href) {
src = data.href;
if ($.isFunction(src)) {
src = src.call(i);
}
} else {
src = i.href;
}
if (isImage(src)) {
img = new Image();
img.src = src;
}
});
}
} else {
$groupControls.hide();
}
if (settings.iframe) {
iframe = $tag('iframe')[0];
if (frameBorder in iframe) {
iframe[frameBorder] = 0;
}
if (allowTransparency in iframe) {
iframe[allowTransparency] = "true";
}
if (!settings.scrolling) {
iframe.scrolling = "no";
}
$(iframe)
.attr({
src: settings.href,
name: (new Date()).getTime(), // give the iframe a unique name to prevent caching
'class': prefix + 'Iframe',
allowFullScreen : true, // allow HTML5 video to go fullscreen
webkitAllowFullScreen : true,
mozallowfullscreen : true
})
.one('load', complete)
.appendTo($loaded);
$events.one(event_purge, function () {
iframe.src = "//about:blank";
});
if (settings.fastIframe) {
$(iframe).trigger('load');
}
} else {
complete();
}
if (settings.transition === 'fade') {
$box.fadeTo(speed, 1, removeFilter);
} else {
removeFilter();
}
};
if (settings.transition === 'fade') {
$box.fadeTo(speed, 0, function () {
publicMethod.position(0, callback);
});
} else {
publicMethod.position(speed, callback);
}
};
publicMethod.load = function (launched) {
var href, setResize, prep = publicMethod.prep, $inline;
active = true;
photo = false;
element = $related[index];
if (!launched) {
makeSettings();
}
if (className) {
$box.add($overlay).removeClass(className);
}
if (settings.className) {
$box.add($overlay).addClass(settings.className);
}
className = settings.className;
trigger(event_purge);
trigger(event_load, settings.onLoad);
settings.h = settings.height ?
setSize(settings.height, 'y') - loadedHeight - interfaceHeight :
settings.innerHeight && setSize(settings.innerHeight, 'y');
settings.w = settings.width ?
setSize(settings.width, 'x') - loadedWidth - interfaceWidth :
settings.innerWidth && setSize(settings.innerWidth, 'x');
// Sets the minimum dimensions for use in image scaling
settings.mw = settings.w;
settings.mh = settings.h;
// Re-evaluate the minimum width and height based on maxWidth and maxHeight values.
// If the width or height exceed the maxWidth or maxHeight, use the maximum values instead.
if (settings.maxWidth) {
settings.mw = setSize(settings.maxWidth, 'x') - loadedWidth - interfaceWidth;
settings.mw = settings.w && settings.w < settings.mw ? settings.w : settings.mw;
}
if (settings.maxHeight) {
settings.mh = setSize(settings.maxHeight, 'y') - loadedHeight - interfaceHeight;
settings.mh = settings.h && settings.h < settings.mh ? settings.h : settings.mh;
}
href = settings.href;
loadingTimer = setTimeout(function () {
$loadingOverlay.appendTo($content);
}, 100);
if (settings.inline) {
// Inserts an empty placeholder where inline content is being pulled from.
// An event is bound to put inline content back when ColorBox closes or loads new content.
$inline = $tag(div).hide().insertBefore($(href)[0]);
$events.one(event_purge, function () {
$inline.replaceWith($loaded.children());
});
prep($(href));
} else if (settings.iframe) {
// IFrame element won't be added to the DOM until it is ready to be displayed,
// to avoid problems with DOM-ready JS that might be trying to run in that iframe.
prep(" ");
} else if (settings.html) {
prep(settings.html);
} else if (isImage(href)) {
$(photo = new Image())
.addClass(prefix + 'Photo')
.bind('error',function () {
settings.title = false;
prep($tag(div, 'Error').html(settings.imgError));
})
.one('load', function () {
var percent;
if (settings.scalePhotos) {
setResize = function () {
photo.height -= photo.height * percent;
photo.width -= photo.width * percent;
};
if (settings.mw && photo.width > settings.mw) {
percent = (photo.width - settings.mw) / photo.width;
setResize();
}
if (settings.mh && photo.height > settings.mh) {
percent = (photo.height - settings.mh) / photo.height;
setResize();
}
}
if (settings.h) {
photo.style.marginTop = Math.max(settings.h - photo.height, 0) / 2 + 'px';
}
if ($related[1] && (settings.loop || $related[index + 1])) {
photo.style.cursor = 'pointer';
photo.onclick = function () {
publicMethod.next();
};
}
if (isIE) {
photo.style.msInterpolationMode = 'bicubic';
}
setTimeout(function () { // A pause because Chrome will sometimes report a 0 by 0 size otherwise.
prep(photo);
}, 1);
});
setTimeout(function () { // A pause because Opera 10.6+ will sometimes not run the onload function otherwise.
photo.src = href;
}, 1);
} else if (href) {
$loadingBay.load(href, settings.data, function (data, status) {
prep(status === 'error' ? $tag(div, 'Error').html(settings.xhrError) : $(this).contents());
});
}
};
// Navigates to the next page/image in a set.
publicMethod.next = function () {
if (!active && $related[1] && (settings.loop || $related[index + 1])) {
index = getIndex(1);
publicMethod.load();
}
};
publicMethod.prev = function () {
if (!active && $related[1] && (settings.loop || index)) {
index = getIndex(-1);
publicMethod.load();
}
};
// Note: to use this within an iframe use the following format: parent.$.fn.colorbox.close();
publicMethod.close = function () {
if (open && !closing) {
closing = true;
open = false;
trigger(event_cleanup, settings.onCleanup);
$window.unbind('.' + prefix + ' .' + event_ie6);
$overlay.fadeTo(200, 0);
$box.stop().fadeTo(300, 0, function () {
$box.add($overlay).css({'opacity': 1, cursor: 'auto'}).hide();
trigger(event_purge);
$loaded.empty().remove(); // Using empty first may prevent some IE7 issues.
setTimeout(function () {
closing = false;
trigger(event_closed, settings.onClosed);
}, 1);
});
}
};
// Removes changes ColorBox made to the document, but does not remove the plugin
// from jQuery.
publicMethod.remove = function () {
$([]).add($box).add($overlay).remove();
$box = null;
$('.' + boxElement)
.removeData(colorbox)
.removeClass(boxElement);
$(document).unbind('click.'+prefix);
};
// A method for fetching the current element ColorBox is referencing.
// returns a jQuery object.
publicMethod.element = function () {
return $(element);
};
publicMethod.settings = defaults;
}(jQuery, document, window));
jQuery(document).ready(function ($) {
$('a.internal-window').each(function (item, index) {
var href = $(this).attr('href'), rez,
type = $(this).data('internal-window-type');
if ((rez = href.match(/(youtube\.com|youtu\.be)\/(v\/|u\/|embed\/|watch\?v=)?([^#\&\?]*).*/i))) {
href = '//www.youtube.com/embed/' + rez[3] + '?autoplay=1&autohide=1&fs=1&rel=0&enablejsapi=1';
type = 'iframe';
} else if ((rez = href.match(/vimeo.com\/(\d+)\/?(.*)/))) {
href = '//player.vimeo.com/video/' + rez[1] + '?hd=1&autoplay=1&show_title=1&show_byline=1&show_portrait=0&color=&fullscreen=1';
type = 'iframe';
} else if ((rez = href.match(/metacafe.com\/watch\/(\d+)\/?(.*)/))) {
href = '//www.metacafe.com/fplayer/' + rez[1] + '/.swf?playerVars=autoPlay=yes';
type = 'iframe';
} else if ((rez = href.match(/dailymotion.com\/video\/(.*)\/?(.*)/))) {
href = '//www.dailymotion.com/swf/video/' + rez[1] + '?additionalInfos=0&autoStart=1';
type = 'iframe';
} else if ((rez = href.match(/twitvid\.com\/([a-zA-Z0-9_\-\?\=]+)/i))) {
href = '//www.twitvid.com/embed.php?autoplay=0&guid=' + rez[1];
type = 'iframe';
} else if ((rez = href.match(/twitpic\.com\/(?!(?:place|photos|events)\/)([a-zA-Z0-9\?\=\-]+)/i))) {
href = '//twitpic.com/show/full/' + rez[1];
type = 'image';
} else if ((rez = href.match(/(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i))) {
href = '//' + rez[1] + '/p/' + rez[2] + '/media/?size=l';
type = 'image';
} else if ((rez = href.match(/maps\.google\.com\/(\?ll=|maps\/?\?q=)(.*)/i))) {
href = 'https://maps.google.com/' + rez[1] + '' + rez[2] + '&output=' + (rez[2].indexOf('layer=c') ? 'svembed' : 'embed');
}
$(this).colorbox({
rel : $(this).data('internal-window-group') ? $(this).data('internal-window-group') : false,
iframe : (type && type == 'iframe'),
photo : (type && type == 'image'),
slideshow : $(this).data('internal-window-slideshow') ? $(this).data('internal-window-slideshow') : false,
innerWidth : ($(this).data('internal-window-width') ? $(this).data('internal-window-width') : false),
innerHeight : ($(this).data('internal-window-height') ? $(this).data('internal-window-height') : false),
maxWidth: window.getWidth(),
maxHeight: window.getHeight(),
href : href,
opacity : 0.8,
current: "{current}/{total}"
});
});
});
/**
* This class is used for the slideshow functionnalities
*
* @author Patrick Jordan <patrick.jordan@pyrameed.com>
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @event Event start This event is fired when the slideshow start
* @event Event stop This event is fired when the slideshow stops
* @event Event pagechange This event is fired when the page is changed
* @event Event imagechange This event is fired when the image is changed
*
*
* @param Element[] pages The pages of the slideshow
* @param Element[] images The images (contained in the pages) of the slideshow
* @param Element imageViewer The image viewer of the slideshow
* @param Element commentViewer The comment viewer of the slideshow
* @optionalparam Element floating The floating element to show selected image
*/
var Slideshow = new Class({
Implements : Events,
/**
* This is the initialization function
*/
initialize : function(images, imageViewer, commentViewer, floating) {
this._images = images;
this._viewer = imageViewer;
this._viewerImageContainer = this._viewer.getElement('div.Synergee-Web-Page-Component-SlideShow-Viewer-Image');
this._viewerImage = this._viewerImageContainer.getElement('img');
this._backImageContainer = this._viewer.getElement('div.Synergee-Web-Page-Component-SlideShow-Viewer-Back');
this._backImage = this._viewerImage.clone();
this._backImage.inject(this._backImageContainer);
this.container = imageViewer.getParent('.Synergee-Web-Page-Component-SlideShow');
// the menu width
this._menu = this.container.getElements('.Synergee-Web-Page-Component-SlideShow-Slides')[0];
this._menuWidth = this._menu.getWidth();
//        this._menuWidth = 525;
this._thumbWidth = this.container.getElements('.Synergee-Web-Page-Component-SlideShow-Slide-Container')[0].getWidth();
//        this._thumbWidth = 66;
this._scrollFx = new Fx.Tween(this._menu, { property : 'margin-left', duration:500});
this._fxImage = new Fx.Tween(this._viewerImageContainer, { property : 'opacity', duration:500});
this._comment = commentViewer;
// The event listener are added
if (this._viewerImage && typeof(this._viewerImage.addEvent) == 'function') {
this._viewerImage.addEvent('load', function(e) {
this._fxImage.set(1);
}.bind(this));
}
if (this._backImage && typeof(this._backImage.addEvent) == 'function') {
this._backImage.addEvent('load', this.centerViewerBackImage.bind(this));
}
this.addEvent('backImageReady', this._displayCurrentImage.bind(this));
if (this._viewerImage.complete) {
this.centerViewerImage();
}
images.each(function(el) {
el.imageJS = new Image();
el.imageJS.onload = function(e) {
this._resizeThumbnailImage(el);
}.bind(this);
if (el.imageJS.complete) {
this._resizeThumbnailImage(el);
}
el.imageJS.src = el.getElement('img').getProperty('src');
el.addEvent('click', function(e) {
this.selectImage(el);
this.stop();
}.bind(this));
}.bind(this));
this._currentImage = 0;
this._currentPage = 0;
this._floating = floating ? new Fx.Morph(floating, {duration: 500, wait: false}) : null;
this._isScrolling = false;
},
/**
* Resize all the thumbnail images
*/
resizeThumbnails : function() {
this._images.each(function(el) {
this._resizeThumbnailImage(el);
}.bind(this));
},
/**
* This function select an image from the slideshow
*
* @param Element oNode The image node
*/
selectImage : function(oNode) {
//currentImage = $$('div.Synergee-Web-Page-Component-SlideShow-Slide').indexOf(oNode);
if (oNode) {
if (this._floating) {
var top = oNode.getTop() - oNode.getParent().getParent().getParent().getTop();
var left = oNode.getLeft() - oNode.getParent().getParent().getParent().getLeft();
this._floating.start({
'top': top + 'px',
'left':left + 'px'
});
}
this._currentImage = this._images.indexOf(oNode);
//this._displayImage.delay(500, this, oNode);
this._displayImage(oNode);
this.fireEvent('imagechange');
}
},
/**
* This function display the image inside the viewer
*
* @param Element oNode The image node
*/
_displayImage : function(oNode) {
var thumbnailImage = oNode.getElement('img');
var nodePosition = this.container.getElements('.Synergee-Web-Page-Component-SlideShow-Slide').indexOf(oNode);
var menuMargin = this._menu.getStyle('margin-left').toInt();
var totalSlides = this.container.getElements('.Synergee-Web-Page-Component-SlideShow-Slide-Container').length;
// calculate the parent width
if(this._menuWidth < (this._thumbWidth * totalSlides) && (((this._thumbWidth * nodePosition) > this._menuWidth/1.5)
|| (-menuMargin*1.5 > (this._thumbWidth * nodePosition)))){
this._scrollFx.start(menuMargin, -(this._thumbWidth * nodePosition)/1.5);
}
this.container.getElements('.Synergee-Web-Page-Component-SlideShow-Slide').removeClass('active');
oNode.addClass('active');
this._backImage.setProperty('src', thumbnailImage.getProperty('src'));
this._backImage.setProperty('alt', thumbnailImage.getProperty('alt'));
},
/**
* This function display the image inside the viewer
*
* @param Element oNode The image node
*/
_displayCurrentImage : function() {
//        var thumbnailImage = this._currentNode;
// A fade in/fade out effect is added
this._fxImage.start(1, 0).chain(function() {
this._viewerImage.setProperty('src', this._backImage.getProperty('src'));
this._viewerImage.setProperty('alt', this._backImage.getProperty('alt'));
this._viewerImage.setProperty('style', this._backImage.getProperty('style'));
this._comment.set("html", this._backImage.getProperty('alt'));
}.bind(this));
},
/**
* Select the next image in the slideshow
*/
selectNextImage : function() {
if (this.hasNextImage() && !this._isScrolling) {
var next = this._images[++this._currentImage];
this.selectImage(next);
this.fireEvent('imagechange');
} else if (!this._isScrolling) {
this.selectImage(this._images[0]);
this.fireEvent('imagechange');
}
},
/**
* Select the previous image in the slideshow
*/
selectPreviousImage : function() {
if (this.hasPreviousImage() && !this._isScrolling) {
var previous = this._images[--this._currentImage];
this.selectImage(previous);
this.fireEvent('imagechange');
} else if (!this._isScrolling) {
this.selectImage(this._images[this._images.length - 1]);
this.fireEvent('pagechange');
this.fireEvent('imagechange');
}
},
/**
* Return if there is previous image
*
* @return Boolean
*/
hasPreviousImage : function() {
return this._currentImage > 0;
},
/**
* Return if there is next image
*
* @return Boolean
*/
hasNextImage : function() {
return this._currentImage < this._images.length - 1;
},
getImages : function () {
return this._images;
},
/**
* This function center the viewer image
*/
centerViewerImage : function() {
this._viewerImage.setStyle('margin-top', (this._viewer.getCoordinates().height - this._viewerImage.getCoordinates().height) / 2 + 'px');
this._viewerImage.setStyle('margin-bottom', (this._viewer.getCoordinates().height - this._viewerImage.getCoordinates().height) / 2 + 'px');
this._viewerImage.setStyle('margin-left', (this._viewer.getCoordinates().width - this._viewerImage.getCoordinates().width) / 2 + 'px');
this._viewerImage.setStyle('margin-right', (this._viewer.getCoordinates().width - this._viewerImage.getCoordinates().width) / 2 + 'px');
},
/**
* This function center the viewer image
*/
centerViewerBackImage : function() {
this._backImage.setStyle('margin-top', (this._viewer.getCoordinates().height - this._backImage.getCoordinates().height) / 2 + 'px');
this._backImage.setStyle('margin-bottom', (this._viewer.getCoordinates().height - this._backImage.getCoordinates().height) / 2 + 'px');
this._backImage.setStyle('margin-left', (this._viewer.getCoordinates().width - this._backImage.getCoordinates().width) / 2 + 'px');
this._backImage.setStyle('margin-right', (this._viewer.getCoordinates().width - this._backImage.getCoordinates().width) / 2 + 'px');
this.fireEvent('backImageReady');
},
/**
* This function resize and crop the thumbnail images
*
* @param Element e the element that contains the image
*/
_resizeThumbnailImage : function(e) {
var slideImage = e.getElement('img');
var slide = slideImage.getParent();
// The slides have to be rectangular
var containerWidth = slide.getCoordinates().width;
var containerHeight = slide.getCoordinates().height;
var containerRatio = containerWidth / containerHeight;
var imageWidth = slideImage.width;
var imageHeight = slideImage.height;
var imageRatio = imageWidth / imageHeight;
if (containerWidth > 0) {
if (imageHeight > imageWidth) {
slideImage.setStyle('width', containerWidth);
slideImage.setStyle('height', Math.round(containerWidth / imageRatio));
slideImage.setStyle('top', Math.round((containerHeight - Math.round(containerWidth / imageRatio)) / 2));
} else {
slideImage.setStyle('width', Math.round(containerWidth * imageRatio));
slideImage.setStyle('height', containerHeight);
slideImage.setStyle('left', Math.round((containerWidth - Math.round(containerWidth * imageRatio)) / 2));
}
}
},
/**
* Start to play the slideshow
*/
play : function() {
if (!this._timeoutID) {
this._timeoutID = this.selectNextImage.periodical(3000, this);
this.fireEvent('start');
}
},
/**
* Stop to play the slideshow
*/
stop : function(e) {
if (this._timeoutID)
this._timeoutID = clearTimeout(this._timeoutID);
this.fireEvent('stop');
},
/**
* Return true if the slide show is playing
*
* @return boolean true if the slide show is playing
*/
isPlaying : function () {
return this._timeoutID ? true : false;
},
/**
* Refresh the slideshow (viewer)
*/
refresh : function() {
this.centerViewerImage();
this.centerViewerBackImage();
this.resizeThumbnails();
}
});
/*
Copyright (c) 2006-7, Tom Carden, Steve Coast, Mikel Maron, Andrew Turner
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the Mapstraction nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Use http://jsdoc.sourceforge.net/ to generate documentation
////////////////////////////
//
// utility to functions, TODO namespace or remove before release
//
///////////////////////////
/**
* $, the dollar function, elegantising getElementById()
* @returns an element
*/
function $m() {
var elements = new Array();
for (var i = 0; i < arguments.length; i++) {
var element = arguments[i];
if (typeof element == 'string')
element = document.getElementById(element);
if (arguments.length == 1)
return element;
elements.push(element);
}
return elements;
}
/**
* loadScript is a JSON data fetcher
*/
function loadScript(src,callback) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
if (callback) {
var evl=new Object();
evl.handleEvent=function (e){callback();};
script.addEventListener('load',evl,true);
}
document.getElementsByTagName("head")[0].appendChild(script);
return;
}
function convertLatLonXY_Yahoo(point,level){ //Mercator
var size = 1 << (26 - level);
var pixel_per_degree = size / 360.0;
var pixel_per_radian = size / (2 * Math.PI);
var origin = new YCoordPoint(size / 2 , size / 2);
var answer = new YCoordPoint();
answer.x = Math.floor(origin.x + point.lon * pixel_per_degree);
var sin = Math.sin(point.lat * Math.PI / 180.0);
answer.y = Math.floor(origin.y + 0.5 * Math.log((1 + sin) / (1 - sin)) * -pixel_per_radian);
return answer;
}
/**
*
*/
function loadStyle(href) {
var link = document.createElement('link');
link.type = 'text/css';
link.rel = 'stylesheet';
link.href = href;
document.getElementsByTagName("head")[0].appendChild(link);
return;
}
/**
* getStyle provides cross-browser access to css
*/
function getStyle(el, prop) {
var y;
if (el.currentStyle)
y = el.currentStyle[prop];
else if (window.getComputedStyle)
y = window.getComputedStyle( el, '').getPropertyValue(prop);
return y;
}
// longitude to metres
// http://www.uwgb.edu/dutchs/UsefulData/UTMFormulas.HTM
// "A degree of longitude at the equator is 111.2km... For other latitudes,
// multiply by cos(lat)"
// assumes the earth is a sphere but good enough for our purposes
function lonToMetres (lon,lat) {
return lon * 111200 * Math.cos(lat * (Math.PI/180));
}
function metresToLon(m,lat) {
return m / (111200*Math.cos(lat * (Math.PI/180)));
}
// stuff to convert google zoom levels to/from degrees
// assumes zoom 0 = 256 pixels = 360 degrees
//         zoom 1 = 256 pixels = 180 degrees
// etc.
function getDegreesFromGoogleZoomLevel (pixels,zoom)
{
return (360*pixels) / (Math.pow(2,zoom+8));
}
function getGoogleZoomLevelFromDegrees (pixels,degrees)
{
return logN ((360*pixels)/degrees, 2) - 8;
}
function logN (number,base)
{
return Math.log(number) / Math.log(base);
}
/////////////////////////////
//
// Mapstraction proper begins here
//
/////////////////////////////
/**
* Mapstraction instantiates a map with some API choice into the HTML element given
* @param {String} element The HTML element to replace with a map
* @param {String} api The API to use, one of 'google', 'yahoo', 'microsoft', 'openstreetmap', 'multimap', 'map24', 'openlayers', 'mapquest'
* @param {Bool} debug optional parameter to turn on debug support - this uses alert panels for unsupported actions
* @constructor
*/
function Mapstraction(element,api,debug) {
this.api = api; // could detect this from imported scripts?
this.maps = new Object();
this.currentElement = $m(element);
this.eventListeners = new Array();
this.markers = new Array();
this.polylines = new Array();
this.images = new Array();
this.loaded = new Object();
this.onload = new Object();
// optional debug support
if(debug == true)
this.debug = true
else
this.debug = false
// This is so that it is easy to tell which revision of this file
// has been copied into other projects.
this.svn_revision_string = '$Revision$';
this.addControlsArgs = new Object();
this.addAPI($m(element),api);
}
/**
* swap will change the current api on the fly
* @param {String} api The API to swap to
*/
Mapstraction.prototype.swap = function(element,api) {
if (this.api == api) { return; }
var center = this.getCenter();
var zoom = this.getZoom();
this.currentElement.style.visibility = 'hidden';
this.currentElement.style.display = 'none';
this.currentElement = $m(element);
this.currentElement.style.visibility = 'visible';
this.currentElement.style.display = 'block';
this.api = api;
if (this.maps[this.api] == undefined) {
this.addAPI($m(element),api);
this.setCenterAndZoom(center,zoom);
for (var i=0; i<this.markers.length; i++) {
this.addMarker( this.markers[i], true);
}
for (var i=0; i<this.polylines.length; i++) {
this.addPolyline( this.polylines[i], true);
}
}else{
//sync the view
this.setCenterAndZoom(center,zoom);
//TODO synchronize the markers and polylines too
// (any overlays created after api instantiation are not sync'd)
}
this.addControls(this.addControlsArgs);
}
Mapstraction.prototype.addAPI = function(element,api) {
me = this;
this.loaded[api] = false;
this.onload[api] = new Array();
switch (api) {
case 'yahoo':
if (YMap) {
this.maps[api] = new YMap(element);
YEvent.Capture(this.maps[api],EventsList.MouseClick,function(event,location) { me.clickHandler(location.Lat,location.Lon,location,me) });
YEvent.Capture(this.maps[api],EventsList.changeZoom,function() { me.moveendHandler(me) });
YEvent.Capture(this.maps[api],EventsList.endPan,function() { me.moveendHandler(me) });
this.loaded[api] = true;
}
else {
alert('Yahoo map script not imported');
}
break;
case 'google':
if (GMap2) {
if (GBrowserIsCompatible()) {
this.maps[api] = new GMap2(element);
GEvent.addListener(this.maps[api], 'click', function(marker,location) {
// If the user puts their own Google markers directly on the map
// then there is no location and this event should not fire.
if ( location ) {
me.clickHandler(location.y,location.x,location,me);
}
});
GEvent.addListener(this.maps[api], 'moveend', function() {me.moveendHandler(me)});
this.loaded[api] = true;
}
else {
alert('browser not compatible with Google Maps');
}
}
else {
alert('Google map script not imported');
}
break;
case 'microsoft':
if (VEMap) {
element.style.position='relative';
var msft_width = parseFloat(getStyle($m(element),'width'));
var msft_height = parseFloat(getStyle($m(element),'height'));
/* Hack so the VE works with FF2 */
var ffv = 0;
var ffn = "Firefox/";
var ffp = navigator.userAgent.indexOf(ffn);
if (ffp != -1) ffv = parseFloat(navigator.userAgent.substring(ffp+ffn.length));
if (ffv >= 1.5) {
Msn.Drawing.Graphic.CreateGraphic=function(f,b) { return new Msn.Drawing.SVGGraphic(f,b) }
}
this.maps[api] = new VEMap(element.id);
this.maps[api].LoadMap();
this.maps[api].AttachEvent("onclick", function(e) { me.clickHandler(e.view.LatLong.Latitude, e.view.LatLong.Longitude, me); });
this.maps[api].AttachEvent("onchangeview", function(e) {me.moveendHandler(me)});
//Source of our trouble with Mapufacture?
this.resizeTo(msft_width, msft_height);
this.loaded[api] = true;
}
else {
alert('Virtual Earth script not imported');
}
break;
case 'openlayers':
this.maps[api] = new OpenLayers.Map(element.id);
this.loaded[api] = true;
break;
case 'openstreetmap':
// for now, osm is a hack on top of google
if (GMap2) {
if (GBrowserIsCompatible()) {
this.maps[api] = new GMap2(element);
GEvent.addListener(this.maps[api], 'click', function(marker,location) {
// If the user puts their own Google markers directly on the map
// then there is no location and this event should not fire.
if ( location ) {
me.clickHandler(location.y,location.x,location,me);
}
});
GEvent.addListener(this.maps[api], 'moveend', function() {me.moveendHandler(me)});
// Add OSM tiles
var copyright = new GCopyright(1, new GLatLngBounds(new GLatLng(-90,-180), new GLatLng(90,180)), 0, "copyleft");
var copyrightCollection = new GCopyrightCollection('OSM');
copyrightCollection.addCopyright(copyright);
var tilelayers = new Array();
tilelayers[0] = new GTileLayer(copyrightCollection, 1, 18);
tilelayers[0].getTileUrl = function (a, b) {
return "http://tile.openstreetmap.org/"+b+"/"+a.x+"/"+a.y+".png";
};
tilelayers[0].isPng = function() { return true;};
tilelayers[0].getOpacity = function() { return 1.0; }
var custommap = new GMapType(tilelayers, new GMercatorProjection(19), "OSM", {errorMessage:"More OSM coming soon"});
this.maps[api].addMapType(custommap);
this.loaded[api] = true;
var myPoint = new LatLonPoint(50.6805,-1.4062505);
this.setCenterAndZoom(myPoint, 11);
this.maps[api].setMapType(custommap);
}
else {
alert('browser not compatible with Google Maps');
}
}
else {
alert('Google map script not imported');
}
break;
case 'multimap':
this.maps[api] = new MultimapViewer( element );
this.maps[api].addEventHandler( 'click', function(eventType, eventTarget, arg1, arg2, arg3) {
if (arg1) {
me.clickHandler(arg1.lat, arg1.lon, me);
}
});
this.maps[api].addEventHandler( 'changeZoom', function(eventType, eventTarget, arg1, arg2, arg3) {
me.moveendHandler(me);
});
this.maps[api].addEventHandler( 'endPan', function(eventType, eventTarget, arg1, arg2, arg3) {
me.moveendHandler(me);
});
this.loaded[api] = true;
break;
case 'map24':
// Copied from Google and modified
if (Map24) {
Map24.loadApi(["core_api","wrapper_api"] , function() {
Map24.MapApplication.init
( { NodeName: element.id, MapType: "Static" } );
me.maps[api] = Map24.MapApplication.Map;
Map24.MapApplication.Map.addListener('Map24.Event.MapClick',
function(e) {
me.clickHandler(e.Coordinate.Latitude/60,
e.Coordinate.Longitude/60,
me);
e.stop();
}
);
Map24.MapApplication.Map.addListener("MapPanStop",
function(e) {
me.moveendHandler(me);
}
);
/**/
var client=Map24.MapApplication.Map.MapClient['Static'];
/*
These *will* cause the specified listener to run when we stop
panning the map, but the default pan stop handler will be
cancelled. The result of this will be that when we have stopped
panning, we will permanently be in 'pan' mode and unable to do
anything else (e.g. click on the map to create a new marker).
var defaultOnPanStop = client.onPanStop;
var defaultOnZoomInStop = client.onZoomInStop;
var defaultOnZoomOutStop = client.onZoomOutStop;
client.onPanStop = function(e)
{ me.moveendHandler(me); defaultOnPanStop(e);
status('DEFAULTONPANSTOP DONE');}
// Handle zoom events - these also fire moveendHandler for the
// other APIs in Mapstraction
client.onZoomInStop = function(e)
{ me.moveendHandler(me); defaultOnZoomInStop(e); }
client.onZoomOutStop = function(e)
{ me.moveendHandler(me); defaultOnZoomOutStop(e);  }
*/
me.loaded[api] = true;
for (var i = 0; i < me.onload[api].length; i++) {
me.onload[api][i]();
}
}, "2.0.1247" );
} else {
alert('map24 api not loaded');
}
break;
case 'mapquest':
MQInitOverlays( function() {
self.loaded[api] = true;
self.maps[api] = new MQTileMap(element);
for (var i = 0; i < self.onload[api].length; i++) {
self.onload[api][i]();
}
});
// MQEventManager.addListener(this.maps[api],"click",function(event,location) { me.clickHandler(location.Lat,location.Lon,location,me) });
// MQEventManager.addListener(this.maps[api],"zoomend",function() { me.moveendHandler(me) });
// MQEventManager.addListener(this.maps[api],"moveend",function() { me.moveendHandler(me) });
break;
case 'freeearth':
this.maps[api] = new FE.Map($m(element));
self = this;
this.maps[api].onLoad = function() {
self.freeEarthLoaded = true;
self.loaded[api] = true;
for (var i = 0; i < self.onload[api].length; i++) {
self.onload[api][i]();
}
}
this.maps[api].load();
break;
default:
if(this.debug)
alert(api + ' not supported by mapstraction');
}
// this.resizeTo(getStyle($m(element),'width'), getStyle($m(element),'height'));
// the above line was called on all APIs but MSFT alters with the div size when it loads
// so you have to find the dimensions and set them again (see msft constructor).
// FIXME: test if google/yahoo etc need this resize called. Also - getStyle returns
// CSS size ('200px') not an integer, and resizeTo seems to expect ints
}
/* Returns the loaded state of a Map Provider
*
* @param {String} api Optional API to query for. If not specified, returns state of the originally created API
* @returns the state of the map loading
* @type Boolean
*/
Mapstraction.prototype.isLoaded = function(api){
if(api == null)
api = this.api;
return this.loaded[api];
}
/* Set the debugging on or off - shows alert panels for functions that don't exist in Mapstraction
*
* @param {Bool} debug true to turn on debugging, false to turn it off
* @returns the state of debugging
* @type Boolean
*/
Mapstraction.prototype.setDebug = function(debug){
if(debug != null)
return this.debug = debug;
else
return this.debug;
}
/* Resize the current map to the specified width and height
* (since it is actually on a child div of the mapElement passed
* as argument to the Mapstraction constructor, the resizing of this
* mapElement may have no effect on the size of the actual map)
*
* @param {int} width The width the map should be.
* @param {int} height The width the map should be.
*/
Mapstraction.prototype.resizeTo = function(width,height){
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.resizeTo(width,height); } );
return;
}
switch (this.api) {
case 'yahoo':
this.maps[this.api].resizeTo(new YSize(width,height));
break;
case 'google':
case 'openstreetmap':
this.currentElement.style.width = width;
this.currentElement.style.height = height;
this.maps[this.api].checkResize();
break;
case 'microsoft':
this.maps[this.api].Resize(width, height);
break;
case 'multimap':
this.currentElement.style.width = width;
this.currentElement.style.height = height;
this.maps[this.api].resize();
break;
case 'mapquest':
this.currentElement.style.width = width;
this.currentElement.style.height = height;
this.maps[this.api].setSize(new MQSize(width, height));
break;
case 'map24':
Map24.MapApplication.Map.Canvas['c'].resizeTo(width,height);
break;
}
}
/////////////////////////
//
// Event Handling
//
///////////////////////////
Mapstraction.prototype.clickHandler = function(lat,lon, me) { //FIXME need to consolidate some of these handlers...
for(var i = 0; i < this.eventListeners.length; i++) {
if(this.eventListeners[i][1] == 'click') {
this.eventListeners[i][0](new LatLonPoint(lat,lon));
}
}
}
Mapstraction.prototype.moveendHandler = function(me) {
for(var i = 0; i < this.eventListeners.length; i++) {
if(this.eventListeners[i][1] == 'moveend') {
this.eventListeners[i][0]();
}
}
}
Mapstraction.prototype.addEventListener = function(type, func) {
var listener = new Array();
listener.push(func);
listener.push(type);
this.eventListeners.push(listener);
}
////////////////////
//
// map manipulation
//
/////////////////////
/**
* addControls adds controls to the map. You specify which controls to add in
* the associative array that is the only argument.
* addControls can be called multiple time, with different args, to dynamically change controls.
*
* args = {
*     pan:      true,
*     zoom:     'large' || 'small',
*     overview: true,
*     scale:    true,
*     map_type: true,
* }
*
* @param {args} array Which controls to switch on
*/
Mapstraction.prototype.addControls = function( args ) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.addControls(args); } );
return;
}
var map = this.maps[this.api];
this.addControlsArgs = args;
switch (this.api) {
case 'google':
case 'openstreetmap':
//remove old controls
if (this.controls) {
while (ctl = this.controls.pop()) {
map.removeControl(ctl);
}
} else {
this.controls = new Array();
}
c = this.controls;
// Google has a combined zoom and pan control.
if ( args.zoom || args.pan ) {
if ( args.zoom == 'large' ) {
c.unshift(new GLargeMapControl());
map.addControl(c[0]);
} else {
c.unshift(new GSmallMapControl());
map.addControl(c[0]);
}
}
if ( args.scale    ) { c.unshift(new GScaleControl()); map.addControl(c[0]); }
if (this.api != "openstreetmap") {
if ( args.overview ) { c.unshift(new GOverviewMapControl()); map.addControl(c[0]); }
if ( args.map_type ) { c.unshift(new GMapTypeControl()); map.addControl(c[0]); }
}
break;
case 'yahoo':
if ( args.pan             ) map.addPanControl();
else map.removePanControl();
if ( args.zoom == 'large' ) map.addZoomLong();
else if ( args.zoom == 'small' ) map.addZoomShort();
else map.removeZoomScale();
break;
case 'openlayers':
// FIXME - which one should this be?
map.addControl(new OpenLayers.Control.LayerSwitcher());
break;
case 'multimap':
//FIXME -- removeAllWidgets();  -- can't call addControls repeatedly
pan_zoom_widget = "MM";
if (args.zoom && args.zoom == "small") { pan_zoom_widget = pan_zoom_widget + "Small"; }
if (args.pan) { pan_zoom_widget = pan_zoom_widget + "Pan"; }
if (args.zoom) { pan_zoom_widget = pan_zoom_widget + "Zoom"; }
pan_zoom_widget = pan_zoom_widget + "Widget";
if (pan_zoom_widget != "MMWidget") {
eval(" map.addWidget( new " + pan_zoom_widget + "() );");
}
if ( args.map_type ) { map.addWidget( new MMMapTypeWidget() ); }
if ( args.overview ) { map.addWidget( new MMOverviewWidget() ); }
break;
case 'mapquest':
//remove old controls
if (this.controls) {
while (ctl = this.controls.pop()) {
map.removeControl(ctl);
}
} else {
this.controls = new Array();
}
c = this.controls;
if ( args.pan ) { c.unshift(new MQPanControl()); map.addControl(c[0], new MQMapCornerPlacement(MQMapCorner.TOP_LEFT, new MQSize(0,0))); }
if ( args.zoom == 'large' ) { c.unshift(new MQLargeZoomControl()); map.addControl(c[0], new MQMapCornerPlacement(MQMapCorner.TOP_LEFT, new MQSize(0,0))); }
else if ( args.zoom == 'small' ) { c.unshift(new MQZoomControl()); map.addControl(c[0],  new MQMapCornerPlacement(MQMapCorner.BOTTOM_LEFT, new MQSize(0,0))); }
// TODO: Map View Control is wonky
if ( args.map_type ) { c.unshift(new MQViewControl()); map.addControl(c[0], new MQMapCornerPlacement(MQMapCorner.TOP_RIGHT, new MQSize(0,0))); }
break;
}
}
/**
* addSmallControls adds a small map panning control and zoom buttons to the map
* Supported by: yahoo, google, openstreetmap, openlayers, multimap, mapquest
*/
Mapstraction.prototype.addSmallControls = function() {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.addSmallControls(); } );
return;
}
var map = this.maps[this.api];
switch (this.api) {
case 'yahoo':
map.addPanControl();
map.addZoomShort();
this.addControlsArgs.pan = true;
this.addControlsArgs.zoom = 'small';
break;
case 'google':
case 'openstreetmap':
map.addControl(new GSmallMapControl());
this.addControlsArgs.zoom = 'small';
break;
case 'openlayers':
map.addControl(new OpenLayers.Control.LayerSwitcher());
break;
case 'multimap':
smallPanzoomWidget = new MMSmallPanZoomWidget();
map.addWidget( smallPanzoomWidget );
this.addControlsArgs.pan = true;
this.addControlsArgs.zoom = 'small';
break;
case 'mapquest':
map.addControl(new MQZoomControl(map));
map.addControl(new PanControl(map));
this.addControlsArgs.pan = true;
this.addControlsArgs.zoom = 'small';
break;
}
}
/**
* addLargeControls adds a small map panning control and zoom buttons to the map
* Supported by: yahoo, google, openstreetmap, multimap, mapquest
*/
Mapstraction.prototype.addLargeControls = function() {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.addLargeControls(); } );
return;
}
var map = this.maps[this.api];
switch (this.api) {
case 'yahoo':
map.addPanControl();
map.addZoomLong();
this.addControlsArgs.pan = true;  // keep the controls in case of swap
this.addControlsArgs.zoom = 'large';
break;
case 'google':
map.addControl(new GMapTypeControl());
map.addControl(new GOverviewMapControl()) ;
this.addControlsArgs.overview = true;
this.addControlsArgs.map_type = true;
case 'openstreetmap':
map.addControl(new GLargeMapControl());
map.addControl(new GScaleControl()) ;
this.addControlsArgs.pan = true;
this.addControlsArgs.zoom = 'large';
this.addControlsArgs.scale = true;
break;
case 'multimap':
panzoomWidget = new MMPanZoomWidget();
map.addWidget( panzoomWidget );
this.addControlsArgs.pan = true;  // keep the controls in case of swap
this.addControlsArgs.zoom = 'large';
break;
case 'mapquest':
map.addControl(new MQLargeZoomControl(map));
map.addControl(new PanControl(map));
map.addControl(new MQViewControl(map));
this.addControlsArgs.pan = true;
this.addControlsArgs.zoom = 'large';
this.addControlsArgs.map_type = true;
break;
}
}
/**
* addMapTypeControls adds a map type control to the map (streets, aerial imagery etc)
* Supported by: yahoo, google, openstreetmap, multimap, mapquest
*/
Mapstraction.prototype.addMapTypeControls = function() {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.addMapTypeControls(); } );
return;
}
var map = this.maps[this.api];
switch (this.api) {
case 'yahoo':
map.addTypeControl();
break;
case 'google':
case 'openstreetmap':
map.addControl(new GMapTypeControl());
break;
case 'multimap':
map.addWidget( new MMMapTypeWidget() );
break;
case 'mapquest':
map.addControl(new MQViewControl(map));
break;
}
}
/**
* dragging
*  enable/disable dragging of the map
*  (only implemented for yahoo and google)
* Supported by: yahoo, google, openstreetmap, multimap
* @param {on} on Boolean
*/
Mapstraction.prototype.dragging = function(on) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.dragging(on); } );
return;
}
var map = this.maps[this.api];
switch (this.api) {
case 'google':
case 'openstreetmap':
if (on) {
map.enableDragging();
} else {
map.disableDragging();
}
break;
case 'yahoo':
if (on) {
map.enableDragMap();
} else {
map.disableDragMap();
}
break;
case 'multimap':
if (on) {
map.setOption("drag","dragmap");
} else {
map.setOption("drag","");
}
break;
case 'mapquest':
map.enableDragging(on);
break;
}
}
/**
* centers the map to some place and zoom level
* @param {LatLonPoint} point Where the center of the map should be
* @param {int} zoom The zoom level where 0 is all the way out.
*/
Mapstraction.prototype.setCenterAndZoom = function(point, zoom) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.setCenterAndZoom(point, zoom); } );
return;
}
var map = this.maps[this.api];
switch (this.api) {
case 'yahoo':
var yzoom = 18 - zoom; // maybe?
map.drawZoomAndCenter(point.toYahoo(),yzoom);
break;
case 'google':
case 'openstreetmap':
map.setCenter(point.toGoogle(), zoom);
break;
case 'microsoft':
map.SetCenterAndZoom(point.toMicrosoft(),zoom);
break;
case 'openlayers':
map.setCenter(new OpenLayers.LonLat(point.lng, point.lat), zoom);
break;
case 'multimap':
map.goToPosition( new MMLatLon( point.lat, point.lng ) );
map.setZoomFactor( zoom );
break;
case 'map24':
var newSettings = new Object();
newSettings.Latitude = point.lat*60;
newSettings.Longitude = point.lon*60;
var client = map.MapClient['Static'];
var dLon = getDegreesFromGoogleZoomLevel
(client.getCanvasSize().Width,zoom);
newSettings.MinimumWidth = lonToMetres (dLon, point.lat);
Map24.MapApplication.center ( newSettings );
break;
case 'mapquest':
// MapQuest's zoom levels appear to be off by '3' from the other providers for the same bbox
map.setCenter(new MQLatLng( point.lat, point.lng ), zoom - 3 );
break;
case 'freeearth':
if (this.freeEarthLoaded) {
map.setTargetLatLng( point.toFreeEarth() );
} else {
self = this;
this.freeEarthOnLoad.push( function() { self.setCenterAndZoom(point); } );
}
break;
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.setCenterAndZoom');
}
}
/**
* addMarker adds a marker pin to the map
* @param {Marker} marker The marker to add
* @param {old} old If true, doesn't add this marker to the markers array. Used by the "swap" method
*/
Mapstraction.prototype.addMarker = function(marker,old) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.addMarker(marker, old); } );
return;
}
var map = this.maps[this.api];
marker.api = this.api;
marker.map = this.maps[this.api];
switch (this.api) {
case 'yahoo':
var ypin = marker.toYahoo();
marker.setChild(ypin);
map.addOverlay(ypin);
if (! old) { this.markers.push(marker); }
break;
case 'google':
case 'openstreetmap':
var gpin = marker.toGoogle();
marker.setChild(gpin);
map.addOverlay(gpin);
if (! old) { this.markers.push(marker); }
break;
case 'microsoft':
var mpin = marker.toMicrosoft();
marker.setChild(mpin); // FIXME: MSFT maps remove the pin by pinID so this isn't needed?
map.AddPushpin(mpin);
if (! old) { this.markers.push(marker); }
break;
case 'openlayers':
//this.map.addPopup(new OpenLayers.Popup("chicken", new OpenLayers.LonLat(5,40), new OpenLayers.Size(200,200), "example popup"));
break;
case 'multimap':
var mmpin = marker.toMultiMap();
marker.setChild(mmpin);
map.addOverlay(mmpin);
if (! old) { this.markers.push(marker); }
break;
case 'map24':
var m24pin = marker.toMap24();
marker.setChild(m24pin);
m24pin.commit();
if (! old) { this.markers.push(marker); }
break;
case 'mapquest':
var mqpin = marker.toMapQuest();
marker.setChild(mqpin);
map.addPoi(mqpin);
if (! old) { this.markers.push(marker); }
break;
case 'freeearth':
var fepin = marker.toFreeEarth();
marker.setChild(fepin);
map.addOverlay(fepin);
if (! old) { this.markers.push(marker); }
break;
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.addMarker');
}
}
/**
* addMarkerWithData will addData to the marker, then add it to the map
* @param{marker} marker The marker to add
* @param{data} data A data has to add
*/
Mapstraction.prototype.addMarkerWithData = function(marker,data) {
marker.addData(data);
this.addMarker(marker);
}
/**
* addPolylineWithData will addData to the polyline, then add it to the map
* @param{polyline} polyline The polyline to add
* @param{data} data A data has to add
*/
Mapstraction.prototype.addPolylineWithData = function(polyline,data) {
polyline.addData(data);
this.addPolyline(polyline);
}
/**
* removeMarker removes a Marker from the map
* @param {Marker} marker The marker to remove
*/
Mapstraction.prototype.removeMarker = function(marker) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.removeMarker(marker); } );
return;
}
var map = this.maps[this.api];
var tmparray = new Array();
while(this.markers.length > 0){
current_marker = this.markers.pop();
if(marker == current_marker) {
switch (this.api) {
case 'google':
case 'openstreetmap':
map.removeOverlay(marker.proprietary_marker);
break;
case 'yahoo':
map.removeOverlay(marker.proprietary_marker);
break;
case 'microsoft':
map.DeletePushpin(marker.pinID);
break;
case 'multimap':
map.removeOverlay(marker.proprietary_marker);
break;
case 'mapquest':
map.removePoi(marker.proprietary_marker);
break;
case 'map24':
marker.proprietary_marker.remove();
break;
}
marker.onmap = false;
break;
} else {
tmparray.push(current_marker);
}
}
this.markers = this.markers.concat(tmparray);
}
/**
* removeAllMarkers removes all the Markers on a map
*/
Mapstraction.prototype.removeAllMarkers = function() {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.removeAllMarkers(); } );
return;
}
var map = this.maps[this.api];
switch (this.api) {
case 'yahoo':
map.removeMarkersAll();
break;
case 'google':
case 'openstreetmap':
map.clearOverlays();
break;
case 'microsoft':
map.DeleteAllPushpins();
break;
case 'multimap':
map.removeAllOverlays();
break;
case 'mapquest':
map.removeAllPois();
break;
case 'map24':
// don't think map24 has a specific method for this
var current_marker;
while(this.markers.length > 0) {
current_marker = this.markers.pop();
current_marker.proprietary_marker.remove();
}
break;
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.removeAllMarkers');
}
this.markers = new Array(); // clear the mapstraction list of markers too
}
/**
* Add a polyline to the map
*/
Mapstraction.prototype.addPolyline = function(polyline,old) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.addPolyline(polyline,old); } );
return;
}
var map = this.maps[this.api];
switch (this.api) {
case 'yahoo':
ypolyline = polyline.toYahoo();
polyline.setChild(ypolyline);
map.addOverlay(ypolyline);
if(!old) {this.polylines.push(polyline);}
break;
case 'google':
case 'openstreetmap':
gpolyline = polyline.toGoogle();
polyline.setChild(gpolyline);
map.addOverlay(gpolyline);
if(!old) {this.polylines.push(polyline);}
break;
case 'microsoft':
mpolyline = polyline.toMicrosoft();
polyline.setChild(mpolyline);
map.AddPolyline(mpolyline);
if(!old) {this.polylines.push(polyline);}
break;
case 'openlayers':
if(this.debug)
alert(this.api + ' not supported by Mapstraction.addPolyline');
break;
case 'multimap':
mmpolyline = polyline.toMultiMap();
polyline.setChild(mmpolyline);
map.addOverlay( mmpolyline );
if(!old) {this.polylines.push(polyline);}
break;
case 'mapquest':
mqpolyline = polyline.toMapQuest();
polyline.setChild(mqpolyline);
map.addOverlay( mqpolyline );
if(!old) {this.polylines.push(polyline);}
break;
case 'map24':
var m24polyline = polyline.toMap24();
polyline.setChild(m24polyline);
m24polyline.commit();
if(!old) {this.polylines.push(polyline);}
break;
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.addPolyline');
}
}
/**
* Remove the polyline from the map
*/
Mapstraction.prototype.removePolyline = function(polyline) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.removePolyline(polyline); } );
return;
}
var map = this.maps[this.api];
var tmparray = new Array();
while(this.polylines.length > 0){
current_polyline = this.polylines.pop();
if(polyline == current_polyline) {
switch (this.api) {
case 'google':
case 'openstreetmap':
map.removeOverlay(polyline.proprietary_polyline);
break;
case 'yahoo':
map.removeOverlay(polyline.proprietary_polyline);
break;
case 'microsoft':
map.DeletePolyline(polyline.pllID);
break;
case 'multimap':
polyline.proprietary_polyline.remove();
break;
case 'mapquest':
map.removeOverlay(polyline.proprietary_polyline);
break;
case 'map24':
polyline.proprietary_polyline.remove();
break;
}
polyline.onmap = false;
break;
} else {
tmparray.push(current_polyline);
}
}
this.polylines = this.polylines.concat(tmparray);
}
/**
* Removes all polylines from the map
*/
Mapstraction.prototype.removeAllPolylines = function() {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.removeAllPolylines(); } );
return;
}
var map = this.maps[this.api];
switch (this.api) {
case 'yahoo':
for(var i = 0, length = this.polylines.length;i < length;i++){
map.removeOverlay(this.polylines[i].proprietary_polyline);
}
break;
case 'google':
case 'openstreetmap':
for(var i = 0, length = this.polylines.length;i < length;i++){
map.removeOverlay(this.polylines[i].proprietary_polyline);
}
break;
case 'microsoft':
map.DeleteAllPolylines();
break;
case 'multimap':
for(var i = 0, length = this.polylines.length;i < length;i++){
this.polylines[i].proprietary_polyline.remove();
}
break;
case 'mapquest':
map.removeAllOverlays();
break;
case 'map24':
// don't think map24 has a specific method for this
var current_polyline;
while(this.polylines.length > 0) {
current_polyline = this.polylines.pop();
current_polyline.proprietary_polyline.remove();
}
break;
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.removeAllPolylines');
}
this.polylines = new Array();
}
/**
* getCenter gets the central point of the map
* @returns  the center point of the map
* @type LatLonPoint
*/
Mapstraction.prototype.getCenter = function() {
if(this.loaded[this.api] == false) {
return null;
}
var map = this.maps[this.api];
var point = undefined;
switch (this.api) {
case 'yahoo':
var pt = map.getCenterLatLon();
point = new LatLonPoint(pt.Lat,pt.Lon);
break;
case 'google':
case 'openstreetmap':
var pt = map.getCenter();
point = new LatLonPoint(pt.lat(),pt.lng());
break;
case 'microsoft':
var pt = map.GetCenter();
point = new LatLonPoint(pt.Latitude,pt.Longitude);
break;
case 'multimap':
var pt = map.getCurrentPosition();
point = new LatLonPoint(pt.y, pt.x);
break;
case 'mapquest':
var pt = map.getCenter();
point = new LatLonPoint(pt.getLatitude(), pt.getLongitude());
break;
case 'map24':
var pt = map.MapClient['Static'].getCurrentMapView().getCenter();
point = new LatLonPoint(pt.Y/60,pt.X/60);
break;
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.getCenter');
}
return point;
}
/**
* setCenter sets the central point of the map
* @param {LatLonPoint} point The point at which to center the map
*/
Mapstraction.prototype.setCenter = function(point) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.setCenter(point); } );
return;
}
var map = this.maps[this.api];
switch (this.api) {
case 'yahoo':
map.panToLatLon(point.toYahoo());
break;
case 'google':
case 'openstreetmap':
map.setCenter(point.toGoogle());
break;
case 'microsoft':
map.SetCenter(point.toMicrosoft());
break;
case 'multimap':
map.goToPosition(point.toMultiMap());
break;
case 'mapquest':
map.setCenter(point.toMapQuest());
break;
case 'freeearth':
if (this.freeEarthLoaded) {
map.setTargetLatLng( point.toFreeEarth() );
} else {
self = this;
this.freeEarthOnLoad.push( function() { self.setCenterAndZoom(point); }
);
}
break;
case 'map24':
// Since center changes the zoom level to default
// we have to get the original metre width and pass it back in when
// centering.
var mv = map.MapClient['Static'].getCurrentMapView();
var newSettings = new Object();
newSettings.MinimumWidth = lonToMetres
(mv.LowerRight.Longitude - mv.TopLeft.Longitude,
(mv.LowerRight.Latitude+mv.TopLeft.Latitude)/2);
newSettings.Latitude =  point.lat*60;
newSettings.Longitude = point.lon*60;
Map24.MapApplication.center(newSettings);
break;
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.setCenter');
}
}
/**
* setZoom sets the zoom level for the map
* MS doesn't seem to do zoom=0, and Gg's sat goes closer than it's maps, and MS's sat goes closer than Y!'s
* TODO: Mapstraction.prototype.getZoomLevels or something.
* @param {int} zoom The (native to the map) level zoom the map to.
*/
Mapstraction.prototype.setZoom = function(zoom) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.setZoom(zoom); } );
return;
}
var map = this.maps[this.api];
switch (this.api) {
case 'yahoo':
var yzoom = 18 - zoom; // maybe?
map.setZoomLevel(yzoom);
break;
case 'google':
case 'openstreetmap':
map.setZoom(zoom);
break;
case 'microsoft':
map.SetZoomLevel(zoom);
break;
case 'multimap':
map.setZoomFactor(zoom);
break;
case 'mapquest':
map.setZoomLevel(zoom - 3); // MapQuest seems off by 3
break;
case 'map24':
// get the current centre than calculate the settings based on this
var point = this.getCenter();
var newSettings = new Object();
newSettings.Latitude = point.lat*60;
newSettings.Longitude = point.lon*60;
var client = map.MapClient['Static'];
var dLon = getDegreesFromGoogleZoomLevel
(client.getCanvasSize().Width,zoom);
newSettings.MinimumWidth = lonToMetres (dLon, point.lat);
Map24.MapApplication.center ( newSettings );
break;
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.setZoom');
}
}
/**
* autoCenterAndZoom sets the center and zoom of the map to the smallest bounding box
*  containing all markers
*
*/
Mapstraction.prototype.autoCenterAndZoom = function() {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.autoCenterAndZoom(); } );
return;
}
var lat_max = -90;
var lat_min = 90;
var lon_max = -180;
var lon_min = 180;
for (var i=0; i<this.markers.length; i++) {
lat = this.markers[i].location.lat;
lon = this.markers[i].location.lon;
if (lat > lat_max) lat_max = lat;
if (lat < lat_min) lat_min = lat;
if (lon > lon_max) lon_max = lon;
if (lon < lon_min) lon_min = lon;
}
this.setBounds( new BoundingBox(lat_min, lon_min, lat_max, lon_max) );
}
/**
* centerAndZoomOnPoints sets the center and zoom of the map from an array of points
*
* This is useful if you don't want to have to add markers to the map
*/
Mapstraction.prototype.centerAndZoomOnPoints = function(points) {
var bounds = new BoundingBox(points[0].lat,points[0].lon,points[0].lat,points[0].lon);
for (var i=1, len = points.length ; i<len; i++) {
bounds.extend(points[i]);
}
this.setBounds(bounds);
}
/**
* getZoom returns the zoom level of the map
* @returns the zoom level of the map
* @type int
*/
Mapstraction.prototype.getZoom = function() {
if(this.loaded[this.api] == false) {
self = this;
return -1;
}
var map = this.maps[this.api];
switch (this.api) {
case 'yahoo':
return 18 - map.getZoomLevel(); // maybe?
case 'google':
case 'openstreetmap':
return map.getZoom();
case 'microsoft':
return map.GetZoomLevel();
case 'multimap':
return map.getZoomFactor();
case 'mapquest':
return map.getZoomLevel() + 3; // Mapquest seems off by 3?
case 'map24':
// since map24 doesn't use a Google-style set of zoom levels, we have
// to round to the nearest zoom
var mv = map.MapClient['Static'].getCurrentMapView();
var dLon = (mv.LowerRight.Longitude - mv.TopLeft.Longitude) / 60;
var width = map.MapClient['Static'].getCanvasSize().Width;
var zoom = getGoogleZoomLevelFromDegrees (width,dLon);
return Math.round(zoom);
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.getZoom');
}
}
/**
* getZoomLevelForBoundingBox returns the best zoom level for bounds given
* @param boundingBox the bounds to fit
* @returns the closest zoom level that contains the bounding box
* @type int
*/
Mapstraction.prototype.getZoomLevelForBoundingBox = function( bbox ) {
if(this.loaded[this.api] == false) {
self = this;
return -1;
}
var map = this.maps[this.api];
// NE and SW points from the bounding box.
var ne = bbox.getNorthEast();
var sw = bbox.getSouthWest();
switch (this.api) {
case 'google':
case 'openstreetmap':
var gbox = new GLatLngBounds( sw.toGoogle(), ne.toGoogle() );
var zoom = map.getBoundsZoomLevel( gbox );
return zoom;
break;
case 'multimap':
var mmlocation = map.getBoundsZoomFactor( sw.toMultiMap(), ne.toMultiMap() );
var zoom = mmlocation.zoom_factor();
return zoom;
break;
case 'map24':
// since map24 doesn't use a Google-style set of zoom levels, we work
// out what zoom level will show the given longitude difference within
// the current map pixel width
var dLon = ne.lon - sw.lon;
var width = map.MapClient['Static'].getCanvasSize().Width;
var zoom = getGoogleZoomLevelFromDegrees (width,dLon);
return Math.round(zoom);
break;
default:
if(this.debug)
alert( this.api + ' not supported by Mapstraction.getZoomLevelForBoundingBox' );
}
}
// any use this being a bitmask? Should HYBRID = ROAD | SATELLITE?
Mapstraction.ROAD = 1;
Mapstraction.SATELLITE = 2;
Mapstraction.HYBRID = 3;
/**
* setMapType sets the imagery type for the map.
* The type can be one of:
* Mapstraction.ROAD
* Mapstraction.SATELLITE
* Mapstraction.HYBRID
* @param {int} type The (native to the map) level zoom the map to.
*/
Mapstraction.prototype.setMapType = function(type) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.setMapType(type); } );
return;
}
var map = this.maps[this.api];
switch (this.api) {
case 'yahoo':
switch(type) {
case Mapstraction.ROAD:
map.setMapType(YAHOO_MAP_REG);
break;
case Mapstraction.SATELLITE:
map.setMapType(YAHOO_MAP_SAT);
break;
case Mapstraction.HYBRID:
map.setMapType(YAHOO_MAP_HYB);
break;
default:
map.setMapType(YAHOO_MAP_REG);
}
break;
case 'google':
case 'openstreetmap':
switch(type) {
case Mapstraction.ROAD:
map.setMapType(G_NORMAL_MAP);
break;
case Mapstraction.SATELLITE:
map.setMapType(G_SATELLITE_MAP);
break;
case Mapstraction.HYBRID:
map.setMapType(G_HYBRID_MAP);
break;
default:
map.setMapType(G_NORMAL_MAP);
}
break;
case 'microsoft':
// TODO: oblique?
switch(type) {
case Mapstraction.ROAD:
map.SetMapStyle(Msn.VE.MapStyle.Road);
break;
case Mapstraction.SATELLITE:
map.SetMapStyle(Msn.VE.MapStyle.Aerial);
break;
case Mapstraction.HYBRID:
map.SetMapStyle(Msn.VE.MapStyle.Hybrid);
break;
default:
map.SetMapStyle(Msn.VE.MapStyle.Road);
}
break;
case 'multimap':
maptypes = map.getAvailableMapTypes();
maptype = -1;
for (var i = 0; i < maptypes.length; i++) {
switch (maptypes[i]) {
case MM_WORLD_MAP:
if (type == Mapstraction.ROAD) {
maptype = maptypes[i];
}
default_type = maptypes[i];
break;
case MM_WORLD_AERIAL:
if (type == Mapstraction.SATELLITE) {
maptype = maptypes[i];
}
break;
case MM_WORLD_HYBRID:
if (type == Mapstraction.HYBRID) {
maptype = maptypes[i];
}
break;
}
}
if (maptype == -1) { maptype = default_type; }
map.setMapType(maptype);
break;
case 'mapquest':
switch (type) {
case Mapstraction.ROAD:
map.setMapType("map");
break;
case Mapstraction.SATELLITE:
map.setMapType("sat");
break;
case Mapstraction.HYBRID:
map.setMapType("hyb");
break;
}
break;
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.setMapType');
}
}
/**
* getMapType gets the imagery type for the map.
* The type can be one of:
* Mapstraction.ROAD
* Mapstraction.SATELLITE
* Mapstraction.HYBRID
*/
Mapstraction.prototype.getMapType = function() {
if(this.loaded[this.api] == false) {
self = this;
return -1;
}
var map = this.maps[this.api];
var type;
switch (this.api) {
case 'yahoo':
type = map.getCurrentMapType();
switch(type) {
case YAHOO_MAP_REG:
return Mapstraction.ROAD;
break;
case YAHOO_MAP_SAT:
return Mapstraction.SATELLITE;
break;
case YAHOO_MAP_HYB:
return Mapstraction.HYBRID;
break;
default:
return null;
}
break;
case 'google':
case 'openstreetmap':
type = map.getCurrentMapType();
switch(type) {
case G_NORMAL_MAP:
return Mapstraction.ROAD;
break;
case G_SATELLITE_MAP:
return Mapstraction.SATELLITE;
break;
case G_HYBRID_MAP:
return Mapstraction.HYBRID;
break;
default:
return null;
}
break;
case 'microsoft':
// TODO: oblique?
type = map.GetMapStyle();
switch(type) {
case Msn.VE.MapStyle.Road:
return Mapstraction.ROAD;
break;
case Msn.VE.MapStyle.Aerial:
return Mapstraction.SATELLITE;
break;
case Msn.VE.MapStyle.Hybrid:
return Mapstraction.HYBRID;
break;
default:
return null;
}
break;
case 'multimap':
maptypes = map.getAvailableMapTypes();
type = map.getMapType();
switch(type) {
case MM_WORLD_MAP:
return Mapstraction.ROAD;
break;
case MM_WORLD_AERIAL:
return Mapstraction.SATELLITE;
break;
case MM_WORLD_HYBRID:
return Mapstraction.HYBRID;
break;
default:
return null;
}
break;
case 'mapquest':
type = map.getMapType();
switch(type) {
case "map":
return Mapstraction.ROAD;
break;
case "sat":
return Mapstraction.SATELLITE;
break;
case "hyb":
return Mapstraction.HYBRID;
break;
default:
return null;
}
break;
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.getMapType');
}
}
/**
* getBounds gets the BoundingBox of the map
* @returns the bounding box for the current map state
* @type BoundingBox
*/
Mapstraction.prototype.getBounds = function () {
if(this.loaded[this.api] == false) {
return null;
}
var map = this.maps[this.api];
switch (this.api) {
case 'google':
case 'openstreetmap':
var gbox = map.getBounds();
var sw = gbox.getSouthWest();
var ne = gbox.getNorthEast();
return new BoundingBox(sw.lat(), sw.lng(), ne.lat(), ne.lng());
break;
case 'yahoo':
var ybox = map.getBoundsLatLon();
return new BoundingBox(ybox.LatMin, ybox.LonMin, ybox.LatMax, ybox.LonMax);
break;
case 'microsoft':
var mbox = map.GetMapView();
var nw = mbox.TopLeftLatLong;
var se = mbox.BottomRightLatLong;
return new BoundingBox(se.Latitude,nw.Longitude,nw.Latitude,se.Longitude);
break;
case 'multimap':
var mmbox = map.getMapBounds();
var sw = mmbox.getSouthWest();
var ne = mmbox.getNorthEast();
return new BoundingBox(sw.lat, sw.lon, ne.lat, ne.lon);
break;
case 'mapquest':
var mqbox = map.getMapBounds(); // MQRectLL
var se = mqbox.getLowerRightLatLng();
var nw = mqbox.getUpperLeftLatLng();
// NW is this correct ???
// return new BoundingBox(se.lat, se.lon, nw.lat, nw.lon);
// should be this instead
return new BoundingBox(se.lat, nw.lon, nw.lat, se.lon);
break;
case 'map24':
var mv = map.MapClient['Static'].getCurrentMapView();
var se = mv.LowerRight;
var nw = mv.TopLeft;
return new BoundingBox (se.Latitude/60, nw.Longitude/60,
nw.Latitude/60, se.Longitude/60 );
break;
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.getBounds');
}
}
/**
* setBounds sets the map to the appropriate location and zoom for a given BoundingBox
* @param {BoundingBox} the bounding box you want the map to show
*/
Mapstraction.prototype.setBounds = function(bounds){
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.setBounds(bounds); } );
return;
}
var map = this.maps[this.api];
var sw = bounds.getSouthWest();
var ne = bounds.getNorthEast();
switch (this.api) {
case 'google':
case 'openstreetmap':
var gbounds = new GLatLngBounds(new GLatLng(sw.lat,sw.lon),new GLatLng(ne.lat,ne.lon));
map.setCenter(gbounds.getCenter(), map.getBoundsZoomLevel(gbounds));
break;
case 'yahoo':
if(sw.lon > ne.lon)
sw.lon -= 360;
var center = new YGeoPoint((sw.lat + ne.lat)/2,
(ne.lon + sw.lon)/2);
var container = map.getContainerSize();
for(var zoom = 1 ; zoom <= 17 ; zoom++){
var sw_pix = convertLatLonXY_Yahoo(sw,zoom);
var ne_pix = convertLatLonXY_Yahoo(ne,zoom);
if(sw_pix.x > ne_pix.x)
sw_pix.x -= (1 << (26 - zoom)); //earth circumference in pixel
if(Math.abs(ne_pix.x - sw_pix.x)<=container.width
&& Math.abs(ne_pix.y - sw_pix.y) <= container.height){
map.drawZoomAndCenter(center,zoom); //Call drawZoomAndCenter here: OK if called multiple times anyway
break;
}
}
break;
case 'microsoft':
map.SetMapView([new VELatLong(sw.lat,sw.lon),new VELatLong(ne.lat,ne.lon)]);
break;
case 'multimap':
var mmlocation = map.getBoundsZoomFactor( sw.toMultiMap(), ne.toMultiMap() );
var center = new LatLonPoint(mmlocation.coords.lat, mmlocation.coords.lon);
this.setCenterAndZoom(center, mmlocation.zoom_factor);
break;
case 'mapquest':
// TODO: MapQuest.setBounds
if(this.debug)
alert(this.api + ' not supported by Mapstraction.setBounds');
break;
case 'freeearth':
var center = new LatLonPoint((sw.lat + ne.lat)/2, (ne.lon + sw.lon)/2);
this.setCenter(center);
break;
case 'map24':
var settings = new Object();
settings.Latitude = ((sw.lat+ne.lat) / 2) * 60;
settings.Longitude = ((sw.lon+ne.lon) / 2) * 60;
// need to convert lat/lon to metres
settings.MinimumWidth = lonToMetres
(ne.lon-sw.lon, (ne.lat+sw.lat)/2);
Map24.MapApplication.center ( settings );
break;
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.setBounds');
}
}
/**
* addImageOverlay layers an georeferenced image over the map
* @param {id} unique DOM identifier
* @param {src} url of image
* @param {opacity} opacity 0-100
* @param {west} west boundary
* @param {south} south boundary
* @param {east} east boundary
* @param {north} north boundary
*/
Mapstraction.prototype.addImageOverlay = function(id, src, opacity, west, south, east, north) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.addImageOverlay(id, src, opacity, west, south, east, north); } );
return;
}
var map = this.maps[this.api];
var b = document.createElement("img");
b.style.display = 'block';
b.setAttribute('id',id);
b.setAttribute('src',src);
b.style.position = 'absolute';
b.style.zIndex = 1;
b.setAttribute('west',west);
b.setAttribute('south',south);
b.setAttribute('east',east);
b.setAttribute('north',north);
switch (this.api) {
case 'google':
case 'openstreetmap':
map.getPane(G_MAP_MAP_PANE).appendChild(b);
this.setImageOpacity(id, opacity);
this.setImagePosition(id);
GEvent.bind(map, "zoomend", this, function(){this.setImagePosition(id)});
GEvent.bind(map, "moveend", this, function(){this.setImagePosition(id)});
break;
case 'multimap':
map.getContainer().appendChild(b);
this.setImageOpacity(id, opacity);
this.setImagePosition(id);
me = this;
map.addEventHandler( 'changeZoom', function(eventType, eventTarget, arg1, arg2, arg3) {
me.setImagePosition(id);
});
map.addEventHandler( 'drag', function(eventType, eventTarget, arg1, arg2, arg3) {
me.setImagePosition(id);
});
map.addEventHandler( 'endPan', function(eventType, eventTarget, arg1, arg2, arg3) {
me.setImagePosition(id);
});
break;
default:
b.style.display = 'none';
if(this.debug)
alert(this.api + "not supported by Mapstraction.addImageOverlay not supported");
break;
}
}
Mapstraction.prototype.setImageOpacity = function(id, opacity) {
if(opacity<0){opacity=0;}  if(opacity>=100){opacity=100;}
var c=opacity/100;
var d=document.getElementById(id);
if(typeof(d.style.filter)=='string'){d.style.filter='alpha(opacity:'+opacity+')';}
if(typeof(d.style.KHTMLOpacity)=='string'){d.style.KHTMLOpacity=c;}
if(typeof(d.style.MozOpacity)=='string'){d.style.MozOpacity=c;}
if(typeof(d.style.opacity)=='string'){d.style.opacity=c;}
}
Mapstraction.prototype.setImagePosition = function(id) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.setImagePosition(id); } );
return;
}
var map = this.maps[this.api];
var x = document.getElementById(id);
var d; var e;
switch (this.api) {
case 'google':
case 'openstreetmap':
d = map.fromLatLngToDivPixel(new GLatLng(x.getAttribute('north'), x.getAttribute('west')));
e = map.fromLatLngToDivPixel(new GLatLng(x.getAttribute('south'), x.getAttribute('east')));
break;
case 'multimap':
d = map.geoPosToContainerPixels(new MMLatLon(x.getAttribute('north'), x.getAttribute('west')));
e = map.geoPosToContainerPixels(new MMLatLon(x.getAttribute('south'), x.getAttribute('east')));
break;
}
x.style.top=d.y+'px';
x.style.left=d.x+'px';
x.style.width=e.x-d.x+'px';
x.style.height=e.y-d.y+'px';
}
/**
* addGeoRSSOverlay adds a GeoRSS overlay to the map
* @param{georssURL} GeoRSS feed URL
*/
Mapstraction.prototype.addGeoRSSOverlay = function(georssURL) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.addGeoRSSOverlay(georssURL); } );
return;
}
var map = this.maps[this.api];
switch (this.api) {
case 'yahoo':
map.addOverlay(new YGeoRSS(georssURL));
break;
// case 'openstreetmap': // OSM uses the google interface, so allow cascade
case 'google':
map.addOverlay(new GGeoXml(georssURL));
break;
case 'microsoft':
var veLayerSpec = new VELayerSpecification();
veLayerSpec.Type = VELayerType.GeoRSS;
veLayerSpec.ID = 1;
veLayerSpec.LayerSource = georssURL;
veLayerSpec.Method = 'get';
// veLayerSpec.FnCallback = onFeedLoad;
map.AddLayer(veLayerSpec);
break;
// case 'openlayers':
// 	map.addLayer(new OpenLayers.Layer.GeoRSS("GeoRSS Layer", georssURL));
// break;
case 'multimap':
break;
case 'freeearth':
if (this.freeEarthLoaded) {
var ferss = new FE.GeoRSS(georssURL);
map.addOverlay(ferss);
} else {
self = this;
this.freeEarthOnLoad.push( function() { self.addGeoRSSOverlay(georssURL); } );
}
break;
default:
if(this.debug)
alert(this.api + ' not supported by Mapstraction.addGeoRSSOverlay');
}
}
/**
* addFilter adds a marker filter
* @param {field} name of attribute to filter on
* @param {operator} presently only "ge" or "le"
* @param {value} the value to compare against
*/
Mapstraction.prototype.addFilter = function(field, operator, value) {
if (! this.filters) {
this.filters = [];
}
this.filters.push( [field, operator, value] );
}
/**
* removeFilter
*/
Mapstraction.prototype.removeFilter = function(field, operator, value) {
if (! this.filters) { return; }
var del;
for (var f=0; f<this.filters.length; f++) {
if (this.filters[f][0] == field &&
(! operator || (this.filters[f][1] == operator && this.filters[f][2] == value))) {
this.filters.splice(f,1);
f--; //array size decreased
}
}
}
/*
* toggleFilter: delete the current filter if present; otherwise add it
*/
Mapstraction.prototype.toggleFilter = function(field, operator, value) {
if (! this.filters) {
this.filters = [];
}
var found = false;
for (var f=0; f<this.filters.length; f++) {
if (this.filters[f][0] == field && this.filters[f][1] == operator && this.filters[f][2] == value) {
this.filters.splice(f,1);
f--; //array size decreased
found = true;
}
}
if (! found) {
this.addFilter(field, operator, value);
}
}
/*
* removeAllFilters
*/
Mapstraction.prototype.removeAllFilters = function() {
this.filters = [];
}
/**
* doFilter executes all filters added since last call
*/
Mapstraction.prototype.doFilter = function() {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.doFilter(); } );
return;
}
var map = this.maps[this.api];
if (this.filters) {
switch (this.api) {
case 'multimap':
/* TODO polylines aren't filtered in multimap */
var mmfilters = [];
for (var f=0; f<this.filters.length; f++) {
mmfilters.push( new MMSearchFilter( this.filters[f][0], this.filters[f][1], this.filters[f][2] ));
}
map.setMarkerFilters( mmfilters );
map.redrawMap();
break;
default:
var vis;
for (var m=0; m<this.markers.length; m++) {
vis = true;
for (var f=0; f<this.filters.length; f++) {
if (! this.applyFilter(this.markers[m], this.filters[f])) {
vis = false;
}
}
if (vis) {
this.markers[m].show();
} else {
this.markers[m].hide();
}
}
/*
for (var p=0; m<this.polylines.length; p++) {
vis = true;
for (var f=0; f<this.filters.length; f++) {
if (! this.applyFilter(this.polylines[p], this.filters[f])) {
vis = false;
}
}
if (vis) {
this.polylines[p].show();
} else {
this.polylines[p].hide();
}
}
*/
break;
}
}
}
Mapstraction.prototype.applyFilter = function(o, f) {
var vis = true;
switch (f[1]) {
case 'ge':
if (o.getAttribute( f[0] ) < f[2]) {
vis = false;
}
break;
case 'le':
if (o.getAttribute( f[0] ) > f[2]) {
vis = false;
}
break;
case 'eq':
if (o.getAttribute( f[0] ) != f[2]) {
vis = false;
}
break;
}
return vis;
}
/**
* getAttributeExtremes returns the minimum/maximum of "field" from all markers
* @param {field} name of "field" to query
* @returns {array} of minimum/maximum
*/
Mapstraction.prototype.getAttributeExtremes = function(field) {
var min;
var max;
for (var m=0; m<this.markers.length; m++) {
if (! min || min > this.markers[m].getAttribute(field)) {
min = this.markers[m].getAttribute(field);
}
if (! max || max < this.markers[m].getAttribute(field)) {
max = this.markers[m].getAttribute(field);
}
}
for (var p=0; m<this.polylines.length; m++) {
if (! min || min > this.polylines[p].getAttribute(field)) {
min = this.polylines[p].getAttribute(field);
}
if (! max || max < this.polylines[p].getAttribute(field)) {
max = this.polylines[p].getAttribute(field);
}
}
return [min, max];
}
/**
* getMap returns the native map object that mapstraction is talking to
* @returns the native map object mapstraction is using
*/
Mapstraction.prototype.getMap = function() {
// FIXME in an ideal world this shouldn't exist right?
return this.maps[this.api];
}
//////////////////////////////
//
//   LatLonPoint
//
/////////////////////////////
/**
* LatLonPoint is a point containing a latitude and longitude with helper methods
* @param {double} lat is the latitude
* @param {double} lon is the longitude
* @returns a new LatLonPoint
* @type LatLonPoint
*/
function LatLonPoint(lat,lon) {
// TODO error if undefined?
//  if (lat == undefined) alert('undefined lat');
//  if (lon == undefined) alert('undefined lon');
this.lat = lat;
this.lon = lon;
this.lng = lon; // lets be lon/lng agnostic
}
/**
* toYahoo returns a Y! maps point
* @returns a YGeoPoint
*/
LatLonPoint.prototype.toYahoo = function() {
return new YGeoPoint(this.lat,this.lon);
}
/**
* toGoogle returns a Google maps point
* @returns a GLatLng
*/
LatLonPoint.prototype.toGoogle = function() {
return new GLatLng(this.lat,this.lon);
}
/**
* toMicrosoft returns a VE maps point
* @returns a VELatLong
*/
LatLonPoint.prototype.toMicrosoft = function() {
return new VELatLong(this.lat,this.lon);
}
/**
* toMultiMap returns a MultiMap point
* @returns a MMLatLon
*/
LatLonPoint.prototype.toMultiMap = function() {
return new MMLatLon(this.lat, this.lon);
}
/**
* toMapQuest returns a MapQuest point
* @returns a MQLatLng
*/
LatLonPoint.prototype.toMapQuest = function() {
return new MQLatLng(this.lat, this.lon);
}
/**
* toFreeEarth returns a FreeEarth point
* @returns a FE.LatLng
*/
LatLonPoint.prototype.toFreeEarth = function() {
return new FE.LatLng(this.lat,this.lon);
}
/**
* toMap24 returns a Map24 point
* @returns a Map24.Point
*/
LatLonPoint.prototype.toMap24 = function() {
return new Map24.Point (this.lon,this.lat);
}
/**
* toString returns a string represntation of a point
* @returns a string like '51.23, -0.123'
* @type String
*/
LatLonPoint.prototype.toString = function() {
return this.lat + ', ' + this.lon;
}
/**
* distance returns the distance in kilometers between two points
* @param {LatLonPoint} otherPoint The other point to measure the distance from to this one
* @returns the distance between the points in kilometers
* @type double
*/
LatLonPoint.prototype.distance = function(otherPoint) {
var d,dr;
with (Math) {
dr = 0.017453292519943295; // 2.0 * PI / 360.0; or, radians per degree
d = cos(otherPoint.lon*dr - this.lon*dr) * cos(otherPoint.lat*dr - this.lat*dr);
return acos(d)*6378.137; // equatorial radius
}
return -1;
}
/**
* equals tests if this point is the same as some other one
* @param {LatLonPoint} otherPoint The other point to test with
* @returns true or false
* @type boolean
*/
LatLonPoint.prototype.equals = function(otherPoint) {
return this.lat == otherPoint.lat && this.lon == otherPoint.lon;
}
//////////////////////////
//
//  BoundingBox
//
//////////////////////////
/**
* BoundingBox creates a new bounding box object
* @param {double} swlat the latitude of the south-west point
* @param {double} swlon the longitude of the south-west point
* @param {double} nelat the latitude of the north-east point
* @param {double} nelon the longitude of the north-east point
* @returns a new BoundingBox
* @type BoundingBox
* @constructor
*/
function BoundingBox(swlat, swlon, nelat, nelon) {
//FIXME throw error if box bigger than world
//alert('new bbox ' + swlat + ',' +  swlon + ',' +  nelat + ',' + nelon);
this.sw = new LatLonPoint(swlat, swlon);
this.ne = new LatLonPoint(nelat, nelon);
}
/**
* getSouthWest returns a LatLonPoint of the south-west point of the bounding box
* @returns the south-west point of the bounding box
* @type LatLonPoint
*/
BoundingBox.prototype.getSouthWest = function() {
return this.sw;
}
/**
* getNorthEast returns a LatLonPoint of the north-east point of the bounding box
* @returns the north-east point of the bounding box
* @type LatLonPoint
*/
BoundingBox.prototype.getNorthEast = function() {
return this.ne;
}
/**
* isEmpty finds if this bounding box has zero area
* @returns whether the north-east and south-west points of the bounding box are the same point
* @type boolean
*/
BoundingBox.prototype.isEmpty = function() {
return this.ne == this.sw; // is this right? FIXME
}
/**
* contains finds whether a given point is within a bounding box
* @param {LatLonPoint} point the point to test with
* @returns whether point is within this bounding box
* @type boolean
*/
BoundingBox.prototype.contains = function(point){
return point.lat >= this.sw.lat && point.lat <= this.ne.lat && point.lon>= this.sw.lon && point.lon <= this.ne.lon;
}
/**
* toSpan returns a LatLonPoint with the lat and lon as the height and width of the bounding box
* @returns a LatLonPoint containing the height and width of this bounding box
* @type LatLonPoint
*/
BoundingBox.prototype.toSpan = function() {
return new LatLonPoint( Math.abs(this.sw.lat - this.ne.lat), Math.abs(this.sw.lon - this.ne.lon) );
}
/**
* extend extends the bounding box to include the new point
*/
BoundingBox.prototype.extend = function(point) {
if(this.sw.lat > point.lat)
this.sw.setLat(point.lat);
if(this.sw.lon > point.lon)
this.sw.setLon(point.lon);
if(this.ne.lat < point.lat)
this.ne.setLat(point.lat);
if(this.ne.lon < point.lon)
this.ne.setLon(point.lon);
return;
}
//////////////////////////////
//
//  Marker
//
///////////////////////////////
/**
* Marker create's a new marker pin
* @param {LatLonPoint} point the point on the map where the marker should go
* @constructor
*/
function Marker(point) {
this.location = point;
this.onmap = false;
this.proprietary_marker = false;
this.attributes = new Array();
this.pinID = "mspin-"+new Date().getTime()+'-'+(Math.floor(Math.random()*Math.pow(2,16)));
}
Marker.prototype.setChild = function(some_proprietary_marker) {
this.proprietary_marker = some_proprietary_marker;
this.onmap = true
}
Marker.prototype.setLabel = function(labelText) {
this.labelText = labelText;
}
/**
* addData conviniently set a hash of options on a marker
*/
Marker.prototype.addData = function(options){
if(options.label)
this.setLabel(options.label);
if(options.infoBubble)
this.setInfoBubble(options.infoBubble);
if(options.icon) {
if(options.iconSize)
this.setIcon(options.icon, new Array(options.iconSize[0], options.iconSize[1]));
else
this.setIcon(options.icon);
}
if(options.infoDiv)
this.setInfoDiv(options.infoDiv[0],options.infoDiv[1]);
if(options.draggable)
this.setDraggable(options.draggable);
if(options.hover)
this.setHover(options.hover);
if(options.hoverIcon)
this.setHoverIcon(options.hoverIcon);
if(options.openBubble)
this.openBubble();
if(options.date)
this.setAttribute( 'date', eval(options.date) );
if(options.category)
this.setAttribute( 'category', options.category );
}
/**
* setInfoBubble sets the html/text content for a bubble popup for a marker
* @param {String} infoBubble the html/text you want displayed
*/
Marker.prototype.setInfoBubble = function(infoBubble) {
this.infoBubble = infoBubble;
}
/**
* setInfoDiv sets the text and the id of the div element where to the information
*  useful for putting information in a div outside of the map
* @param {String} infoDiv the html/text you want displayed
* @param {String} div the element id to use for displaying the text/html
*/
Marker.prototype.setInfoDiv = function(infoDiv,div){
this.infoDiv = infoDiv;
this.div = div;
}
/**
* setIcon sets the icon for a marker
* @param {String} iconUrl The URL of the image you want to be the icon
*/
Marker.prototype.setIcon = function(iconUrl, iconSize){
this.iconUrl = iconUrl;
if(iconSize)
this.iconSize = iconSize;
}
Marker.prototype.setHoverIcon = function(hoverIconUrl){
this.hoverIconUrl = hoverIconUrl;
}
/**
* setDraggable sets the draggable state of the marker
* @param {Bool} draggable set to true if marker should be draggable by the user
*/
Marker.prototype.setDraggable = function(draggable) {
this.draggable = draggable;
}
/**
* setHover sets that the marker info is displayed on hover
* @param {Bool} hover set to true if marker should display info on hover
*/
Marker.prototype.setHover = function(hover) {
this.hover = hover;
}
/**
* toYahoo returns a Yahoo Maps compatible marker pin
* @returns a Yahoo Maps compatible marker
*/
Marker.prototype.toYahoo = function() {
var ymarker;
if(this.iconUrl) {
ymarker = new YMarker(this.location.toYahoo (),new YImage(this.iconUrl));
} else {
ymarker = new YMarker(this.location.toYahoo());
}
if(this.iconSize) {
ymarker.size = new YSize(this.iconSize[0], this.iconSize[1]);
}
if(this.labelText) {
ymarker.addLabel(this.labelText);
}
if(this.infoBubble) {
var theInfo = this.infoBubble;
var event_action;
if(this.hover) {
event_action = EventsList.MouseOver;
}
else {
event_action = EventsList.MouseClick;
}
YEvent.Capture(ymarker, event_action, function() {
ymarker.openSmartWindow(theInfo); });
}
if(this.infoDiv) {
var theInfo = this.infoDiv;
var div = this.div;
var event_div;
if(this.hover) {
event_action = EventsList.MouseOver;
}
else {
event_action = EventsList.MouseClick;
}
YEvent.Capture(ymarker, event_action, function() {
document.getElementById(div).innerHTML = theInfo;});
}
return ymarker;
}
/**
* toGoogle returns a Google Maps compatible marker pin
* @returns Google Maps compatible marker
*/
Marker.prototype.toGoogle = function() {
var options = new Object();
if(this.labelText) {
options.title =  this.labelText;
}
if(this.iconUrl){
var icon = new GIcon(G_DEFAULT_ICON,this.iconUrl);
if(this.iconSize)
icon.iconSize = new GSize(this.iconSize[0], this.iconSize[1]);
options.icon = icon;
}
if(this.draggable){
options.draggable = this.draggable;
}
var gmarker = new GMarker( this.location.toGoogle(),options);
if(this.infoBubble) {
var theInfo = this.infoBubble;
var event_action;
if(this.hover) {
event_action = "mouseover";
}
else {
event_action = "click";
}
GEvent.addListener(gmarker, event_action, function() {
gmarker.openInfoWindowHtml(theInfo, {maxWidth: 100});
});
}
if(this.hoverIconUrl) {
GEvent.addListener(gmarker, "mouseover", function() {
gmarker.setImage(this.hoverIconUrl);
});
GEvent.addListener(gmarker, "mouseout", function() {
gmarker.setImage(this.iconUrl);
});
}
if(this.infoDiv){
var theInfo = this.infoDiv;
var div = this.div;
var event_action;
if(this.hover) {
event_action = "mouseover";
}
else {
event_action = "click";
}
GEvent.addListener(gmarker, event_action, function() {
document.getElementById(div).innerHTML = theInfo;
});
}
return gmarker;
}
/**
* toMicrosoft returns a MSFT VE compatible marker pin
* @returns MSFT VE compatible marker
*/
Marker.prototype.toMicrosoft = function() {
var pin = new VEPushpin(this.pinID,this.location.toMicrosoft(),
this.iconUrl,this.labelText,this.infoBubble);
return pin;
}
/**
* toMap24 returns a Map24 Location
* @returns Map24 Location
*/
Marker.prototype.toMap24 = function() {
var ops = new Object();
ops.Longitude = this.location.lon*60;
ops.Latitude = this.location.lat*60;
if(this.infoBubble) {
// not sure how map24 differentiates between tooltips and
// info bubble content
ops.TooltipContent =  this.infoBubble;
}
if(this.labelText) {
// ????
}
ops.LogoURL = this.iconUrl ? this.iconUrl :
"http://www.free-map.org.uk/images/marker.png";
ops.TooltipLayout = Map24.MapObject.LAYOUT_BUBBLE;
if(this.hover) {
ops.TooltipOpen = "OnMouseOver";
//        ops.TooltipClose = "OnMouseOut";
} else {
ops.TooltipOpen = "OnClick";
//        ops.TooltipClose = "OnMouseOut";
}
var m24Location = new Map24.Location ( ops );
return m24Location;
}
/**
* toMultiMap returns a MultiMap compatible marker pin
* @returns MultiMap compatible marker
*/
Marker.prototype.toMultiMap = function() {
if (this.iconUrl) {
var icon = new MMIcon(this.iconUrl);
icon.iconSize = new MMDimensions(32, 32); //how to get this?
var mmmarker = new MMMarkerOverlay( this.location.toMultiMap(), {'icon' : icon} );
} else {
var mmmarker = new MMMarkerOverlay( this.location.toMultiMap());
}
if(this.labelText){
}
if(this.infoBubble) {
mmmarker.setInfoBoxContent(this.infoBubble);
}
if(this.infoDiv) {
}
for (var key in this.attributes) {
mmmarker.setAttribute(key, this.attributes[ key ]);
}
return mmmarker;
}
/**
* toMapQuest returns a MapQuest compatible marker pin
* @returns MapQuest compatible marker
*/
Marker.prototype.toMapQuest = function() {
var mqmarker = new MQPoi( this.location.toMapQuest() );
if(this.iconUrl){
var mqicon = new MQMapIcon();
mqicon.setImage(this.iconUrl,32,32,true,false);
// TODO: Finish MapQuest icon params - icon file location, width, height, recalc infowindow offset, is it a PNG image?
mqmarker.setIcon(mqicon);
// mqmarker.setLabel('Hola!');
}
if(this.labelText) { mqmarker.setInfoTitleHTML( this.labelText ); }
if(this.infoBubble) { mqmarker.setInfoContentHTML( this.infoBubble ); }
if(this.infoDiv){
var theInfo = this.infoDiv;
var div = this.div;
MQEventManager.addListener(mqmarker, "click", function() {
document.getElementById(div).innerHTML = theInfo;
});
}
return mqmarker;
}
/**
* toFreeEarth returns a FreeEarth compatible marker pin
* @returns FreeEarth compatible marker
*/
Marker.prototype.toFreeEarth = function() {
var feicon;
if (this.iconUrl) {
feicon = new FE.Icon(this.iconUrl);
} else {
feicon = new FE.Icon("http://freeearth.poly9.com/images/bullmarker.png");
}
var femarker = new FE.Pushpin( this.location.toFreeEarth(), feicon);
if(this.infoBubble) {
var theBubble = this.infoBubble;
FE.Event.addListener(femarker, 'click', function() {
femarker.openInfoWindowHtml( theBubble, 200, 100 );
} );
}
if(this.infoDiv) {
var theInfo = this.infoDiv;
var div = this.div;
FE.Event.addListener(femarker, 'click', function() {
document.getElementById(div).innerHTML = theInfo;
});
}
return femarker;
}
/**
* setAttribute: set an arbitrary key/value pair on a marker
* @arg(String) key
* @arg value
*/
Marker.prototype.setAttribute = function(key,value) {
this.attributes[key] = value;
}
/**
* getAttribute: gets the value of "key"
* @arg(String) key
* @returns value
*/
Marker.prototype.getAttribute = function(key) {
return this.attributes[key];
}
/**
* openBubble opens the infoBubble
*/
Marker.prototype.openBubble = function() {
if( this.api) {
switch (this.api) {
case 'yahoo':
var ypin = this.proprietary_marker;
ypin.openSmartWindow(this.infoBubble);
break;
case 'google':
case 'openstreetmap':
var gpin = this.proprietary_marker;
gpin.openInfoWindowHtml(this.infoBubble);
break;
case 'microsoft':
var pin = this.proprietary_marker;
// bloody microsoft -- this is broken
var el = $m(this.pinID + "_" + this.maps[this.api].GUID).onmouseover;
setTimeout(el, 1000); // wait a second in case the map is booting as it cancels the event
break;
case 'multimap':
this.proprietary_marker.openInfoBox();
break;
case 'mapquest':
// MapQuest hack to work around bug when opening marker
this.proprietary_marker.setRolloverEnabled(false);
this.proprietary_marker.showInfoWindow();
this.proprietary_marker.setRolloverEnabled(true);
break;
}
} else {
alert('You need to add the marker before opening it');
}
}
/**
* hide the marker
*/
Marker.prototype.hide = function() {
if (this.api) {
switch (this.api) {
case 'google':
case 'openstreetmap':
this.proprietary_marker.hide();
break;
case 'yahoo':
this.proprietary_marker.hide();
break;
case 'map24':
this.proprietary_marker.hide();
break;
case 'multimap':
this.proprietary_marker.setVisibility(false);
break;
case 'mapquest':
this.proprietary_marker.setVisible(false);
break;
default:
if(this.debug)
alert(this.api + "not supported by Marker.hide");
}
}
}
/**
* show the marker
*/
Marker.prototype.show = function() {
if (this.api) {
switch (this.api) {
case 'google':
case 'openstreetmap':
this.proprietary_marker.show();
break;
case 'map24':
this.proprietary_marker.show();
break;
case 'yahoo':
this.proprietary_marker.unhide();
break;
case 'multimap':
this.proprietary_marker.setVisibility(true);
break;
case 'mapquest':
this.proprietary_marker.setVisible(true);
break;
default:
if(this.debug)
alert(this.api + "not supported by Marker.show");
}
}
}
///////////////
// Polyline ///
///////////////
function Polyline(points) {
this.points = points;
this.attributes = new Array();
this.onmap = false;
this.proprietary_polyline = false;
this.pllID = "mspll-"+new Date().getTime()+'-'+(Math.floor(Math.random()*Math.pow(2,16)));
}
/**
* addData conviniently set a hash of options on a polyline
*/
Polyline.prototype.addData = function(options){
if(options.color)
this.setColor(options.color);
if(options.width)
this.setWidth(options.width); // NW corrected from setInfoBubble()
if(options.opacity)
this.setIcon(options.opacity);
if(options.date)
this.setAttribute( 'date', eval(options.date) );
if(options.category)
this.setAttribute( 'category', options.category );
}
Polyline.prototype.setChild = function(some_proprietary_polyline) {
this.proprietary_polyline = some_proprietary_polyline;
this.onmap = true;
}
//in the form: #RRGGBB
//Note map24 insists on upper case, so we convert it.
Polyline.prototype.setColor = function(color){
this.color = (color.length==7 && color[0]=="#") ? color.toUpperCase() : color;
}
//An integer
Polyline.prototype.setWidth = function(width){
this.width = width;
}
//A float between 0.0 and 1.0
Polyline.prototype.setOpacity = function(opacity){
this.opacity = opacity;
}
Polyline.prototype.toYahoo = function() {
var ypolyline;
var ypoints = [];
for (var i = 0, length = this.points.length ; i< length; i++){
ypoints.push(this.points[i].toYahoo());
}
ypolyline = new YPolyline(ypoints,this.color,this.width,this.opacity);
return ypolyline;
}
Polyline.prototype.toGoogle = function() {
var gpolyline;
var gpoints = [];
for (var i = 0,  length = this.points.length ; i< length; i++){
gpoints.push(this.points[i].toGoogle());
}
gpolyline = new GPolyline(gpoints,this.color,this.width,this.opacity);
return gpolyline;
}
Polyline.prototype.toMap24 = function() {
var m24polyline;
var m24longs = "";
var m24lats = "";
for (var i=0; i<this.points.length; i++) {
if(i) {
m24longs += "|";
m24lats += "|";
}
m24longs += (this.points[i].lon*60);
m24lats += (this.points[i].lat*60);
}
m24polyline = new Map24.Polyline({
Longitudes: m24longs,
Latitudes: m24lats,
Color: this.color || "black",
Width: this.width || 3
});
return m24polyline;
}
Polyline.prototype.toMicrosoft = function() {
var mpolyline;
var mpoints = [];
for (var i = 0, length = this.points.length ; i< length; i++){
mpoints.push(this.points[i].toMicrosoft());
}
var color;
var opacity = this.opacity ||1.0;
if(this.color){
color = new VEColor(parseInt(this.color.substr(1,2),16),parseInt(this.color.substr(3,2),16),parseInt(this.color.substr(5,2),16), opacity);
}else{
color = new VEColor(0,255,0, opacity);
}
mpolyline = new VEPolyline(this.pllID,mpoints,color,this.width);
return mpolyline;
}
Polyline.prototype.toMultiMap = function() {
var mmpolyline;
var mmpoints = [];
for (var i = 0, length = this.points.length ; i< length; i++){
mmpoints.push(this.points[i].toMultiMap());
}
mmpolyline = new MMPolyLineOverlay(mmpoints, this.color, this.opacity, this.width, false, undefined);
return mmpolyline;
}
Polyline.prototype.toMapQuest = function() {
var mqpolyline = new MQLineOverlay();
mqpolyline.setColor(this.color||"red");
mqpolyline.setBorderWidth(this.width || 3);
mqpolyline.setKey("Line");
mqpolyline.setColorAlpha(this.opacity);
var mqpoints = new MQLatLngCollection();
for (var i = 0, length = this.points.length ; i< length; i++){
mqpoints.add(this.points[i].toMapQuest());
}
mqpolyline.setShapePoints(mqpoints);
return mqpolyline;
}
Polyline.prototype.toFreeEarth = function() {
var fepoints = new Array();
for (var i = 0, length = this.points.length ; i< length; i++){
fepoints.push(this.points[i].toFreeEarth());
}
var fepolyline = new FE.Polyline(fepoints, this.color || '0xff0000', this.width || 1, this.opacity || 1);
return fepolyline;
}
/**
* setAttribute: set an arbitrary key/value pair on a polyline
* @arg(String) key
* @arg value
*/
Polyline.prototype.setAttribute = function(key,value) {
this.attributes[key] = value;
}
/**
* getAttribute: gets the value of "key"
* @arg(String) key
* @returns value
*/
Polyline.prototype.getAttribute = function(key) {
return this.attributes[key];
}
/**
* show: not yet implemented
*/
Polyline.prototype.show = function() {
if (this.api) {
}
}
/**
* hide: not yet implemented
*/
Polyline.prototype.hide = function() {
if (this.api) {
}
};
/////////////
/// Route ///
/////////////
/**
* Show a route from MapstractionRouter on a mapstraction map
* Currently only supported by MapQuest
* @params {Object} route The route object returned in the callback from MapstractionRouter
*/
Mapstraction.prototype.showRoute = function(route) {
if(this.loaded[this.api] == false) {
self = this;
this.onload[this.api].push( function() { self.showRoute(route); } );
return;
}
var map = this.maps[this.api];
switch (this.api) {
case 'mapquest':
map.addRouteHighlight(route['bounding_box'],"http://map.access.mapquest.com",route['session_id'],true);
break;
default:
if(this.debug)
alert(api + ' not supported by Mapstration.showRoute');
break;
}
};
/*
---
name: Chosen
description: Creates a Picker, which can be used for anything
authors: Patrick Filler, Jules Janssen, Jonnathan Soares
requires: [Core/*, More/Locale]
provides: Chosen
...
*/
Elements.implement({
chosen: function(data, options){
if(Browser.ie7) {
return;
}
return this.each(function(el){
if (!el.hasClass("chzn-done")) {
return new Chosen(el, data, options);
}
});
}
});
var Chosen = new Class({
active_field: false,
mouse_on_container: false,
results_showing: false,
result_highlighted: null,
result_single_selected: null,
choices: 0,
initialize: function(elmn){
this.click_test_action = this.test_active_click.bind(this);
this.form_field = elmn;
this.is_multiple = this.form_field.multiple;
this.is_rtl = this.form_field.hasClass("chzn-rtl");
this.set_up_html();
this.register_observers();
},
set_up_html: function(){
var container_div, dd_top, dd_width, sf_width;
if (!this.form_field.id) this.form_field.id = String.uniqueID();
this.container_id = this.form_field.id.replace(/(:|\.)/g, '_') + "_chzn";
this.f_width = this.form_field.getCoordinates().width;
this.default_text = this.form_field.get('data-placeholder') ? this.form_field.get('data-placeholder') : Locale.get('Chosen.placeholder', this.form_field.multiple);
container_div = new Element('div', {
'id': 		this.container_id,
'class': 	'chzn-container'+ (this.is_rtl ? ' chzn-rtl' : '')
}).setStyle('width', this.f_width);
if (this.is_multiple){
container_div.set('html', '<ul class="chzn-choices"><li class="search-field"><input type="text" value="' + this.default_text + '" class="default" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>');
} else {
container_div.set('html', '<a href="javascript:void(0)" class="chzn-single"><span>' + this.default_text + '</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" /></div><ul class="chzn-results"></ul></div>');
}
this.form_field.setStyle('display', 'none').grab(container_div, 'after');
this.container = document.id(this.container_id);
this.container.addClass("chzn-container-" + (this.is_multiple ? "multi" : "single"));
this.dropdown = this.container.getElement('div.chzn-drop');
dd_top = this.container.getCoordinates().height;
dd_width = this.f_width - this.dropdown.get_side_border_padding();
this.dropdown.setStyles({
'width': 	dd_width,
'top': 		dd_top
});
this.search_field = this.container.getElement('input');
this.search_results = this.container.getElement('ul.chzn-results');
this.search_field_scale();
this.search_no_results = this.container.getElement('li.no-results');
if (this.is_multiple){
this.search_choices = this.container.getElement('ul.chzn-choices');
this.search_container = this.container.getElement('li.search-field');
} else {
this.search_container = this.container.getElement('div.chzn-search');
this.selected_item = this.container.getElement('.chzn-single');
sf_width = dd_width - this.search_container.get_side_border_padding() - this.search_field.get_side_border_padding();
this.search_field.setStyle('width', sf_width);
}
this.results_build();
this.set_tab_index();
},
register_observers: function(){
this.container.addEvents({
click: this.container_click.bind(this),
mouseenter: this.mouse_enter.bind(this),
mouseleave: this.mouse_leave.bind(this)
});
this.search_results.addEvents({
click: this.search_results_click.bind(this),
mouseover: this.search_results_mouseover.bind(this),
mouseout: this.search_results_mouseout.bind(this)
});
this.form_field.addEvent("liszt:updated", this.results_update_field.bind(this));
this.search_field.addEvents({
blur: this.input_blur.bind(this),
keyup: this.keyup_checker.bind(this),
keydown: this.keydown_checker.bind(this)
});
if (this.is_multiple){
this.search_choices.addEvent("click", this.choices_click.bind(this));
this.search_field.addEvent("focus", this.input_focus.bind(this));
} else {
this.selected_item.addEvent("focus", this.activate_field.bind(this));
}
},
container_click: function(evt){
if (!this.pending_destroy_click){
if (!this.active_field){
if (this.is_multiple){
this.search_field.value = '';
}
document.addEvent('click', this.click_test_action);
this.results_toggle();
}else if (!this.is_multiple && evt && (evt.target === this.selected_item || evt.target.getParents('a.chzn-single').length)){
document.addEvent('click', this.click_test_action);
evt.preventDefault();
this.results_show();
}
this.activate_field();
} else {
this.pending_destroy_click = false;
}
if (evt && evt.type === "click"){
evt.stopPropagation();
}
},
mouse_enter: function(){
this.mouse_on_container = true;
},
mouse_leave: function(){
this.mouse_on_container = false;
},
input_focus: function(evt){
if (!this.active_field){
setTimeout(this.container_click.bind(this), 50);
}
},
input_blur: function(evt){
if (!this.mouse_on_container){
this.active_field = false;
setTimeout(this.blur_test.bind(this), 100);
}
},
blur_test: function(evt){
if (!this.active_field && this.container.hasClass("chzn-container-active")){
this.close_field();
}
},
close_field: function(){
document.removeEvent('click', this.click_test_action);
if (!this.is_multiple){
this.selected_item.set('tabindex', this.search_field.get('tabindex'));
this.search_field.set('tabindex', -1);
}
this.active_field = false;
this.results_hide();
this.container.removeClass("chzn-container-active");
this.winnow_results_clear();
this.clear_backstroke();
this.show_search_field_default();
this.search_field_scale();
},
activate_field: function(){
if (!this.is_multiple && !this.active_field){
this.search_field.set('tabindex', this.selected_item.get('tabindex'));
this.selected_item.set('tabindex', -1);
}
this.container.addClass("chzn-container-active");
this.active_field = true;
this.search_field.set('value', this.search_field.get('value'));
this.search_field.focus();
},
test_active_click: function(evt){
if (evt.target.getParents('#' + this.container_id).length){
this.active_field = true;
} else {
this.close_field();
}
},
results_build: function(){
this.parsing = true;
this.results_data = this.form_field.select_to_array();
if (this.is_multiple && this.choices > 0){
this.search_choices.getElements("li.search-choice").destroy();
this.choices = 0;
}else if (!this.is_multiple){
this.selected_item.getElements("span").set('text', this.default_text);
}
var content = '';
this.results_data.each(function(data){
if (data.group){
content += this.result_add_group(data);
}else if (!data.empty){
content += this.result_add_option(data);
if (data.selected && this.is_multiple){
this.choice_build(data);
}else if (data.selected && !this.is_multiple){
this.selected_item.getElements("span").set('text', data.text);
}
}
}, this);
this.show_search_field_default();
this.search_field_scale();
this.search_results.set('html', content);
this.parsing = false;
},
result_add_group: function(group){
if (!group.disabled){
group.dom_id =  this.container_id + "_g_" + group.array_index;
return '<li id="' + group.dom_id + '" class="group-result"><div>'+ group.label + '</div></li>';
} else {
return '';
}
},
result_add_option: function(option){
var classes;
if (!option.disabled){
option.dom_id =  this.container_id + "_o_" + option.array_index;
classes = option.selected && this.is_multiple ? [] : ["active-result"];
if (option.selected){
classes.push("result-selected");
}
if (option.group_array_index != null){
classes.push("group-option");
}
return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '"><div>'+ option.html + '</div></li>';
} else {
return '';
}
},
results_update_field: function(){
this.result_clear_highlight();
this.result_single_selected = null;
this.results_build();
},
result_do_highlight: function(el){
var high_bottom, high_top, maxHeight, visible_bottom, visible_top;
if (el){
this.result_clear_highlight();
this.result_highlight = el;
this.result_highlight.addClass("highlightened");
maxHeight = parseInt(this.search_results.getStyle("maxHeight"), 10);
visible_top = this.search_results.getScroll().y;
visible_bottom = maxHeight + visible_top;
high_top = this.result_highlight.getPosition(this.search_results).y + this.search_results.getScroll().y;
high_bottom = high_top + this.result_highlight.getCoordinates().height;
if (high_bottom >= visible_bottom){
this.search_results.scrollTo(0, (high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0);
}else if (high_top < visible_top){
this.search_results.scrollTo(0, high_top);
}
}
},
result_clear_highlight: function(){
if (this.result_highlight){
this.result_highlight.removeClass("highlightened");
}
this.result_highlight = null;
},
results_toggle: function(){
if (this.results_showing) {
this.results_hide();
} else {
this.results_show();
}
},
results_show: function(){
var dd_top;
if (!this.is_multiple){
this.selected_item.addClass("chzn-single-with-drop");
if (this.result_single_selected){
this.result_do_highlight(this.result_single_selected);
}
}
dd_top = this.is_multiple ? this.container.getCoordinates().height : this.container.getCoordinates().height - 1;
this.dropdown.setStyles({
"top": dd_top,
"left": 0
});
this.results_showing = true;
this.search_field.focus();
this.search_field.set('value', this.search_field.get('value'));
this.winnow_results();
},
results_hide: function(){
if (!this.is_multiple){
this.selected_item.removeClass("chzn-single-with-drop");
}
this.result_clear_highlight();
this.dropdown.setStyle('left', -9000);
this.results_showing = false;
},
set_tab_index: function(el){
var ti;
if (this.form_field.get('tabindex')){
ti = this.form_field.get('tabindex');
this.form_field.set('tabindex', -1);
if (this.is_multiple){
this.search_field.set('tabindex', ti);
} else {
this.selected_item.set('tabindex', ti);
this.search_field.set('tabindex', -1);
}
}
},
show_search_field_default: function(){
if (this.is_multiple && this.choices < 1 && !this.active_field){
this.search_field.set('value', this.default_text);
this.search_field.addClass("default");
} else {
this.search_field.set('value', "");
this.search_field.removeClass("default");
}
},
search_results_click: function(evt){
var target = evt.target.hasClass("active-result") ? evt.target : evt.target.getParent(".active-result");
if (target){
this.result_highlight = target;
this.result_select();
}
},
search_results_mouseover: function(evt){
var target = evt.target.hasClass("active-result") ? evt.target : evt.target.getParent(".active-result");
if (target){
this.result_do_highlight(target);
}
},
search_results_mouseout: function(evt){
if (evt.target.hasClass("active-result") || evt.target.getParent('.active-result')){
this.result_clear_highlight();
}
},
choices_click: function(evt){
evt.preventDefault();
if (this.active_field && !(evt.target.hasClass("search-choice") || evt.target.getParent('.search-choice')) && !this.results_showing){
this.results_show();
}
},
choice_build: function(item){
var choice_id = this.container_id + "_c_" + item.array_index;
this.choices += 1;
var el = new Element('li', {'id': choice_id})
.addClass('search-choice')
.set('html', '<span>' + item.html + '</span><a href="#" class="search-choice-close" rel="' + item.array_index + '"></a>');
this.search_container.grab(el, 'before');
document.id(choice_id).getElement("a")
.addEvent('click', this.choice_destroy_link_click.bind(this));
},
choice_destroy_link_click: function(evt){
evt.preventDefault();
this.pending_destroy_click = true;
this.choice_destroy(evt.target);
},
choice_destroy: function(link){
this.choices -= 1;
this.show_search_field_default();
if (this.is_multiple && this.choices > 0 && this.search_field.value.length < 1){
this.results_hide();
}
this.result_deselect(link.get("rel"));
link.getParent('li').destroy();
},
result_select: function(){
var high, high_id, item, position;
if (this.result_highlight){
high = this.result_highlight;
high_id = high.get("id");
this.result_clear_highlight();
high.addClass("result-selected");
if (this.is_multiple){
this.result_deactivate(high);
} else {
this.result_single_selected = high;
}
position = high_id.substr(high_id.lastIndexOf("_") + 1);
item = this.results_data[position];
item.selected = true;
this.form_field.options[item.options_index].selected = true;
if (this.is_multiple){
this.choice_build(item);
} else {
this.selected_item.getElement("span").set('text', item.text);
}
this.results_hide();
this.search_field.set('value', "");
this.form_field.fireEvent("change");
this.search_field_scale();
}
},
result_activate: function(el){
el.addClass("active-result").setStyle('display', 'block');
},
result_deactivate: function(el){
el.removeClass("active-result").setStyle('display', 'none');
},
result_deselect: function(pos){
var result, result_data;
result_data = this.results_data[pos];
result_data.selected = false;
this.form_field.options[result_data.options_index].selected = false;
result = document.id( this.container_id + "_o_" + pos);
result.removeClass("result-selected").addClass("active-result").setStyle('display', 'block');
this.result_clear_highlight();
this.winnow_results();
this.form_field.fireEvent("change");
this.search_field_scale();
},
results_search: function(evt){
if (this.results_showing){
this.winnow_results();
} else {
this.results_show();
}
},
winnow_results: function(){
var found, option, parts, regex, result_id, results, searchText, startpos, text, zregex;
this.no_results_clear();
results = 0;
searchText = this.search_field.get('value') === this.default_text ? "" : new Element('div', {text: this.search_field.get('value').trim()}).get('html');
regex = new RegExp('^' + searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
zregex = new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
this.results_data.each(function(option){
if (!option.disabled && !option.empty){
if (option.group){
document.id(option.dom_id).setStyle('display', 'none');
}else if (!(this.is_multiple && option.selected)){
found = false;
result_id = option.dom_id
if (regex.test(option.html)){
found = true;
results += 1;
}else if (option.html.indexOf(" ") >= 0 || option.html.indexOf("[") === 0){
parts = option.html.replace(/\[|\]/g, "").split(" ");
if (parts.length){
parts.each(function(part){
if (regex.test(part)){
found = true;
results += 1;
}
});
}
}
if (found){
if (searchText.length){
startpos = option.html.search(zregex);
text = option.html.substr(0, startpos + searchText.length) + '</em>' + option.html.substr(startpos + searchText.length);
text = text.substr(0, startpos) + '<em>' + text.substr(startpos);
} else {
text = option.html;
}
if (document.id(result_id).get('html') !== text){
document.id(result_id).set('html', text);
}
this.result_activate(document.id(result_id));
if (option.group_array_index != null){
document.id(this.results_data[option.group_array_index].dom_id).setStyle('display', 'block');
}
} else {
if (this.result_highlight && result_id === this.result_highlight.get('id')){
this.result_clear_highlight();
}
this.result_deactivate(document.id(result_id));
}
}
}
}, this);
if (results < 1 && searchText.length){
this.no_results(searchText);
} else {
this.winnow_results_set_highlight();
}
},
winnow_results_clear: function(){
this.search_field.set('value', '');
this.search_results.getElements("li").each(function(li){
li.hasClass("group-result") ? li.setStyle('display', 'block') : !this.is_multiple || !li.hasClass("result-selected") ? this.result_activate(li) : void 0;
}, this);
},
winnow_results_set_highlight: function(){
var do_high;
if (!this.result_highlight){
do_high = this.search_results.getElement(".active-result");
if (do_high){
this.result_do_highlight(do_high);
}
}
},
no_results: function(terms){
var no_results_html = new Element('li', {'class': 'no-results'}).set('html', Locale.get('Chosen.noResults')+' "<span></span>"');
no_results_html.getElement("span").set('html', terms);
this.search_results.grab(no_results_html);
},
no_results_clear: function(){
this.search_results.getElements(".no-results").destroy();
},
keydown_arrow: function(){
var first_active, next_sib;
if (!this.result_highlight){
first_active = this.search_results.getElement("li.active-result");
if (first_active){
this.result_do_highlight(first_active);
}
}else if (this.results_showing){
next_sib = this.result_highlight.getNext("li.active-result");
if (next_sib){
this.result_do_highlight(next_sib);
}
}
if (!this.results_showing){
this.results_show();
}
},
keyup_arrow: function(){
if (!this.results_showing && !this.is_multiple){
this.results_show();
}else if (this.result_highlight){
var prev_sibs = this.result_highlight.getAllPrevious("li.active-result");
if (prev_sibs.length){
this.result_do_highlight(prev_sibs[0]);
} else {
if (this.choices > 0){
this.results_hide();
}
this.result_clear_highlight();
}
}
},
keydown_backstroke: function(){
if (this.pending_backstroke){
this.choice_destroy(this.pending_backstroke.getElement("a"));
this.clear_backstroke();
} else {
this.pending_backstroke = this.search_choices.getLast("li.search-choice");
this.pending_backstroke.addClass("search-choice-focus");
}
},
clear_backstroke: function(){
if (this.pending_backstroke){
this.pending_backstroke.removeClass("search-choice-focus");
}
this.pending_backstroke = null;
},
keyup_checker: function(evt){
this.search_field_scale();
switch (evt.key){
case 'backspace':
if (this.is_multiple && this.backstroke_length < 1 && this.choices > 0){
this.keydown_backstroke();
}else if (!this.pending_backstroke){
this.result_clear_highlight();
this.results_search();
}
break;
case 'enter':
evt.preventDefault();
if (this.results_showing){
this.result_select();
}
break;
case 'esc':
if (this.results_showing) {
this.results_hide();
}
break;
case 'tab':
case 'up':
case 'down':
case 'shift':
break;
default:
this.results_search();
}
},
keydown_checker: function(evt){
this.search_field_scale();
if (evt.key !== 'backspace' && this.pending_backstroke){
this.clear_backstroke();
}
switch(evt.key){
case 'backspace':
this.backstroke_length = this.search_field.value.length;
break;
case 'tab':
this.mouse_on_container = false;
break;
case 'enter':
evt.preventDefault();
break;
case 'up':
evt.preventDefault();
this.keyup_arrow();
break;
case 'down':
this.keydown_arrow();
break;
}
},
search_field_scale: function(){
var dd_top, div, h, style, style_block, styles, w, _i, _len;
if (this.is_multiple){
h = 0;
w = 0;
style_block = {
position: 'absolute',
visibility: 'hidden'
};
styles = this.search_field.getStyles('font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing');
Object.merge(style_block, styles);
div = new Element('div', {
'styles': style_block
});
div.set('text', this.search_field.get('value'));
document.body.grab(div);
w = div.getCoordinates().width + 25;
div.destroy();
if (w > this.f_width - 10) {
w = this.f_width - 10;
}
this.search_field.setStyle('width', w);
dd_top = this.container.getCoordinates().height;
this.dropdown.setStyle('top', dd_top);
}
}
});
Element.implement({
get_side_border_padding: function(){
var styles = this.getStyles('padding-left', 'padding-right', 'border-left-width', 'border-right-width');
var notNull = Object.filter(styles, function(value){
return (typeof(value) == 'string');
});
var mapped = Object.map(notNull, function(s){ return s.toInt();});
var array = Object.values(mapped);
var result = 0, l = array.length;
if (l){
while (l--) result += array[l];
}
return result;
},
select_to_array: function(){
var parser = new SelectParser();
this.getChildren().each(function(child){
parser.add_node(child);
});
return parser.parsed;
}
});
var SelectParser = new Class({
options_index: 0,
parsed: [],
add_node: function(child){
if (child.nodeName === "OPTGROUP"){
this.add_group(child);
} else {
this.add_option(child);
}
},
add_group: function(group){
var group_position = this.parsed.length;
this.parsed.push({
array_index: group_position,
group: true,
label: group.label,
children: 0,
disabled: group.disabled
});
group.getChildren().each(function(option){
this.add_option(option, group_position, group.disabled);
}, this);
},
add_option: function(option, group_position, group_disabled){
if (option.nodeName === "OPTION") {
if (option.text !== ""){
if (group_position != null) {
this.parsed[group_position].children += 1;
}
this.parsed.push({
array_index: this.parsed.length,
options_index: this.options_index,
value: option.value,
text: option.text,
html: option.innerHTML,
selected: option.selected,
disabled: group_disabled === true ? group_disabled : option.disabled,
group_array_index: group_position
});
} else {
this.parsed.push({
array_index: this.parsed.length,
options_index: this.options_index,
empty: true
});
}
this.options_index += 1;
}
}
});/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param TransferListItemDTO schoolTransfersDTO the transfers informations
* @param SchoolPriceEstimation parent the parent
**/
var SchoolSelector = new Class({
Implements: Events,
/**
* This is the initialization function
*
* @param Object translateDTO the object containing the translations for this class
* @param SchoolsListDTO schoolsList The Schools list
* @param Objec parent The class that calls this class
*/
initialize : function(translateDTO, schoolsList, parent, advancedSchoolSelector) {
this._translateDTO = translateDTO;
this._booking = this._parent = parent;
this._schoolsList = schoolsList;
this._schoolsHash = new Hash();
this._cityCountryHash = new Hash();
this._classNameBase = this._parent.getClassNameBase();
if (this._schoolsList.length == 1 && (!this._parent.getType || this._parent.getType() == 'PriceEstimation')) {
this._selectedSchoolId = this._schoolsList[0].schoolId;
} else {
this._selectedSchoolId = null;
}
this.populateData();
},
populateData : function () {
this.programs = new Hash();
for (var i = 0; i < this._schoolsList.length; i++) {
var programId = this._schoolsList[i].schoolProgramId;
var countryCode = this._schoolsList[i].schoolCountryCode;
var cityId = this._schoolsList[i].schoolCityId;
if (!this.programs.has(programId)) {
var program = new Object();
program.name = this._schoolsList[i].schoolProgramName;
program.id = this._schoolsList[i].schoolProgramId;
program.countries = new Hash();
this.programs.set(this._schoolsList[i].schoolProgramId, program);
}
if (!this.programs.get(programId).countries.has(countryCode)) {
var country = new Object();
country.name = this._schoolsList[i].schoolCountryName;
country.id = countryCode;
country.cities = new Hash();
this.programs.get(programId).countries.set(countryCode, country);
}
if (!this.programs.get(programId).countries.get(countryCode).cities.has(cityId)) {
var city = new Object();
city.name = this._schoolsList[i].schoolCityName;
city.id = cityId;
city.schools = new Hash();
this.programs.get(programId).countries.get(countryCode).cities.set(cityId, city);
}
if (!this._cityCountryHash.has(cityId)) {
this._cityCountryHash.set(cityId, countryCode)
}
if (!this.programs.get(programId).countries.get(countryCode).cities.get(cityId).schools.has(this._schoolsList[i].schoolId)) {
var school = new Object();
school.name = this._schoolsList[i].schoolName;
school.id = this._schoolsList[i].schoolId;
if (!this._schoolsHash.has(school.id)) {
this._schoolsHash.set(school.id, this._schoolsList[i]);
}
this.programs.get(programId).countries.get(countryCode).cities.get(cityId).schools.set(this._schoolsList[i].schoolId, school);
}
}
this._data = new Object();
this._data.programs = new Array();
for (var i = 0; i < this.programs.getKeys().length; i++) {
var program = this.programs.get(this.programs.getKeys()[i]);
var programObj = new Object();
programObj.id = program.id;
programObj.name = program.name;
programObj.countries = new Array();
for (var j = 0; j < program.countries.getKeys().length; j++) {
var country = program.countries.get(program.countries.getKeys()[j]);
var countryObj = new Object();
countryObj.id = country.id;
countryObj.name = country.name;
countryObj.cities = new Array();
for (var k = 0; k < country.cities.getKeys().length; k++) {
var city = country.cities.get(country.cities.getKeys()[k]);
var cityObj = new Object();
cityObj.id = city.id;
cityObj.name = city.name;
cityObj.schools = new Array();
for (var l = 0; l < city.schools.getKeys().length; l++) {
var school = city.schools.get(city.schools.getKeys()[l]);
var schoolObj = new Object();
schoolObj.id = school.id;
schoolObj.name = school.name;
cityObj.schools.push(schoolObj);
}
cityObj.schools.sort(this.sortFunction);
countryObj.cities.push(cityObj);
}
countryObj.cities.sort(this.sortFunction);
programObj.countries.push(countryObj);
}
programObj.countries.sort(this.sortFunction);
this._data.programs.push(programObj);
}
this._data.programs.sort(this.sortFunction);
},
render : function () {
var programsTemplate = this._booking.getProgramsListTemplate(this._data);
var countriesTemplate = this._booking.getCountriesListTemplate(null, this._data);
var citiesTemplate = this._booking.getCitiesListTemplate(null, null, this._data);
var schoolsTemplate = this._booking.getSchoolsListTemplate(null, null, null, this._data);
programsTemplate.getElement().render(this._data, programsTemplate.getDirective());
countriesTemplate.getElement().render(false, countriesTemplate.getDirective());
citiesTemplate.getElement().render(false, citiesTemplate.getDirective());
schoolsTemplate.getElement().render(false, schoolsTemplate.getDirective());
this._programSelectorList = this._booking.populateList(document.getElement(programsTemplate.getSelector()));
this._programSelectorList.addEvent('change', function(e) {
this.onProgramChange(this._programSelectorList.getValue());
}.bind(this));
this._countrySelectorList = this._booking.populateList(document.getElement(countriesTemplate.getSelector()));
this._countrySelectorList.addEvent('change', function(e) {
this.onCountryChange(this._countrySelectorList.getValue());
}.bind(this));
this._citySelectorList = this._booking.populateList(document.getElement(citiesTemplate.getSelector()));
this._citySelectorList.addEvent('change', function(e) {
this.onCityChange(this._citySelectorList.getValue());
}.bind(this));
this._schoolSelectorList = this._booking.populateList(document.getElement(schoolsTemplate.getSelector()));
this._schoolSelectorList.addEvent('change', function(e) {
this.onSchoolChange(this._schoolSelectorList.getValue());
}.bind(this));
this.fireEvent('updated');
},
sortFunction : function(a, b) {
var a = a.name.toLowerCase();
var b = b.name.toLowerCase();
if (a < b) {
return -1;
} else if (a > b) {
return  1;
} else {
return 0;
}
},
/**
* Function called when program is changed
*
* @param SchoolListItem item The new item
*/
onProgramChange : function(programId) {
if (this._selectedProgramId != programId) {
this._programSelectorList.setValue(programId);
this._selectedProgramId = programId;
this._selectedCityId = null;
this._selectedCountryId = null;
this._selectedSchoolId = null;
this._countries = new Array();
this._cities = new Array();
this._schools = new Array();
for (var i = 0; i < this._data.programs.length; i++) {
if (this._data.programs[i].id == programId || !programId) {
this._countries = this._data.programs[i].countries;
break;
}
}
var countriesTemplate = this._booking.getCountriesListTemplate(this._selectedProgramId, this._countries);
var citiesTemplate = this._booking.getCitiesListTemplate(this._selectedProgramId, this._selectedCountryId, this._cities);
var schoolsTemplate = this._booking.getSchoolsListTemplate(this._selectedProgramId, this._selectedCountryId, this._selectedCityId, this._schools);
citiesTemplate.getElement().render(false, citiesTemplate.getDirective());
schoolsTemplate.getElement().render(false, schoolsTemplate.getDirective());
this._citySelectorList.updateListOptions(document.getElement(citiesTemplate.getSelector()));
this._schoolSelectorList.updateListOptions(document.getElement(schoolsTemplate.getSelector()));
if (programId && programId > 0) {
countriesTemplate.getElement().render(this._countries, countriesTemplate.getDirective());
this._countrySelectorList.updateListOptions(document.getElement(countriesTemplate.getSelector()));
if (this._countries.length == 1) {
this._countrySelectorList.setValue(this._countries[0].id);
}
} else {
countriesTemplate.getElement().render(false, countriesTemplate.getDirective());
this._countrySelectorList.updateListOptions(document.getElement(countriesTemplate.getSelector()));
}
}
this.fireEvent('updated');
this.fireEvent('changeSchool');
},
/**
* Function called when the country change
* @param SchoolListItem item The new item
*/
onCountryChange : function(countryCode) {
if (this._selectedCountryId != countryCode) {
this._countrySelectorList.setValue(countryCode);
this._selectedCountryId = countryCode;
this._selectedCityId = null;
this._selectedSchoolId = null;
this._cities = new Array();
this._schools = new Array();
for (var i = 0; i < this._countries.length; i++) {
if (this._countries[i].id == countryCode) {
this._cities = this._countries[i].cities;
break;
}
}
var citiesTemplate = this._booking.getCitiesListTemplate(this._selectedProgramId, this._selectedCountryId, this._cities);
var schoolsTemplate = this._booking.getSchoolsListTemplate(this._selectedProgramId, this._selectedCountryId, this._selectedCityId, this._schools);
schoolsTemplate.getElement().render(false, schoolsTemplate.getDirective());
this._schoolSelectorList.updateListOptions(document.getElement(schoolsTemplate.getSelector()));
if (countryCode) {
citiesTemplate.getElement().render(this._cities, citiesTemplate.getDirective());
this._citySelectorList.updateListOptions(document.getElement(citiesTemplate.getSelector()));
if (this._cities.length == 1) {
this._citySelectorList.setValue(this._cities[0].id);
}
} else {
citiesTemplate.getElement().render(false, citiesTemplate.getDirective());
this._citySelectorList.updateListOptions(document.getElement(citiesTemplate.getSelector()));
}
this.fireEvent('updated');
this.fireEvent('changeSchool');
}
},
/**
* Function called when the city change
* @param SchoolListItem item The new item
*/
onCityChange : function(cityId) {
if (this._selectedCityId != cityId) {
this._citySelectorList.setValue(cityId);
this._selectedCityId = cityId;
this._selectedSchoolId = null;
this._schools = new Array();
for (var i = 0; i < this._cities.length; i++) {
if (this._cities[i].id == cityId) {
this._schools = this._cities[i].schools;
break;
}
}
var schoolsTemplate = this._booking.getSchoolsListTemplate(this._selectedProgramId, this._selectedCountryId, this._selectedCityId, this._schools);
if (cityId > 0) {
schoolsTemplate.getElement().render(this._schools, schoolsTemplate.getDirective());
this._schoolSelectorList.updateListOptions(document.getElement(schoolsTemplate.getSelector()));
if (this._schools.length == 1) {
this._schoolSelectorList.setValue(this._schools[0].id);
}
} else {
schoolsTemplate.getElement().render(false, schoolsTemplate.getDirective());
this._schoolSelectorList.updateListOptions(document.getElement(schoolsTemplate.getSelector()));
}
this.fireEvent('updated');
this.fireEvent('changeSchool');
}
},
/**
* Function called when the school change
* @param SchoolListItem item The new item
*/
onSchoolChange : function(schoolId) {
if (this._selectedSchoolId != schoolId) {
this._selectedSchoolId = schoolId;
this.fireEvent('updated');
this.fireEvent('changeSchool');
}
},
/**
* Update the selector with the given parameter
*
*
* @param integer school The School Id
* @param integer program The program Id
* @param string country The country code
* @param integer city The city Id
*/
updateSelector : function(school, program, country, city) {
if (school.toInt()) {
if (this._schoolsHash.has(school)) {
var schoolObject = this._schoolsHash.get(school);
this.onProgramChange(schoolObject.schoolProgramId);
this.onCountryChange(schoolObject.schoolCountryCode);
this.onCityChange(schoolObject.schoolCityId);
this.onSchoolChange(school);
}
} else {
if (program.toInt()) {
this.onProgramChange(program);
if (country) {
this.onCountryChange(country);
}
if (city) {
if (!country) {
this.onCountryChange(this._cityCountryHash.get(city));
}
this.onCityChange(city);
}
}
}
},
/**
* Returns the number of schoolSelector
*
* @return number The number of schoolSelectors
*/
getSelectedSchoolId : function() {
if(this._schoolsList.length == 1)
return this._schoolsList[0].schoolId;
return this._selectedSchoolId;
}
});var BookingManager = new Class({
Implements: [Events, Options],
options: {
shoppingCartPage: false, // True if it's a shopping cart page
courseTypeContainer : '',
accommodationTypeContainer : '',
insuranceContainer : '',
promotionIntroductionContainer : '',
promotionContainer : '',
onlyWebsiteSchools : false
},
/**
* This is the initialization function
*/
initialize : function(options) {
this.setOptions(options);
this.createDirectives();
var programDir = this._directives.get('programsListDirective');
var countryDir = this._directives.get('countriesListDirective');
var cityDir = this._directives.get('citiesListDirective');
var schoolDir = this._directives.get('schoolsListDirective');
this.booking = new Booking({
shoppingCartPage: this.options.shoppingCartPage,
input: $('bookingOption'),
output: $('bookingCart'),
schoolsSelectorContainer: $('schoolsSelector'),
header: $('bookingLinks'), // the header block
printOutput: $("schoolPriceEstimationPrint"),
courseTypeContainer : this.options.courseTypeContainer,
accommodationTypeContainer : this.options.accommodationTypeContainer,
insuranceContainer : this.options.insuranceContainer,
promotionIntroductionContainer : this.options.promotionIntroductionContainer,
promotionContainer : this.options.promotionContainer,
onlyWebsiteSchools : this.options.onlyWebsiteSchools,
getProgramsListTemplateCallback : function  (data) {
return new BookingTemplateObject({selector : '.program-selector',
directive : programDir,
element : $p('.program-selector')});
},
getCountriesListTemplateCallback : function  (programId, data) {
switch (parseInt(programId)) {
default :
return new BookingTemplateObject({selector : '.country-selector',
directive : countryDir
, element : $p('.country-selector')});
break;
}
},
getCitiesListTemplateCallback : function  (programId, countryCode, data) {
return new BookingTemplateObject({selector : '.city-selector',
directive : cityDir,
element : $p('.city-selector')});
},
getSchoolsListTemplateCallback : function  (programId, countryCode, cityId, data) {
return new BookingTemplateObject({selector : '.school-selector',
directive : schoolDir,
element : $p('.school-selector')});
}
});
// The school selector options
this.booking.addEvent('schoolsSelectorLoaded', function() {
this.setActive($('schoolsSelector'), true);
try {
$('schoolsSelector').getElements('select').chosen();
} catch(ex) {
}
this.setActive($$('.country-selector')[0], $$('.country-selector select')[0].getElements('option').length);
this.setActive($$('.city-selector')[0], $$('.city-selector select')[0].getElements('option').length);
this.setActive($$('.school-selector')[0], $$('.school-selector select')[0].getElements('option').length);
this.booking.getSchoolsSelector().addEvent('changeSchool', function() {
$$('.item-selector').fireEvent('liszt:updated');
});
this.booking.getSchoolsSelector().addEvent('updated', function() {
if (!$$('.country-selector')[0].getElement('.chzn-container ')) {
try {
$$('.country-selector select').chosen();
} catch(ex) {
}
}
this.setActive($$('.country-selector')[0], $$('.country-selector select')[0].getElements('option[value]').length);
if (!$$('.city-selector')[0].getElement('.chzn-container ')) {
try {
$$('.city-selector select').chosen();
} catch(ex) {
}
}
this.setActive($$('.city-selector')[0], $$('.city-selector select')[0].getElements('option[value]').length);
if (!$$('.school-selector')[0].getElement('.chzn-container ')) {
try {
$$('.school-selector select').chosen();
} catch(ex) {
}
}
this.setActive($$('.school-selector')[0], $$('.school-selector select')[0].getElements('option[value]').length);
}.bind(this));
this.setActive($('schoolsSelector'), false);
}.bind(this));
this.booking.addEvent('initialized', function(){
this.fireEvent('initialized');
}.bind(this));
$("bookingOption").store('booking', this.booking);
// override this method if you want a customized list
this.booking.populateList = function (list, values) {
if (!values)
values = null;
return new BookingList(list, values, '.item-option', '.selector-button');
};
this._initializeBookingEvents();
this.booking.launch();
$('promoField').addEvent('focus', function(e) {
if ($('promoField').get('value') == this.booking.getTranslations().promotions.enterYourCode) {
$('promoField').set('value', '');
}
}.bind(this));
$('promoField').addEvent('blur', function(e) {
if ($('promoField').get('value') == '') {
$('promoField').set('value', this.booking.getTranslations().promotions.enterYourCode)
}
}.bind(this));
$('promotionForm').addEvent('submit', function(e) {
e.stop();
var code = $('promoField').get('value');
if (code && code != this.booking.getTranslations().promotions.enterYourCode) {
$('promoField').setAttribute('disabled', 'disabled');
this.booking.getPromotionsManager().validateCode(code);
}
}.bind(this));
},
/**
* Create a list object container
*
* @param list
* @param values
*/
createList : function(list, values) {
if (!values)
values = null;
return new BookingList(list, values, '.item-option', '.selector-button');
},
updateSchoolDescriptions : function(school) {
if(school.elementContainer){
var courses = school.elementContainer.getElements('.item-option-course');
var groupToolTip;
var sameCourseType;
var oldCourseType;
var description;
var currentGroup = new Array();
courses.each(function(item, index) {
if (!item.getParent('.item-category') || groupToolTip != item.getParent('.item-category').getElement('.item-tool-tip')) {
if (groupToolTip && currentGroup.length && sameCourseType) {
groupToolTip.setAttribute('title', description);
groupToolTip.removeClass('hidden');
var tooltip = new SynergeeTips(groupToolTip, {'className':'Booking-Tool-Tip Tool-tip', 'hideDelay':1000, 'fixed':true, 'closeButton':true, 'maxHeight':280});
} else {
currentGroup.each(function(tip, index) {
tip.removeClass('hidden');
var tooltip = new SynergeeTips(tip, {'className':'Booking-Tool-Tip Tool-tip', 'hideDelay':1000, 'fixed':true, 'closeButton':true, 'maxHeight':280});
}.bind(this));
}
currentGroup = new Array();
sameCourseType = item.getParent('.item-category');
oldCourseType = -1;
groupToolTip = item.getParent('.item-category') ? item.getParent('.item-category').getElement('.item-tool-tip') : null;
}
var toolTipEl = item.getElement('.item-tool-tip');
if (toolTipEl) {
var courseInfo = school.getCoursesManager().getCourseInformationDTO(parseInt(item.getAttribute('value')));
if (courseInfo) {
if (oldCourseType != -1 && courseInfo.courseTypeId != oldCourseType) {
sameCourseType = false;
}
oldCourseType = courseInfo.courseTypeId;
description = this.booking.getDescription('course', courseInfo.courseTypeId);
if (description) {
currentGroup.push(toolTipEl);
toolTipEl.setAttribute('title', description);
}
} else {
sameCourseType = false;
}
}
}.bind(this));
if (groupToolTip && currentGroup.length && sameCourseType) {
groupToolTip.setAttribute('title', description);
groupToolTip.removeClass('hidden');
var tooltip = new SynergeeTips(groupToolTip, {'className':'Booking-Tool-Tip Tool-tip', 'hideDelay':1000, 'fixed':true, 'closeButton':true, 'maxHeight':280});
} else {
currentGroup.each(function(tip, index) {
tip.removeClass('hidden');
var tooltip = new SynergeeTips(tip, {'className':'Booking-Tool-Tip Tool-tip', 'hideDelay':1000, 'fixed':true, 'closeButton':true, 'maxHeight':280});
}.bind(this));
}
var accommodations = school.elementContainer.getElements('.item-option-accommodation');
accommodations.each(function(item, index) {
var toolTipEl = item.getParent('.item-category').getElement('.item-tool-tip');
if (toolTipEl) {
var accommodationInfo = school.getAccommodationsManager().getAccommodationOptionInformations(parseInt(item.getAttribute('value')));
if (accommodationInfo) {
description = this.booking.getDescription('accommodation', accommodationInfo.accommodationDTO.accommodationType);
if (description) {
currentGroup.push(toolTipEl);
toolTipEl.setAttribute('title', description);
toolTipEl.removeClass('hidden');
var tooltip = new SynergeeTips(toolTipEl, {'className':'Booking-Tool-Tip Tool-tip', 'hideDelay':1000, 'fixed':true, 'closeButton':true, 'maxHeight':280});
}
}
}
}.bind(this));
}
},
_initializeBookingEvents : function () {
this.booking.addEvent('descriptionsUpdated', function() {
this.booking.getSchools().each(function(school, index) {
this.updateSchoolDescriptions.delay(700, this, school);
}.bind(this));
var insurances = $('insurancesContainer').getElements('.item-option-insurance');
insurances.each(function(item, index) {
var toolTipEl = item.getElement('.item-tool-tip');
if (toolTipEl) {
var description = this.booking.getDescription('insurance', parseInt(item.getAttribute('value')));
if (description) {
toolTipEl.setAttribute('title', description);
toolTipEl.removeClass('hidden');
var tooltip = new SynergeeTips(toolTipEl, {'className':'Booking-Tool-Tip Tool-tip', 'hideDelay':1000, 'fixed':true, 'closeButton':true, 'maxHeight':280});
}
}
}.bind(this));
}.bind(this));
// when the booking is initialized we start the rendering of the booking
this.booking.addEvent('initialized', function() {
//
// The main schools rendering
//
var schoolsTemplate = this.getSchoolsTemplate(this.booking.getSchoolsDTO());
schoolsTemplate.getElement().render(this.booking.getSchoolsDTO(), schoolsTemplate.getDirective());
//
// display or not the school selector
//
if (!this.booking.getIsShoppingCartPage() && this.booking._initDTO.schoolsList.length > 1) {
$('schoolsSelector').removeClass('inActive');
}
this.booking.getPromotionsManager().addEvent('promotionAlreadyAdded', function() {
this.booking.displayMessage(this.booking.getTranslations().promotions.promotionAlreadyAdded);
$('promoField').set('value', this.booking.getTranslations().promotions.enterYourCode);
$('promoField').removeAttribute('disabled');
}.bind(this));
this.booking.getPromotionsManager().addEvent('promotionAdded', function() {
$('promoField').set('value', this.booking.getTranslations().promotions.enterYourCode);
$('promoField').removeAttribute('disabled');
//            this.booking.displayMessage(this.booking.getTranslations().promotions.promotionAlreadyAdded);
}.bind(this));
this.booking.getPromotionsManager().addEvent('invalidCode', function() {
this.booking.displayMessage(this.booking.getTranslations().promotions.invalidCode);
$('promoField').removeAttribute('disabled');
$('promoField').set('value', this.booking.getTranslations().promotions.enterYourCode);
}.bind(this));
this.bookingCalendar = new BookingCalendar({
days: this.booking._translateDTO.days, // days of the week starting at sunday
months: this.booking._translateDTO.months,
offset: this.booking._translateDTO.firstDayOfWeek
});
this.bookingCalendar.addEvent('dateselected', function(e) {
this.bookingCalendar.hide();
this.bookingCalendar.getCallBackMethod().call(this.bookingCalendar.getBind(), this.bookingCalendar.getSelectedDate());
}.bind(this));
// The currency selector
var currencies = this.booking.currency.getAvailableCurrencies();
var currenciesListTp = this.getCurrenciesListTemplate(this.booking, currencies);
if (currenciesListTp) {
$p(currenciesListTp.getSelector()).render(currencies, currenciesListTp.getDirective());
if (currencies.length > 1) {
$$('.currencies-list select').set('value', this.booking.currency.getCurrency());
$$('.currencies-list select').addEvent('change', function(e) {
var value = $$('.currencies-list select').get('value');
if (value.length && value[0] != this.booking.currency.getCurrency() && value[0] != '') {
this.booking.currency.setCurrency(value[0]);
}
}.bind(this));
$$('.currencies-list select').chosen();
}
}
//
// The insurances List
//
var insurancesListTp = this.getInsurancesListTemplate(this.booking);
if (insurancesListTp) {
var insurances = new Array();
this.booking.getInsurancesManager().getInsurances().each(function(item, index) {
insurances.push(item);
}.bind(this));
insurances.sort(function(a, b){
var nameA = a.name.toLowerCase(), nameB = b.name.toLowerCase()
if (nameA < nameB) //sort string ascending
return -1
if (nameA > nameB)
return 1
return 0 //default return value (no sorting)
});
$p(insurancesListTp.getSelector()).render(insurances, insurancesListTp.getDirective());
if(!insurances.length){
$('insurancesContainer').addClass('hidden');
}else{
$('insurancesContainer').removeClass('hidden');
}
$$('.insurances-list .item-option-radio').addEvent('change', function(e) {
var insuranceId = parseInt(e.target.value);
if (e.target.checked) {
// check if it's already added
var addInsurance = true;
this.booking.getInsurancesManager().getAddedInsurances().each(function(item, index) {
if (parseInt(item.getInsuranceId()) == insuranceId) {
// the course is already in the basket
addInsurance = false;
}
}.bind(this));
if (addInsurance) {
this.booking.getInsurancesManager().addInsurance(insuranceId);
}
} else {
this.booking.getInsurancesManager().getAddedInsurances().each(function(item, index) {
if (parseInt(item.getInsuranceId()) == insuranceId) {
this.booking.getInsurancesManager().deleteInsurance(item);
}
}.bind(this));
}
}.bind(this));
}
//
// when an insurance is added, it adds some listener to
//
this.booking.getInsurancesManager().addEvent('insuranceAdded', function(insurance) {
// the insurance events
insurance.addEvent('refresh', function(e) {
if (insurance.getElement()) {
insurance.getElement().getParent().getElement('input').checked = 'checked';
}
}.bind(this));
insurance.fireEvent('refresh');
}.bind(this));
//
// refresh the rendering of the insurances
//
this.booking.getInsurancesManager().addEvent('insurancesUpdated', function(e) {
$$('.insurance-container').addClass('hidden');
this.booking.getInsurancesManager().getAddedInsurances().each(function(insurance, index) {
insurance.setElement($$('.insurances-list')[0].getElement(('.item-option-insurance[value=' + insurance.getInsuranceId() + '] .insurance-container')));
insurance.fireEvent('refresh');
}.bind(this));
this.updateInsurancesList();
}.bind(this));
//
// Check each school in the booking
//
this.booking.getSchools().each(function(school, index) {
$('bookingOption').removeClass('inActive');
$('bookingCart').removeClass('inActive');
school.elementContainer = document.getElement(schoolsTemplate.getSelector()).getElements('.school-container')[index];
//
// the courses list
//
var schoolCoursesListTp = this.getSchoolCoursesListTemplate(school._schoolDTO);
if (schoolCoursesListTp) {
// retrieve the courses list
var coursesTree = new Array();
var courses = school.getCoursesManager().getCourses();
// Retrieve the minimum price to display
courses.each(function(item, index){
var priceFound = false;
item.prices.each(function(price, index){
if(price.currency == bookingManager.booking.currency.getCurrency()){
item.displayPrice = price.price;
priceFound = true;
}
});
if(!priceFound)
item.displayPrice = item.price;
});
$p(schoolCoursesListTp.getSelector(), school.elementContainer).render(courses, schoolCoursesListTp.getDirective());
school.elementContainer.getElements('.item-option-course .item-option-radio').addEvent('change', function(e) {
var courseId = parseInt(e.target.value);
var addCourse = true;
school.getCoursesManager()._courses.each(function(item, index) {
if (parseInt(item.getCourseDetails().id) == courseId) {
// the course is already in the basket
addCourse = false;
}
}.bind(this));
if (addCourse) {
// check if there is an old course
this._lastSchoolDTO = school.getDTO();
school.getCoursesManager().removeAllCourses(false, true);
var course = school.getCoursesManager().addCourseFromId(courseId);
if (this._lastSchoolDTO.fixedDatesCourses.length || this._lastSchoolDTO.variableDatesCourses.length) {
var oldCourse = this._lastSchoolDTO.variableDatesCourses.length ? this._lastSchoolDTO.variableDatesCourses[0] : this._lastSchoolDTO.fixedDatesCourses[0];
course.populateFromDTO(oldCourse);
}
if (this._lastSchoolDTO && this._lastSchoolDTO.accommodations.length) {
school.getAccommodationsManager().removeAllAccommodations(true);
school.getAccommodationsManager().addAccommodation(null, this._lastSchoolDTO.accommodations[0].id);
}
}
}.bind(this));
}
//
// the optional courses list
//
var courses = school.getCoursesManager().getOptionalCourses();
// Retrieve the minimum price to display
courses.each(function(item, index){
var priceFound = false;
item.prices.each(function(price, index){
if(price.currency == bookingManager.booking.currency.getCurrency()){
item.displayPrice = price.price;
priceFound = true;
}
});
if(!priceFound)
item.displayPrice = item.price;
});
var schoolOptionalCoursesListTp = this.getSchoolOptionalCoursesListTemplate(school._schoolDTO);
if (schoolOptionalCoursesListTp) {
$p(schoolOptionalCoursesListTp.getSelector(), school.elementContainer).render(courses, schoolOptionalCoursesListTp.getDirective());
if(!school.getCoursesManager().getOptionalCourses().length){
$$('.options-section').addClass('hidden');
}else {
$$('.options-section').removeClass('hidden');
}
school.elementContainer.getElements('.item-option-optional-course .item-option-radio').addEvent('change', function(e) {
var courseId = parseInt(e.target.value);
if (e.target.checked) {
// check if it's already added
var addCourse = true;
school.getCoursesManager()._optionalCourses.each(function(item, index) {
if (parseInt(item.getCourseDetails().id) == courseId) {
// the course is already in the basket
addCourse = false;
}
}.bind(this));
if (addCourse) {
var optionalCourse = school.getCoursesManager().addCourseFromId(courseId);
if (optionalCourse && optionalCourse.getCourseValidStartDates().length) {
optionalCourse.setCourseStartDate(optionalCourse.getCourseValidStartDates()[0]);
// set the first possible duration by default
if (!optionalCourse.getCourseDuration()) {
if (optionalCourse.getCourseValidDurations().length)
optionalCourse.setCourseDuration(optionalCourse.getCourseValidDurations()[0]);
}
}
}
} else {
school.getCoursesManager()._optionalCourses.each(function(item, index) {
if (parseInt(item.getCourseDetails().id) == parseInt(courseId)) {
school.getCoursesManager().deleteCourse(item);
}
}.bind(this));
}
}.bind(this));
}
//
// The accommodations list
//
var schoolAccommodationsListTp = this.getSchoolAccommodationsListTemplate(school._schoolDTO);
if (schoolAccommodationsListTp) {
var accommodations = school.getAccommodationsManager()._schoolAccommodationsDTO;
// Retrieve the minimum price to display
accommodations.each(function(item, index){
item.option.each(function(opt, index){
var priceFound = false;
opt.prices.each(function(price, index){
if(price.currency == bookingManager.booking.currency.getCurrency()){
opt.displayPrice = price.price;
priceFound = true;
}
});
if(!priceFound)
opt.displayPrice = opt.price;
});
});
$p(schoolAccommodationsListTp.getSelector(), school.elementContainer).render(school.getAccommodationsManager()._schoolAccommodationsDTO, schoolAccommodationsListTp.getDirective());
school.elementContainer.getElements('.school-accommodations-list .item-option-radio').addEvent('change', function(e) {
var accommodationOptionId = parseInt(e.target.value);
if (accommodationOptionId) {
var accommodationInfo = school.getAccommodationsManager()._accommodationsOptions.get(accommodationOptionId);
// check if it's already added
var addAccommodation = true;
school.getAccommodationsManager().getAccommodations().each(function(item, index) {
if (parseInt(item.getAccommodationOptionDetails().id) == accommodationInfo.optionDTO.id) {
// the course is already in the basket
addAccommodation = false;
}
}.bind(this));
if (addAccommodation) {
school.getAccommodationsManager().removeAllAccommodations(true);
school.getAccommodationsManager().addAccommodation(accommodationInfo.accommodationDTO.id, accommodationInfo.optionDTO.id);
}
} else {
school.getAccommodationsManager().removeAllAccommodations();
}
}.bind(this));
}
//
// The Transfers list
//
var schoolTransfersListTp = this.getSchoolTransfersListTemplate(school._schoolDTO);
if (schoolTransfersListTp) {
$p(schoolTransfersListTp.getSelector(), school.elementContainer).render(school.getTransfersManager()._schoolTransfersDTO, schoolTransfersListTp.getDirective());
school.elementContainer.getElements('.school-transfers-list .item-option-radio').addEvent('change', function(e) {
var transferId = parseInt(e.target.value);
if (transferId) {
// check if it's already added
var addTransfer = true;
school.getTransfersManager().getTransfers().each(function(item, index) {
if (parseInt(item.getTransferId()) == transferId) {
// the course is already in the basket
addTransfer = false;
}
}.bind(this));
if (addTransfer) {
school.getTransfersManager().removeAllTransfers(true);
school.getTransfersManager().addTransfer(transferId);
}
} else {
school.getTransfersManager().removeAllTransfers();
}
}.bind(this));
}
//
// when a course is added, it adds some listener to
//
school.getCoursesManager().addEvent('courseAdded', function(course) {
// the courses events
course.addEvent('refresh', function(e) {
if (course.getElement()) {
course.getElement().getParent().getElement('input').checked = 'checked';
course.getElement().removeClass('hidden');
switch (course.getCourseType()) {
case 'optional':
course.refreshLists();
case 'variable':
// start date template
var startDatesTemplate = this.getSchoolCourseStartDatesListTemplate('startDate', course);
if (startDatesTemplate) {
$p(startDatesTemplate.getSelector(), course.getElement()).render({'date' : course.getCourseStartDate() ? course.getCourseStartDate().format(course._dateFormat, course._monthsTranslations) : this.booking._translateDTO.courses.courseObject.dateSelect}, startDatesTemplate.getDirective());
var element = course.getElement().getElement(startDatesTemplate.getSelector());
element.addEvent('click', function() {
this.bookingCalendar.setValidDates(course.getCourseValidStartDates(), 0, 0, 0);
if (course.getCourseStartDate()) {
this.bookingCalendar.setSelectedDate(course.getCourseStartDate(), 0, 0, 0);
}
else {
if (course.getCourseValidStartDates().length)
this.bookingCalendar.displayMonths(course.getCourseValidStartDates()[0].getMonth(), course.getCourseValidStartDates()[0].getFullYear());
}
this.bookingCalendar.show(element, function(date) {
course.setCourseStartDate(date.relativeDate);
// set the first possible duration by default
if (!course.getCourseDuration()) {
if (course.getCourseValidDurations().length)
course.setCourseDuration(course.getCourseValidDurations()[0]);
}
}, this);
}.bind(this));
}
//
// the course duration template
//
var durationsListTemplate = this.getSchoolCourseDurationsListTemplate(course._courseDuration, course);
if (durationsListTemplate) {
var durations = course._durations;
durations.each(function(item, index){
if(item.id == '1'){
item.name =  this.booking.getTranslations().courses.courseObject.week.replace("%0", item.id)
} else {
item.name =  this.booking.getTranslations().courses.courseObject.weeks.replace("%0", item.id)
}
}.bind(this));
$p(durationsListTemplate.getSelector(), course.getElement()).render(durations, durationsListTemplate.getDirective());
// the durations list
if (course.durationsList) {
course.durationsList.removeEvents('change');
course.durationsList.updateListOptions(course.getElement().getElement(durationsListTemplate.getSelector()));
} else {
course.durationsList = this.createList(course.getElement().getElement(durationsListTemplate.getSelector()));
}
course.durationsList.setValidOptions(course.getCourseValidDurations());
course.durationsList.setValue(course.getCourseDuration());
course.durationsList.addEvent('change', function(e) {
course.setCourseDuration(course.durationsList.getValue());
}.bind(this));
}
var courseEndDateTemplate = this.getSchoolCourseEndDateTemplate(course);
if (courseEndDateTemplate && course.getElement()) {
var endDate = course.getCourseEndDate() ? course.getCourseEndDate().format(course._dateFormat, course._monthsTranslations) : '';
$p(courseEndDateTemplate.getSelector(), course.getElement()).render({date : endDate}, courseEndDateTemplate.getDirective());
}
break;
case 'fixed':
course.getElement().removeClass('hidden');
//
// start date template
//
var startDatesListTemplate = this.getSchoolCourseStartDatesListTemplate(course.getCourseStartDate(), course);
if (startDatesListTemplate) {
$p(startDatesListTemplate.getSelector(), course.getElement()).render(course._sessionsObjects, startDatesListTemplate.getDirective());
// the start dates list
if (course.startDatesList)
course.startDatesList.removeEvents('change');
course.startDatesList = this.createList(course.getElement().getElement(startDatesListTemplate.getSelector()));
course.startDatesList.setValidOptions(course.getValidCourseSessions());
if (course.getSession()) {
course.startDatesList.setValue(course.getCourseDetails().sessions.indexOf(course.getSession()));
}
course.startDatesList.addEvent('change', function(e) {
var sessionIndex = parseInt(course.startDatesList.getValue());
course.setSessionIndex(sessionIndex);
}.bind(this));
}
var courseEndDateTemplate = this.getSchoolCourseEndDateTemplate(course);
if (courseEndDateTemplate) {
var endDate = course.getCourseEndDate() ? course.getCourseEndDate().format(course._dateFormat, course._monthsTranslations) : '';
$p(courseEndDateTemplate.getSelector(), course.getElement()).render({date : endDate}, courseEndDateTemplate.getDirective());
}
var courseDurationTemplate = this.getSchoolCourseDurationValueTemplate(course);
if (courseDurationTemplate) {
var duration = course.getCourseDuration() ? course.getCourseDuration() : '';
if(duration == '1'){
duration =  this.booking.getTranslations().courses.courseObject.week.replace("%0", duration)
} else {
duration =  this.booking.getTranslations().courses.courseObject.weeks.replace("%0", duration)
}
$p(courseDurationTemplate.getSelector(), course.getElement()).render({duration : duration}, courseDurationTemplate.getDirective());
}
break;
}
course.getElement().getElements('select.item-selector').chosen();
}
}.bind(this));
}.bind(this));
//
// refresh the rendering of the courses
//
school.getCoursesManager().addEvent('coursesUpdated', function(e) {
$$('.course-container').addClass('hidden');
$$('.optional-course-container').addClass('hidden');
school.getCoursesManager()._courses.each(function(course, index) {
course.setElement(school.elementContainer.getElement('.item-option-course[value=' + course.getCourseDetails().id + '] .course-container'));
course.fireEvent('refresh');
}.bind(this));
school.getCoursesManager()._optionalCourses.each(function(course, index) {
course.setElement(school.elementContainer.getElement('.item-option-optional-course[value=' + course.getCourseDetails().id + '] .optional-course-container'));
course.fireEvent('refresh');
}.bind(this));
this.updateCoursesList(school);
this.updateAccommodationsList(school);
this.updateInsurancesList();
}.bind(this));
//
// when an accommodation is added, it adds some listener to
//
school.getAccommodationsManager().addEvent('accommodationAdded', function(accommodation) {
// the accommodation events
accommodation.addEvent('refresh', function(e) {
if (accommodation.getElement()) {
accommodation.getElement().getParent().getElement('input').checked = 'checked';
accommodation.getElement().removeClass('hidden');
accommodation.refreshValidStartDates();
// start date template
var startDatesTemplate = this.getSchoolAccommodationsDateValueTemplate('startDate', accommodation);
if (startDatesTemplate) {
$p(startDatesTemplate.getSelector(), accommodation.getElement()).render({'date' : accommodation.getAccommodationStartDate() ? accommodation._calculatedStartDate.format(accommodation._dateFormat, accommodation._monthsTranslations) : this.booking._translateDTO.accommodations.accommodationObject.dateSelect}, startDatesTemplate.getDirective());
var element = accommodation.getElement().getElement(startDatesTemplate.getSelector());
element.addEvent('click', function() {
this.bookingCalendar.setValidDates(accommodation.getValidStartDates(), accommodation.getAvailableDaysBefore(), 0, accommodation._accommodationOptionDetails.additionalNightsOnBounds);
if (accommodation._accommodationStartDate) {
this.bookingCalendar.setSelectedDate(accommodation._accommodationStartDate, accommodation._additionalNightsBefore, 0);
}
else {
if (accommodation.getValidStartDates().length)
this.bookingCalendar.displayMonths(accommodation.getValidStartDates()[0].getMonth(), accommodation.getValidStartDates()[0].getFullYear());
}
this.bookingCalendar.show(element, function(date) {
accommodation.setStartDate(date.relativeDate, date.additionalNightsBefore);
}, this);
}.bind(this));
}
// end date template
var endDatesTemplate = this.getSchoolAccommodationsDateValueTemplate('endDate', accommodation);
if (endDatesTemplate) {
$p(endDatesTemplate.getSelector(), accommodation.getElement()).render({'date' : accommodation._accommodationEndDate ? accommodation._calculatedEndDate.format(accommodation._dateFormat, accommodation._monthsTranslations) : 'select an end date'}, endDatesTemplate.getDirective());
var endDateElement = accommodation.getElement().getElement(endDatesTemplate.getSelector());
endDateElement.addEvent('click', function() {
this.bookingCalendar.setValidDates(accommodation.getValidEndDates(), 0, accommodation.getAvailableDaysAfter(), accommodation._accommodationOptionDetails.additionalNightsOnBounds);
if (accommodation._accommodationEndDate) {
this.bookingCalendar.setSelectedDate(accommodation._accommodationEndDate, 0, accommodation._additionalNightsAfter);
}
else {
if (accommodation.getValidEndDates().length)
this.bookingCalendar.displayMonths(accommodation.getValidStartDates()[0].getMonth(), accommodation.getValidStartDates()[0].getFullYear());
}
this.bookingCalendar.show(endDateElement, function(date) {
accommodation.setEndDate(date.relativeDate, date.additionalNightsAfter);
}, this);
}.bind(this));
}
// to ensure the value is a string
var validDurations = new Array();
accommodation.getValidDurations().each(function(item, index) {
validDurations.push(item + '');
}.bind(this));
// the accommodation duration list template and functionality
var durationsListTemplate = this.getSchoolAccommodationDurationsListTemplate(accommodation._accommodationDuration, validDurations);
if (durationsListTemplate) {
var durations = new Array();
var durations = new Array();
accommodation.getDurations().each(function(item, index) {
if (parseInt(item) == 1) {
var name = this.booking.getTranslations().accommodations.accommodationObject.week.replace("%0", item)
} else {
var name = this.booking.getTranslations().accommodations.accommodationObject.weeks.replace("%0", item)
}
durations.push({id : item, name : name});
}.bind(this));
if(validDurations.length > 1){
$p(durationsListTemplate.getSelector(), accommodation.getElement()).render(durations, durationsListTemplate.getDirective());
// the durations list
if (accommodation.durationsList) {
accommodation.durationsList.removeEvents('change');
accommodation.durationsList.updateListOptions(accommodation.getElement().getElement(durationsListTemplate.getSelector()));
} else {
accommodation.durationsList = this.createList(accommodation.getElement().getElement(durationsListTemplate.getSelector()));
}
accommodation.durationsList.setValidOptions(validDurations);
accommodation.durationsList.setValue(accommodation._accommodationDuration);
accommodation.durationsList.addEvent('change', function(e) {
accommodation.setDuration(accommodation.durationsList.getValue());
}.bind(this));
} else {
var dur = accommodation.getAccommodationDuration();
if (parseInt(dur) == 1) {
dur = this.booking.getTranslations().accommodations.accommodationObject.week.replace("%0", dur)
} else {
dur = this.booking.getTranslations().accommodations.accommodationObject.weeks.replace("%0", dur)
}
$p(durationsListTemplate.getSelector(), accommodation.getElement()).render({'duration' : dur}, durationsListTemplate.getDirective());
}
}
accommodation.getElement().getElements('select.item-selector').chosen();
}
}.bind(this));
accommodation.fireEvent('refresh');
}.bind(this));
// refresh the rendering of the accommodations
school.getAccommodationsManager().addEvent('accommodationsUpdated', function(e) {
$$('.accommodation-container').addClass('hidden');
var addedAccommodations = new Array();
school.getAccommodationsManager().getAccommodations().each(function(accommodation, index) {
addedAccommodations.push(parseInt(accommodation.getAccommodationOptionDetails().id));
accommodation.setElement(school.elementContainer.getElement('.item-option-accommodation[value=' + accommodation.getAccommodationOptionDetails().id + '] .accommodation-container'));
accommodation.fireEvent('refresh');
}.bind(this));
// check the accommodations list
$$('.item-option-accommodation input').each(function(item, index) {
if (!addedAccommodations.contains(parseInt(item.value)))
item.checked = null;
}.bind(this));
if (!addedAccommodations.length) {
$('noAccommodationOption').checked = 'checked';
}
this.updateTransfersList(school);
this.updateInsurancesList();
}.bind(this));
//
// when an transfer is added, it adds some listener to
//
school.getTransfersManager().addEvent('transferAdded', function(transfer) {
// the transfer events
transfer.addEvent('refresh', function(e) {
if (transfer.getElement()) {
transfer.getElement().getParent().getElement('input').checked = 'checked';
transfer.getElement().removeClass('hidden');
var options = transfer.getOptions();
options.each(function(item, index) {
switch (item.name) {
case 'oneWay':
item.name = this.booking.getTranslations().transfers.transferObject.oneWay;
break;
case 'wayBack':
item.name = this.booking.getTranslations().transfers.transferObject.wayBack;
break;
case 'roundTrip':
item.name = this.booking.getTranslations().transfers.transferObject.roundTrip;
break;
}
}.bind(this));
// the transfer type template
var optionsListTemplate = this.getSchoolTransferOptionsListTemplate(options, transfer);
if (optionsListTemplate) {
$p(optionsListTemplate.getSelector(), transfer.getElement()).render(options, optionsListTemplate.getDirective());
// the options list
if (transfer.optionsList) {
transfer.optionsList.removeEvents('change');
transfer.optionsList.updateListOptions(transfer.getElement().getElement(optionsListTemplate.getSelector()));
} else {
transfer.optionsList = this.createList(transfer.getElement().getElement(optionsListTemplate.getSelector()));
}
transfer.optionsList.setValue(transfer.getTransferType());
transfer.optionsList.addEvent('change', function(e) {
transfer.setTransferType(transfer.optionsList.getValue());
}.bind(this));
}
transfer.getElement().getElements('select.item-selector').chosen();
}
}.bind(this));
transfer.fireEvent('refresh');
}.bind(this));
// refresh the rendering of the transfers
school.getTransfersManager().addEvent('transfersUpdated', function(e) {
$$('.transfer-container').addClass('hidden');
var addedTransfers = new Array();
school.getTransfersManager().getTransfers().each(function(transfer, index) {
addedTransfers.push(transfer.getTransferId());
transfer.setElement(school.elementContainer.getElement('.item-option-transfer[value=' + transfer.getTransferId() + '] .transfer-container'));
transfer.fireEvent('refresh');
}.bind(this));
// check the transfers list
$$('.item-option-transfer input').each(function(item, index) {
if (!addedTransfers.contains(parseInt(item.value)))
item.checked = null;
}.bind(this));
if (!addedTransfers.length) {
$('noTransferOption').checked = 'checked';
}
}.bind(this));
school.getCoursesManager().fireEvent('coursesUpdated');
school.getAccommodationsManager().fireEvent('AccommodationsUpdated');
$$('.Component-ItemAdded').addClass('hidden');
}.bind(this));
this.fireEvent('schoolsCreated');
}.bind(this));
},
/**
* Update the optional courses list
*
* @param SchoolObject school The school
*/
updateCoursesList : function(school) {
if (!school) {
this.booking.getSchools().each(function(school, index) {
this.updateCoursesList(school);
}.bind(this));
} else {
var courses = school.getCoursesManager().getCourses();
//            school.elementContainer.getElements('.school-courses-list .item-category').addClass('invalid');
school.elementContainer.getElements('.school-courses-list .item-option-radio').each(function(item, index) {
//                item.removeAttribute('disabled');
//                item.getParent('.item-option').removeClass('invalid');
}.bind(this));
var validOptionalCourses = school.getCoursesManager().getValidOptionalCourses();
school.elementContainer.getElements('.school-optional-courses-list .item-option-radio').each(function(item, index) {
if (validOptionalCourses.contains(parseInt(item.getAttribute('value')))) {
item.removeAttribute('disabled');
item.getParent('.item-option').removeClass('invalid');
} else {
item.setAttribute('disabled', 'disabled');
item.getParent('.item-option').addClass('invalid');
}
}.bind(this));
}
},
/**
* Update the accommodations list
*
* @param SchoolObject school The school
*/
updateAccommodationsList : function(school) {
var validAccommodations = school.getAccommodationsManager().getValidAccommodationsList();
school.elementContainer.getElements('.school-accommodations-list .item-category').addClass('invalid');
school.elementContainer.getElements('.school-accommodations-list .item-option-radio').each(function(item, index) {
if ((!school.getMandatoryAccommodations().length && item.getAttribute('id') == 'noAccommodationOption') || validAccommodations.contains(parseInt(item.getAttribute('value')))) {
item.removeAttribute('disabled');
item.getParent('.item-option').removeClass('invalid');
if(item.getParent('.item-category'))item.getParent('.item-category').removeClass('invalid');
} else {
item.setAttribute('disabled', 'disabled');
item.getParent('.item-option').addClass('invalid');
}
}.bind(this));
},
/**
* Update the transfers list
*
* @param SchoolObject school The school
*/
updateTransfersList : function(school) {
var validTransfers = school.getTransfersManager().getValidTransfersList();
school.elementContainer.getElements('.school-transfers-list .item-option-radio').each(function(item, index) {
if (item.getAttribute('id') == 'noTransferOption' || validTransfers.contains(parseInt(item.getAttribute('value')))) {
item.removeAttribute('disabled');
item.getParent('.item-option').removeClass('invalid');
} else {
item.setAttribute('disabled', 'disabled');
item.getParent('.item-option').addClass('invalid');
}
}.bind(this));
},
/**
* Update the insurances list
*/
updateInsurancesList : function() {
var validInsurances = this.booking.getInsurancesManager().getValidInsurancesList();
var validInsurancesIds = new Array();
validInsurances.each(function(item, index) {
validInsurancesIds.push(item.id);
}.bind(this));
this.booking.getInsurancesManager().getCompletedInsurances().each(function(item, index) {
if (!item.getMandatory())
validInsurancesIds.push(parseInt(item.getInsuranceId()));
}.bind(this));
$$('.insurances-list')[0].getElements('.item-option-radio').each(function(item, index) {
if (validInsurancesIds.contains(parseInt(item.getAttribute('value')))) {
item.removeAttribute('disabled');
item.getParent('.item-option').removeClass('invalid');
} else {
item.setAttribute('disabled', 'disabled');
item.getParent('.item-option').addClass('invalid');
}
}.bind(this));
},
/**
* Create the directives for this template
*
*/
createDirectives : function() {
this._directives = new Hash();
// The templates directives
// More informations on : http://beebole.com/pure/
var schoolsListDirectiveProgram = {
'.item-option' : {
"program<-programs" : {
'.' : "program.name",
'.@value' : "program.id"
}
}
};
var schoolsListDirectiveObject = {
".item-option": {
'listItem <-': {
'.': "listItem.name",
'.@value' : "listItem.id"}
}};
var schoolCoursesListDirectiveObject = {
".item-category": {
'category <-': {
'.item-category-name': "category.name",
".item-option-course": {
'listItem <- category.courses': {
'.item-option-radio@id': "listItem.id",
'.item-option-radio@value': "listItem.id",
'.item-option-name@for': "listItem.id",
'.item-option-name': "listItem.name",
'.item-option-price': "listItem.price",
'.@value' : "listItem.id"}
}
}
}};
var schoolCoursesListDirectiveObject = {
".item-option-course": {
'listItem <- ': {
'.item-option-radio@id': "listItem.id",
'.item-option-radio@value': "listItem.id",
'.item-option-name@for': "listItem.id",
'.item-option-name': "listItem.name",
'.item-option-price': "listItem.displayPrice",
'.@value' : "listItem.id"
}
}
};
var schoolOptionalCoursesListDirectiveObject = {
".item-option-course": {
'listItem <- ': {
'.item-option-radio@id': "listItem.id",
'.item-option-radio@value': "listItem.id",
'.item-option-name@for': "listItem.id",
'.item-option-name': "listItem.name",
'.item-option-price': "listItem.displayPrice",
'.@value' : "listItem.id"}
}
};
var schoolAccommodationsListDirectiveObject = {
".item-category": {
'category <-': {
'.item-category-name': "category.name",
".item-option-accommodation" : {
'listItem <- category.option': {
'.item-option-radio@id': "listItem.id",
'.item-option-radio@value': "listItem.id",
'.item-option-name@for': "listItem.id",
'.item-option-name': "listItem.name",
'.item-option-price': "listItem.displayPrice",
'.@value' : "listItem.id"
}
}
}
}};
var schoolTransfersListDirectiveObject = {
".item-option-transfer": {
'listItem <-': {
'.item-option-radio@id': "listItem.id",
'.item-option-radio@value': "listItem.id",
'.item-option-name@for': "listItem.id",
'.item-option-name': "listItem.name",
'.@value' : "listItem.id"}
}
};
var insurancesListDirectiveObject = {
".item-option-insurance": {
'listItem <-': {
'.item-option-radio@id': "listItem.id",
'.item-option-radio@value': "listItem.id",
'.item-option-name@for': "listItem.id",
'.item-option-name': "listItem.name",
'.@value' : "listItem.id"}
}};
var currenciesListDirectiveObject = {
".item-option": {
'listItem <-': {
'.': "listItem.currencyName",
'.@value' : "listItem.currencyCode"
}
}
};
var currencyDirectiveObject = {
".currencyItem": {
'listItem <-': {
'.': "listItem.currencyName"
}
}
};
var schoolAccommodationDateDirectiveObject = {
".accommodation-date-value": "date"
};
var accommodationValueDirectiveObject = {
".accommodation-duration-value": "duration"
};
var schoolCourseDateDirectiveObject = {
".course-date-value": "date"
};
this._directives.set('programsListDirective', $p(".program-selector").compile(schoolsListDirectiveProgram));
this._directives.set('countriesListDirective', $p(".country-selector-template1").compile(schoolsListDirectiveObject));
this._directives.set('citiesListDirective', $p(".city-selector").compile(schoolsListDirectiveObject));
this._directives.set('schoolsListDirective', $p(".school-selector").compile(schoolsListDirectiveObject));
var schoolsDirective = {
".school-container" :{
"school <-" : {
'.school-title' :  "school.schoolName",
'.school-description-city' :  "school.schoolCityName",
'.school-description-country' :  "school.schoolCountryCode"
}
}
};
this._directives.set('schoolsDirective', $p(".schools-container").compile(schoolsDirective));
this._directives.set('schoolCoursesListDirective', $p(".school-courses-list").compile(schoolCoursesListDirectiveObject));
this._directives.set('schoolOptionalCoursesListDirective', $p(".school-optional-courses-list").compile(schoolOptionalCoursesListDirectiveObject));
this._directives.set('schoolAccommodationsListDirective', $p(".school-accommodations-list").compile(schoolAccommodationsListDirectiveObject));
this._directives.set('schoolAccommodationsStartDateDirective', $p(".accommodation-startdate").compile(schoolAccommodationDateDirectiveObject));
this._directives.set('schoolAccommodationsEndDateDirective', $p(".accommodation-enddate").compile(schoolAccommodationDateDirectiveObject));
this._directives.set('schoolAccommodationsDurationDirective', $p(".accommodation-duration-template1").compile(schoolsListDirectiveObject));
this._directives.set('schoolAccommodationsDurationDirective2', $p(".accommodation-duration-template2").compile(accommodationValueDirectiveObject));
this._directives.set('schoolTransfersListDirective', $p(".school-transfers-list").compile(schoolTransfersListDirectiveObject));
this._directives.set('insurancesListDirective', $p(".insurances-list").compile(insurancesListDirectiveObject));
this._directives.set('currenciesListDirective', $p(".currencies-list-a").compile(currenciesListDirectiveObject));
this._directives.set('currencyDirective', $p(".currencies-list-b").compile(currencyDirectiveObject));
var coursesDirective = {};
var optionalCoursesDirective = {};
var courseEndDateDirective = {
'.course-enddate-value' : "date"
};
var courseDurationDirective = {
'.course-duration-value' : "duration"
};
var accommodationsDirective = {};
var transfersDirective = {
".transfer-container" :{
"transfer <-" : {
'.transfer-title' :  "transfer._transferName"
}
}
};
transfersDirective = {};
var transferFixedOptionDirectiveObject = {
".transfer-value" :{
"listItem <-" : {
'.' :  "listItem.name"
}
}
};
this._directives.set('schoolCoursesDirective', $p(".item-option-course").compile(coursesDirective));
this._directives.set('schoolOptionalCoursesDirective', $p(".item-option-optional-course").compile(optionalCoursesDirective));
this._directives.set('fixedCourseStartDatesListDirective', $p(".course-startdates-list").compile(schoolsListDirectiveObject));
this._directives.set('courseStartDatesListDirective', $p(".course-startdate").compile(schoolCourseDateDirectiveObject));
this._directives.set('courseDurationsListDirective', $p(".course-durations-list").compile(schoolsListDirectiveObject));
this._directives.set('courseDurationValueDirective', $p(".course-duration-item").compile(courseDurationDirective));
this._directives.set('schoolCourseEndDateDirective', $p(".course-enddate").compile(courseEndDateDirective));
this._directives.set('schoolAccommodationsDirective', $p(".accommodation-container").compile(accommodationsDirective));
this._directives.set('schoolTransfersDirective', $p(".transfer-container").compile(transfersDirective));
this._directives.set('schoolTransferOptionDirective', $p(".transfer-type-list-a").compile(schoolsListDirectiveObject));
this._directives.set('schoolTransferFixedOptionDirective', $p(".transfer-type-value-b").compile(transferFixedOptionDirectiveObject));
},
getSchoolsTemplate : function  (data) {
return new BookingTemplateObject({selector : '.schools-container',
directive : this._directives.get('schoolsDirective'),
element : $p('.schools-container')});
},
getSchoolCoursesListTemplate : function  (data) {
return new BookingTemplateObject({selector : '.school-courses-list',
directive : this._directives.get('schoolCoursesListDirective'),
element : $p('.school-courses-list')});
},
getSchoolOptionalCoursesListTemplate : function  (data) {
return new BookingTemplateObject({selector : '.school-optional-courses-list',
directive : this._directives.get('schoolOptionalCoursesListDirective'),
element : $p('.school-optional-courses-list')});
},
getSchoolCoursesTemplate : function  (data) {
return new BookingTemplateObject({
selector : '.courses-container',
directive : this._directives.get('schoolCoursesDirective'),
element : $p('.courses-container')});
},
getSchoolAccommodationsTemplate : function  (data) {
return new BookingTemplateObject({
selector : '.accommodations-container',
directive : this._directives.get('schoolAccommodationsDirective'),
element : $p('.accommodations-container')});
},
getSchoolOptionalCoursesTemplate : function  (data) {
return new BookingTemplateObject({selector : '.optional-courses-container',
directive : this._directives.get('schoolOptionalCoursesDirective'),
element : $p('.optional-courses-container')});
},
getSchoolCourseStartDatesListTemplate : function  (startDate, course) {
if (course.getCourseType() == 'fixed') {
return new BookingTemplateObject({selector : '.course-startdate-selector-container',
directive : this._directives.get('fixedCourseStartDatesListDirective'),
element : $p('.course-startdate-selector-container')});
}
return new BookingTemplateObject({selector : '.course-startdate-selector-container',
directive : this._directives.get('courseStartDatesListDirective'),
element : $p('.course-startdate-selector-container')});
},
getSchoolCourseDurationsListTemplate : function  (duration, data) {
return new BookingTemplateObject({selector : '.course-duration',
directive : this._directives.get('courseDurationsListDirective'),
element : $p('.course-duration')});
},
getSchoolCourseDurationValueTemplate : function  (duration, data) {
return new BookingTemplateObject({selector : '.course-duration',
directive : this._directives.get('courseDurationValueDirective'),
element : $p('.course-duration')});
},
getSchoolCourseEndDateTemplate : function  (endDate, data) {
return new BookingTemplateObject({selector : '.course-enddate',
directive : this._directives.get('schoolCourseEndDateDirective'),
element : $p('.course-enddate')});
},
getSchoolAccommodationsListTemplate : function  (data) {
return new BookingTemplateObject({selector : '.school-accommodations-list',
directive : this._directives.get('schoolAccommodationsListDirective'),
element : $p('.school-accommodations-list')});
},
getSchoolAccommodationsDateValueTemplate : function  (type, data) {
switch (type) {
case 'startDate' :
return new BookingTemplateObject({selector : '.accommodation-startdate',
directive : this._directives.get('schoolAccommodationsStartDateDirective'),
element : $p('.accommodation-startdate')});
break;
case 'endDate' :
return new BookingTemplateObject({selector : '.accommodation-enddate',
directive : this._directives.get('schoolAccommodationsEndDateDirective'),
element : $p('.accommodation-enddate')});
break;
}
},
getSchoolAccommodationDurationsListTemplate : function  (duration, validDurations) {
return new BookingTemplateObject({selector : '.accommodation-duration',
directive :validDurations.length > 1 ? this._directives.get('schoolAccommodationsDurationDirective') : this._directives.get('schoolAccommodationsDurationDirective2'),
element : $p('.accommodation-duration')});
},
getSchoolTransfersListTemplate : function  (data) {
return new BookingTemplateObject({selector : '.school-transfers-list',
directive : this._directives.get('schoolTransfersListDirective'),
element : $p('.school-transfers-list')});
},
getSchoolTransfersTemplate : function  (data) {
return new BookingTemplateObject({
selector : '.transfers-container',
directive : this._directives.get('schoolTransfersDirective'),
element : $p('.transfers-container')});
},
getSchoolTransferOptionsListTemplate : function  (options, transfer) {
return new BookingTemplateObject({selector : '.transfer-type-list',
directive : options.length > 1 ?  this._directives.get('schoolTransferOptionDirective') : this._directives.get('schoolTransferFixedOptionDirective') ,
element : $p('.transfer-type-list')});
},
getInsurancesListTemplate : function  (data) {
return  new BookingTemplateObject({selector : '.insurances-list',
directive : this._directives.get('insurancesListDirective'),
element : $p('.insurances-list')});
},
getCurrenciesListTemplate : function  (data, currencies) {
return  new BookingTemplateObject({selector : '.currencies-list',
directive : currencies.length > 1 ? this._directives.get('currenciesListDirective') : this._directives.get('currencyDirective'),
element : $p('.currencies-list')});
},
setActive : function(element, active){
if(active){
element.removeClass('inActive');
}else {
element.addClass('inActive');
}
},
/**
* Display the waiting panel
*
* @param Element panel
*/
displayWaitingPanel : function(panel) {
if(panel)
panel.removeClass('waiting-panel-hidden');
},
/**
* Hide the waiting panel
*
* @param panel
*/
hideWaitingPanel : function(panel) {
if(panel)
panel.addClass('waiting-panel-hidden');
}
});
function printPriceEstimation() {
window.print();
/**
if ($("bookingOption") && $("bookingOption").retrieve('booking')) {
$("bookingOption").retrieve('booking').print();
}    **/
return false;
}
function addToMyQuotes() {
if ($("bookingOption") && $("bookingOption").retrieve('booking')) {
$("bookingOption").retrieve('booking').addToMyQuotes();
}
return false;
}
function addToCart() {
if ($("bookingOption") && $("bookingOption").retrieve('booking')) {
$("bookingOption").retrieve('booking').addToCart();
}
return false;
}
function mailQuote() {
if ($("bookingOption") && $("bookingOption").retrieve('booking')) {
$("bookingOption").retrieve('booking').mailQuote();
}
return false;
}
function checkout() {
if (bookingManager.booking) {
bookingManager.booking.checkout();
}
return false;
}
/**
* This class is used for the price estimation functionnalities
*
* @event Event heightModified this event is fired when the price estimation is displayed
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param Element input the input div
* @param Element output the output div
**/
//var Booking = Events.extend({
var BookingEngine = new Class({
Implements: [Events, Options],
options: {
shoppingCartPage: false, // True if it's a shopping cart page
input: $('bookingOption'), // The input block
output: $('bookingCart'), // The ouput block
schoolsSelectorContainer: $('schoolsSelector'), // The schools Selector Container
header: $('bookingLinks'), // the header block
printOutput: $("schoolPriceEstimationPrint"), // the print output div
courseTypeContainer : '',
accommodationTypeContainer : '',
insuranceContainer : '',
promotionIntroductionContainer : '',
promotionContainer : '',
onlyWebsiteSchools : false,
syncHeight : true
},
/**
* This is the initialization function
*/
initialize : function(options) {
this.setOptions(options);
this.bookingAutoInit = true;
this._customValues = new Hash();
this._selectedSchoolId = null;
this._schools = new Array();
this._courseTypesFilter = [];
this._levelFilter = 0;
if (window.location.href.indexOf('?') != -1) {
var params = window.location.href.substr(window.location.href.indexOf('?') + 1);
}
var me = this;
// translation request
this._xhrTranslateInit = new Request({
url : this.getBookingJsonUrl() + '&' + (params ? params + '&' : '') + 'step=initTranslations&onlyWebsiteSchools=' + (this.options.onlyWebsiteSchools ? 1 : 0) + '&shoppingCartPage=' + (this.options.shoppingCartPage ? 1 : 0),
onRequest: function() {
},
onSuccess: function(responseText) {
me.fireEvent('translaterequestsuccess', responseText);
this.initTranslations(responseText);
}.bind(this),
onFailure: function() {
}
}
);
// the ajax call to init the shopping cart
this._xhrInit = new Request(
{
url: this.getBookingJsonUrl(),
onRequest: function() {
},
onSuccess: function(responseText) {
this.initBooking(responseText);
}.bind(this),
onFailure: function() {
}
});
// the checkout request
this._xhrCheckout = new BookingRequest({
url : this.getBookingJsonUrl(),
onSuccess : function(responseText) {
this._displayOrderFormResult(responseText);
}.bind(this)
});
// the main request that will display the price estimation result
this._xhr = new Request({
url : this.getBookingJsonUrl(),
onRequest: function() {
},
onSuccess: function(responseText) {
this.displayPriceEstimation(responseText);
}.bind(this),
onFailure: function() {
}
});
},
/**
* Start the booking
*/
launch : function () {
// The Init call is made
this.fireEvent('onLauch');
this._xhrTranslateInit.send();
},
/**
* Return the json current url
* todo
*
* return string
*/
getBookingJsonUrl : function () {
//        return 'http://127.0.0.1/synergee/Sources/booking.html?componentName=Booking&componentId=informationText';
return 'http://192.168.1.202/synergee/Sources/booking.html?componentName=Booking&componentId=informationText';
},
/**
* called when a school is loaded
*
*
* @param e
*/
initBooking : function(e) {
eval(e);
//        this.schoolsContainer.empty();
if (typeof(getInit) == 'function') {
var initDTO = getInit();
this._schools = initDTO[0];
this._options = initDTO[2];
this._insurances = initDTO[3];
this._promotions = initDTO[4];
// The schools displayed in the shopping cart are stocked in
if(!this.schools)
this.schools = new Array();
// the menu item translations are set
if (this.options.widgetMode && this.options.shoppingCartPage) {
this.shoppingCartFirstItem.set("html", this._translateDTO.firstItem);
this.shoppingCartFirstItemName.set("html", this._translateDTO.yourShoppingCartMenuItem);
this.formLinkSecondItem.set("html", this._translateDTO.secondItem);
this.formLinkSecondItemName.set("html", this._translateDTO.formMenuItem);
this.confirmationLinkThirdItem.set("html", this._translateDTO.thirdItem);
this.confirmationLinkThirdItemName.set("html", this._translateDTO.confirmationMenuItem);
}
this.insurancesManager = new SchoolInsurances(this._insurances, this);
this.insurancesManager.addEvent('insurancesUpdated', this.refreshPrice.bind(this));
this.promotionsManager =  new SchoolPromotions(this._promotions, this);
this.promotionsManager.addEvent('promotionsUpdated', this.refreshPrice.bind(this));
var schools = [];
// if there is no school added in the shopping cart or the shopping cart is opened in the designer
if (this.options.widgetMode && (!this._schools.length || document.isInDesignMode())) {
if (this.options.shoppingCartPage) {
/* var emptyMessage = new Element('div', {'class': this._classNameBase + 'Empty'});
emptyMessage.set("html", this._translateDTO.empty);
emptyMessage.inject(this.schoolsContainer);*/
}
this.setSameColumnSize(this.options.input, this.options.output);
}
else {
for (var i = 0; i < this._schools.length; i++) {
this._schoolPriceCalculatorDTO = this._schools[i];
var school = new School(this._schools[i], this);
school.addEvent('refreshPrice', this.refreshPrice.bind(this));/*
if (this.options.widgetMode)
school.addEvent('heightModified', this.onHeightModified.bind(this));*/
schools.push(school);
this.schools.push(school);
}
this._oldDTO = JSON.encode(this.getDTO());
// The shopping cart is populated if there are some informations
/*    if (this.options.widgetMode){
this.populateFromDTO.delay(50, this, initDTO[1]);
this.retrieveSchoolItemsDescription();
}*/
}
this.fireEvent('initialized', [schools]);
}
},
getSchoolsSelector : function () {
return this.schoolSelector;
},
/**
* Return the schools
*
* @return School[] The added schools
*/
getSchools : function() {
return this.schools;
},
/**
* Return the insurances manager
*
* @return InsurancesManager The insurances manager
*/
getInsurancesManager : function() {
return this.insurancesManager;
},
/**
* Return the promotions manager
*
*
* @return PromotionsManager The promotions manager
*/
getPromotionsManager : function() {
return this.promotionsManager;
},
/**
* Return the schools DTO
*
* @return School[] The added schools
*/
getSchoolsDTO : function() {
return this._schools;
},
/**
* Refresh the price
*/
refreshPrice : function() {
if (this.options.shoppingCartPage) {
if (this.formLink.active) {
this.saveFormFields();
this.formLink.addClass(this._classNameBase + 'Links-Inactive');
this.formLink.active = false;
this.orderForm.initialized = false;
}
}
this.updateValues();
if (this._oldDTO != JSON.encode(this.getDTO())) {
this.fireEvent('onPriceRefresh');
var params = 'shoppingCartPage=' + this.options.shoppingCartPage;
if (this.options.promotionIntroductionContainer != '') params = params + '&pIntroContainer=' + this.options.promotionIntroductionContainer;
if (this.options.promotionContainer != '') params = params + '&pContainer=' + this.options.promotionContainer;
if (this._selectedSchoolId)params = params + '&schoolId=' + this._selectedSchoolId;
this._xhr.cancel();
this._xhr.send(params + '&format=json&dto=' + JSON.encode(this.getDTO()));
this._oldDTO = JSON.encode(this.getDTO());
}
},
loadSchool : function(schoolId, addSchool){
if(schoolId != this._selectedSchoolId){
if (!addSchool && this.schools) {
this.schools = new Array();
}
if (schoolId) {
this._xhr.cancel();
var xhrLoadSchool = new Request(
{
url: this.getBookingJsonUrl(),
onRequest: function() {
},
onSuccess: function(responseText) {
this.initBooking(responseText);
}.bind(this),
onFailure: function() {
}
});
var xhrParam = 'step=init';
xhrParam += '&schoolId=' + schoolId;
if(this.schools && this.schools.length && addSchool){
var loadedSchools = '';
for(var i = 0; i < this.schools.length; i++){
loadedSchools = loadedSchools + this.schools[i]._schoolDTO.schoolId + (i < this.schools.length-1 ? '|': '');
}
xhrParam += '&alreadyLoadedSchools=' + loadedSchools;
}
this._selectedSchoolId = schoolId;
this.fireEvent('beforeschoolload', schoolId);
// send an ajax call to add the school
this.bookingAutoInit = false;
xhrLoadSchool.send(xhrParam);
}
}
},
/**
* called when the height of a column is modified so the two column are the same size
*
*/
onHeightModified : function (e) {
/*      if(this.options.widgetMode){
this.fireEvent('beforeHeightUpdated');
this.setSameColumnSize(this.options.input, this.options.output);
this.fireEvent('afterHeightUpdated');
}*/
},
/**
*
* create the shopping cart top links components
*
*/
createShoppingCartLinks : function() {
this.shoppingCartLink = new Element('div', {'class':  this._classNameBase + 'Links-Active'});
this.shoppingCartLink.inject(this.header);
this.shoppingCartLink.addEvent("click", this.displayShoppingCart.bind(this));
this.shoppingCartLink.active = true;
this.shoppingCartLink.addClass('Selected');
this.shoppingCartLink.addEvent("mouseenter", function() {
if (this.shoppingCartLink.active) {
this.shoppingCartLink.addClass(this._classNameBase + 'Links-Hover');
}
}.bind(this));
this.shoppingCartLink.addEvent("mouseleave", function() {
if (this.shoppingCartLink.active) {
this.shoppingCartLink.removeClass(this._classNameBase + 'Links-Hover');
}
}.bind(this));
this.shoppingCartFirstItem = new Element('div', {'class':  this._classNameBase + 'Links-Number'});
this.shoppingCartFirstItem.inject(this.shoppingCartLink);
this.shoppingCartFirstItemName = new Element('div', {'class':  this._classNameBase + 'Links-Name'});
this.shoppingCartFirstItemName.inject(this.shoppingCartLink);
// The form link
this.formLink = new Element('div', {'class':  this._classNameBase + 'Links-Active'});
this.formLink.inject(this.header);
this.formLink.addEvent("click", this._displayOrderForm.bind(this));
this.formLink.active = false;
this.formLink.removeClass('Selected');
this.formLinkSecondItem = new Element('div', {'class':  this._classNameBase + 'Links-Number'});
this.formLinkSecondItem.inject(this.formLink);
this.formLinkSecondItemName = new Element('div', {'class':  this._classNameBase + 'Links-Name'});
this.formLinkSecondItemName.inject(this.formLink);
this.formLink.addClass(this._classNameBase + 'Links-Inactive');
this.formLink.addEvent("mouseenter", function() {
if (this.formLink.active) {
this.formLink.addClass(this._classNameBase + 'Links-Hover');
}
}.bind(this));
this.formLink.addEvent("mouseleave", function() {
if (this.formLink.active) {
this.formLink.removeClass(this._classNameBase + 'Links-Hover');
}
}.bind(this));
// The confirmation link
this.confirmationLink = new Element('div', {'class':  this._classNameBase + 'Links-Active'});
this.confirmationLink.inject(this.header);
this.confirmationLink.active = false;
this.confirmationLink.removeClass('Selected');
this.confirmationLinkThirdItem = new Element('div', {'class':  this._classNameBase + 'Links-Number'});
this.confirmationLinkThirdItem.inject(this.confirmationLink);
this.confirmationLinkThirdItemName = new Element('div', {'class':  this._classNameBase + 'Links-Name'});
this.confirmationLinkThirdItemName.inject(this.confirmationLink);
this.confirmationLink.addClass(this._classNameBase + 'Links-Inactive');
this.confirmationLink.addEvent("mouseenter", function() {
if (this.confirmationLink.active) {
this.confirmationLink.addClass(this._classNameBase + 'Links-Hover');
}
}.bind(this));
this.confirmationLink.addEvent("mouseleave", function() {
if (this.confirmationLink.active) {
this.confirmationLink.removeClass(this._classNameBase + 'Links-Hover');
}
}.bind(this));
},
initTranslations : function(e) {
eval(e);
if (typeof(getInit) == 'function') {
this._initDTO = getInit();
this._translateDTO = this._initDTO.translateDTO;
this.participants = new SchoolParticipants(this._translateDTO.participants, this._initDTO.multipleBooking, this);
this.currency = new SchoolCurrencySelector(this._translateDTO.currencies, this._initDTO.defaultCurrency, this._initDTO.websiteCurrencies, this);
this.currency.addEvent('refreshPrice', this.refreshPrice.bind(this));
this._currencyConversionEnabled = this._initDTO.currencyConversionEnabled;
if (!this.options.shoppingCartPage) {
this.schoolSelector = new SchoolSelector(this._translateDTO.schoolSelector, this._initDTO.schoolsList, this, this._initDTO.advancedSchoolList);
// when a school is selected
this.schoolSelector.addEvent('changeSchool', function() {
if (this.schoolSelector.getSelectedSchoolId()) {
this.loadSchool(this.schoolSelector.getSelectedSchoolId());
}
}.bind(this));
this.fireEvent('schoolsSelectorLoaded');
}
this.fireEvent('onTranslationsLoaded');
if (this.schoolSelector) {
if (this._initDTO.schoolsList.length == 1) {
this.schoolSelector.fireEvent('changeSchool');
} else {
this.schoolSelector.updateSelector(this._initDTO.selectedSchool, this._initDTO.selectedProgram, this._initDTO.selectedCountry, this._initDTO.selectedCity);
}
}
}
},
/**
* return the translations
*/
getTranslations : function() {
if(this._translateDTO)
return this._translateDTO;
return null;
},
/**
* Called before the price is updated
*/
updateValues : function() {
if (this.insurancesManager) {
this.insurancesManager.refreshInsurances();
}
},
/**
* Send the request to refresh the price
*/
retrieveSchoolItemsDescription : function() {
this._schoolDescriptionsXHR = new Request({
url : window.getCurrentJsonUrl(this.options.input),
onRequest: function() {
},
onSuccess: function(e) {
this.schoolDescriptions = JSON.decode(e);
this.fireEvent('descriptionsUpdated');
}.bind(this),
onFailure: function() {
}
});
if (this.schools) {
var school = new Object();
school.courseTypeContainer = this.options.courseTypeContainer ? this.options.courseTypeContainer : null;
school.insuranceContainer = this.options.insuranceContainer ? this.options.insuranceContainer : null;
school.accommodationTypeContainer = this.options.accommodationTypeContainer ? this.options.accommodationTypeContainer : null;
school.courseTypes = new Array();
school.accommodationTypes = new Array();
school.insurances = new Array();
for (var j = 0; j < this.schools.length; j ++) {
// variable date course
for (var i = 0; i < this.schools[j]._schoolDTO.schoolVariableDatesCourses.length; i++) {
if (!school.courseTypes.contains(this.schools[j]._schoolDTO.schoolVariableDatesCourses[i]).courseTypeId) {
school.courseTypes.push(this.schools[j]._schoolDTO.schoolVariableDatesCourses[i].courseTypeId);
}
}
// fixed date course
for (var i = 0; i < this.schools[j]._schoolDTO.schoolFixedDatesCourses.length; i++) {
if (!school.courseTypes.contains(this.schools[j]._schoolDTO.schoolFixedDatesCourses[i]).courseTypeId) {
school.courseTypes.push(this.schools[j]._schoolDTO.schoolFixedDatesCourses[i].courseTypeId);
}
}
// optional course
for (var i = 0; i < this.schools[j]._schoolDTO.schoolOptionalCourses.length; i++) {
if (!school.courseTypes.contains(this.schools[j]._schoolDTO.schoolOptionalCourses[i]).courseTypeId) {
school.courseTypes.push(this.schools[j]._schoolDTO.schoolOptionalCourses[i].courseTypeId);
}
}
for (var i = 0; i < this.schools[j]._schoolDTO.schoolAccommodations.length; i++) {
if (!school.accommodationTypes.contains(this.schools[j]._schoolDTO.schoolAccommodations[i]).accommodationType) {
school.accommodationTypes.push(this.schools[j]._schoolDTO.schoolAccommodations[i].accommodationType);
}
}
if (this._insurances) {
for (var i = 0; i < this._insurances.length; i++) {
school.insurances.push(this._insurances[i].id);
}
}
}
this._schoolDescriptionsXHR.cancel();
this._schoolDescriptionsXHR.send('step=initDescriptions&schoolInfos=' + JSON.encode(school));
}
},
getSchoolDescriptions : function() {
if (this.schoolDescriptions)return this.schoolDescriptions;
return null;
},
/**
* Return the description for an item
*
* @param string descriptionType The type  course, accommodation, insurance
* @param integer id The item id
*/
getDescription : function(descriptionType, id) {
var descriptions = this.getSchoolDescriptions();
var description = null;
if (descriptions) {
switch (descriptionType) {
case 'course':
if (descriptions.courses[id]) {
description = descriptions.courses[id].description;
}
break;
case 'accommodation':
if (descriptions.accommodations[id]) {
description = descriptions.accommodations[id].description;
}
break;
case 'insurance':
if (descriptions.insurances[id]) {
description = descriptions.insurances[id].description;
}
break;
}
}
return description;
},
/**
* Return the description for an item
*
* @param string descriptionType The type  course, accommodation, insurance
* @param integer id The item id
*/
getDescriptionDetails : function(descriptionType, id) {
var descriptions = this.getSchoolDescriptions();
var description = null;
if (descriptions) {
switch (descriptionType) {
case 'course':
if (descriptions.courses[id]) {
description = descriptions.courses[id];
}
break;
case 'accommodation':
if (descriptions.accommodations[id]) {
description = descriptions.accommodations[id];
}
break;
case 'insurance':
if (descriptions.insurances[id]) {
description = descriptions.insurances[id];
}
break;
}
}
return description;
},
/**
* Set the level filter
* @param level
*/
setLevelFilter : function(level){
if(this._levelFilter != level){
this._levelFilter = level;
this.schools.each(function(item, index) {
item.courses.onCourseUpdated();
}.bind(this));
}
},
getLevelFilter : function(){
return this._levelFilter;
},
/**
* Set the course type filter
* @param courseTypes
*/
setCourseTypesFilter : function(courseTypes){
if(this._courseTypesFilter != courseTypes){
this._courseTypesFilter = courseTypes;
this.schools.each(function(item, index) {
item.courses.onCourseUpdated();
}.bind(this));
}
},
getCourseTypesFilter : function(){
return this._courseTypesFilter;
},
/**
* Return true if we are on a shopping cart page
*/
getIsShoppingCartPage : function () {
return this.options.shoppingCartPage;
},
/**
* Return the completed courses
*/
getCompletedCourses : function() {
var completedCourses = new Array();
this.schools.each(function(item, index) {
completedCourses = completedCourses.concat(item.getCompletedCourses());
}.bind(this));
return completedCourses;
},
/**
* return the completed accommodations
*/
getCompletedAccommodations : function() {
var completedAccommodations = new Array();
this.schools.each(function(item, index) {
completedAccommodations = completedAccommodations.concat(item.getCompletedAccommodations());
}.bind(this));
return completedAccommodations;
},
/**
* Return the quote start date
*
* @return Date the quote start date
*/
getStartDate : function() {
var startDate = null;
var courses = this.getCompletedCourses();
var accommodations = this.getCompletedAccommodations();
if (courses.length) {
startDate = courses[0].getCourseStartDate();
if (accommodations.length && accommodations[0].getAccommodationStartDate() < startDate) {
startDate = accommodations[0].getAccommodationStartDate();
}
}
return startDate;
},
/**
* Return the quote end date
*
* @return Date the quote end date
*/
getEndDate : function() {
var endDate = null;
var courses = this.getCompletedCourses();
var accommodations = this.getCompletedAccommodations();
if (courses.length) {
for (var i = 0; i < courses.length; i++) {
if (courses[i].getCourseDetails().type != 'optional' && courses[i].getCourseEndDate() > endDate) {
endDate = courses[i].getCourseEndDate();
}
}
if (accommodations.length) {
for (i = 0; i < accommodations.length; i++) {
if (accommodations[i].getAccommodationEndDate() > endDate) {
endDate = accommodations[i].getAccommodationEndDate();
}
}
}
}
return endDate;
},
/**
*  This method return the localized month translations
*
*  @return String[] The localized month translations
*/
getMonthTranslations : function() {
return this._translateDTO.months;
},
/**
* This method send an ajax call to get the generated form
*/
checkout : function() {
if (this.options.shoppingCartPage) {
var completedCourses = 0;
for (var i = 0; i < this.schools.length; i++) {
completedCourses += this.schools[i].getCompletedCourses().length;
}
if (completedCourses > 0) {
if (!this.orderForm.initialized) {
this.fireEvent('onCheckout');
this._xhrCheckout.send('step=checkout&dto=' + JSON.encode(this.getDTO()) + '&shoppingCartPage=' + this.options.shoppingCartPage);
}
else {
this._displayOrderForm();
}
}
}
},
/**
* Display the shopping cart
*
*/
displayShoppingCart : function() {
if (this.options.input.getStyle('display') == 'none') {
this.orderForm.setStyle('display', 'none');
this.orderConfirmation.setStyle('display', 'none');
this.confirmationLink.addClass(this._classNameBase + 'Links-Inactive');
this.confirmationLink.active = false;
this.shoppingCartLink.addClass('Selected');
this.formLink.removeClass('Selected');
this.confirmationLink.removeClass('Selected');
this.options.input.setStyle('display', 'block');
//            this.options.schoolsSelectorContainer.setStyle('display', 'block');
this._outputContainer.setStyle('display', 'block');
this.fireEvent('viewChanged', 'cart');
}
},
/**
* Display the result of the price estimation
*
*/
_displayOrderForm : function() {
if (this.formLink.active) {
if (!this._options.contactForm) $('formSameParticipantContainer').setStyle('display', 'none');
this.options.input.setStyle('display', 'none');
if (!this._displayOutput)this._outputContainer.setStyle('display', 'none');
this.confirmationLink.addClass(this._classNameBase + 'Links-Inactive');
this.confirmationLink.active = false;
this.formLink.addClass('Selected');
this.formLink.addClass('Selected');
this.shoppingCartLink.removeClass('Selected');
this.confirmationLink.removeClass('Selected');
this.orderConfirmation.setStyle('display', 'none');
//            this.options.schoolsSelectorContainer.setStyle('display', 'none');
this.orderForm.setStyle('display', 'block');
this.fireEvent('viewChanged', 'form');
}
else {
return false;
}
},
/**
* Display the result of the price estimation
*
* @param Event e The result of the price estimation
*/
displayPriceEstimation : function(e) {
if(this.options.widgetMode){
this.options.output.set("html", e);
this.onHeightModified();
var infos = this.options.output.getElements('.PromotionMoreInfos');
for (i = 0; i < infos.length; i++) {
new SynergeeTips(infos[i], {'className':'Booking-Tool-Tip Tool-tip', 'hideDelay':1000, 'fixed':true, 'closeButton':true, 'maxHeight':280});
}
}
// check if there are complete courses
var completedCourses = 0;
if(this.schools){
for (var i = 0; i < this.schools.length; i++) {
completedCourses += this.schools[i].getCompletedCourses().length;
}
}
if (completedCourses) {
if (e.replace(/^\s+|\s+$/g, '') == "") {
this.fireEvent.delay(100, this, 'refreshPrice');
}
else {
if (this.options.shoppingCartPage) {
$('bookingCheckoutButton').setStyle('visibility', '');
}
}
}
this.fireEvent('onPriceRefreshComplete', e);
},
/**
* Display the order form with the ajax call
*
* @param Event e The result of the price estimation
*/
_displayOrderFormResult : function(e) {
this.formLink.removeClass(this._classNameBase + 'Links-Inactive');
this.formLink.active = true;
this.formLink.addClass('Selected');
this.shoppingCartLink.removeClass('Selected');
this.confirmationLink.removeClass('Selected');
this.orderForm.initialized = true;
this.orderForm.set("html", e);
if (!this._options.contactForm) {
$('formSameParticipantContainer').setStyle('display', 'none');
}
var scripts = this.orderForm.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
eval(scripts[i].innerHTML);
}
this._displayOrderForm();
},
/**
* Display the result of the price estimation
*
* @param Event e The result of the price estimation
*/
displayOrderConfirmationResult : function(data) {
this.options.input.setStyle('display', 'none');
if (!this._displayOutput)this._outputContainer.setStyle('display', 'none');
this.orderForm.setStyle('display', 'none');
this.orderConfirmation.setStyle('display', 'block');
this.confirmationLink.removeClass(this._classNameBase + 'Links-Inactive');
this.confirmationLink.active = true;
this.formLink.removeClass('Selected');
this.shoppingCartLink.removeClass('Selected');
this.confirmationLink.addClass('Selected');
this.orderConfirmation.set("html", data);
var scripts = this.orderConfirmation.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
eval(scripts[i].innerHTML);
}
this.fireEvent('viewChanged', 'confirmation');
},
/**
*  Send the order form to save the user values in the session
*
**/
saveFormFields : function() {
if (!$('sameParticipant').get('value')) {
$('orderFormRequest').setProperty('action', window.getCurrentPageUrl());
if (!$('orderFormRequest').getChildren().contains($('id_componentName'))) {
if ($('id_componentName')) {
$('id_componentName').destroy();
}
(new Element('input').setProperties({name:'componentName',id:'id_componentName',type:'hidden'})).injectBefore($('orderFormRequest').getChildren()[0]);
}
$('id_componentName').setProperty('value', $('orderFormRequest').getComponentName());
if (!$('orderFormRequest').getChildren().contains($('id_componentId'))) {
if ($('id_componentId')) {
$('id_componentId').destroy();
}
(new Element('input').setProperties({name:'componentId',id:'id_componentId',type:'hidden'})).inject($('orderFormRequest').getChildren()[0], 'before');
}
$('id_componentId').setProperty('value', $('orderFormRequest').getComponentId());
$('id_step').setProperty('value', 'saveFields');
$('orderFormRequest').send();
}
else {
$('orderFormRequestNoContactParticipant').setProperty('action', window.getCurrentPageUrl());
if (!$('orderFormRequestNoContactParticipant').getChildren().contains($('id_componentName'))) {
if ($('id_componentName')) {
$('id_componentName').destroy();
}
(new Element('input').setProperties({name:'componentName',id:'id_componentName',type:'hidden'})).inject($('orderFormRequestNoContactParticipant').getChildren()[0], 'before');
}
$('id_componentName').setProperty('value', $('orderFormRequestNoContactParticipant').getComponentName());
if (!$('orderFormRequestNoContactParticipant').getChildren().contains($('id_componentId'))) {
if ($('id_componentId')) {
$('id_componentId').destroy();
}
(new Element('input').setProperties({name:'componentId',id:'id_componentId',type:'hidden'})).inject($('orderFormRequestNoContactParticipant').getChildren()[0], 'before');
}
$('id_componentId').setProperty('value', $('orderFormRequestNoContactParticipant').getComponentId());
$('orderFormRequestNoContactParticipant').getElement('input[id=id_step]').setProperty('value', 'saveFields');
$('orderFormRequestNoContactParticipant').send();
}
},
/**
*  Display a waiting panel layer on an element given in parameter
* todo remove
*
* @param Element element The element to put the layer on
*/
displayWaitingPanel : function(element) {
},
/**
* Hide the waiting panel
* todo remove
*/
hideWaitingPanel : function() {
},
/**
*  Display a waiting panel layer on an element given in parameter
*
* @param Element element The element to put the layer on
*/
displayMessage : function(sMessage) {
// refresh the height of the waiting panel layer
var coordinates = this.options.input.getParent().getCoordinates();
this._bookingMessagePanel.set("html", sMessage);
var messagePanelCoordinates = this._bookingMessagePanel.getCoordinates();
this._bookingMessagePanel.setStyles({
width : (200),
left : (coordinates.left + (coordinates.width / 2 - 100)),
top : (coordinates.top + (coordinates.height / 2 - messagePanelCoordinates.height / 2))
});
this._bookingMessageFxStyle.start(0, 1);
this._bookingMessageFxStyle.delayedFunction = this._bookingMessageFxStyle.start.delay(3000, this._bookingMessageFxStyle, [1,0]);
},
/**
* Returns an object containing the informations for the calculation price
*
* @return Object The informations needed to calculate the price
*/
getDTO : function() {
var priceEstimationDTO = new Object();
priceEstimationDTO.variableDatesCourses = new Array();
priceEstimationDTO.fixedDatesCourses = new Array();
priceEstimationDTO.optionalCourses = new Array();
priceEstimationDTO.accommodations = new Array();
priceEstimationDTO.transfers = new Array();
// For each school, we retrieve all the informations
if (this.schools) {
for (var i = 0; i < this.schools.length; i++) {
priceEstimationDTO.variableDatesCourses = priceEstimationDTO.variableDatesCourses.concat(this.schools[i].courses.getVariableCoursesDTO());
priceEstimationDTO.fixedDatesCourses = priceEstimationDTO.fixedDatesCourses.concat(this.schools[i].courses.getFixedCoursesDTO());
priceEstimationDTO.optionalCourses = priceEstimationDTO.optionalCourses.concat(this.schools[i].courses.getOptionalCoursesDTO());
priceEstimationDTO.accommodations = priceEstimationDTO.accommodations.concat(this.schools[i].accommodations.getDTO());
priceEstimationDTO.transfers = priceEstimationDTO.transfers.concat(this.schools[i].transfers.getDTO());
}
}
priceEstimationDTO.numberOfPeople = this.participants.getParticipant();
if (this._currencyConversionEnabled) {
priceEstimationDTO.currency = this.currency.getCurrency();
}
else {
priceEstimationDTO.currency = null;
}
priceEstimationDTO.insurances = new Object();
if (this.insurancesManager) {
priceEstimationDTO.insurances = this.insurancesManager.getDTO();
}
priceEstimationDTO.promotions = new Array();
if (this.promotionsManager) {
priceEstimationDTO.promotions = this.promotionsManager.getDTO();
}
priceEstimationDTO.customValues = new Array();
this._customValues.each(function(value, key, hash) {
var valueDTO = new Object();
valueDTO.key = key;
valueDTO.value = html_entity_decode(value);
priceEstimationDTO.customValues.push(valueDTO);
}.bind(this));
return priceEstimationDTO;
},
/**
* populate the price calculator with the DTO given in parameter
*
* @param ShoppingCartDTO The DTO To populate the shopping cart
*/
populateFromDTO : function(toPopulateDTO) {
this.fireEvent('beforePopulate', [this, toPopulateDTO]);
this.participants.setParticipant(toPopulateDTO.numberOfPeople);
for (var i = 0; i < this.schools.length; i++) {
this.schools[i].populateFromDTO(toPopulateDTO);
}
this.insurancesManager.populateFromDTO(toPopulateDTO.insurances);
this.promotionsManager.populateFromDTO(toPopulateDTO.promotions);
this._customValues = new Hash();
if(toPopulateDTO.customValues){
toPopulateDTO.customValues.each(function(item, index){
this.setCustomValue(item.key, html_entity_decode(item.value));
}.bind(this));
}
this.fireEvent('populated');
this.fireEvent('refreshPrice');
},
/**
* Set a custom value that will be saved in the form
*
* @param string key the key
* @param mixed value The value
*/
setCustomValue : function(key, value) {
if(key != 'undefined'){
if (this._customValues.has(key))
this._customValues.erase(key);
this._customValues.set(key, value);
}
},
getCustomValue : function(key, value){
if(this._customValues.has(key))
return this._customValues.get(key);
return null;
},
/**
* Remove a school from the shopping cart
*
* @param Element school Remove the school given in param
*/
removeSchool : function(school) {
this.schools.erase(school);
//        this.displayWaitingPanel(this.options.output);
// The price must be refreshed to update the session
if(this.options.widgetMode){
this._xhr.cancel();
this._xhr.send(window.getCurrentJsonUrl(this.options.input), 'dto=' + JSON.encode(this.getDTO()) + '&shoppingCartPage=' + this.options.shoppingCartPage + '&sId=' + school._schoolPriceCalculatorDTO.schoolId + '&sPrg=' + school._schoolPriceCalculatorDTO.schoolProgram);
// if the shopping cart is empty
if (!this.schools.length) {
var emptyMessage = new Element('div', {'class': this._classNameBase + 'Empty'});
emptyMessage.set("html", this._translateDTO.empty);
// clean the input
this.options.input.set("html", '');
// set the initialization to false
this.formLink.addClass(this._classNameBase + 'Links-Inactive');
this.formLink.active = false;
this.orderForm.initialized = false;
// inject the empty shopping cart message
emptyMessage.inject(this.options.input);
this.setSameColumnSize(this.options.input, this.options.output);
}
}
},
/**
* function called to sort an array of courses
*
* @param courseDTO a The first course to compare
* @param courseDTO b The second course to compare
*/
sortByPositionFunction : function(a, b) {
return a.position - b.position;
},
/**
*  This method return the localized dateFormat
*
*  @return String The localized date format
*/
getDateFormat : function() {
return this._schoolPriceCalculatorDTO.dateFormat;
},
/**
*  Return the classNameBase
*/
getClassNameBase : function() {
return this._classNameBase;
},
/**
*  Return the type
*/
getType : function() {
return 'ShoppingCart';
},
/**
* refresh accommodations object
*
*/
refreshAccommodations : function() {
for (var i = 0; i < this.schools.length; i++) {
this.schools[i].accommodations.refreshAccommodations();
}
},
/**
* Returns the number of participant
*
* @return number The number of participants
*
*/
getParticipants : function() {
return this.participants.getParticipant() ? this.participants.getParticipant() : 1;
},
/**
* This method return the list container
*
* @return Element The element containing the lists
*/
getListContainer : function() {
return this._listContainer;
},
/**
* This function set the same height for the column
*
* @param Element column1 The first column
* @param Element column2 The second column
*/
setSameColumnSize : function(column1, column2) {
if(this.options.syncHeight){
column1.setStyle('height', 'auto');
column2.setStyle('height', 'auto');
var firstColumnHeight = parseInt(column1.getCoordinates().height, 10);
var secondColumnHeight = parseInt(column2.getCoordinates().height, 10);
if (firstColumnHeight > secondColumnHeight) {
column2.setStyle('height', firstColumnHeight - parseInt(column2.getStyle('margin-top')) - column2.getStyle('padding-top').toInt() - column2.getStyle('padding-bottom').toInt() - column2.getStyle('border-top-width').toInt() - column2.getStyle('border-bottom-width').toInt());
}
else if (firstColumnHeight < secondColumnHeight) {
column1.setStyle('height', secondColumnHeight - parseInt(column1.getStyle('margin-top')) - column1.getStyle('padding-top').toInt() - column1.getStyle('padding-bottom').toInt() - column1.getStyle('border-top-width').toInt() - column1.getStyle('border-bottom-width').toInt());
}
}
// refresh the size of the waiting panel
/*    if (this._waitingPanelLayer.elementTo) {
var coordinates = this._waitingPanelLayer.elementTo.getCoordinates();
this._waitingPanelLayer.setStyles({
height : (coordinates.height + 2),
width : (coordinates.width + 2),
left : (coordinates.left - 2),
top : (coordinates.top - 2)
});
this._waitingImageLayer.setStyles({
height : (coordinates.height + 2),
width : (coordinates.width + 2),
left : (coordinates.left - 2),
top : (coordinates.top - 2)
});
}*/
},
/**
* Return the contact information for a given form id
* @param integer contactId The contact form id
*
* @return contactId The contact informations
*/
getContactInformation : function(contactId) {
var contactInformations = new Array();
// retrieve the input type one
var inputs = $('participant' + contactId + 'Form').getElements('input');
for (var i = 0; i < inputs.length; i++) {
var information = new Object();
if(inputs[i].get('type') == 'checkbox' || inputs[i].get('type') == 'radio'){
if(inputs[i].checked) {
information.id = inputs[i].getProperty('id');
information.value = inputs[i].get('value');
contactInformations.push(information);
}
}else {
information.id = inputs[i].getProperty('id');
information.value = inputs[i].get('value');
contactInformations.push(information);
}
}
// retrieve the select type one
var inputs = $('participant' + contactId + 'Form').getElements('select');
for (var i = 0; i < inputs.length; i++) {
var information = new Object();
information.id = inputs[i].getProperty('id');
information.value = inputs[i].get('value');
contactInformations.push(information);
}
// retrieve the textarea type one
var inputs = $('participant' + contactId + 'Form').getElements('textarea');
for (var i = 0; i < inputs.length; i++) {
var information = new Object();
information.id = inputs[i].getProperty('id');
information.value = inputs[i].get('value');
contactInformations.push(information);
}
return contactInformations;
},
/**
* Populate a contact form from another one
*
* @param string sourceId The source contact form id
* @param string targetId The target contact form id
*/
populateContactForm : function(sourceId, targetId) {
if (sourceId != '00') {
var contactInformation = this.getContactInformation(sourceId);
for (var i = 0; i < contactInformation.length; i++) {
var fieldId = contactInformation[i].id.slice(contactInformation[i].id.indexOf("."));
var field = $("id_" + targetId + fieldId);
if (field) {
if (field.getAttribute('type') == 'radio' || field.getAttribute('type') == 'checkbox') {
if (contactInformation[i].value) {
field.setAttribute("checked", "checked");
}
}
else {
field.value = contactInformation[i].value;
}
}
}
$('id_' + targetId + '.contact.firstName').fireEvent('change');
}
},
/**
* Display the right participant form
*/
displayParticipantsForm :  function() {
this._currentForm = ($('sameParticipant').checked ? $('orderFormRequestNoContactParticipant') : $('orderFormRequest'));
if ($('sameParticipant').checked) {
if (this._selectedParticipantForm.participantId)this.populateContactForm(this._selectedParticipantForm.participantId, '0' + this._selectedParticipantForm.participantId);
$('participant0').setStyle('display', 'none');
$('multiProfileFormBottom').setStyle('display', 'none');
$('standardForms').setStyle('display', 'none');
if ($('orderFormRequestNoContactParticipant'))$('orderFormRequestNoContactParticipant').setStyle('display', 'block');
}
else {
if (this._selectedParticipantForm.participantId)this.populateContactForm('0' + this._selectedParticipantForm.participantId, this._selectedParticipantForm.participantId);
$('participant0').setStyle('display', 'block');
$('multiProfileFormBottom').setStyle('display', 'block');
$('standardForms').setStyle('display', 'block');
if ($('orderFormRequestNoContactParticipant'))$('orderFormRequestNoContactParticipant').setStyle('display', 'none');
}
if (!$('participant2Form') && $('sameParticipant').checked) {
$('formParticipantsContainer').setStyle('display', 'none');
$('multiProfileForm').setStyle('display', 'none');
}
else {
$('formParticipantsContainer').setStyle('display', 'block');
$('multiProfileForm').setStyle('display', 'block');
}
if ($('sameParticipant').checked) {
$('participant1').fireEvent('click');
$('participant0').optionElement.setStyle('display', 'none');
}
else {
$('participant0').fireEvent('click');
}
this.updateButtonText($('participant0'), this.emptyParticipantName);
$('orderFormRequest').getValidator().validate(false, true, true);
},
/**
* Update the text button
* @param button button The item to update
* @param string emptyParticipantName The empty participant name translation
*/
updateButtonText : function(button, emptyParticipantName) {
if ($('sameParticipant').checked) {
var participantId = '0' + button.participantId;
}
else {
var participantId = button.participantId;
}
if ($('id_' + participantId + '.contact.firstName')) {
var text = button.textValue;
if (!text) text = '';
if (!$('id_' + participantId + '.contact.firstName').get('value') && !$('id_' + participantId + '.contact.lastName').get('value')) {
text = text.replace("%0", emptyParticipantName);
text = text.replace("%1", '');
}
else {
text = text.replace("%0", $('id_' + participantId + '.contact.firstName').get('value'));
text = text.replace("%1", ' ' + $('id_' + participantId + '.contact.lastName').get('value'));
}
$('participant' + (button.participantId ? button.participantId : '0')).getElements('span')[1].set("html", text);
button.optionElement.set("html", text);
}
this.updateCopyFromButton();
},
/**
*  Update the copy from button
*/
updateCopyFromButton : function() {
for (var i = 0; i < $('copyFrom').getChildren().length; i++) {
if (this._selectedParticipantForm.participantId == $('copyFrom').getChildren()[i].getAttribute('value')) {
$('copyFrom').getChildren()[i].setStyle('display', 'none');
}
else {
if ($('id_' + ($('sameParticipant').checked ? '0' : '') + i + '.contact.firstName') && $('id_' + ($('sameParticipant').checked ? '0' : '') + i + '.contact.firstName').get('value') && $('id_' + ($('sameParticipant').checked ? '0' : '') + i + '.contact.lastName').get('value')) {
$('copyFrom').getChildren()[i].setStyle('display', 'block');
}
else {
$('copyFrom').getChildren()[i].setStyle('display', 'none');
}
}
}
for (var i = 0; i < $('copyFrom').getChildren().length; i++) {
if ($('copyFrom').getChildren()[i].getStyle('display') != 'none') {
$('copyFromContainer').setStyle('display', 'inline');
break;
}
if (i == $('copyFrom').getChildren().length - 1) {
if ($('copyFrom').getChildren()[i].getStyle('display') == 'none') {
$('copyFromContainer').setStyle('display', 'none');
}
}
}
},
/**
* initialize the form button event
*
* @param string nextParticipantButtonName  The translation
* @param string previousParticipantButtonName The translation
* @param string firstParticipantButtonName The translation
* @param string toContactButtonName The translation
* @param string emptyParticipantName The translation
*/
addFormButtonEvents : function (nextParticipantButtonName, previousParticipantButtonName, firstParticipantButtonName, toContactButtonName, emptyParticipantName, participantNr) {
this._currentForm = ($('sameParticipant').checked ? $('orderFormRequestNoContactParticipant') : $('orderFormRequest'));
this.nextParticipantButtonName = nextParticipantButtonName;
this.previousParticipantButtonName = previousParticipantButtonName;
this.firstParticipantButtonName = firstParticipantButtonName;
this.toContactButtonName = toContactButtonName;
this.emptyParticipantName = emptyParticipantName;
this.participantNr = participantNr;
this.overrideFormMethod();
// Add the event on the selector
$('copyFromButton').addEvent('click', function(e) {
var from = ($('sameParticipant').checked ? '0' : '') + $('copyFrom').value;
var to = ($('sameParticipant').checked ? '0' : '') + this._selectedParticipantForm.participantId;
this.populateContactForm(from, to);
}.bind(this));
this._selectedParticipantForm = $('participant01Form');
// Add the events on the buttons
$$('.Component-Form-Contact-Button').each(function(item, index) {
item.setIsValid = function(isValid, displayErrorMessage) {
if (!isValid) {
item.addClass('Component-Form-Contact-Button-Invalid');
if (displayErrorMessage) {
item.displayErrorMessage = true;
}
}
else {
item.removeClass('Component-Form-Contact-Button-Invalid');
item.displayErrorMessage = false;
}
};
item.textValue = item.getElements('span')[1].innerHTML;
item.participantId = parseInt(item.getProperty('id').replace('participant', ''), 10);
// Add the style event on the buttons
item.addEvent("mouseenter", function() {
item.getElements('span')[0].addClass('Component-Links-Number-Hover');
item.getElements('span')[1].addClass('Component-Links-Name-Hover');
}.bind(this));
item.addEvent("mouseleave", function() {
if (item.participantId != this._selectedParticipantForm.participantId) {
item.getElements('span')[0].removeClass('Component-Links-Number-Hover');
item.getElements('span')[1].removeClass('Component-Links-Name-Hover');
}
}.bind(this));
// Add the click event on the button
item.addEvent('click', function() {
this.participantButtonEvent(item);
}.bind(this));
var optionElement = new Element('option');
optionElement.setProperty('value', item.participantId);
item.optionElement = optionElement;
optionElement.setStyle('display', 'none');
item.optionElement.inject($('copyFrom'));
this.updateButtonText(item, this.emptyParticipantName);
$('id_' + item.participantId + '.contact.firstName').addEvent('change', function() {
this.updateButtonText(item, this.emptyParticipantName);
}.bind(this));
$('id_' + item.participantId + '.contact.lastName').addEvent('change', function() {
this.updateButtonText(item, this.emptyParticipantName);
}.bind(this));
if (item.participantId) {
$('id_0' + item.participantId + '.contact.firstName').addEvent('change', function() {
this.updateButtonText(item, this.emptyParticipantName);
}.bind(this));
$('id_0' + item.participantId + '.contact.lastName').addEvent('change', function() {
this.updateButtonText(item, this.emptyParticipantName);
}.bind(this));
}
}.bind(this));
// The navigations shortcuts events
$('nextParticipantTop').addEvent('click', function(item) {
$('nextParticipant').fireEvent('click');
}.bind(this));
$('previousParticipantTop').addEvent('click', function(item) {
$('previousParticipant').fireEvent('click');
}.bind(this));
$('nextParticipant').addEvent('click', this.goToNextParticipant.bind(this));
$('previousParticipant').addEvent('click', this.goToPreviousParticipant.bind(this));
$('nextParticipantNoContact').addEvent('click', this.goToNextParticipant.bind(this));
$('previousParticipantNoContact').addEvent('click', this.goToPreviousParticipant.bind(this));
// The submit shortcuts event
$('submitFormTop').addEvent('click', function() {
if ($('sameParticipant').checked) {
$('id_submitNoContact').click();
}
else {
$('id_submit').click();
}
}.bind(this));
$('orderFormRequest').addEvent('ajaxcall', function() {
this.fireEvent('onAjaxCall');
}.bind(this));
$('orderFormRequest').addEvent('invalidfield', function() {
this.fireEvent('onAjaxResponse')
//            this.hideWaitingPanel();
}.bind(this));
$('orderFormRequestNoContactParticipant').addEvent('ajaxcall', function() {
this.fireEvent('onAjaxCall');
//            this.displayWaitingPanel($$(".Synergee-Web-Page-Component-Booking-Container")[0]);
}.bind(this));
$('orderFormRequestNoContactParticipant').addEvent('invalidfield', function() {
this.fireEvent('onAjaxResponse');
//            this.hideWaitingPanel();
}.bind(this));
$('sameParticipant').addEvent('click', function() {
this.displayParticipantsForm();
}.bind(this));
$('notSameParticipant').addEvent('click', function() {
this.displayParticipantsForm();
}.bind(this));
//        this.hideWaitingPanel();
this.displayParticipantsForm();
},
/**
* Go to the next participant form
*/
goToNextParticipant : function() {
if ($('participant' + (this._selectedParticipantForm.participantId + 1))) {
$('participant' + (this._selectedParticipantForm.participantId + 1)).fireEvent('click');
}
},
/**
* Go to the previous participant form
*/
goToPreviousParticipant : function() {
var participantId = (this._selectedParticipantForm.participantId - 1);
if (!participantId) participantId = '0';
if ($('participant' + participantId)) {
$('participant' + participantId).fireEvent('click');
}
},
/**
* Override some of the form methods
*/
overrideFormMethod : function() {
// override the validate method
$('orderFormRequest').getValidator()._validate = $('orderFormRequest').getValidator().validate;
$('orderFormRequest').getValidator().validate = this.formValidate;
// The validate Form method
$('orderFormRequest').getValidator().validateForm = this.validateForm;
// override the focus method and displayErrorMessage on form items
$('orderFormRequest').getValidator()._fields.each(this.overrideFieldMethod.bind(this));
// override the validate method
$('orderFormRequestNoContactParticipant').getValidator()._validate = $('orderFormRequestNoContactParticipant').getValidator().validate;
$('orderFormRequestNoContactParticipant').getValidator().validate = this.formValidate;
// The validate Form method
$('orderFormRequestNoContactParticipant').getValidator().validateForm = this.validateForm;
// override the focus method and displayErrorMessage on form items
$('orderFormRequestNoContactParticipant').getValidator()._fields.each(this.overrideFieldMethod.bind(this));
},
/**
* Valid a form
*
* @param boolean  ajaxCall true if the validation must be made by the backend
* @param boolean forButton True if it's just for a button
* @param boolean notDisplayErrorMsg To not display error msg when not needed
*/
formValidate : function(ajaxCall, forButton, notDisplayErrorMsg) {
// if this is not the button that calls that method
$$('.Component-Form-Contact-Button').each(function(item) {
item.setIsValid(true);
}.bind(this));
if (!forButton) {
this._validate(ajaxCall);
// if it's the button that calls that method, just displaying the error messages
}
else {
// The error message of the form is hidden
this._formElement.hideErrorMessage();
var valid = true;
var invalidFields = new Array();
for (var i = 0; i < this._fields.length; i++) {
for (var j = 0; j < this._fields[i].getValidators().length; j++) {
if (!this._fields[i].getValidators()[j].isValid(this._fields[i])) {
invalidFields.push(this._fields[i]);
}
}
}
// The valid fields error message are hidden
for (var i = 0; i < this._fields.length; i++) {
if (!invalidFields.contains(this._fields[i])) {
this._fields[i].hideErrorMessage();
}
}
// The invalidate fields error message are displayed
if (invalidFields.length) {
for (var i = 0; i < invalidFields.length; i++) {
invalidFields[i].displayErrorMessage(notDisplayErrorMsg);
}
}
}
},
/**
* Validate a form
* @param integer formId The form id
* @param boolean displayErrorMessages True to display the error msg
*/
validateForm : function(formId, displayErrorMessages) {
// The error message of the form is hidden
this._formElement.hideErrorMessage();
$('participant' + (formId ? formId : '0')).setIsValid(true, false);
var valid = true;
var invalidFields = new Array();
for (var i = 0; i < this._fields.length; i++) {
for (var j = 0; j < this._fields[i].getValidators().length; j++) {
if (!this._fields[i].getValidators()[j].isValid(this._fields[i])) {
var name = this._fields[i].getProperty('name');
if (parseInt(name.split(".", 1)[0], 10) == formId) {
invalidFields.push(this._fields[i]);
}
}
}
}
// The valid fields error message are hidden
for (var i = 0; i < this._fields.length; i++) {
if (!invalidFields.contains(this._fields[i])) {
this._fields[i].hideErrorMessage();
}
}
// The invalidate fields error message are displayed
if (invalidFields.length) {
$('participant' + (formId ? formId : '0')).setIsValid(false, false);
if (displayErrorMessages) {
for (var i = 0; i < invalidFields.length; i++) {
invalidFields[i].displayErrorMessage(false);
}
}
}
},
/**
* Override the field methods
* @param FieldElement item The Field
*/
overrideFieldMethod : function(item) {
item._focus = item.focus;
item.focus = function() {
var name = this.getProperty('name');
var fieldParticipantId = parseInt(name.split(".", 1)[0], 10);
if ($('participant' + fieldParticipantId)) {
$('participant' + fieldParticipantId).fireEvent('click');
}
this._focus();
};
item._displayErrorMessage = item.displayErrorMessage;
item.displayErrorMessage = function(notDisplayErrorMsg) {
var button = parseInt(this.getProperty('name').split(".", 1)[0]);
if ($('participant' + button)) {
$('participant' + button).setIsValid(false, !notDisplayErrorMsg);
if ($('participant' + button).displayErrorMessage) {
this._displayErrorMessage();
}
}
else {
this._displayErrorMessage();
}
};
item.addEvent('change', function(e) {
var button = item.getProperty('name').split(".", 1)[0];
this._currentForm.getValidator().validateForm(parseInt(button, 10));
}.bind(this));
},
/**
* Add the event on the participant buttons
* @param Element item The button
*/
participantButtonEvent : function(item) {
var nav = navigator.appVersion.split("MSIE");
var navVersion = parseFloat(nav[1]);
if (!((navVersion >= 5.5) && (navVersion < 7))) {
//            this.displayWaitingPanel(this._selectedParticipantForm);
}
// hide the current active form
var oldSelectedButton = $('participant' + this._selectedParticipantForm.participantId);
oldSelectedButton.removeClass('Component-Form-Participant-Button-Selected');
this._selectedParticipantForm.setStyle('display', 'none');
this._selectedParticipantForm.setStyle('opacity', 0);
// select the new active form
item.addClass('Component-Form-Participant-Button-Selected');
if ($('sameParticipant').checked) {
if (this._selectedParticipantForm.participantId)this.populateContactForm('0' + this._selectedParticipantForm.participantId, this._selectedParticipantForm.participantId);
this._selectedParticipantForm = $('participant0' + item.getProperty('id').replace('participant', '') + 'Form');
}
else {
if (this._selectedParticipantForm.participantId)this.populateContactForm(this._selectedParticipantForm.participantId, '0' + this._selectedParticipantForm.participantId);
this._selectedParticipantForm = $(item.getProperty('id') + 'Form');
}
oldSelectedButton.fireEvent("mouseleave");
this._selectedParticipantForm.setStyle('opacity', 1);
this._selectedParticipantForm.setStyle('display', 'block');
item.fireEvent('mouseenter');
// if the button is "invalid" state, ask the form to validate the fields
// in order to display the right values
if (item.displayErrorMessage) {
this._currentForm.getValidator().validateForm(this._selectedParticipantForm.participantId, true);
}
if (!((navVersion >= 5.5) && (navVersion < 7))) {
//            this.hideWaitingPanel.delay(400, this);
}
// update the navigation buttons, depends of its position
if (this._selectedParticipantForm.participantId == 0) {
$('previousParticipant').setStyle('display', 'none');
$('previousParticipantNoContact').setStyle('display', 'none');
$('previousParticipantTop').setStyle('display', 'none');
$('nextParticipant').setAttribute('value', this.firstParticipantButtonName);
$('nextParticipantNoContact').setAttribute('value', this.firstParticipantButtonName);
$('nextParticipantTop').setAttribute('value', this.firstParticipantButtonName);
}
else {
$('previousParticipantTop').setStyle('display', 'inline');
$('previousParticipant').setStyle('display', 'inline');
$('previousParticipantNoContact').setStyle('display', 'inline');
$('nextParticipantTop').setAttribute('value', this.nextParticipantButtonName);
$('nextParticipant').setAttribute('value', this.nextParticipantButtonName);
$('nextParticipantNoContact').setAttribute('value', this.nextParticipantButtonName);
}
if (this._selectedParticipantForm.participantId == 1) {
if ($('sameParticipant').checked) {
$('previousParticipant').setStyle('display', 'none');
$('previousParticipantNoContact').setStyle('display', 'none');
$('previousParticipantTop').setStyle('display', 'none');
}
else {
$('previousParticipantTop').setAttribute('value', this.toContactButtonName);
$('previousParticipant').setAttribute('value', this.toContactButtonName);
$('previousParticipantNoContact').setAttribute('value', this.toContactButtonName);
}
}
else {
$('previousParticipantTop').setAttribute('value', this.previousParticipantButtonName);
$('previousParticipant').setAttribute('value', this.previousParticipantButtonName);
$('previousParticipantNoContact').setAttribute('value', this.previousParticipantButtonName);
}
if ((this._selectedParticipantForm.participantId + 1) == $$('.Component-Form-Contact-Button').length) {
$('nextParticipant').setStyle('display', 'none');
$('nextParticipantNoContact').setStyle('display', 'none');
$('nextParticipantTop').setStyle('display', 'none');
$('id_submit').setStyle('display', 'inline');
$('id_submitNoContact').setStyle('display', 'inline');
$('submitFormTop').setStyle('display', 'inline');
}
else {
$('nextParticipant').setStyle('display', 'inline');
$('nextParticipantNoContact').setStyle('display', 'inline');
$('nextParticipantTop').setStyle('display', 'inline');
$('id_submit').setStyle('display', 'none');
$('id_submitNoContact').setStyle('display', 'none');
$('submitFormTop').setStyle('display', 'none');
}
this.updateCopyFromButton();
},
/**
* Display the price estimation in a printing window
*
* Note : Not used anymore
*/
print : function() {
var xhr = new Request({
url : window.getCurrentJsonUrl(this.options.input),
onSuccess : function(html) {
if (/msie/i.test(navigator.userAgent)) {
// IE
// seems to be a bug in IE that makes contentWindow raise an error in some cases
try {
var w = this.options.output.contentWindow;
} catch(ex) {
}
var d = w ? w.document : null;
} else {
try {
var d = this.options.output.contentDocument;
}
catch (ex) {
}	// in case of cross domain scripting
var w = d ? d.defaultView : null;
}
if (d) {
try {
// We set the content
d.open();
d.write(html);
d.close();
var body = d.getElementsByTagName("body")[0];
body.style.textAlign = 'left';
body.className = 'Synergee-Web-Page-Component-Booking-Print-Body';
// We add the stylesheets
var styleSheets = document.getElementsByTagName("LINK");
var head = d.getElementsByTagName("HEAD")[0];
if (!head) {
head = d.createElement("HEAD");
d.getElementsByTagName("HTML")[0].insertBefore(head, d.getElementsByTagName("BODY")[0]);
}
for (var i = 0; i < styleSheets.length; i++) {
if (styleSheets[i].getAttribute("rel") == 'stylesheet') {
var linkElement = d.createElement('LINK');
linkElement.setAttribute('href', styleSheets[i].getAttribute("href"));
linkElement.setAttribute('rel', 'stylesheet');
linkElement.setAttribute('type', 'text/css');
head.appendChild(linkElement);
}
}
// We print the window
w.focus();
w.printTimout = function(ms) {
var _self = this;
setTimeout(function(ms) {
_self.print();
}, ms);
};
w.printTimout(2000);
} catch (ex) {
}
}
}.bind(this)
});
xhr.send('format=print&dto=' + JSON.encode(this.getDTO()) + '&schoolId=' + this.schoolSelector.getSelectedSchoolId());
Lightbox.show('{hiddenId|' + this.options.output.id + '}', '', 'width=750, height=370');
},
/**
* Display the price estimation in a printing window
*/
mailQuote : function() {
var xhr = new Request(
{
url : window.getCurrentJsonUrl(this.options.input),
onSuccess : function(html) {
this.messageWindow.displayMessage(this._translateDTO.mailSent, this._translateDTO.mailWindowTitle, 4000);
}.bind(this)
}
);
if (!this.messageWindow)this.messageWindow = new WindowMessagePanel(this.options.input);
this.messageWindow.displayMessage('<form><label  class="Synergee-Web-Page-Form-Label Synergee-Web-Page-SchoolPriceEstimation-Print-Label"  for="quoteNameInput">' + this._translateDTO.recipientMail + '</label><input id="recipientMailInput" class="Synergee-Web-Page-Form-Text Synergee-Web-Page-SchoolPriceEstimation-Print-TextInput" type="text"></input>' +
'<label  class="Synergee-Web-Page-Form-Label Synergee-Web-Page-SchoolPriceEstimation-Print-Label"  for="quoteNameSubject">' + this._translateDTO.mailSubjectLabel + '</label><input id="quoteNameSubject" class="Synergee-Web-Page-Form-Text Synergee-Web-Page-SchoolPriceEstimation-Print-TextInput" type="text" value="' + this._translateDTO.mailSubject + '"></input>' +
'<label  class="Synergee-Web-Page-Form-Label Synergee-Web-Page-SchoolPriceEstimation-Print-Label"  for="quoteNameContent">' + this._translateDTO.mailContentLabel + '</label><textarea id="quoteNameContent" class="Synergee-Web-Page-Form-Textarea Synergee-Web-Page-SchoolPriceEstimation-Print-TextInput Synergee-Web-Page-SchoolPriceEstimation-Print-TextArea" >' + this._translateDTO.mailContent + '</textarea>' +
'<input type="submit" class="Synergee-Web-Page-Form-Submit Synergee-Web-Page-SchoolPriceEstimation-Print-Submit" value="Send"/></form>', this._translateDTO.mailWindowTitle);
var form = this.messageWindow.getContent().getElement('form');
var mailTo = this.messageWindow.getContent().getElement('input[id=recipientMailInput]');
var mailSubject = this.messageWindow.getContent().getElement('input[id=quoteNameSubject]');
var mailMessage = this.messageWindow.getContent().getElement('textarea[id=quoteNameContent]');
form.addEvent('submit', function(e) {
e = new Event(e);
xhr.send('format=mail&dto=' + JSON.encode(this.getDTO()) + '&schoolId=' + this._selectedSchoolId + '&mailTo=' + mailTo.get('value') + '&mailSubject=' + mailSubject.get('value') + '&mailMessage=' + mailMessage.get('value'));
this.messageWindow.hideMessage();
e.stop();
}.bind(this));
this.messageWindow.getContent().getElement('input').focus.delay(300, this.messageWindow.getContent().getElement('input'));
},
/**
* Add the quote to the quotes manager
*/
addToMyQuotes : function() {
var xhr = new Request({
url : window.getCurrentJsonUrl(this.options.input),
onSuccess : function(quotesNumber) {
document.fireEvent('quoteChanged', quotesNumber);
}.bind(this)
});
var params = 'step=addToQuotes';
if (this._selectedSchoolId) {
params = params + '&schoolId=' + this._selectedSchoolId;
}
xhr.send(params);
},
addToCart : function() {
var xhr = new Request({
url : window.getCurrentJsonUrl(this.options.input),
onSuccess : function(quotesNumber) {
document.fireEvent('cartChanged', quotesNumber);
}.bind(this)
});
xhr.send('step=addToCart&schoolId=' + this._selectedSchoolId);
},
emptyCart : function () {
var xhr = new Request({
url : window.getCurrentJsonUrl(this.options.input),
onSuccess : function() {
document.fireEvent('cartChanged', 0);
this.fireEvent('cartEmpty');
}.bind(this)
});
xhr.send('step=emptyCart');
}
});
/**
* Display the price estimation in a printing window
*/
function printConfirmation() {
var printOutput = $('shoppingCartConfirmationPrint');
Lightbox.show('{hiddenId|' + printOutput.id + '}', '', 'width=750, height=370');
if (/msie/i.test(navigator.userAgent)) {
// IE
// seems to be a bug in IE that makes contentWindow raise an error in some cases
try {
var w = printOutput.contentWindow;
}
catch(ex) {
}
var d = w ? w.document : null;
}
else {
try {
var d = printOutput.contentDocument;
}
catch (ex) {
}	// in case of cross domain scripting
var w = d ? d.defaultView : null;
}
if (d) {
try {
var shoppingCartContainer = new Element('div');
shoppingCartContainer.addClass('Synergee-Web-Page-Component-ShoppingCart');
var child = $('formConfirmationContent').clone();
child.addClass('Component-Print-Container');
child.inject(shoppingCartContainer);
child.removeChild(child.getElement('script'));
d.open();
d.write('<html><head/><body>' + shoppingCartContainer.innerHTML + '</body></html>');
d.close();
var body = d.getElementsByTagName("body")[0];
body.style.textAlign = 'left';
var printHeader = d.getElementById('printHeader');
if (printHeader)printHeader.setAttribute('class', 'Synergee-Web-Page-Component-Text');
var printBottom = d.getElementById('printBottom');
if (printBottom)printBottom.setAttribute('class', 'Synergee-Web-Page-Component-Text Component-BottomText');
if (d.getElementById('printButtonContainer')) {
d.getElementById('printButtonContainer').removeChild(d.getElementById('printButtonContainer').childNodes[0]);
}
// We add the stylesheets
var styleSheets = document.getElementsByTagName("LINK");
var head = d.getElementsByTagName("HEAD")[0];
if (!head) {
head = d.createElement("HEAD");
d.getElementsByTagName("HTML")[0].insertBefore(head, d.getElementsByTagName("BODY")[0]);
}
for (var i = 0; i < styleSheets.length; i++) {
if (styleSheets[i].getAttribute("rel") == 'stylesheet') {
var linkElement = d.createElement('LINK');
linkElement.setAttribute('href', styleSheets[i].getAttribute("href"));
linkElement.setAttribute('rel', 'stylesheet');
linkElement.setAttribute('type', 'text/css');
head.appendChild(linkElement);
}
}
w.focus();
w.print();
}
catch (ex) {
}
}
};
/**
* This class is used for the price estimation functionnalities
*
* @event Event heightModified this event is fired when the price estimation is displayed
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param Element input the input div
* @param Element output the output div
**/
//var Booking = Events.extend({
var Booking = new Class({
Extends:BookingEngine,
/**
* This is the initialization function
*/
initialize:function (options) {
this.parent(options);
this.getProgramsListTemplate = this.options.getProgramsListTemplateCallback;
this.getCountriesListTemplate = this.options.getCountriesListTemplateCallback;
this.getCitiesListTemplate = this.options.getCitiesListTemplateCallback;
this.getSchoolsListTemplate = this.options.getSchoolsListTemplateCallback;
this._outputContainer = this.options.output;
this._directives = new Hash();
// retrieve the right result div
while (!this._outputContainer.hasClass('Component-Cart')) {
this._outputContainer = this._outputContainer.getParent();
}
this.header = this.options.header;
if (!this.options.shoppingCartPage && this.header) {
this.header.setStyle('display', 'none');
}
this.shoppingCartContainer = this.options.input.getParent();
this._displayOutput = false;
this._listContainer = new Element('div', {'id':'lists'});
this._listContainer.inject(document.getElement('body'));
this._classNameBase = 'Component-';
this._bookingMessagePanel = new Element('div', {'class':this._classNameBase + 'bookingToolTip Tool-tip', 'id':'bookingMessagePanel'});
this._bookingMessageFxStyle = new Fx.Tween(this._bookingMessagePanel, {
duration:100,
property:'opacity'
});
this._bookingMessagePanel.inject(document.getElement('body'));
this._bookingMessagePanel.setStyle('position', 'absolute');
this._bookingMessagePanel.setStyles({
opacity:0,
top:0
});
this._bookingMessagePanel.addEvent('click', function () {
clearTimeout(this._bookingMessageFxStyle.delayedFunction);
this._bookingMessageFxStyle.start(1, 0);
}.bind(this));
// the top links are created
// the shopping cart link
if (this.options.shoppingCartPage) {
this.createShoppingCartLinks();
// add the order form container
this.orderForm = new Element('div');
this.orderForm.inject(this.shoppingCartContainer);
this.orderForm.setStyle('display', 'none');
this.orderForm.initialized = false;
// add the order confirmation page container
this.orderConfirmation = new Element('div');
this.orderConfirmation.inject(this.shoppingCartContainer);
this.orderConfirmation.setStyle('display', 'none');
}
this._fxStyle = new Fx.Tween(this.options.output, {
duration:100,
property:'opacity'
});
},
/**
* Start the booking
*/
launch:function () {
// The Init call is made
if (!document.isInDesignMode()) {
this.parent();
}
},
/**
* Return the json current url
*
* return string
*/
getBookingJsonUrl:function () {
return window.getCurrentJsonUrl(this.options.input);
},
/**
* called when a school is loaded
*
*
* @param e
*/
initBooking:function (e) {
eval(e);
//        this.schoolsContainer.empty();
if (typeof(getInit) == 'function') {
var initDTO = getInit();
this._schools = initDTO[0];
this._options = initDTO[2];
this._insurances = initDTO[3];
this._promotions = initDTO[4];
// The schools displayed in the shopping cart are stocked in
if (!this.schools)
this.schools = new Array();
// the menu item translations are set
if (this.options.shoppingCartPage) {
this.shoppingCartFirstItem.set("html", this._translateDTO.firstItem);
this.shoppingCartFirstItemName.set("html", this._translateDTO.yourShoppingCartMenuItem);
this.formLinkSecondItem.set("html", this._translateDTO.secondItem);
this.formLinkSecondItemName.set("html", this._translateDTO.formMenuItem);
this.confirmationLinkThirdItem.set("html", this._translateDTO.thirdItem);
this.confirmationLinkThirdItemName.set("html", this._translateDTO.confirmationMenuItem);
}
this.insurancesManager = new SchoolInsurances(this._insurances, this);
this.insurancesManager.addEvent('insurancesUpdated', this.refreshPrice.bind(this));
this.promotionsManager = new SchoolPromotions(this._promotions, this);
this.promotionsManager.addEvent('promotionsUpdated', this.refreshPrice.bind(this));
var schools = [];
// if there is no school added in the shopping cart or the shopping cart is opened in the designer
if (!this._schools.length || document.isInDesignMode()) {
if (this.options.shoppingCartPage) {
/* var emptyMessage = new Element('div', {'class': this._classNameBase + 'Empty'});
emptyMessage.set("html", this._translateDTO.empty);
emptyMessage.inject(this.schoolsContainer);*/
}
this.setSameColumnSize(this.options.input, this.options.output);
}
else {
for (var i = 0; i < this._schools.length; i++) {
this._schoolPriceCalculatorDTO = this._schools[i];
var school = new School(this._schools[i], this);
school.addEvent('refreshPrice', this.refreshPrice.bind(this));
//                            if (this.options.widgetMode)
school.addEvent('heightModified', this.onHeightModified.bind(this));
schools.push(school);
this.schools.push(school);
}
this._oldDTO = JSON.encode(this.getDTO());
// The shopping cart is populated if there are some informations
//                      if (this.options.widgetMode){
this.populateFromDTO.delay(50, this, initDTO[1]);
this.retrieveSchoolItemsDescription();
//                      }
}
this.fireEvent('initialized', [schools]);
}
},
initTranslations:function (e) {
eval(e);
if (typeof(getInit) == 'function') {
this._initDTO = getInit();
this._translateDTO = this._initDTO.translateDTO;
this.participants = new SchoolParticipants(this._translateDTO.participants, this._initDTO.multipleBooking, this);
this.currency = new SchoolCurrencySelector(this._translateDTO.currencies, this._initDTO.defaultCurrency, this._initDTO.websiteCurrencies, this);
this.currency.addEvent('refreshPrice', this.refreshPrice.bind(this));
this._currencyConversionEnabled = this._initDTO.currencyConversionEnabled;
if (!this.options.shoppingCartPage) {
this.schoolSelector = new SchoolSelector(this._translateDTO.schoolSelector, this._initDTO.schoolsList, this, this._initDTO.advancedSchoolList);
//                        if(this.options.widgetMode)
this.schoolSelector.render();
// when a school is selected
this.schoolSelector.addEvent('changeSchool', function () {
if (this.schoolSelector.getSelectedSchoolId()) {
this.loadSchool(this.schoolSelector.getSelectedSchoolId());
}
}.bind(this));
this.fireEvent('schoolsSelectorLoaded');
}
/*
if(!this.options.widgetMode){
this._initDTO.schoolsList.each(function(item, index){
item.schoolCountryName = html_entity_decode(item.schoolCountryName);
item.schoolCityName = html_entity_decode(item.schoolCityName);
item.schoolName = html_entity_decode(item.schoolName);
item.schoolProgramName = html_entity_decode(item.schoolProgramName);
item.schoolGroupName = html_entity_decode(item.schoolGroupName);
});
}*/
//                    if(this.options.widgetMode)
this.participants.getHtml().inject(this.options.input);
this.fireEvent('onTranslationsLoaded');
if (this.schoolSelector) {
if (this._initDTO.schoolsList.length == 1) {
this.schoolSelector.fireEvent('changeSchool');
} else {
this.schoolSelector.updateSelector(this._initDTO.selectedSchool, this._initDTO.selectedProgram, this._initDTO.selectedCountry, this._initDTO.selectedCity);
}
}
//                    if(this.options.shoppingCartPage)
if(this.bookingAutoInit){
this.bookingAutoInit = false;
this._xhrInit.send('step=init&shoppingCartPage=' + this.options.shoppingCartPage);
}
}
},
/**
* Refresh the price
*/
refreshPrice:function () {
if (this.options.shoppingCartPage) {
if (this.formLink.active) {
this.saveFormFields();
this.formLink.addClass(this._classNameBase + 'Links-Inactive');
this.formLink.active = false;
this.orderForm.initialized = false;
}
}
this.updateValues();
if (this._oldDTO != JSON.encode(this.getDTO())) {
this.fireEvent('onPriceRefresh');
var params = 'shoppingCartPage=' + this.options.shoppingCartPage;
if (this.options.promotionIntroductionContainer != '') params = params + '&pIntroContainer=' + this.options.promotionIntroductionContainer;
if (this.options.promotionContainer != '') params = params + '&pContainer=' + this.options.promotionContainer;
if (this._selectedSchoolId)params = params + '&schoolId=' + this._selectedSchoolId;
this._xhr.cancel();
this._xhr.send(params + '&dto=' + JSON.encode(this.getDTO()));
this._oldDTO = JSON.encode(this.getDTO());
}
},
loadSchool : function (schoolId, addSchool) {
if (schoolId != this._selectedSchoolId) {
if (!addSchool && this.schools) {
this.schools = new Array();
}
if (schoolId) {
this.options.output.empty();
this._xhrInit.cancel();
this._xhr.cancel();
var xhrLoadSchool = new Request(
{
url:this.getBookingJsonUrl(),
onRequest:function () {
},
onSuccess:function (responseText) {
this.initBooking(responseText);
}.bind(this),
onFailure:function () {
}
});
var xhrParam = 'step=init';
xhrParam += '&schoolId=' + schoolId;
if(this.schools && this.schools.length && addSchool){
var loadedSchools = '';
for(var i = 0; i < this.schools.length; i++){
loadedSchools = loadedSchools + this.schools[i]._schoolDTO.schoolId + (i < this.schools.length-1 ? '|': '');
}
xhrParam += '&alreadyLoadedSchools=' + loadedSchools;
}
this._selectedSchoolId = schoolId;
this.fireEvent('beforeschoolload', schoolId);
// send an ajax call to add the school
this.bookingAutoInit = false;
xhrLoadSchool.send(xhrParam);
}
}
},
/**
* called when the height of a column is modified so the two column are the same size
*
*/
onHeightModified:function (e) {
//                if(this.options.widgetMode){
this.fireEvent('beforeHeightUpdated');
this.setSameColumnSize(this.options.input, this.options.output);
this.fireEvent('afterHeightUpdated');
//                }
},
/**
* Display the result of the price estimation
*
* @param Event e The result of the price estimation
*/
displayPriceEstimation:function (e) {
//                if(this.options.widgetMode){
this.options.output.set("html", e);
this.onHeightModified();
var infos = this.options.output.getElements('.PromotionMoreInfos');
for (i = 0; i < infos.length; i++) {
new SynergeeTips(infos[i], {'className':'Booking-Tool-Tip Tool-tip', 'hideDelay':1000, 'fixed':true, 'closeButton':true, 'maxHeight':280});
}
//                }
// check if there are complete courses
var completedCourses = 0;
for (var i = 0; i < this.schools.length; i++) {
completedCourses += this.schools[i].getCompletedCourses().length;
}
if (completedCourses) {
if (e.replace(/^\s+|\s+$/g, '') == "") {
this.fireEvent.delay(100, this, 'refreshPrice');
}
else {
if (this.options.shoppingCartPage) {
$('bookingCheckoutButton').setStyle('visibility', '');
}
}
}
this.fireEvent('onPriceRefreshComplete', e);
},
/**
* Remove a school from the shopping cart
*
* @param Element school Remove the school given in param
*/
removeSchool:function (school) {
this.schools.erase(school);
//        this.displayWaitingPanel(this.options.output);
// The price must be refreshed to update the session
//                if(this.options.widgetMode){
this._xhr.cancel();
this._xhr.send(window.getCurrentJsonUrl(this.options.input), 'dto=' + JSON.encode(this.getDTO()) + '&shoppingCartPage=' + this.options.shoppingCartPage + '&sId=' + school._schoolPriceCalculatorDTO.schoolId + '&sPrg=' + school._schoolPriceCalculatorDTO.schoolProgram);
// if the shopping cart is empty
if (!this.schools.length) {
var emptyMessage = new Element('div', {'class':this._classNameBase + 'Empty'});
emptyMessage.set("html", this._translateDTO.empty);
// clean the input
this.options.input.set("html", '');
// set the initialization to false
this.formLink.addClass(this._classNameBase + 'Links-Inactive');
this.formLink.active = false;
this.orderForm.initialized = false;
// inject the empty shopping cart message
emptyMessage.inject(this.options.input);
this.setSameColumnSize(this.options.input, this.options.output);
}
//                }
}
}
);
var BookingCalendar = new Class({
Implements : [Events, Options],
options: {
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // days of the week starting at sunday
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
offset: 0, // first day of the week: 0 = sunday, 1 = monday, etc..
onHideStart: Class.empty,
onHideComplete: Class.empty,
onShowStart: Class.empty,
onShowComplete: Class.empty
},
// initialize: calendar constructor
// @param props (obj) optional properties
initialize: function(options) {
if(options.days && !options.days.length){
options.days = this.options.days;
}
if(options.months && !options.months.length){
options.months = this.options.months;
}
this.setOptions(options);
this._validDates = new Array();
this._selectedDate = null;
this._linkedElement = null;
this._maxMonth = null;
this._minMonth = null;
var today = new Date();
today.setHours(0);
today.setMinutes(0);
today.setSeconds(0);
today.setMilliseconds(0);
this._todayTime = today.getTime();
// create cal element with css styles required for proper cal functioning
this.calendar = new Element('div');
this.calendar.inject(document.body);
this.calendar.setStyles({ left: '-1000px', opacity: 0, display:'none', position: 'absolute', top: '-1000px', zIndex: 1000 });
this.calendar.addClass('BookingCalendar Component-List');
// initialize fade method
this.fx = new Fx.Tween(this.calendar ,
{
property : 'opacity',
onStart: function() {
if (this.calendar.getStyle('opacity') == 0) { // show
this.calendar.setStyle('display', 'block');
this.fireEvent('onShowStart', this.element);
}
else { // hide
this.fireEvent('onHideStart', this.element);
}
}.bind(this),
onComplete: function() {
if (this.calendar.getStyle('opacity') == 0) { // hidden
this.calendar.setStyle('display', 'none');
this.fireEvent('onHideComplete', this.element);
}
else { // shown
this.fireEvent('onShowComplete', this.element);
}
}.bind(this)
});
this.calendarPreviousMenu = new Element('input').setProperties({'type':'button', value: '<'}).addClass('NavButton PrevMonth Component-List-Item').inject(this.calendar);
this.calendarNextMenu = new Element('input').setProperties({'type':'button' , value: '>'}).addClass('NavButton NextMonth Component-List-Item').inject(this.calendar);
this.calendarInner = new Element('div',{}).addClass('CalendarInner').inject(this.calendar);
this.calendarFirstMonthContainer = new SynergeeCalendarMonth(this.options, this);
this.calendarFirstMonthContainer.getContainer().inject(this.calendarInner);
this.calendarSecondMonthContainer = new SynergeeCalendarMonth(this.options, this);
this.calendarSecondMonthContainer.getContainer().inject(this.calendarInner);
this.calendarPreviousMenu.addEvent('click', this.prevMonth.bind(this));
this.calendarNextMenu.addEvent('click', this.nextMonth.bind(this));
if(Browser.ie6){
this.calendarPreviousMenu.addEvent('dblclick', this.prevMonth.bind(this));
this.calendarNextMenu.addEvent('dblclick', this.nextMonth.bind(this));
}
document.addEvent('click', function(event){
this.hide();
}.bind(this));
this.calendar.addEvent('click', function(e){
if(Browser.ie){
window.event.cancelBubble = true;
}else {
e.stopPropagation();
}
}.bind(this));
this.calendar.addEvent('mouseleave', function(){
this._hideDelay = this.hide.delay(2500, this);
}.bind(this));
this.calendar.addEvent('mouseenter', function(){
clearTimeout(this._hideDelay);
}.bind(this));
this.calendarFirstMonthContainer.addEvent('dateselected', this._onDateSelected.bind(this));
this.calendarSecondMonthContainer.addEvent('dateselected', this._onDateSelected.bind(this));
var id = 0;
var d = new Date(); // today
d.setDate(1);
this.displayMonths(d.getMonth(), d.getFullYear());
},
/**
* Display the given month
*
* @param integer month The month 0-11
* @param integer year the year
*/
displayMonths : function(month, year) {
if(parseInt(year + (month > 9 ? '' : '0') + month) > parseInt(this.calendarFirstMonthContainer.year + (this.calendarFirstMonthContainer.month > 9 ? '' : '0') + this.calendarFirstMonthContainer.month)){
this._direction = 1;
} else {
this._direction = 0;
}
clearTimeout(this._updateFirstCalendarTimer);
clearTimeout(this._updateSecondCalendarTimer);
if(this._minMonth && parseInt(this._minMonth) < parseInt(year + (month > 9 ? '' : '0') + month)){
this.calendarPreviousMenu.setStyle('display', '');
}else {
this.calendarPreviousMenu.setStyle('display', 'none');
}
this.calendarFirstMonthContainer.month = month;
this.calendarFirstMonthContainer.year = year;
this.calendarFirstMonthContainer.calendarTitle.set("html", this.options.months[month] + ' ' + year);
month = this.calendarFirstMonthContainer.month;
year = this.calendarFirstMonthContainer.year;
if (month == 11) {
month = 0;
year++;
} else {
month++;
}
this.calendarSecondMonthContainer.month = month;
this.calendarSecondMonthContainer.year = year;
this.calendarSecondMonthContainer.calendarTitle.set("html", this.options.months[month] + ' ' + year);
if(this._maxMonth && parseInt(this._maxMonth) > parseInt(year + (month > 9 ? '' : '0') + month)){
this.calendarNextMenu.setStyle('display', '');
}else {
this.calendarNextMenu.setStyle('display', 'none');
}
this._updateFirstCalendarTimer = this.calendarFirstMonthContainer.updateCalendar.delay(100, this.calendarFirstMonthContainer, this._direction);
this._updateSecondCalendarTimer = this.calendarSecondMonthContainer.updateCalendar.delay(100, this.calendarSecondMonthContainer, this._direction);
},
/**
* go to the next month
*/
nextMonth : function(){
var month = this.calendarFirstMonthContainer.month;
var year = this.calendarFirstMonthContainer.year;
if(month == 11){
month= 0;
year++;
}else {
month++;
}
this.displayMonths(month, year);
},
/**
* Go to the previous month
*/
prevMonth : function(){
var month = this.calendarFirstMonthContainer.month;
var year = this.calendarFirstMonthContainer.year;
if(month == 0){
month = 11;
year--;
}else {
month--;
}
this.displayMonths(month, year);
},
/**
* Show the calendar
*
* @param element
*/
show : function(element, callback, bind){
this._callback = callback;
this._bind = bind;
this._linkedElement = element;
var ec = element.getCoordinates();
this.calendar.setStyles({
opacity : 0,
display : 'none',
left : ec.left + ec.width,
top : ec.top
});
this.fx.start(0,1);
},
getBind : function(){
return this._bind;
},
getCallBackMethod : function () {
return this._callback;
},
/**
* Hide the calendar
*/
hide : function(){
if(this.calendar.getStyle('opacity') != 0)
this.fx.start(1,0);
},
/**
* When a date is selected
*
* @param date
*/
_onDateSelected : function(date){
this._selectedDate = date.value;
this.fireEvent('dateselected');
},
/**
* Return the selected date
*/
getSelectedDate : function() {
return this._selectedDate;
},
/**
* set the selected date
* @param date
* @param before
* @param after
*/
setSelectedDate : function(date, before, after) {
var year = date.getFullYear();
var month = date.getMonth();
var dateDay = date.getDate();
this._validDates.each(function(item, index){
if(item.year == year && item.month == month && item.date == dateDay && before == item.additionalNightsBefore && after == item.additionalNightsAfter){
this._selectedDate = item;
}
}.bind(this));
this.displayMonths(month, year);
},
/**
* Set the valid dates
*
* @param date[] validDates The valid date
* @param integer additionalNightsBefore the valid days before the dates
* @param integer additionalNightsAfter The valid days after the dates
* @param boolean onBoundsOnly True if the days before and after are only valid on the boundary dates
*/
setValidDates : function(validDates, additionalNightsBefore, additionalNightsAfter, onBoundsOnly){
if(!additionalNightsBefore)additionalNightsBefore = 0;
if (!additionalNightsAfter)additionalNightsAfter = 0;
this._validDates = new Array();
if (validDates.length) {
this._additionalNightsBefore = additionalNightsBefore;
this._additionalNightsAfter = additionalNightsAfter;
var dateIndex = new Date();
dateIndex.setHours(12);
dateIndex.setMinutes(0);
dateIndex.setSeconds(0);
dateIndex.setMilliseconds(0);
var dayTime = (86400) * 1000; // ms
var firstDate = validDates[0];
validDates.each(function(item, index) {
if (this._additionalNightsBefore && (index == 0 || !onBoundsOnly)) {
dateIndex.setDate(1);
dateIndex.setYear(item.getFullYear());
dateIndex.setMonth(item.getMonth());
dateIndex.setDate(item.getDate());
for (var i = 1; i <= this._additionalNightsBefore; i++) {
dateIndex.setTime(dateIndex.getTime() - dayTime);
if(this._todayTime <= dateIndex.getTime()){
var dateObject = new Object();
dateObject.relativeDate = item;
dateObject.additionalNightsBefore = i;
dateObject.additionalNightsAfter = 0;
dateObject.year = dateIndex.getFullYear();
dateObject.month = dateIndex.getMonth();
dateObject.date = dateIndex.getDate();
dateObject.time = dateIndex.getTime();
this._validDates.push(dateObject);
}
}
this._validDates.reverse();
}
var dateObject = new Object();
dateObject.relativeDate = item;
dateObject.additionalNightsBefore = 0;
dateObject.additionalNightsAfter = 0;
dateObject.year = item.getFullYear();
dateObject.month = item.getMonth();
dateObject.date = item.getDate();
dateObject.time = item.getTime();
this._validDates.push(dateObject);
if (this._additionalNightsAfter && (index == validDates.length - 1 || !onBoundsOnly)) {
dateIndex.setDate(1);
dateIndex.setYear(item.getFullYear());
dateIndex.setMonth(item.getMonth());
dateIndex.setDate(item.getDate());
for (var i = 1; i <= this._additionalNightsAfter; i++) {
dateIndex.setTime(dateIndex.getTime() + dayTime);
var dateObject = new Object();
dateObject.relativeDate = item;
dateObject.additionalNightsBefore = 0;
dateObject.additionalNightsAfter = i;
dateObject.year = dateIndex.getFullYear();
dateObject.month = dateIndex.getMonth();
dateObject.date = dateIndex.getDate();
dateObject.time = dateIndex.getTime();
this._validDates.push(dateObject);
}
}
if (index == 0) {
firstDate = this._validDates[0];
}
}.bind(this));
}
if(this._validDates.length){
this._maxMonth = this._validDates.getLast().year  + (this._validDates.getLast().month > 9 ? '' : '0') + this._validDates.getLast().month;
this._minMonth = firstDate.year + (firstDate.month > 9 ? '' : '0') + firstDate.month;
}else {
this._maxMonth = null;
this._minMonth = null;
}
this.displayMonths(this.calendarFirstMonthContainer.month, this.calendarFirstMonthContainer.year);
},
/**
* Return the valid dates
*/
getValidDates : function () {
return this._validDates;
},
/**
* Return the valid days before
*/
getValidDaysBefore : function () {
return this._additionalNightsBefore;
},
/**
* Return the linked element
*/
getLinkedElement : function () {
return this._linkedElement;
},
/**
* Return the valid days after
*/
getValidDaysAfter : function () {
return this._additionalNightsAfter;
}
});
//
//BookingCalendar.implement(new Events);
//BookingCalendar.implement(new Options);
var SynergeeCalendarMonth = new Class({
Implements : [Events, Options],
options: {
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // days of the week starting at sunday
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
offset: 1 // first day of the week: 0 = sunday, 1 = monday, etc..
},
initialize: function(options, parent) {
this.setOptions(options);
this._parent = parent;
this.calendarFirstMonthContainer = new Element('div', {}).addClass('MonthContainer');
this.calendarHeaders = new Element('div',{}).addClass('CalendarHeader').inject(this.calendarFirstMonthContainer);
this.calendarTitleRow = new Element('div',{}).addClass('CalendarTitleRow').inject(this.calendarHeaders);
this.calendarTitle  = new Element('div',{}).addClass('CalendarTitle').inject(this.calendarTitleRow);
this.calendarWeekDaysHeader = new Element('div').addClass('WeekDaysHeader').inject(this.calendarHeaders);
this.calendarContent = new Element('div',{}).addClass('CalendarContent').inject(this.calendarFirstMonthContainer);
this.calendarContentScroller = new Element('div', {}).addClass('CalendarContentScroller').inject(this.calendarContent);
this.calendarContent.addEvent('click', this._onClick.bind(this));
//        this.fxScroll = new Fx.Styles(this.calendarContentScroller,  {duration: 100, transition: Fx.Transitions.linear});
this.fxScroll = new Fx.Morph(this.calendarContentScroller,  {duration: 100, transition: Fx.Transitions.linear});
if(!this.options.offset)
this.options.offset = 0;
for(var i = 0; i < 7; i++){
var dayNumber = i+this.options.offset;
if(dayNumber > 6)dayNumber = dayNumber-7;
var dayName = this.options.days[dayNumber];
var dayTitle = new Element('div').addClass('WeekDayHeader').inject(this.calendarWeekDaysHeader);
dayName = dayName.replace(/"/g, '');
dayTitle.set("html", dayName.substr(0,2));
}
for(var l = 0; l < 6; l++){
var line = new Element('div').addClass('WeekElement').inject(this.calendarContentScroller);
for(i = 0; i < 7; i++){
var day = new Element('div').addClass('DayElement').inject(line);
}
}
},
getContainer : function(){
return this.calendarFirstMonthContainer;
},
updateCalendar : function(direction){
var d = new Date(); // today
d.setDate(1);
d.setMonth(this.month);
d.setYear(this.year);
// find the start date
while (d.getDay() != this.options.offset) {
d.setDate(d.getDate() - 1);
}
if (direction) {
this.fxScroll.start({
'margin-left' : [0, -150]
}).chain(function() {
this.start({
'margin-left' : [150, 0]
});
});
} else {
this.fxScroll.start({
'margin-left' : [0, 150]
}).chain(function() {
this.start({
'margin-left' : [-150, 0]
});
});
}
// filter the date concerned by the current month
var validDates = new Hash();
if (this._parent.getValidDates().length) {
this._parent.getValidDates().each(function(item, index) {
// if we are on the current month
if(true || (item.month == this.month && item.year == this.year)){
// if there is already an entry for this date
if(validDates.has(item.year + '-' + item.month + '-' + item.date)){
var currentEntry = validDates.get(item.year + '-' + item.month + '-' + item.date);
// if the space between the real date and the new date is bigger than the new one we replace it
if((currentEntry.additionalNightsBefore + currentEntry.additionalNightsAfter) > (item.additionalNightsBefore + item.additionalNightsAfter)){
validDates.set(item.year + '-' + item.month + '-' + item.date, item);
}
}else {
validDates.set(item.year + '-' + item.month + '-' + item.date, item);
}
}
}.bind(this));
}
for (var l = 0; l < 6; l++) {
var line = this.calendarContentScroller.getChildren()[l];
for (var i = 0; i < 7; i++) {
if (line.getChildren()[i]) {
var dayElement = line.getChildren()[i];
dayElement.valid = true;
dayElement.date = d.getDate();
dayElement.year = d.getFullYear();
dayElement.month = d.getMonth();
dayElement.removeClass('Selected');
dayElement.set("html", dayElement.date);
dayElement.removeClass('MainDate');
dayElement.removeClass('NotCurrentMonth');
if ((validDates.has(dayElement.year + '-' + dayElement.month + '-' + dayElement.date) )) {
dayElement.addClass('Component-List-Item');
dayElement.removeClass('Component-NotSelectionable');
dayElement.value = validDates.get(dayElement.year + '-' + dayElement.month + '-' + dayElement.date);
if (dayElement.value.additionalNightsBefore == 0 && dayElement.value.additionalNightsAfter == 0) {
dayElement.addClass('MainDate');
}
if (this._parent.getSelectedDate() && this._parent.getSelectedDate().time == dayElement.value.time) {
dayElement.addClass('Selected');
}
} else {
dayElement.addClass('Component-NotSelectionable');
dayElement.removeClass('Component-List-Item');
dayElement.valid = false;
}
if (dayElement.month != this.month) {
dayElement.addClass('NotCurrentMonth');
}
}
d.setDate(d.getDate() + 1);
}
}
},
_onClick : function(e){
if(Browser.ie){
window.event.cancelBubble = true;
}else {
e.stopPropagation();
}
var target = e.target || e.srcElement;
if(target.hasClass('DayElement') && target.valid){
$$('.DayElement').removeClass('Selected');
target.addClass('Selected');
this.fireEvent('dateselected', target);
}
},
getSelectedDate : function(){
return this._parent.getSelectedDate();
}
});
//
//SynergeeCalendarMonth.implement(new Events);
//SynergeeCalendarMonth.implement(new Options);
/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param Element button the button attached to the list
*
**/
var BookingList = new Class({
Implements: Events,
/**
* This is the initialization function
* @param Element eListElement The main list element
* @param sOptionsSelector The options selector
*/
initialize : function(eListElement, values,  sOptionsSelector, sButtonSelector) {
this._list = eListElement;
this._listElement = eListElement;
this._buttonSelector = sButtonSelector;
this._values = null;
if(values)
this._values = values;
this._optionsSelector = sOptionsSelector;
this._listOptions = new Array();
this._selected = null;
this.updateListOptions(this._list, this._values);
},
/**
*
*/
updateListOptions : function (eListElement, values, sOptionsSelector, eButton) {
if (eListElement){
this._list = eListElement;
}
if(this._list.get('tag') != 'select' && this._list.getElement('select')){
this._list = this._list.getElement('select');
}
if (sOptionsSelector)
this._optionsSelector = sOptionsSelector;
if (values){
this._values = values;
}else {
this._values = null;
}
if(this._buttonSelector && this._list.getElement(this._buttonSelector)){
this._button = new BookingListButton(this._list.getElement(this._buttonSelector));
this._button.addEvent('click', function(){
}.bind(this));
}
this._listOptions = new Array();
this._list.addEvent('change', function(item, index){
this.setValue(this._list.get('value'));
}.bind(this));
var optionsElements = this._list.getElements(this._optionsSelector);
for (var i = 0; i < optionsElements.length; i++) {
var value = (this._values ? this._values[i] : optionsElements[i].getAttribute('value'));
var listItem = new BookingListItem(value, optionsElements[i]);
this._listOptions.push(listItem);
listItem.addEvent('selected', function (e) {
if (!this._selected || this._selected != e) {
this._selected = e;
for (var j = 0; j < this._listOptions.length; j++) {
if (this._listOptions[j] != this._selected)
this._listOptions[j].setSelected(false);
}
if (this._button) {
if (this._selected.getValue() && this._selected.getValue() != '') {
this._button.setText(this._selected.getText());
} else {
this._button.reset();
}
}
this.fireEvent('change');
}
}.bind(this));
}
/*if(this._list.hasClass('item-selector')){
this._list.addEvent('change', function(){
this.setValue(this._list.getSelected()[0].getAttribute('value'));
this.fireEvent('change');
}.bind(this));
}else {
this._listElement = this._list.getElement('.item-selector');
this._listElement.addEvent('change', function(){
this.setValue(this._listElement.getSelected()[0].getAttribute('value'));
this.fireEvent('change');
}.bind(this));
}*/
},
setValidOptions : function (validOptions) {
this._listOptions.each(function(option, index){
option.setIsValid(validOptions.contains(option.getValue()));
}.bind(this));
},
/**
*  Return the selected item
*
*  @return BookingListItem The list item
*/
getSelectedItem : function () {
return this._selected;
},
/**
*  Return the selected item
*
*  @return BookingListItem The list item
*/
getValue : function () {
return this._selected.getValue();
},
/**
* Set selected the value
* @param value
* @return true if the value is found
*/
setValue : function(value) {
var valueFound = false;
for(var i = 0; i < this._listOptions.length; i++) {
if(this._listOptions[i].getValue() == value){
this._listOptions[i].setSelected(true);
this._selected = this._listOptions[i];
valueFound = true;
}
}
if(!valueFound){
for (var j = 0; j < this._listOptions.length; j++) {
this._listOptions[j].setSelected(false);
}
}
return valueFound;
}
});
var BookingListItem = new Class({
Implements: Events,
/**
* This is the initialization function
* @param value String The list item value
* @param element Element The options selector
*/
initialize : function(value, element) {
this._value = value;
this._element = element;
this._originalText = this._text = element.get('html');
this._isValid = true;
if (this._element.get('tag') != 'option') {
this._element.addEvent('click', function() {
if (this._isValid) {
this.setSelected(true);
} else {
this.setSelected(false);
}
}.bind(this));
}
},
/**
* Return the item value
*
* @return string The value
*/
getValue : function () {
return this._value;
},
/**
*  Set the validity status
*/
setIsValid : function (isValid){
this._isValid = isValid;
if(this._isValid) {
this._element.removeAttribute('disabled');
} else {
this._element.setAttribute('disabled', 'disabled');
}
},
/**
* Return true if valid
*/
getIsValid : function() {
return this._isValid;
},
/**
* Return list item text value
*
* @return String the item text value
*/
getText : function () {
return this._text;
},
/**
* Set the item selected
*
* @param boolean selected True if the item should be selected
*/
setSelected : function (selected) {
if(this._selected != selected) {
this._selected = selected;
if (this._selected) {
if (this._element.get('tag') == 'option') {
this._element.setAttribute('selected', 'selected');
}
this._element.addClass('selected');
this.fireEvent('selected', this);
} else {
if (this._element.get('tag') == 'option') {
this._element.removeAttribute('selected');
}
this._element.removeClass('selected');
}
}
}
});
var BookingListButton = new Class({
Implements: Events,
/**
* This is the initialization function
* @param Element element The button Element
*/
initialize : function(element) {
this._button = element;
this._originalText = element.get('html');
this._button.addEvent('click', function(){
this.fireEvent('click');
}.bind(this));
},
setText : function(text) {
this._button.set('html', text);
},
reset : function () {
this._button.set('html', this._originalText);
}
});
/**
* This class is used for the price estimation functionnalities
*
* @event Event heightModified this event is fired when the price estimation is displayed
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param Element input the input div
* @param Element output the output div
**/
var BookingRequest = new Class({
Extends: Request,
success: function(text, xml){
this.onSuccess(text, xml);
}
});
/**
* This class is used for the price estimation functionnalities
*
* @event Event heightModified this event is fired when the price estimation is displayed
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param Element input the input div
* @param Element output the output div
**/
var BookingTemplateObject = new Class({
Implements: Options,
options: {
element : null,
selector : '',
directive : null
},
/**
* This is the initialization function
*/
initialize : function(options) {
this.setOptions(options);
},
getElement : function() {
return this.options.element;
},
getDirective : function() {
return this.options.directive;
},
getSelector : function() {
return this.options.selector;
}
});
/*!
PURE Unobtrusive Rendering Engine for HTML
Licensed under the MIT licenses.
More information at: http://www.opensource.org
Copyright (c) 2011 Michael Cvilic - BeeBole.com
Thanks to Rog Peppe for the functional JS jump
revision: 2.71
*/
var $p, pure = $p = function(){
var sel = arguments[0],
ctxt = false;
if(typeof sel === 'string'){
ctxt = arguments[1] || false;
}else if(sel && !sel[0] && !sel.length){
sel = [sel];
}
return $p.core(sel, ctxt);
};
$p.core = function(sel, ctxt, plugins){
//get an instance of the plugins
var templates = [];
plugins = plugins || getPlugins();
//search for the template node(s)
switch(typeof sel){
case 'string':
templates = plugins.find(ctxt || document, sel);
if(templates.length === 0) {
error('The template "' + sel + '" was not found');
}
break;
case 'undefined':
error('The root of the template is undefined, check your selector');
break;
default:
templates = sel;
}
for(var i = 0, ii = templates.length; i < ii; i++){
plugins[i] = templates[i];
}
plugins.length = ii;
// set the signature string that will be replaced at render time
var Sig = '_s' + Math.floor( Math.random() * 1000000 ) + '_',
// another signature to prepend to attributes and avoid checks: style, height, on[events]...
attPfx = '_a' + Math.floor( Math.random() * 1000000 ) + '_',
// rx to parse selectors, e.g. "+tr.foo[class]"
selRx = /^(\+)?([^\@\+]+)?\@?([^\+]+)?(\+)?$/,
// set automatically attributes for some tags
autoAttr = {
IMG:'src',
INPUT:'value'
},
// check if the argument is an array - thanks salty-horse (Ori Avtalion)
isArray = Array.isArray ?
function(o) {
return Array.isArray(o);
} :
function(o) {
return Object.prototype.toString.call(o) === "[object Array]";
};
/* * * * * * * * * * * * * * * * * * * * * * * * * *
core functions
* * * * * * * * * * * * * * * * * * * * * * * * * */
// error utility
function error(e){
if(typeof console !== 'undefined'){
console.log(e);
debugger;
}
throw('pure error: ' + e);
}
//return a new instance of plugins
function getPlugins(){
var plugins = $p.plugins,
f = function(){};
f.prototype = plugins;
// do not overwrite functions if external definition
f.prototype.compile    = plugins.compile || compile;
f.prototype.render     = plugins.render || render;
f.prototype.autoRender = plugins.autoRender || autoRender;
f.prototype.find       = plugins.find || find;
// give the compiler and the error handling to the plugin context
f.prototype._compiler  = compiler;
f.prototype._error     = error;
return new f();
}
// returns the outer HTML of a node
function outerHTML(node){
// if IE, Chrome take the internal method otherwise build one
return node.outerHTML || (
function(n){
var div = document.createElement('div'), h;
div.appendChild( n.cloneNode(true) );
h = div.innerHTML;
div = null;
return h;
})(node);
}
// returns the string generator function
function wrapquote(qfn, f){
return function(ctxt){
return qfn('' + f.call(ctxt.item || ctxt.context, ctxt));
};
}
// default find using querySelector when available on the browser
function find(n, sel){
if(typeof n === 'string'){
sel = n;
n = false;
}
if(typeof document.querySelectorAll !== 'undefined'){
return (n||document).querySelectorAll( sel );
}else{
return error('You can test PURE standalone with: iPhone, FF3.5+, Safari4+ and IE8+\n\nTo run PURE on your browser, you need a JS library/framework with a CSS selector engine');
}
}
// create a function that concatenates constant string
// sections (given in parts) and the results of called
// functions to fill in the gaps between parts (fns).
// fns[n] fills in the gap between parts[n-1] and parts[n];
// fns[0] is unused.
// this is the inner template evaluation loop.
function concatenator(parts, fns){
return function(ctxt){
var strs = [ parts[ 0 ] ],
n = parts.length,
fnVal, pVal, attLine, pos;
for(var i = 1; i < n; i++){
fnVal = fns[i].call( this, ctxt );
pVal = parts[i];
// if the value is empty and attribute, remove it
if(fnVal === ''){
attLine = strs[ strs.length - 1 ];
if( ( pos = attLine.search( /[^\s]+=\"?$/ ) ) > -1){
strs[ strs.length - 1 ] = attLine.substring( 0, pos );
pVal = pVal.substr( 1 );
}
}
strs[ strs.length ] = fnVal;
strs[ strs.length ] = pVal;
}
return strs.join('');
};
}
// parse and check the loop directive
function parseloopspec(p){
var m = p.match( /^(\w+)\s*<-\s*(\S+)?$/ );
if(m === null){
error('bad loop spec: "' + p + '"');
}
if(m[1] === 'item'){
error('"item<-..." is a reserved word for the current running iteration.\n\nPlease choose another name for your loop.');
}
if( !m[2] || (m[2] && (/context/i).test(m[2]))){ //undefined or space(IE)
m[2] = function(ctxt){return ctxt.context;};
}
return {name: m[1], sel: m[2]};
}
// parse a data selector and return a function that
// can traverse the data accordingly, given a context.
function dataselectfn(sel){
if(typeof(sel) === 'function'){
return sel;
}
//check for a valid js variable name with hyphen(for properties only), $, _ and :
var m = sel.match(/^[a-zA-Z\$_\@][\w\$:-]*(\.[\w\$:-]*[^\.])*$/);
if(m === null){
var found = false, s = sel, parts = [], pfns = [], i = 0, retStr;
// check if literal
if(/\'|\"/.test( s.charAt(0) )){
if(/\'|\"/.test( s.charAt(s.length-1) )){
retStr = s.substring(1, s.length-1);
return function(){ return retStr; };
}
}else{
// check if literal + #{var}
while((m = s.match(/#\{([^{}]+)\}/)) !== null){
found = true;
parts[i++] = s.slice(0, m.index);
pfns[i] = dataselectfn(m[1]);
s = s.slice(m.index + m[0].length, s.length);
}
}
if(!found){
return function(){ return sel; };
}
parts[i] = s;
return concatenator(parts, pfns);
}
m = sel.split('.');
return function(ctxt){
var data = ctxt.context || ctxt,
v = ctxt[m[0]],
i = 0;
if(v && v.item){
i += 1;
if(m[i] === 'pos'){
//allow pos to be kept by string. Tx to Adam Freidin
return v.pos;
}else{
data = v.item;
}
}
var n = m.length;
for(; i < n; i++){
if(!data){break;}
data = data[m[i]];
}
return (!data && data !== 0) ? '':data;
};
}
// wrap in an object the target node/attr and their properties
function gettarget(dom, sel, isloop){
var osel, prepend, selector, attr, append, target = [];
if( typeof sel === 'string' ){
osel = sel;
var m = sel.match(selRx);
if( !m ){
error( 'bad selector syntax: ' + sel );
}
prepend = m[1];
selector = m[2];
attr = m[3];
append = m[4];
if(selector === '.' || ( !selector && attr ) ){
target[0] = dom;
}else{
target = plugins.find(dom, selector);
}
if(!target || target.length === 0){
return error('The node "' + sel + '" was not found in the template:\n' + outerHTML(dom).replace(/\t/g,'  '));
}
}else{
// autoRender node
prepend = sel.prepend;
attr = sel.attr;
append = sel.append;
target = [dom];
}
if( prepend || append ){
if( prepend && append ){
error('append/prepend cannot take place at the same time');
}else if( isloop ){
error('no append/prepend/replace modifiers allowed for loop target');
}else if( append && isloop ){
error('cannot append with loop (sel: ' + osel + ')');
}
}
var setstr, getstr, quotefn, isStyle, isClass, attName, setfn;
if(attr){
isStyle = (/^style$/i).test(attr);
isClass = (/^class$/i).test(attr);
attName = isClass ? 'className' : attr;
setstr = function(node, s) {
node.setAttribute(attPfx + attr, s);
if (attName in node && !isStyle) {
try{node[attName] = '';}catch(e){} //FF4 gives an error sometimes
}
if (node.nodeType === 1) {
node.removeAttribute(attr);
isClass && node.removeAttribute(attName);
}
};
if (isStyle || isClass) {//IE no quotes special care
if(isStyle){
getstr = function(n){ return n.style.cssText; };
}else{
getstr = function(n){ return n.className;	};
}
}else {
getstr = function(n){ return n.getAttribute(attr); };
}
quotefn = function(s){ return s.replace(/\"/g, '&quot;'); };
if(prepend){
setfn = function(node, s){ setstr( node, s + getstr( node )); };
}else if(append){
setfn = function(node, s){ setstr( node, getstr( node ) + s); };
}else{
setfn = function(node, s){ setstr( node, s ); };
}
}else{
if (isloop) {
setfn = function(node, s) {
var pn = node.parentNode;
if (pn) {
//replace node with s
pn.insertBefore(document.createTextNode(s), node.nextSibling);
pn.removeChild(node);
}
};
} else {
if (prepend) {
setfn = function(node, s) { node.insertBefore(document.createTextNode(s), node.firstChild);	};
} else if (append) {
setfn = function(node, s) { node.appendChild(document.createTextNode(s));};
} else {
setfn = function(node, s) {
while (node.firstChild) { node.removeChild(node.firstChild); }
node.appendChild(document.createTextNode(s));
};
}
}
quotefn = function(s) { return s; };
}
return { attr: attr, nodes: target, set: setfn, sel: osel, quotefn: quotefn };
}
function setsig(target, n){
var sig = Sig + n + ':';
for(var i = 0; i < target.nodes.length; i++){
// could check for overlapping targets here.
target.set( target.nodes[i], sig );
}
}
// read de loop data, and pass it to the inner rendering function
function loopfn(name, dselect, inner, sorter, filter){
return function(ctxt){
var a = dselect(ctxt),
old = ctxt[name],
temp = { items : a },
filtered = 0,
length,
strs = [],
buildArg = function(idx, temp, ftr, len){
//keep the current loop. Tx to Adam Freidin
var save_pos = ctxt.pos,
save_item = ctxt.item,
save_items = ctxt.items;
ctxt.pos = temp.pos = idx;
ctxt.item = temp.item = a[ idx ];
ctxt.items = a;
//if array, set a length property - filtered items
typeof len !== 'undefined' &&  (ctxt.length = len);
//if filter directive
if(typeof ftr === 'function' && ftr.call(ctxt.item, ctxt) === false){
filtered++;
return;
}
strs.push( inner.call(ctxt.item, ctxt ) );
//restore the current loop
ctxt.pos = save_pos;
ctxt.item = save_item;
ctxt.items = save_items;
};
ctxt[name] = temp;
if( isArray(a) ){
length = a.length || 0;
// if sort directive
if(typeof sorter === 'function'){
a.sort(sorter);
}
//loop on array
for(var i = 0, ii = length; i < ii; i++){
buildArg(i, temp, filter, length - filtered);
}
}else{
if(a && typeof sorter !== 'undefined'){
error('sort is only available on arrays, not objects');
}
//loop on collections
for(var prop in a){
a.hasOwnProperty( prop ) && buildArg(prop, temp, filter);
}
}
typeof old !== 'undefined' ? ctxt[name] = old : delete ctxt[name];
return strs.join('');
};
}
// generate the template for a loop node
function loopgen(dom, sel, loop, fns){
var already = false, ls, sorter, filter, prop;
for(prop in loop){
if(loop.hasOwnProperty(prop)){
if(prop === 'sort'){
sorter = loop.sort;
continue;
}else if(prop === 'filter'){
filter = loop.filter;
continue;
}
if(already){
error('cannot have more than one loop on a target');
}
ls = prop;
already = true;
}
}
if(!ls){
error('Error in the selector: ' + sel + '\nA directive action must be a string, a function or a loop(<-)');
}
var dsel = loop[ls];
// if it's a simple data selector then we default to contents, not replacement.
if(typeof(dsel) === 'string' || typeof(dsel) === 'function'){
loop = {};
loop[ls] = {root: dsel};
return loopgen(dom, sel, loop, fns);
}
var spec = parseloopspec(ls),
itersel = dataselectfn(spec.sel),
target = gettarget(dom, sel, true),
nodes = target.nodes;
for(i = 0; i < nodes.length; i++){
var node = nodes[i],
inner = compiler(node, dsel);
fns[fns.length] = wrapquote(target.quotefn, loopfn(spec.name, itersel, inner, sorter, filter));
target.nodes = [node];		// N.B. side effect on target.
setsig(target, fns.length - 1);
}
return target;
}
function getAutoNodes(n, data){
var ns = n.getElementsByTagName('*'),
an = [],
openLoops = {a:[],l:{}},
cspec,
isNodeValue,
i, ii, j, jj, ni, cs, cj;
//for each node found in the template
for(i = -1, ii = ns.length; i < ii; i++){
ni = i > -1 ?ns[i]:n;
if(ni.nodeType === 1 && ni.className !== ''){
//when a className is found
cs = ni.className.split(' ');
// for each className
for(j = 0, jj=cs.length;j<jj;j++){
cj = cs[j];
// check if it is related to a context property
cspec = checkClass(cj, ni.tagName);
// if so, store the node, plus the type of data
if(cspec !== false){
isNodeValue = (/nodevalue/i).test(cspec.attr);
if(cspec.sel.indexOf('@') > -1 || isNodeValue){
ni.className = ni.className.replace('@'+cspec.attr, '');
if(isNodeValue){
cspec.attr = false;
}
}
an.push({n:ni, cspec:cspec});
}
}
}
}
function checkClass(c, tagName){
// read the class
var ca = c.match(selRx),
attr = ca[3] || autoAttr[tagName],
cspec = {prepend:!!ca[1], prop:ca[2], attr:attr, append:!!ca[4], sel:c},
i, ii, loopi, loopil, val;
// check in existing open loops
for(i = openLoops.a.length-1; i >= 0; i--){
loopi = openLoops.a[i];
loopil = loopi.l[0];
val = loopil && loopil[cspec.prop];
if(typeof val !== 'undefined'){
cspec.prop = loopi.p + '.' + cspec.prop;
if(openLoops.l[cspec.prop] === true){
val = val[0];
}
break;
}
}
// not found check first level of data
if(typeof val === 'undefined'){
val = dataselectfn(cspec.prop)(isArray(data) ? data[0] : data);
// nothing found return
if(val === ''){
return false;
}
}
// set the spec for autoNode
if(isArray(val)){
openLoops.a.push( {l:val, p:cspec.prop} );
openLoops.l[cspec.prop] = true;
cspec.t = 'loop';
}else{
cspec.t = 'str';
}
return cspec;
}
return an;
}
// returns a function that, given a context argument,
// will render the template defined by dom and directive.
function compiler(dom, directive, data, ans){
var fns = [], j, jj, cspec, n, target, nodes, itersel, node, inner, dsel, sels, sel, sl, i, h, parts,  pfns = [], p;
// autoRendering nodes parsing -> auto-nodes
ans = ans || data && getAutoNodes(dom, data);
if(data){
// for each auto-nodes
while(ans.length > 0){
cspec = ans[0].cspec;
n = ans[0].n;
ans.splice(0, 1);
if(cspec.t === 'str'){
// if the target is a value
target = gettarget(n, cspec, false);
setsig(target, fns.length);
fns[fns.length] = wrapquote(target.quotefn, dataselectfn(cspec.prop));
}else{
// if the target is a loop
itersel = dataselectfn(cspec.sel);
target = gettarget(n, cspec, true);
nodes = target.nodes;
for(j = 0, jj = nodes.length; j < jj; j++){
node = nodes[j];
inner = compiler(node, false, data, ans);
fns[fns.length] = wrapquote(target.quotefn, loopfn(cspec.sel, itersel, inner));
target.nodes = [node];
setsig(target, fns.length - 1);
}
}
}
}
// read directives
for(sel in directive){
if(directive.hasOwnProperty(sel)){
i = 0;
dsel = directive[sel];
sels = sel.split(/\s*,\s*/); //allow selector separation by quotes
sl = sels.length;
do{
if(typeof(dsel) === 'function' || typeof(dsel) === 'string'){
// set the value for the node/attr
sel = sels[i];
target = gettarget(dom, sel, false);
setsig(target, fns.length);
fns[fns.length] = wrapquote(target.quotefn, dataselectfn(dsel));
}else{
// loop on node
loopgen(dom, sel, dsel, fns);
}
}while(++i < sl);
}
}
// convert node to a string
h = outerHTML(dom);
// IE adds an unremovable "selected, value" attribute
// hard replace while waiting for a better solution
h = h.replace(/<([^>]+)\s(value\=""|selected)\s?([^>]*)>/ig, "<$1 $3>");
// remove attribute prefix
h = h.split(attPfx).join('');
// slice the html string at "Sig"
parts = h.split( Sig );
// for each slice add the return string of
for(i = 1; i < parts.length; i++){
p = parts[i];
// part is of the form "fn-number:..." as placed there by setsig.
pfns[i] = fns[ parseInt(p, 10) ];
parts[i] = p.substring( p.indexOf(':') + 1 );
}
return concatenator(parts, pfns);
}
// compile the template with directive
// if a context is passed, the autoRendering is triggered automatically
// return a function waiting the data as argument
function compile(directive, ctxt, template){
var rfn = compiler( ( template || this[0] ).cloneNode(true), directive, ctxt);
return function(context){
return rfn({context:context});
};
}
//compile with the directive as argument
// run the template function on the context argument
// return an HTML string
// should replace the template and return this
function render(ctxt, directive){
var fn = typeof directive === 'function' && directive, i = 0, ii = this.length;
for(; i < ii; i++){
this[i] = replaceWith( this[i], (fn || plugins.compile( directive, false, this[i] ))( ctxt, false ));
}
context = null;
return this;
}
// compile the template with autoRender
// run the template function on the context argument
// return an HTML string
function autoRender(ctxt, directive){
var fn = plugins.compile( directive, ctxt, this[0] );
for(var i = 0, ii = this.length; i < ii; i++){
this[i] = replaceWith( this[i], fn( ctxt, false));
}
context = null;
return this;
}
function replaceWith(elm, html) {
var ne,
ep = elm.parentNode,
depth = 0;
if(!ep){ //if no parents
ep = document.createElement('DIV');
ep.appendChild(elm);
}
switch (elm.tagName) {
case 'TBODY': case 'THEAD': case 'TFOOT':
html = '<TABLE>' + html + '</TABLE>';
depth = 1;
break;
case 'TR':
html = '<TABLE><TBODY>' + html + '</TBODY></TABLE>';
depth = 2;
break;
case 'TD': case 'TH':
html = '<TABLE><TBODY><TR>' + html + '</TR></TBODY></TABLE>';
depth = 3;
break;
}
tmp = document.createElement('SPAN');
tmp.style.display = 'none';
document.body.appendChild(tmp);
tmp.innerHTML = html;
ne = tmp.firstChild;
while (depth--) {
ne = ne.firstChild;
}
ep.insertBefore(ne, elm);
ep.removeChild(elm);
document.body.removeChild(tmp);
elm = ne;
ne = ep = null;
return elm;
}
return plugins;
};
$p.plugins = {};
$p.libs = {
dojo:function(){
if(typeof document.querySelector === 'undefined'){
$p.plugins.find = function(n, sel){
return dojo.query(sel, n);
};
}
},
domassistant:function(){
if(typeof document.querySelector === 'undefined'){
$p.plugins.find = function(n, sel){
return $(n).cssSelect(sel);
};
}
DOMAssistant.attach({
publicMethods : [ 'compile', 'render', 'autoRender'],
compile:function(directive, ctxt){
return $p([this]).compile(directive, ctxt);
},
render:function(ctxt, directive){
return $( $p([this]).render(ctxt, directive) )[0];
},
autoRender:function(ctxt, directive){
return $( $p([this]).autoRender(ctxt, directive) )[0];
}
});
},
jquery:function(){
if(typeof document.querySelector === 'undefined'){
$p.plugins.find = function(n, sel){
return jQuery(n).find(sel);
};
}
jQuery.fn.extend({
directives:function(directive){
this._pure_d = directive; return this;
},
compile:function(directive, ctxt){
return $p(this).compile(this._pure_d || directive, ctxt);
},
render:function(ctxt, directive){
return jQuery( $p( this ).render( ctxt, this._pure_d || directive ) );
},
autoRender:function(ctxt, directive){
return jQuery( $p( this ).autoRender( ctxt, this._pure_d || directive ) );
}
});
},
mootools:function(){
if(typeof document.querySelector === 'undefined'){
$p.plugins.find = function(n, sel){
return $(n).getElements(sel);
};
}
Element.implement({
compile:function(directive, ctxt){
return $p(this).compile(directive, ctxt);
},
render:function(ctxt, directive){
return $p([this]).render(ctxt, directive);
},
autoRender:function(ctxt, directive){
return $p([this]).autoRender(ctxt, directive);
}
});
},
prototype:function(){
if(typeof document.querySelector === 'undefined'){
$p.plugins.find = function(n, sel){
n = n === document ? n.body : n;
return typeof n === 'string' ? $$(n) : $(n).select(sel);
};
}
Element.addMethods({
compile:function(element, directive, ctxt){
return $p([element]).compile(directive, ctxt);
},
render:function(element, ctxt, directive){
return $p([element]).render(ctxt, directive);
},
autoRender:function(element, ctxt, directive){
return $p([element]).autoRender(ctxt, directive);
}
});
},
sizzle:function(){
if(typeof document.querySelector === 'undefined'){
$p.plugins.find = function(n, sel){
return Sizzle(sel, n);
};
}
},
sly:function(){
if(typeof document.querySelector === 'undefined'){
$p.plugins.find = function(n, sel){
return Sly(sel, n);
};
}
},
yui:function(){ //Thanks to https://github.com/soljin
if(typeof document.querySelector === 'undefined'){
YUI().use("node",function(Y){
$p.plugins.find = function(n, sel){
return Y.NodeList.getDOMNodes(Y.one(n).all(sel));
};
});
}
YUI.add("pure-yui",function(Y){
Y.Node.prototype.directives = function(directive){
this._pure_d = directive; return this;
};
Y.Node.prototype.compile = function(directive, ctxt){
return $p([this._node]).compile(this._pure_d || directive, ctxt);
};
Y.Node.prototype.render = function(ctxt, directive){
return Y.one($p([this._node]).render(ctxt, this._pure_d || directive));
};
Y.Node.prototype.autoRender = function(ctxt, directive){
return Y.one($p([this._node]).autoRender(ctxt, this._pure_d || directive));
};
},"0.1",{requires:["node"]});
}
};
// get lib specifics if available
(function(){
var libkey =
typeof dojo         !== 'undefined' && 'dojo' ||
typeof DOMAssistant !== 'undefined' && 'domassistant' ||
typeof jQuery       !== 'undefined' && 'jquery' ||
typeof MooTools     !== 'undefined' && 'mootools' ||
typeof Prototype    !== 'undefined' && 'prototype' ||
typeof Sizzle       !== 'undefined' && 'sizzle' ||
typeof Sly          !== 'undefined' && 'sly' ||
typeof YUI          !== 'undefined' && 'yui';
libkey && $p.libs[libkey]();
//for node.js
if(typeof exports !== 'undefined'){
exports.$p = $p;
}
})();/**
* This class is used for the price estimation functionnalities
*
* @event Event heightModified this event is fired when the price estimation is displayed
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param Element input the input div
* @param Element output the output div
**/
var School = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(oSchoolDTO,  oBooking) {
this._booking = this._parent = oBooking;
this._schoolDTO = oSchoolDTO;
this._customValues = new Hash();
// create the courses part
this.courses = new SchoolCourses(this._schoolDTO.schoolFixedDatesCourses, this._schoolDTO.schoolVariableDatesCourses, this._schoolDTO.schoolOptionalCourses, this);
this.accommodations = new SchoolAccommodations(this._schoolDTO.schoolAccommodations, this);
this.transfers = new SchoolTransfers(this._schoolDTO.schoolTransfers, this);
this.courses.addEvent('coursesUpdated', function() {
this.accommodations.refreshAccommodations();
this.refreshPrice();
}.bind(this));
this.courses.addEvent('heightModified', function() {
this.fireEvent('heightModified');
}.bind(this));
this.accommodations.addEvent('accommodationsUpdated', function() {
this.transfers.refreshTransfers();
this.checkMandatoryCourseAccommodations();
this.refreshPrice();
}.bind(this));
this.accommodations.addEvent('heightModified', function() {
this.fireEvent('heightModified');
}.bind(this));
this.transfers.addEvent('transfersUpdated', function() {
this.refreshPrice();
this.fireEvent('heightModified');
}.bind(this));
return '';
this._listContainer = new Element('div');
//        this._classNameBase = this._booking.getClassNameBase();
this._schoolPriceCalculatorDTO = oSchoolDTO;
this._translateDTO = {};
this.transfers = new SchoolTransfers(this._schoolPriceCalculatorDTO.schoolTransfers, this._translateDTO.transfers, this);
this.courses.addEvent('refreshPrice', function() {
this.transfers.refreshTransfers();
this.fireEvent('refreshPrice');
this.fireEvent('schoolUpdated');
}.bind(this));
this.courses.addEvent('heightModified', function() {
this.fireEvent('heightModified');
}.bind(this));
this.accommodations.addEvent('refreshAccommodations', function() {
this.transfers.refreshTransfers();
this.fireEvent('refreshPrice');
}.bind(this));
this.transfers.addEvent('refreshPrice', function() {
this.fireEvent('refreshPrice');
}.bind(this));
this.addEvent('refreshAccommodations', function() {
this.accommodations.refreshAccommodations('lists');
this.transfers.refreshTransfers();
this.checkMandatoryCourseAccommodations();
});
},
/**
* Return the courses manager
*/
getCoursesManager : function () {
return this.courses;
},
/**
* Return the courses manager
*/
getTransfersManager : function () {
return this.transfers;
},
/**
* Return the courses manager
*/
getAccommodationsManager : function () {
return this.accommodations;
},
refreshPrice : function ( ){
clearTimeout(this._refreshPrice);
this._refreshPrice = this.fireEvent.delay(300, this, 'refreshPrice')
},
getBooking : function(){
return this._booking;
},
/**
* Returns an object containing the informations for the calculation price
*
* @return Object The informations needed to calculate the price
*/
getDTO : function() {
var priceEstimationDTO = new Object();
priceEstimationDTO.variableDatesCourses = this.courses.getVariableCoursesDTO();
priceEstimationDTO.fixedDatesCourses = this.courses.getFixedCoursesDTO();
priceEstimationDTO.optionalCourses = this.courses.getOptionalCoursesDTO();
priceEstimationDTO.accommodations = this.accommodations.getDTO();
priceEstimationDTO.transfers = this.transfers.getDTO();
return priceEstimationDTO;
},
/**
* populate the price calculator with the DTO given in parameter
*
* @param DTO toPopulateDTO the DTO to populate from
*/
populateFromDTO : function(toPopulateDTO) {
this.courses.populateCoursesFromDTO(toPopulateDTO.fixedDatesCourses, toPopulateDTO.variableDatesCourses, toPopulateDTO.optionalCourses);
this.accommodations.populateFromDTO(toPopulateDTO.accommodations);
this.transfers.populateFromDTO(toPopulateDTO.transfers);
this.courses.fireEvent("refreshCourses");
this.courses.onCourseUpdated();
},
/**
* Check the mandatory accommodations
*
*
*/
checkMandatoryCourseAccommodations : function(){
var validCourses = this.courses.getValidAddedCourses();
if(validCourses.length){
var mandatoryAccommodations = this.courses.getMandatoryAccommodations();
if(mandatoryAccommodations.length){
var courseToCheck = new Array();
// find which course have a problem
var accommodations = this.accommodations.getCompletedAccommodations();
for (var i = 0; i < validCourses.length; i++) {
if (validCourses[i].getCourseDetails().type != 'optional' && validCourses[i].getCourseDetails().accommodationMandatory == true) {
var courseDetails = validCourses[i].getCourseDetails();
var isOk = false;
for(var j = 0; j < accommodations.length; j++){
if(accommodations[j].getIsAttachedToCourse() && accommodations[j].getAttachedToCourse().courseId == courseDetails.id){
isOk = true;
}
}
if(!isOk){
courseToCheck.push(validCourses[i]);
}
}
}
/*if(courseToCheck.length){
if(courseToCheck[0].getCourseDetails().type == "variableDate"){
var durations = courseToCheck[0].getCourseValidDurations();
for(var i = 0; i < durations.length; i++){
if(parseInt(durations[i]) > parseInt(courseToCheck[0].getCourseDuration())){
courseToCheck[0].setCourseDuration(durations[i]);
//                                durations[i].getHtml().fireEvent('click');
break;
}
}
if(i == durations.length){
courseToCheck[0].deleteCourse();
}
}else{
courseToCheck[0].deleteCourse();
}
}*/
}
}
},
/**
* Returns the accommodation base start date
*
* @return Date The accommodations start date
*/
getAccommodationStartDate : function() {
return this.courses.getAccommodationStartDate();
},
/**
* Returns the end date of the accommodation
*
* @return Date The end date of the accommodation
*/
getAccommodationEndDate : function() {
return this.courses.getAccommodationEndDate();
},
/**
* Return the max accommodation end date for a course
*
* @param integer courseId The Course ID
* @param Date startDate the course start date
* @param SchoolAccommodationObject accommodation The accommodation
*
* @return Date The course accommodation end date
*/
getCourseAccommodationEndDate : function(courseId, startDate, accommodation) {
var course = this.courses.getValidAddedCourses().filter(function(item) {
return item.getCourseDetails().id == courseId && item.getCourseStartDate().getTime() == startDate.getTime();
});
if (course.length) {
var endDate = (new Date()).fromIso(course[0].getDTO().endDate);
var endDateDay = endDate.getDay();
if (endDateDay == 0) endDateDay = 7;
while (endDateDay != accommodation.getAccommodationOptionDetails().accommodationEndDay) {
endDate.setDate(endDate.getDate() + 1);
endDateDay = endDate.getDay();
if (endDateDay == 0) endDateDay = 7;
}
return endDate;
}
return false;
},
/**
* return true if the course is a fixed one type
*
* @param String coursePosition The position of the course -1 for the last one
*/
getIsFixedDateCourse : function(coursePosition) {
var courses = this.courses.getCompletedCourses(true);
if (courses.length) {
switch (coursePosition) {
case -1 :
return courses.getLast().getCourseType() == 'fixed';
break;
default :
if (courses[coursePosition]) {
return courses[coursePosition].getCourseType() == 'fixed';
} else {
return null;
}
break;
}
}
},
/**
*  Return the courses end date
*
* @return Date the courses end date
*
*/
getCoursesEndDate : function() {
var completedCourses = this.courses.getCompletedCourses();
if (completedCourses.length) {
var endDate = completedCourses[0].getDTO().endDate;
for (var i = 0; i < completedCourses.length; i++) {
if (completedCourses[i].getDTO().endDate > endDate) {
endDate = completedCourses[0].getDTO().endDate;
}
}
return endDate;
}
return null;
},
/**
* This method returns the number of completed courses
*
* @return Number The number of courses completed
*
*/
getCompletedCourses : function() {
return this.courses.getCompletedCourses();
},
/**
*  Returns the available accommodations
*
* @return Array of integer The Accommodation IDs
*/
getAvailableAccommodations : function() {
return this.courses.getAvailableAccommodations();
},
/**
* This method returns the completed accommodations
*
* @return SchoolAccommodationObject[] The completed accommodations
*
*/
getCompletedAccommodations : function() {
return this.accommodations.getCompletedAccommodations();
},
/**
* This method returns the number of completed transfers
*
* @return Number The number of transfers completed
*
*/
getCompletedTransfers : function() {
return this.transfers.getCompletedTransfers();
},
/**
* Returns the number of participant
*
* @return Number The number of participants
*
*/
getParticipants : function() {
return this._booking.getParticipants();
},
/**
* Return an array containing the mandatory accommodations with their start and end date
*
* @return Array of MandatoryAccommodationsDTO
*/
getMandatoryAccommodations : function() {
return this.courses.getMandatoryAccommodations();
},
/**
* Return true if the transfer is mandatory
*
* @return boolean true if the transfers are mandatory
*/
getIsTransferMandatory : function() {
return this.courses.getIsTransferMandatory();
},
/**
* Return an array containing the available transfers
*
* @return Array of transfers ID
*/
getAvailableTransfers : function() {
return this.accommodations.getAvailableTransfers();
},
/**
* Returns the  parent
*
* @return Object the parent
*
*/
getParent : function() {
return this._parent;
}
});/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2011 Pyrameed
*
* @event Event deleteAccommodation This event is fired when a course is deleted
*
* @param AccommodationListItemDTO accommodationDetails The object containing the accommodation informations
* @param AccommodationListItemDTO accommodationOptionDetails The object containing the accommodation options informations
* @param Object translateDTO The object containing all the translations for this class
* @param SchoolCourses parent The parent
*
**/
var SchoolAccommodationObject = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(accommodationDetails, accommodationOptionDetails, oSchoolAccommodationsContainer) {
this._created = false;
this._translateDTO = {};
this._accommodationDetails = accommodationDetails;
this._accommodationOptionDetails = accommodationOptionDetails;
this._schoolAccommodations = oSchoolAccommodationsContainer;
this._booking = this._schoolAccommodations.getSchool().getBooking();
this._booking.addEvent('descriptionsUpdated', function(){
this.updateDescriptions();
}.bind(this));
this._dateFormat = this._booking.getDateFormat();
this._monthsTranslations = this._booking.getMonthTranslations();
this._validDurations = new Array();
this._additionalNightsBefore = 0;
this._additionalNightsAfter = 0;
this._accommodationStartDate = null;
this._accommodationEndDate = null;
this._accommodationDuration = null;
this._accommodationAttachedToCourse = null;
this._validStartDates = new Array();
this._calculatedStartDate = new Date();
this._calculatedEndDate = new Date();
this.refreshValidStartDates();
// check if it's the first accommodation :
if (this._schoolAccommodations.getAccommodations().length == 0 /**&& !attachedAccommodation**/) {
// if it is the first to be added, set the right date
// find the earliest start date
var startDate;
if (this._validStartDates.length) {
startDate = this._validStartDates[0];
}
if (startDate) {
this.setStartDate(startDate);
// find the longest duration
var duration;
for (var i = this._validDurations.length - 1; i >= 0; i--) {
duration = parseInt( this._validDurations[i]);
break;
}
if (duration) {
this.setDuration(duration);
}
}
}
},
/**
* Set the start date
*
*
* @param startDate
* @param additionalNightsBefore
*/
setStartDate : function(startDate, additionalNightsBefore) {
if(typeof(additionalNightsBefore)=='undefined'){
additionalNightsBefore = this._additionalNightsBefore;
}
if ((!this._accommodationStartDate || this._accommodationStartDate.toIso() != startDate.toIso()) || additionalNightsBefore != this._additionalNightsBefore) {
this._accommodationStartDate = startDate;
this._additionalNightsBefore = additionalNightsBefore;
this.updateStartDate();
this._onStartDateChange();
}
},
/**
* Set the duration
*
* @param integer the accommodation duration
*/
setDuration : function(duration){
if(parseInt(this._accommodationDuration) != parseInt(duration)){
// check if the duration is valid
if(this.getValidDurations().contains(parseInt(duration))){
this._accommodationDuration = duration;
this._onDurationChange();
}else {
this.deleteAccommodation();
}
}
},
/**
* Set the end date
*
* @param endDate
* @param additionalNightsAfter
*/
setEndDate : function(endDate, additionalNightsAfter) {
if (typeof(additionalNightsAfter) == 'undefined') {
additionalNightsAfter = this._additionalNightsAfter;
}
if ((!this._accommodationEndDate || this._accommodationEndDate.getTime() != endDate.getTime()) || additionalNightsAfter != this._additionalNightsAfter) {
this._accommodationEndDate = endDate;
this._additionalNightsAfter = additionalNightsAfter;
this.updateEndDate();
this._onEndDateChange();
}
},
/**
* When the duration change
*/
_onDurationChange : function () {
// if there is a start date, we need to update the end date
if(this._accommodationStartDate) {
this._accommodationEndDate = this.getCalculatedEndDate();
}
this.updateEndDate();
this.fireEvent('accommodationUpdated');
},
/**
* When the start date change
*/
_onStartDateChange : function () {
this.refreshValidDurations();
// if there is a start date, we need to update the end date
if(this._accommodationEndDate) {
var duration = this._schoolAccommodations.getDurationInWeeks(this._accommodationEndDate, this._accommodationStartDate);
if (duration < this.getAccommodationOptionDetails().minimumDurationInWeeks){
this.setDuration(this.getAccommodationOptionDetails().minimumDurationInWeeks);
}else {
this._accommodationDuration = duration;
}
}
this.fireEvent('accommodationUpdated');
},
/**
* When the end date change
*/
_onEndDateChange : function () {
this.refreshValidDurations();
// if there is a start date, we need to update the end date
if (this._accommodationStartDate) {
this.updateEndDate();
var duration = this._schoolAccommodations.getDurationInWeeks(this._accommodationEndDate, this._accommodationStartDate);
if (duration < this.getAccommodationOptionDetails().minimumDurationInWeeks) {
this.setDuration(this.getAccommodationOptionDetails().minimumDurationInWeeks);
} else {
this._accommodationDuration = duration;
}
}
this.fireEvent('accommodationUpdated');
},
/**
* Return the accommodation details DTO
*
* @return AccommodationDetailsDTO The Accommodation details
*/
getAccommodationDetails : function() {
return this._accommodationDetails;
},
/**
* Return the accommodation details DTO
*
* @return AccommodationOptionDetailsDTO The Accommodation option details
*/
getAccommodationOptionDetails : function() {
return this._accommodationOptionDetails;
},
/**
* Returns an object containing the informations for the calculation price
*
* @return Object The informations needed to calculate the price
*/
getDTO : function() {
var accommodationDTO = new Object();
accommodationDTO.id = this._accommodationOptionDetails.id;
accommodationDTO.startDate = this._accommodationStartDate ? this._accommodationStartDate.toIso() : null;
accommodationDTO.endDate = this._accommodationEndDate ? this._accommodationEndDate.toIso() : null;
accommodationDTO.additionalNightsBefore = this._additionalNightsBefore;
accommodationDTO.additionalNightsAfter = this._additionalNightsAfter;
return accommodationDTO;
},
/**
* Populate the accommodation from the given DTO
*
*
* @param AccommodationDTO toPopulateDTO The DTO to populate from
*/
populateFromDTO : function(toPopulateDTO) {
var startDate = toPopulateDTO.startDate;
var endDate = toPopulateDTO.endDate;
if(!(startDate instanceof Date)){
startDate = (new Date()).fromIso(startDate);
}
if(!(endDate instanceof Date)){
endDate = (new Date()).fromIso(endDate);
}
this.setStartDate(startDate , toPopulateDTO.additionalNightsBefore ? parseInt(toPopulateDTO.additionalNightsBefore) : 0);
this.setEndDate(endDate , toPopulateDTO.additionalNightsAfter ? parseInt(toPopulateDTO.additionalNightsAfter) : 0);
this.fireEvent('accommodationUpdated');
},
/**
* Return the number of days available before
*/
getAvailableDaysBefore : function() {
/* if(this._accommodationOptionDetails.additionalNightsOnBounds && !this.getAttachedToCourse()){
var accommodations = this._schoolAccommodations.getAccommodations();
if(accommodations.length > 1 && accommodations.indexOf(this) != 0){
return 0;
}
}*/
return this._accommodationOptionDetails.additionalNightsBefore;
},
/**
* return the numbers of days available after
*/
getAvailableDaysAfter : function() {
/*if(this._accommodationOptionDetails.additionalNightsOnBounds){
var accommodations = this._schoolAccommodations.getAccommodations();
if(accommodations.length > 1 && accommodations.getLast() != this){
return 0;
}
}*/
return this._accommodationOptionDetails.additionalNightsAfter;
},
/**
* set The accommodations start date for the given accommodation
*
* @param Date startDate The Start date
*/
setAttachedAccommodationStartDate : function(startDate) {
this._accommodationStartDate = startDate;
this.updateStartDate();
},
/**
* set The accommodations duration for the given accommodation
*
* @param integer duration The duration
*/
setAttachedAccommodationDuration : function(duration) {
this._accommodationDuration = duration;
this._accommodationEndDate = this.getCalculatedEndDate();
this.updateEndDate();
},
/**
* Update the descriptions
*/
updateDescriptions : function(){
if (this._moreInfo && this._booking.getDescription && this._booking.getDescription('accommodation', this._accommodationDetails.accommodationType) && this._booking.getDescription('accommodation', this._accommodationDetails.accommodationType) != '') {
this._moreInfo.setAttribute('title', this._accommodationDetails.name + ' :: ' + this._booking.getDescription('accommodation', this._accommodationDetails.accommodationType));
this._moreInfo.setStyle('display','');
if(!this._descriptionToolTip ){
this._descriptionToolTip = new SynergeeTips(this._moreInfo, {'className':'Booking-Tool-Tip Tool-tip', 'hideDelay':1000, 'fixed':true, 'closeButton':true, 'maxHeight':280});
}else {
this._descriptionToolTip.build(this._moreInfo);
}
}else {
if(this._moreInfo)
this._moreInfo.setStyle('display','none');
}
},
/**
* Refresh the list for the possible durations of this accommodation
*
* @param SchoolList The duration list
*/
refreshValidDurations : function() {
this._validDurations = this._schoolAccommodations.getAccommodationValidDuration(this, this.getDurations());
},
/**
* Return the valid durations
*/
getValidDurations : function () {
this.refreshValidDurations();
if(!this._validDurations)
this._validDurations = new Array();
return this._validDurations;
},
/**
* Refresh the list of possible startdates
*
* @param Array list The list of possible startdates
*/
refreshValidStartDates :  function(){
this._validStartDates = this._schoolAccommodations.getAccommodationValidStartDates(this);
},
/**
* return the valid start dates for this accommodation
*
* @return Array of valid start dates
*/
getValidStartDates : function() {
if(!this._validStartDates){
this.refreshValidStartDates();
}
return this._validStartDates;
},
/**
* Generate the accommodations list
*
* @param SchoolList list The list we want to refresh
*
*/
getVisualAccommodation : function(list) {
/*    list.clear();
// Get the school accommodations informations to create the list
this._schoolAccommodations.getSchoolAccommodationsDTO().each(function(SectionItem, index) {
var section = SectionItem;
var sectionId = parseInt(SectionItem.id);
var value = new Object();
value.type = 'category';
value.sectionId = sectionId;
list.addItem(value, SectionItem.name);
list.getListItems().getLast().getHtml().addClass(this._classNameBase + 'List-Item-Category');
// for each accommodation options in the accommodation section
SectionItem.option.each(function(item, index) {
item.parentId = sectionId;
list.addItem(item, item.name);
list.getListItems().getLast().getHtml().addClass(this._classNameBase + 'List-Item-Accommodation');
// add the event on the option button
list.getListItems().getLast().getHtml().addEvent('click', function(e) {
this.setAccommodation(section, item);
}.bind(this));
}.bind(this));
}.bind(this));
list.addItem('error', this._translateDTO.notAvailable);
if (this._schoolAccommodations.getSchool().getCompletedCourses().length > 0) {
list.getListItems().getLast().setIsValid(false);
} else {
list.getListItems().getLast().setIsValid(true);
}*/
},
/**
* Replace an accommodation with another one
*
* @param Object accommodation The new accommodation DTO
* @param Object accommodationOption The new accommodation options DTO
*/
setAccommodation : function(accommodationSection, accommodationOption) {
// set the new accommodation informations
/*   this._accommodationDetails = accommodationSection;
this._accommodationOptionDetails = accommodationOption;
if(this._additionalNightsBefore >  this._accommodationOptionDetails.additionalNightsBefore){
this._additionalNightsBefore = this._accommodationOptionDetails.additionalNightsBefore;
}
if(this._additionalNightsAfter >  this._accommodationOptionDetails._additionalNightsAfter){
this._additionalNightsAfter = this._accommodationOptionDetails._additionalNightsAfter;
}
// set the new title and subtitle
this._title.set("html", this._accommodationDetails.name);
this._subTitle.set("html", this._accommodationOptionDetails.name);
if(this._accommodationOptionDetails.noAccommodation){
this._subTitle.setStyle('display','none');
}else{
this._subTitle.removeAttribute('style');
}
this._title.removeEvents('click');
this._durationValue.removeEvents('click');
this._accommodationOptionsList.deleteList();
this._accommodationDurationsList.deleteList();
this.updateDescriptions();
this.updateStartDate();
this._title.removeEvents();
this._durationValue.removeEvents();
//        this._accommodationOptionsList = new SchoolList(this._title, this._classNameBase);
//        this._accommodationDurationsList = new SchoolList(this._durationValue, this._classNameBase);
//        this.getVisualAccommodation(this._accommodationOptionsList);
this._accommodationOptionsList.getHtml().inject(this._listContainer);
this._accommodationOptionsList.initializeEvents();
this.refreshValidStartDates();
if(!this._accommodationOptionDetails.additionalNightsOnBounds && this._accommodationStartDate.getTime() != this._validStartDates[0].getTime()){
this._additionalNightsBefore = 0;
}
this.getVisualDurations(this._accommodationDurationsList);
this._accommodationDurationsList.getHtml().inject(this._listContainer);
this._accommodationDurationsList.initializeEvents();
if(!this._accommodationOptionDetails.additionalNightsOnBounds && this._accommodationDuration != this.getValidDurations().getLast()){
this._additionalNightsAfter = 0;
}
this.updateStartDate();
this.updateEndDate();
// we ask the parent to check if the other accommodations are ok
this._schoolAccommodations.checkAccommodations(this);
// and the fireAnEvent to the parent to refresh the accommodations
this._schoolAccommodations.fireEvent('refreshAccommodations');*/
},
/**
* Update the start date label
*/
updateStartDate : function () {
if (this._additionalNightsBefore && !this.getAvailableDaysBefore()) {
this._additionalNightsBefore = 0;
}
if (this._accommodationStartDate) {
this._calculatedStartDate.setDate(1);
this._calculatedStartDate.setYear(this._accommodationStartDate.getFullYear());
this._calculatedStartDate.setMonth(this._accommodationStartDate.getMonth());
this._calculatedStartDate.setDate(this._accommodationStartDate.getDate());
this._calculatedStartDate.setDate(this._calculatedStartDate.getDate() - this._additionalNightsBefore);
}
},
/**
* Update the end date label
*/
updateEndDate : function () {
if (this._additionalNightsAfter && !this.getAvailableDaysAfter()) {
this._additionalNightsAfter = 0;
}
if (this._accommodationEndDate) {
// We set the date at 1, because if it's at 31 and we set a 30days month it won't set the right month
this._calculatedEndDate.setDate(1);
this._calculatedEndDate.setYear(this._accommodationEndDate.getFullYear());
this._calculatedEndDate.setMonth(this._accommodationEndDate.getMonth());
this._calculatedEndDate.setDate(this._accommodationEndDate.getDate());
this._calculatedEndDate.setDate(this._accommodationEndDate.getDate() + this._additionalNightsAfter);
}
},
/**
* Returns an array of startDates depending of the course added
*
* @return Array An array of Dates
*/
getStartDates : function() {
var startDatesList = new Array();
var startDate = this._schoolAccommodations.getSchool().getAccommodationStartDate();
var endDate = this._schoolAccommodations.getSchool().getAccommodationEndDate();
if(!startDate) return startDatesList;
// if the first course is not a fixed one
if (!this._schoolAccommodations.getSchool().getIsFixedDateCourse(0)) {
var day = startDate.getDay();
if (day == 0)day = 7;
if (this.getAccommodationOptionDetails().accommodationStartDay > 0) {
while (this.getAccommodationOptionDetails().accommodationStartDay != day) {
startDate.setDate(startDate.getDate() - 1);
day = startDate.getDay();
if (day == 0)day = 7;
}
}
}
// if the last course is not a fixed one
if (!this._schoolAccommodations.getSchool().getIsFixedDateCourse(-1)) {
var endDateDay = endDate.getDay();
if (endDateDay == 0) endDateDay = 7;
if (this.getAccommodationOptionDetails().accommodationEndDay > 0) {
while (endDateDay != this.getAccommodationOptionDetails().accommodationEndDay) {
endDate.setDate(endDate.getDate() + 1);
endDateDay = endDate.getDay();
if (endDateDay == 0) endDateDay = 7;
}
}
endDate.setDate(endDate.getDate() - 7* this.getAccommodationOptionDetails().minimumDurationInWeeks + 3);
}
if(startDate && endDate){
while (startDate < endDate) {
startDatesList.push(new Date(startDate));
startDate.setDate(startDate.getDate() + 7);
}
}
return startDatesList;
},
/**
* Returns the endDate
*
* @return Date The end date
*/
getEndDate : function() {
var endDate = this._schoolAccommodations.getAccommodationEndDate(this);
var endDay = parseInt(this.getAccommodationOptionDetails().accommodationEndDay);
if(endDay == 7)endDay = 0;
while(endDay != endDate.getDay()){
endDate.setDate(endDate.getDate() + 1);
}
return endDate;
},
/**
* Returns the possible durations
*
* @return Array An array of possible durations
*/
getDurations : function() {
var durations = new Array();
var minDuration = parseInt(this.getAccommodationOptionDetails().minimumDurationInWeeks);
var maxDuration = this._schoolAccommodations.getDurationInWeeks(this.getEndDate(), this.getValidStartDates()[0]);
for (var i = minDuration; i <= maxDuration; i++) {
durations.push(i);
}
return durations;
},
/**
* Return the endDate for an accommodation with its startDate and duration
*
* @return Date The accommodation end date.
*/
getCalculatedEndDate : function() {
var endDate = new Date(this._accommodationStartDate);
endDate.setDate(endDate.getDate() - 3 + this._accommodationDuration * 7);
var endDay = parseInt(this.getAccommodationOptionDetails().accommodationEndDay);
if(endDay == 7)endDay = 0;
while(endDay != endDate.getDay()){
endDate.setDate(endDate.getDate() + 1);
}
if (endDate > this.getEndDate()) {
endDate = this.getEndDate();
this._accommodationEndDate = endDate;
this._accommodationDuration = this._schoolAccommodations.getDurationInWeeks(this._accommodationEndDate, this._accommodationStartDate);
}
if(this._additionalNightsAfter && !this.getAvailableDaysAfter()){
this._additionalNightsAfter = 0;
}
return endDate;
},
/**
* Returns true if all the accommodation informations are complete
*
* @return Boolean Return true if all the accommodation informations are completed
*/
isComplete : function() {
return this._accommodationDuration && this._accommodationStartDate && this._accommodationEndDate;
},
/**
* Returns the accommodation Start date
*
* @return Date The accommodation start date
*/
getAccommodationStartDate : function() {
return this._accommodationStartDate;
},
/**
* Returns the accommodation end date
*
* @return Date The accommodation end date
*/
getAccommodationEndDate : function() {
return this._accommodationEndDate;
},
/**
* Returns the accommodation duration
*
* @return Number The accommodation duration
*/
getAccommodationDuration : function() {
return this._accommodationDuration;
},
/**
* Returns an object containing the accommodation option details
*
* @return object The accommodation option details
*/
getAccommodationOptionDetails : function() {
return this._accommodationOptionDetails;
},
/**
* set the accommodations attached to a course
*
* @param isAttached CourseDetailDTO The course to attach to
*/
setIsAttachedToCourse : function(isAttached) {
this._accommodationAttached = isAttached;
},
/**
* set the accommodations attached to a course
*
* @param course CourseDetailDTO The course to attach to
*/
setAttachedToCourse : function(course) {
this._accommodationAttachedToCourse = course;
},
/**
* set the accommodations attached to a course
*
* @return CourseDetailDTO The course details attached to this accommodation
*/
getAttachedToCourse : function() {
return this._accommodationAttachedToCourse;
},
/**
* set the accommodations attached to a course
*
* @return CourseDetailDTO The course details attached to this accommodation
*/
getIsAttachedToCourse : function() {
return this._accommodationAttached;
},
/**
* return the valid start dates for this accommodation
*
* @return Array of valid start dates
*/
getValidEndDates : function() {
var validEndDates = new Array();
if(this._accommodationStartDate){
var endDate = new Date(this._accommodationStartDate);
endDate.setDate(endDate.getDate() - 3 + this._validDurations[0] * 7);
var endDay = parseInt(this.getAccommodationOptionDetails().accommodationEndDay);
if(endDay == 7)endDay = 0;
while(endDay != endDate.getDay()){
endDate.setDate(endDate.getDate() + 1);
}
validEndDates.push(endDate);
var endDateTime = endDate.getTime();
var week = 86000 * 1000 * 7;
this.getValidDurations().each(function(item, index) {
// we already added the first end date
if (index > 0) {
endDateTime += week;
var date = new Date(endDate);
date.setDate(date.getDate() + (7 * index));
validEndDates.push(date);
}
}.bind(this));
}
return validEndDates;
},
/**
* Set the element container of this accommodation
*
* @param element
*/
setElement : function(element){
this._element = element;
},
/**
* Return the element container of this accommodation
*
* @param element
*/
getElement : function(){
return this._element;
},
/**
* Delete this accommodation
*/
deleteAccommodation : function () {
this.fireEvent('delete');
}
});/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
*
* @param AccommodationListItemDTO schoolAccommodationsDTO The DTO containing all the accommodations informations
* @param Object translateDTO The DTO containing all the translations for this class
* @param Element eListContainer The list container
* @param School oSchool The school
**/
var SchoolAccommodations = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(schoolAccommodationsDTO, oSchool) {
this._schoolAccommodationsDTO = schoolAccommodationsDTO;
this._accommodations = new Array();
this._school = oSchool;
this._booking = this._school.getParent();
this._accommodationsOptions = new Hash();
this._schoolAccommodationsDTO.each(function(accommodation, index){
accommodation.option.each(function(option, index){
this._accommodationsOptions.set(option.id,{accommodationDTO: accommodation, optionDTO : option });
}.bind(this));
}.bind(this));
this._calendar = new BookingCalendar({
days: this._booking._translateDTO.days, // days of the week starting at sunday
months: this._booking._translateDTO.months,
offset: this._booking._translateDTO.firstDayOfWeek
});
this._calendar.addEvent('dateselected', function(e){
this._calendar.hide();
this._calendar.getCallBackMethod().call(this._calendar.getBind(), this._calendar.getSelectedDate());
}.bind(this));
this.addEvent('refreshAccommodations', function(item, index) {
this.refreshAccommodations();
}.bind(this));
},
/**
* Update the rendering of the accommodations
*/
render : function () {
if(!this._schoolAccommodationsList){
var schoolAccommodationsList = this._booking.getSchoolAccommodationsListTemplate(this._schoolDTO);
if(schoolAccommodationsList){
$p(schoolAccommodationsList.getSelector(), this._school.getSchoolElement()).render(this._schoolAccommodationsDTO, schoolAccommodationsList.getDirective());
this._schoolAccommodationsList = this._booking.populateList(this._school.getSchoolElement().getElement(schoolAccommodationsList.getSelector()));
this._schoolAccommodationsList.addEvent('change', function(e) {
var value = this._accommodationsOptions.get(parseInt(this._schoolAccommodationsList.getValue()));
this.addAccommodation(value.accommodationDTO.id, value.optionDTO.id);
}.bind(this));
}
}
var accommodationsTemplate = this._booking.getSchoolAccommodationsTemplate(this._accommodations);
$p(accommodationsTemplate.getSelector(), this._school.getSchoolElement()).render(this._accommodations, accommodationsTemplate.getDirective());
this._accommodations.each(function(accommodation, index) {
// todo find a way to retrieve the right accommodation container
accommodation.setElement(this._school.getSchoolElement().getElements('.accommodation-container')[index]);
this._school.getSchoolElement().getElements('.accommodation-container')[index].getElement('.accommodation-delete-button').addEvent('click', function() {
this.deleteAccommodation(accommodation);
}.bind(this));
accommodation.render();
}.bind(this));
},
/**
* Return the calendar
*/
getCalendar : function(){
return this._calendar;
},
/**
* Returns an object containing the informations for the calculation price
*
* @return Array The informations needed to calculate the price
*/
getDTO : function() {
var accommodationsDTO = new Array();
this._accommodations.each(function(item, index) {
if (item.isComplete()) {
accommodationsDTO.push(item.getDTO());
}
});
return accommodationsDTO;
},
/**
* Populate the accommodation from a dto
*
* @param Array toPopulateDTO The accommodations to add
*/
populateFromDTO : function(toPopulateDTO) {
var accommodations = new Array();
if (toPopulateDTO) {
toPopulateDTO.each(function(item, index) {
// check
this._schoolAccommodationsDTO.each(function(accommodationItem, accommodationIndex) {
var accommodationToAdd = accommodationItem.option.filter(function(optionItem, optionIndex) {
return optionItem.id == item.id;
});
if (accommodationToAdd[0]) {
accommodationToAdd[0].parentId = accommodationItem.id;
accommodationToAdd[0].dto = item;
accommodations.push(accommodationToAdd[0]);
}
}.bind(this));
}.bind(this));
}
if (accommodations.length) {
for (var i = 0; i < accommodations.length; i++) {
this.addAccommodation(accommodations[i].parentId, accommodations[i].id);
if (this._accommodations.length)
this._accommodations.getLast().populateFromDTO(accommodations[i].dto);
}
}
},
/**
*  Return the classNameBase
*/
getClassNameBase : function() {
return this._classNameBase;
},
/**
* return the accommodation option information
* @param optionId
*/
getAccommodationOptionInformations : function (optionId) {
if (this._accommodationsOptions.has(optionId)) {
return this._accommodationsOptions.get(optionId);
}
return null;
},
getValidAccommodationsList : function () {
var availableAccommodations = this.getAvailableAccommodations();
var accommodationStartDate = this.getSchoolAccommodationStartDate();
var accommodationEndDate = this.getAccommodationEndDate();
var validAccommodations = new Array();
if(availableAccommodations.length){
this._accommodationsOptions.each(function(item, key) {
if (availableAccommodations.contains(parseInt(item.accommodationDTO.id))) {
var startDate = new Date(accommodationStartDate);
var day = startDate.getDay();
if (day == 0)day = 7;
while (parseInt(item.optionDTO.accommodationStartDay) != day) {
startDate.setDate(startDate.getDate() - 1);
day = startDate.getDay();
if (day == 0)day = 7;
}
var endDate = new Date(accommodationEndDate);
var day = endDate.getDay();
if (day == 0)day = 7;
while (parseInt(item.optionDTO.accommodationEndDay) != day) {
endDate.setDate(endDate.getDate() + 1);
day = endDate.getDay();
if (day == 0)day = 7;
}
}
var available = true;
// check the availability for this accommodation
if (accommodationStartDate && accommodationEndDate && item.optionDTO.availabilities && item.optionDTO.availabilities.length) {
var availabilities = item.optionDTO.availabilities;
var startWeek = accommodationStartDate.getWeek();
var startYear = accommodationStartDate.getFullYear();
var endWeek = accommodationEndDate.getWeek();
var duration = this.getDurationInWeeks(accommodationEndDate, accommodationStartDate);
available = false;
var i = 0;
if (startWeek == endWeek) {
if (availabilities[startYear + '' + endWeek] == undefined) {
available = true;
}
} else {
// if there are no accommodations added and the accommodation is not available the first possible week it's removed
if(!this.getAccommodations().length && availabilities[startYear + '' + startWeek] === 0){
available = false;
} else {
// otherwise we test if the accommodation is available
while (duration > 0 && startWeek != endWeek && !available) {
if (availabilities[startYear + '' + startWeek] == undefined) {
available = true;
break;
}
if (startWeek == 52) {
startWeek = 1;
startYear++;
} else {
startWeek++;
}
duration--;
}
}
}
}
// if the accommodation is available,
// the duration is longer than the minimum duration of the accommodation and the number of participant is big enough
if (available && availableAccommodations.contains(parseInt(item.accommodationDTO.id))
&& this.getDurationInWeeks(endDate, startDate) >= item.optionDTO.minimumDurationInWeeks
&& this.getSchoolParticipants() >= item.optionDTO.minimumNumberOfPeople) {
validAccommodations.push(parseInt(key));
}
}.bind(this));
}
return validAccommodations;
},
/**
* Refresh the accommodation list
*
* @param SchoolList list The list to refresh
*
*/
refreshVisualAccommodationList : function(list) {
var availableAccommodations = this.getAvailableAccommodations();
var accommodationStartDate = this.getSchoolAccommodationStartDate();
var accommodationEndDate = this.getAccommodationEndDate();
/*
list.getListItems().each(function(item, index) {
if (availableAccommodations.contains(item.getValue().parentId)) {
var startDate = new Date(accommodationStartDate);
var day = startDate.getDay();
if (day == 0)day = 7;
while (parseInt(item.getValue().accommodationStartDay) != day) {
startDate.setDate(startDate.getDate() - 1);
day = startDate.getDay();
if (day == 0)day = 7;
}
var endDate = new Date(accommodationEndDate);
var day = endDate.getDay();
if (day == 0)day = 7;
while (parseInt(item.getValue().accommodationEndDay) != day) {
endDate.setDate(endDate.getDate() + 1);
day = endDate.getDay();
if (day == 0)day = 7;
}
}
var available =  true;
// check the availability for this accommodation
if(accommodationStartDate && accommodationEndDate && item.getValue().availabilities && item.getValue().availabilities.length){
var availabilities = item.getValue().availabilities;
var startWeek = accommodationStartDate.getWeek();
var startYear = accommodationStartDate.getFullYear();
var endWeek = accommodationEndDate.getWeek();
var duration  = this.getDurationInWeeks(accommodationEndDate, accommodationStartDate);
available = false;
var i = 0;
if(startWeek == endWeek){
if(availabilities[startYear + '' + endWeek] == undefined){
available = true;
}
}else {
while (duration > 0 && startWeek != endWeek && !available){
if(availabilities[startYear + '' + startWeek] == undefined){
available = true;
}
if(startWeek == 52){
startWeek = 1;
startYear++;
} else {
startWeek++;
}
duration--;
}
}
}
// if the accommodation is available,
// the duration is longer than the minimum duration of the accommodation and the number of participant is big enough
if (availableAccommodations.contains(item.getValue().parentId)
&& this.getDurationInWeeks(endDate, startDate) >= item.getValue().minimumDurationInWeeks
&& this.getSchoolParticipants() >= item.getValue().minimumNumberOfPeople) {
item.setIsSelectionnable(available);
} else {
// if the item is not a category,
if (item.getValue().type != 'category') {
item.setIsSelectionnable(false);
}
}
}.bind(this));
if (this.getCompletedCourses().length > 0) {
list.getListItems().getLast().setIsValid(false);
} else {
list.getListItems().getLast().setIsValid(true);
}*/
},
/**
* Add an accommodation
*
* @param Number id The accommodation ID
* @param Number optionID The accommodation Option ID
* @param Boolean attachedAccommodation True if the accommodation is attached (false by default)
*
* @return True if the accommodation is added
*/
addAccommodation : function(id, optionId, attachedAccommodation) {
var accommodationsDetailsDTO = Array.from(this._schoolAccommodationsDTO);
var accommodationDetailsDTO = null;
for (var i = 0; i < accommodationsDetailsDTO.length; i++) {
var accommodation = accommodationsDetailsDTO[i];
var accommodationOptionDetails = accommodation.option.filter(function(item, index) {
return item.id == optionId;
}.bind(this));
if (accommodationOptionDetails.length) {
accommodationDetailsDTO = accommodation;
break;
}
}
if(accommodationDetailsDTO) {
var newAccommodation = new SchoolAccommodationObject(accommodationDetailsDTO, accommodationOptionDetails[0], this);
// if the accommodation has valid start dates it is added
if (newAccommodation.getStartDates().length) {
this._accommodations.push(newAccommodation);
newAccommodation.addEvent('accommodationUpdated', function() {
this.checkAccommodations(newAccommodation);
this.refreshAccommodations();
}.bind(this));
newAccommodation.addEvent('deleteAccommodation', function(e) {
this.deleteAccommodation(newAccommodation);
}.bind(this));
// if the newly added accommodation is populated from course infos
if (newAccommodation.isComplete()) {
this.checkAccommodations(newAccommodation);
this.refreshAccommodations();
}
this.fireEvent('accommodationAdded', newAccommodation);
return newAccommodation;
}
}
return false;
},
replaceAccommodation : function (oldAccommodation, newAccommodationId) {
var accommodationIndex = this._accommodations.indexOf(oldAccommodation);
var dto = oldAccommodation.getDTO();
var accommodationsDetailsDTO = Array.from(this._schoolAccommodationsDTO);
var accommodationDetailsDTO = null;
for (var i = 0; i < accommodationsDetailsDTO.length; i++) {
var accommodation = accommodationsDetailsDTO[i];
var accommodationOptionDetails = accommodation.option.filter(function(item, index) {
return item.id == newAccommodationId;
}.bind(this));
if (accommodationOptionDetails.length) {
accommodationDetailsDTO = accommodation;
break;
}
}
if (accommodationDetailsDTO) {
var newAccommodation = new SchoolAccommodationObject(accommodationDetailsDTO, accommodationOptionDetails[0], this);
this._accommodations.splice(accommodationIndex, 1, newAccommodation);
newAccommodation.populateFromDTO(dto);
newAccommodation.addEvent('accommodationUpdated', function() {
this.checkAccommodations(newAccommodation);
this.refreshAccommodations();
}.bind(this));
newAccommodation.addEvent('deleteAccommodation', function(e) {
this.deleteAccommodation(newAccommodation);
}.bind(this));
// if the newly added accommodation is populated from course infos
if (newAccommodation.isComplete()) {
this.checkAccommodations(newAccommodation);
this.refreshAccommodations();
}
this.fireEvent('accommodationAdded', newAccommodation);
return newAccommodation;
}
},
/**
* Refresh all the accommodations
*
* it is called when an accommodation or a course is modified
*
* @optionalparam String option The options - lists if the list have to be regenerated
* @optionalparam boolean noPriceRefresh True if the price should not be refreshed
*/
refreshAccommodations : function() {
var mandatoryAccommodations = this._school.getMandatoryAccommodations();
this.refreshVisualAccommodationList(this.accommodationsList);
if (this._accommodations.length || mandatoryAccommodations.length) {
// if an accommodation need more than the number of people selected, it's deleted
var accommodationModified = false;
for (var i = 0; i < this._accommodations.length; i++) {
if (this._accommodations[i].getAccommodationOptionDetails().minimumNumberOfPeople > this.getSchoolParticipants()) {
this.deleteAccommodation(this._accommodations[i]);
accommodationModified = true;
i--;
}
}
if (accommodationModified) this._school.fireEvent('refreshPrice');
// if there are no more courses, all the accommodations are deleted
if (this.getCompletedCourses().length == 0) {
var accommodations = this._accommodations.length;
for (var i = 0; i < this._accommodations.length; i++) {
if (this._accommodations.length) {
this.deleteAccommodation(this._accommodations[i]);
i--;
}
}
// and if there was any accommodations and there is no transfers, we send a refresh price (else the refresh price will be made by the school transfers)
if (accommodations && !this._school.getCompletedTransfers()) {
this._school.fireEvent('refreshPrice');
}
} else {
// getting the mandatory accommodations
var mandatoryAccommodations = this._school.getMandatoryAccommodations();
// checking if the there are no unavailable accommodation on the basket
var availableAccommodations = this.getAvailableAccommodations();
// remove the unauthorized accommodations
for (var i = 0; i < this._accommodations.length; i++) {
if (!availableAccommodations.contains(parseInt(this._accommodations[i]._accommodationDetails.id))) {
this.deleteAccommodation(this._accommodations[i], true);
i--;
}
}
for (var i = 0; i < this._accommodations.length; i++) {
this._accommodations[i].setIsAttachedToCourse(false);
}
// if there are mandatory accommodations
if (mandatoryAccommodations.length) {
this.checkMandatoryAccommodations(mandatoryAccommodations);
}
// retrieve the max start and end date value (based on the courses and acommodation start day and end day)
var startDate = this.getAccommodationStartDate();
var endDate = this.getAccommodationEndDate();
if (this._accommodations.length && endDate < this._accommodations.getLast().getAccommodationEndDate()) {
var endDay = parseInt(this._accommodations.getLast().getAccommodationOptionDetails().accommodationEndDay);
if (endDay == 7)endDay = 0;
while (endDay != endDate.getDay()) {
endDate.setDate(endDate.getDate() + 1);
}
if (endDate < this._accommodations.getLast().getAccommodationEndDate()) {
// delete the accommodations that are beyond the end date
for (var i = this._accommodations.length - 1; i > 0; i--) {
if (this._accommodations[i].getAccommodationEndDate() > endDate) {
this.deleteAccommodation(this._accommodations[i], true);
}
}
// if there are still accommodations the last one is adapted to fit the new dates
if (this._accommodations.length) {
var duration = this.getDurationInWeeks(endDate, this._accommodations.getLast().getAccommodationStartDate());
// if the new duration is smaller than the minimum
if (duration < this._accommodations.getLast().getAccommodationOptionDetails().minimumDurationInWeeks) {
duration = this._accommodations.getLast().getAccommodationOptionDetails().minimumDurationInWeeks;
// if it can't be adapted, it is deleted
if (this.getDurationInWeeks(endDate, startDate) < duration) {
this.deleteAccommodation(this._accommodations.getLast(), true);
} else {
this._accommodations.getLast().setEndDate(endDate);
}
} else {
this._accommodations.getLast().setEndDate(endDate);
}
}
}
}
// if the new accommodation startDate is smaller than the old one
if (this._accommodations.length && startDate > this._accommodations[0].getAccommodationStartDate()
&& !this._accommodations[0].getIsAttachedToCourse()) {
// delete the accommodation with a start date smaller than the new course start date
for (var i = 0; i < this._accommodations.length; i++) {
if (this._accommodations[i].getAccommodationStartDate() < startDate) {
this.deleteAccommodation(this._accommodations[i], true);
i--;
}
}
// if there are still accommodations the start is updated
if (this._accommodations.length) {
this._accommodations[0].setStartDate(startDate);
}
}
/* for (var i = 0; i < this._accommodations.length; i++) {
if (this._accommodations[i].isComplete()) {
refreshPrice = true;
break;
}
}*/
// check if the add button must be displayed or not
if ((this.getCompletedCourses().length == mandatoryAccommodations.length
&& mandatoryAccommodations.length <= this._accommodations.length)
|| (this._accommodations.length && this._accommodations.getLast().getIsAttachedToCourse() == true
&& this._accommodations.getLast().getAttachedToCourse().courseId == this._school.getCoursesManager().getValidAddedCourses(true).getLast().getCourseDetails().id)) {
} else {
}
}
}
this.fireEvent('accommodationsUpdated');
},
/**
* check if the mandatory accommodations are correctly added and displayed
*
* @param Array mandatoryAccommodations The mandatory accommodations list
* @optionalparam Integer notToAdd The accommodationOption id to not add
*
*/
checkMandatoryAccommodations : function(mandatoryAccommodations) {
// check each mandatory accommodations
for (var i = 0; i < mandatoryAccommodations.length; i++) {
// getting the accommodations informations to add
var accommodationsToAdd = this._schoolAccommodationsDTO.filter(function(item, index) {
return mandatoryAccommodations[i].ids.contains(parseInt(item.id));
});
// if there is a valid accommodation to add
if (accommodationsToAdd.length) {
var accommodationsAlreadyAdded = new Array();
// checking if the accommodation is already present
if (this._accommodations.length) {
this._accommodations.each(function(item, index) {
for (var j = 0; j < accommodationsToAdd.length; j++) {
if (item._accommodationDetails.id == accommodationsToAdd[j].id && !(item.getIsAttachedToCourse())) {
accommodationsAlreadyAdded.push(item);
}
}
}.bind(this));
}
// if there are no accommodation added
if (!accommodationsAlreadyAdded.length) {
var added = false;
var j = 0;
var k = 0;
while (!added) {
// find a valid accommodation to add in the list
if (j < accommodationsToAdd.length) {
// find the first valid accommodation option
if (k < accommodationsToAdd[j].option.length) {
this.addAccommodation(accommodationsToAdd[j].id, accommodationsToAdd[j].option[k].id, true);
if (this._accommodations.length) {
this._accommodations[this._accommodations.length - 1].setIsAttachedToCourse(true);
this._accommodations[this._accommodations.length - 1].setAttachedToCourse(mandatoryAccommodations[i]);
// set the values
var startDate = this.getAccommodationValidStartDates(this._accommodations[this._accommodations.length - 1], this._accommodations[this._accommodations.length - 1]._accommodationStartDatesList);
if (startDate.length) {
this._accommodations[this._accommodations.length - 1].setAttachedAccommodationStartDate(startDate[0]);
var duration = this.getAccommodationValidDuration(this._accommodations[this._accommodations.length - 1], this._accommodations[this._accommodations.length - 1].getDurations());
if (duration.length) {
this._accommodations[this._accommodations.length - 1].setAttachedAccommodationDuration(duration[0]);
}
}
if (startDate.length && duration.length) {
added = true;
} else {
// remove the accommodation and try to add the next one
this._accommodations[this._accommodations.length - 1].deleteAccommodation();
this._accommodations.erase(this._accommodations[this._accommodations.length - 1]);
k++;
}
}else{
j++;
}
} else {
j++;
}
} else {
added = true;
}
}
} else {
accommodationsAlreadyAdded[0].setIsAttachedToCourse(true);
accommodationsAlreadyAdded[0].setAttachedToCourse(mandatoryAccommodations[i]);
var startDate = this.getAccommodationValidStartDates(accommodationsAlreadyAdded[0], accommodationsAlreadyAdded[0]._accommodationStartDatesList);
if (startDate.length) {
accommodationsAlreadyAdded[0].setAttachedAccommodationStartDate(startDate[0]);
var duration = this.getAccommodationValidDuration(accommodationsAlreadyAdded[0], accommodationsAlreadyAdded[0].getDurations());
if (duration.length) {
accommodationsAlreadyAdded[0].setAttachedAccommodationDuration(duration[0]);
//                          accommodationsAlreadyAdded[0].refreshAccommodationLists();
//                            this._accommodations[this._accommodations.length - 1].reGenerateAccommodationLists();
}
}
// if something was not valid, remove the accommodation and add another one
if (!startDate.length || !duration.length) {
this.deleteAccommodation(accommodationsAlreadyAdded[0]);
i--;
}
}
}
}
},
/**
* Check all the accommodations and modify them to fit the accommodation passed in parameter
*
* Called when an accommodation is updated
*
* @param SchoolAccommodationsObject accommodation The accommodation to check
*
*/
checkAccommodations : function(accommodation) {
if (accommodation.isComplete()) {
var index = this._accommodations.indexOf(accommodation);
// if it's the first accommodation and there are more than one accommodation
if (index == 0 && this._accommodations.length > 1) {
if (accommodation.getAccommodationEndDate() > this._accommodations[index + 1].getAccommodationStartDate()
&& this._accommodations[index + 1].isComplete() && accommodation.isComplete()) {
var nextAccommodation = this._accommodations[index + 1];
// set the new data for the next accommodation
if (!nextAccommodation.getIsAttachedToCourse()) {
nextAccommodation.setStartDate(accommodation.getAccommodationEndDate());
}
}
}
// if it's not the first accommodation
if (index > 0) {
// set the previous valid accommodation with valid data (endDate is set with the actual accommodation startDate and duration is calculated)
// if the startDate is higher than the previous end Date
if (accommodation.getAccommodationStartDate() < this._accommodations[index - 1].getAccommodationEndDate() && this._accommodations[index - 1].isComplete() && accommodation.isComplete()) {
var prevAccommodation = this._accommodations[index - 1];
// set the new data for this accommodation
if (!prevAccommodation.getIsAttachedToCourse()) {
prevAccommodation.setEndDate(accommodation.getAccommodationStartDate());
}
}
}
// if it's not the first and not the last one
if (index > 0 && this._accommodations.getLast() != accommodation) {
if (accommodation.getAccommodationEndDate() > this._accommodations[index + 1].getAccommodationStartDate() && this._accommodations[index + 1].isComplete() && accommodation.isComplete()) {
// set the new data for this accommodation
nextAccommodation = this._accommodations[index + 1];
if (!nextAccommodation.getIsAttachedToCourse()) {
nextAccommodation.setStartDate(accommodation.getAccommodationEndDate());
}
}
}
}
},
/**
* Return the school accommodation start date
*
* @return Date the school accommodations start date
*/
getSchoolAccommodationStartDate : function() {
return this._school.getAccommodationStartDate();
},
/**
* Return the participants number
*/
getSchoolParticipants : function() {
return this._school.getParticipants();
},
/**
* Returns the possible startDate for the accommodations
*
*
* @return Date The possible startdates
*/
getAccommodationStartDate : function() {
var startDate = this.getSchoolAccommodationStartDate();
if(startDate && this._accommodations.length){
var day = startDate.getDay();
if (day == 0)day = 7;
if(this._accommodations[0] && this._accommodations[0].getAccommodationOptionDetails().accommodationStartDay > 0){
while (this._accommodations[0].getAccommodationOptionDetails().accommodationStartDay != day) {
startDate.setDate(startDate.getDate() - 1);
day = startDate.getDay();
if (day == 0)day = 7;
}
}
}
return startDate;
},
/**
* Returns the valid startDates for an accommodation given in parameter
*
* @param SchoolAccommodationObject accommodation The accommodation
* @param Array accommodationStartDates Array of number containing all the possible start dates
*
* @return Array An array of possible start dates
*/
getAccommodationValidStartDates : function(accommodation, accommodationStartDates) {
var validStartDates = new Array();
var startDates = accommodation.getStartDates();
var minimumDuration = accommodation.getAccommodationOptionDetails().minimumDurationInWeeks;
var availabilities = accommodation.getAccommodationOptionDetails().availabilities;
// if this accommodation is not the first, we remove the minimum durations of the previous one
if (this._accommodations.length && this._accommodations.indexOf(accommodation) != 0  ) {
var accommodationPosition = this._accommodations.indexOf(accommodation);
if(accommodationPosition == -1) accommodationPosition = this._accommodations.length;
for (var i = 0; i < accommodationPosition; i++) {
if (this._accommodations[i].isComplete()) {
if(this._accommodations[i].getIsAttachedToCourse() == true){
startDates.splice(0, this._accommodations[i]._accommodationDuration);
}else{
startDates.splice(0, this._accommodations[i].getAccommodationOptionDetails().minimumDurationInWeeks);
}
}
}
}
// if this accommodation is not the last, we remove the minimum durations of the next one
if (this._accommodations.getLast() != accommodation && this._accommodations.indexOf(accommodation) != -1) {
for (var i = this._accommodations.length - 1; i > this._accommodations.indexOf(accommodation); i--) {
if (this._accommodations[i].isComplete()) {
if(this._accommodations[i].getIsAttachedToCourse() == true){
startDates.splice(startDates.length - this._accommodations[i]._accommodationDuration, this._accommodations[i]._accommodationDuration);
}else{
startDates.splice(startDates.length - this._accommodations[i].getAccommodationOptionDetails().minimumDurationInWeeks, this._accommodations[i].getAccommodationOptionDetails().minimumDurationInWeeks);
}
}
}
}
startDates.each(function(item, index) {
var date = item;
var year = date.getFullYear();
var week = date.getWeek();
if (accommodation.getAccommodationOptionDetails().accommodationStartDay == 7) {
if (week > 1)week = week + 1;
}
// check the availabilities for this start date
var toAdd = true;
if (availabilities) {
for (var i = 0; i < parseInt(minimumDuration); i++) {
if (week > 52) {
var lastWeek = (new Date(date.getFullYear(), 12, 31)).getWeek();
if (week < lastWeek) {
week = week - lastWeek;
year = year + 1;
}
}
if (availabilities[year + '' + week] == 0) {
toAdd = false;
}
week++;
}
}
if (toAdd) {
validStartDates.push(date);
}
}.bind(this));
// if the accommodation is attached to a course, we must find the only course start date available
if (accommodation.getIsAttachedToCourse()) {
var courseStartDate = (new Date()).fromIso(accommodation.getAttachedToCourse().startDate);
var courseStartTime = courseStartDate.getTime();
for (var i = 0; i < validStartDates.length; i++) {
var n = (i == 0? i : i + 1);
if(validStartDates.length > n) {
var validStartTime = validStartDates[n].getTime();
// if the start date is smaller that the course start time (minus 3 days) we remove it
if (validStartTime < (courseStartTime - (86400 * 3 * 1000))) {
validStartDates.splice(0, i + 1);
i--;
}
// if the start date is bigger that the course start time (+3 days) we remove it
if (validStartTime > (courseStartTime + (86400 * 3 * 1000))) {
validStartDates.splice(n, validStartDates.length - 1);
}
}
}
}
return validStartDates;
},
/**
* Return the added accommodations
*/
getAccommodations : function(){
return this._accommodations;
},
/**
* Returns the final endDate for a given accommodation
*
* @param SchoolAccommodationObject accommodation The accommodation
*
* @return Date the endDate
*/
getAccommodationEndDate : function(accommodation) {
return this._school.getAccommodationEndDate(accommodation);
},
/**
* Returns an array of valid durations calculated from the startdates of the accommodation and
* the endDate returned by the parent
*
* @return Array An Array of number containing the valid durations
*/
getAccommodationValidDuration : function(accommodation, durationList) {
// check if this accommodation is attached to a course
if (accommodation.getIsAttachedToCourse()) {
var accommodationEndDate = this._school.getCourseAccommodationEndDate(accommodation.getAttachedToCourse().courseId, (new Date()).fromIso(accommodation.getAttachedToCourse().startDate), accommodation);
// if there is an accommodation end date returned
if (accommodationEndDate) {
return [this.getDurationInWeeks(accommodationEndDate, accommodation._accommodationStartDate)];
} else {
return [];
}
}
// if the accommodations is not attached
var validDurations = new Array();
var durations = durationList;
var durationsToRemove = 0;
if(!durations)
return validDurations;
// if the accommodation has a start date
if (accommodation.getAccommodationStartDate()) {
var difference = this.getDurationInWeeks(this.getAccommodationEndDate(accommodation), accommodation.getAccommodationStartDate());
durationsToRemove = durations.length - 1 + parseInt(accommodation.getAccommodationOptionDetails().minimumDurationInWeeks) - Math.round(difference);
if(this._accommodations.indexOf(accommodation) >= 0){
for (var i = this._accommodations.indexOf(accommodation); i < this._accommodations.length; i++) {
if (accommodation != this._accommodations[i] && this._accommodations[i].isComplete()) {
if (this._accommodations[i].getIsAttachedToCourse() == true) {
durationsToRemove = parseInt(durationsToRemove + parseInt(this._accommodations[i]._accommodationDuration));
} else {
durationsToRemove = parseInt(durationsToRemove + parseInt(this._accommodations[i].getAccommodationOptionDetails().minimumDurationInWeeks));
}
}
}
}
} else {
for (var i = 0; i < this._accommodations.length; i++) {
if (accommodation != this._accommodations[i] && this._accommodations[i].isComplete()) {
durationsToRemove = parseInt(durationsToRemove + parseInt(this._accommodations[i].getAccommodationOptionDetails().minimumDurationInWeeks));
}
}
}
var week = -1;
var year = -1;
if (accommodation.getAccommodationStartDate()) {
week  = accommodation.getAccommodationStartDate().getWeek();
year  = accommodation.getAccommodationStartDate().getFullYear();
if(accommodation.getAccommodationOptionDetails().accommodationStartDay == 7){
if(week > 1)week = week+1;
}
}
// add the duration items
if(durationsToRemove < 0)durationsToRemove = 0;
for(var i = 0; i < (durations.length - durationsToRemove); i++){
var value = durations[i], currentYear = '', currentWeek = '';
if(week != -1){
currentWeek = week + parseInt(value)-1;
currentYear = currentWeek > 52 ? accommodation.getAccommodationStartDate().getFullYear()+1 : accommodation.getAccommodationStartDate().getFullYear();
currentWeek = currentWeek > 52 ? week-52 : week;
}
if(week == -1 || !accommodation.getAccommodationOptionDetails().availabilities ||
accommodation.getAccommodationOptionDetails().availabilities[currentYear + '' + currentWeek] != 0){
validDurations.push(parseInt(value));
}else{
break;
}
}
if (accommodation.getIsAttachedToCourse()) {
if (duration && validDurations.contains(duration)) {
return [parseInt(duration)];
} else {
return [];
}
}
return validDurations;
},
/**
* Returns the end date of the previous accommodation
*
* @param SchoolAccommodationObject accommodation The accommodation
*/
getPreviousEndDate : function(accommodation) {
var previousEndDate = this.getAccommodationEndDate();
for (var i = 0; i < this._accommodations.indexOf(accommodation); i++) {
if (this._accommodations[i].isComplete()) previousEndDate = this._accommodations[i].getAccommodationEndDate();
}
return previousEndDate;
},
/**
* Returns the duration in weeks between a startDate and an end date
*
* @param Date endDate The end date
* @param Date starDate The startdate
* @return Number The duration in week
*/
getDurationInWeeks : function(endDate, startDate) {
var duration = endDate - startDate;
duration = Math.round(duration / (1000 * 60 * 60 * 24 * 7));
if(duration == 0 && startDate.getTime() < endDate.getTime() ){
duration = 1;
}
return duration;
},
/**
* Returns the completed accommodations
*
* @return SchoolAccommodationObject[] The completed accommodations
*/
getCompletedAccommodations : function() {
var accommodations = new Array();
this._accommodations.each(function(item, index) {
if (item.isComplete) {
accommodations.push(item);
}
});
return accommodations;
},
/**
* Return the completed courses for this school (if any)
*
*/
getCompletedCourses : function() {
return this._school.getCompletedCourses();
},
/**
* Delete the accommodation given in parameter
*
* @param SchoolAccommodationObject accommodation The accommodation to delete
* @param boolean silentDelete True to not send the event
*/
deleteAccommodation : function(accommodation, silentDelete) {
this._accommodations.erase(accommodation);
this.refreshAccommodations();
/*  this._school.fireEvent('heightModified');
if (!this._accommodations.length && !silentDelete) {
this._school.fireEvent('refreshPrice');
}*/
},
/**
* Remove all the courses
*/
removeAllAccommodations : function (noRefresh) {
this._accommodations = new Array();
if(!noRefresh)this.refreshAccommodations();
},
/**
*  Returns the available accommodations
*
* @return Array of integer The Accommodation IDs
*/
getAvailableAccommodations : function() {
return this._school.getAvailableAccommodations();
},
/**
* Return an array containing the available transfers
*
* @return Array of transfers ID
*/
getAvailableTransfers : function() {
var availableTransfers = new Array();
for (var i = 0; i < this._accommodations.length; i++) {
availableTransfers = availableTransfers.combine(this._accommodations[i].getAccommodationDetails().availableTransfers);
}
return availableTransfers;
},
/**
* Return the max accommodation end date for a course
*
* @param integer courseId The Course ID
* @param Date startDate the course start date
*
* @return Date The course accommodation end date
*/
getCourseAccommodationEndDate : function(courseId, startDate, accommodation) {
return this._school.getCourseAccommodationEndDate(courseId, startDate, accommodation);
},
/**
* Returns the parent of this object
*
* @return Object The parent object
*/
getSchool : function() {
return this._school;
}
,
/**
* Returns the school accommodations DTO
*
* @return Object The school accommodations DTO
*/
getSchoolAccommodationsDTO : function() {
return this._schoolAccommodationsDTO;
}
});/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @event Event deleteCourse This event is fired when a course is deleted
*
* @param FixedDatesCourseListItemDTO courseDetails courseDetails The object containing the course informations
* @param Object translateDTO The object containing all the translations for this class
* @param SchoolCourses parent The parent
*
**/
var SchoolCourseObject = new Class({
Implements: Events,
/**
* This is the initialization function
*
*/
initialize : function(courseDetails, schoolCourses, courseType) {
this._translateDTO = {};
this._courseDetails = courseDetails;
this._schoolCourses = this._parent = schoolCourses;
this._courseType = courseType;
this._booking = this._schoolCourses.getSchool().getBooking();
this._booking.addEvent('descriptionsUpdated', function(){
this.updateDescriptions();
}.bind(this));
this._dateFormat = this._booking.getDateFormat();
this._monthsTranslations = this._booking.getMonthTranslations();
this._courseStartDate = null;
this._courseEndDate = null;
this._courseDuration = null;
},
/**
* Return the course details DTO
*
* @return CourseDetailsDTO The course Details DTO
*/
getCourseDetails : function() {
return this._courseDetails;
},
/**
* Return true if the course is complete
*
* @return Boolean Return true if the course is complete
*/
isComplete : function() {
if (this._courseStartDate && this._courseEndDate && this._courseDuration) {
return true;
} else {
return false;
}
},
updateDescriptions : function() {
if (this._booking.getDescription && this._booking.getDescription('course', this._courseDetails.courseTypeId) && this._booking.getDescription('course', this._courseDetails.courseTypeId) != '') {
this._descriptionText = this._booking.getDescription('course', this._courseDetails.courseTypeId);
/*   this._moreInfo.setAttribute('title', this._courseDetails.name + ' :: ' + this._booking.getDescription('course', this._courseDetails.courseTypeId));
this._moreInfo.setStyle('display', '');
if (!this._descriptionToolTip) {
this._descriptionToolTip = new SynergeeTips(this._moreInfo, {'className':'Booking-Tool-Tip Tool-tip', 'hideDelay':1000, 'fixed':true, 'closeButton':true, 'maxHeight':280});
} else {
this._descriptionToolTip.build(this._moreInfo);
}
*/
} else {
//            this._moreInfo.setStyle('display', 'none');
}
},
getDescriptionText : function () {
return this._descriptionText;
},
/*
updateDescriptions : function() {
if (this._booking.getDescription && this._booking.getDescription('course', this._courseDetails.courseTypeId) && this._booking.getDescription('course', this._courseDetails.courseTypeId) != '') {
this._moreInfo.setAttribute('title', this._courseDetails.name + ' :: ' + this._booking.getDescription('course', this._courseDetails.courseTypeId));
this._moreInfo.setStyle('display', '');
if (!this._descriptionToolTip) {
this._descriptionToolTip = new SynergeeTips(this._moreInfo, {'className':'Booking-Tool-Tip Tool-tip', 'hideDelay':1000, 'fixed':true, 'closeButton':true, 'maxHeight':280});
} else {
this._descriptionToolTip.build(this._moreInfo);
}
} else {
//            this._moreInfo.setStyle('display', 'none');
}
},*/
/**
* return the course start date
*
* @return Date The course start date
*/
getCourseStartDate : function() {
return this._courseStartDate;
},
/**
* return the course start date
*
* @return Date The course start date
*/
getCourseEndDate : function() {
return this._courseEndDate;
},
/**
*  return the course duration
*
* @return integer The course duration
*/
getCourseDuration : function() {
return this._courseDuration;
},
/**
*  Returns the available accommodations for this course
*
* @return Array of integer The Accommodation IDs
*/
getAvailableAccommodations : function() {
return this._courseDetails.availableAccommodations;
},
/**
* Return an object containing the informations for the calculation price
*
* @return Object The informations needed to calculate the price
*/
getDTO : function() {
var courseDTO = new Object();
courseDTO.id = this._courseDetails.id;
courseDTO.startDate = this._courseStartDate ? this._courseStartDate.toIso() : null;
courseDTO.endDate = this._courseEndDate ? this._courseEndDate.toIso() : null;
return courseDTO;
},
/**
* Return the duration in weeks.
* To perform the calculation we count the number of time the week day of the start date
* can be repeated between the start date and the end date.
*
* @param date startDate The start date
* @param date endDate The end date
* @return integer the number of weeks
*/
getCalculatedDuration : function(startDate, endDate) {
var numberOfWeeks = 0;
if(startDate && endDate){
var refStartDate = startDate.getTime();
var refEndDate = endDate.getTime() - (86400 * 3 *1000) ;
while (refStartDate <= refEndDate) {
numberOfWeeks++;
refStartDate += (86400 * 7 * 1000);
}
if(numberOfWeeks == 0 && startDate.getTime() < endDate.getTime() ){
numberOfWeeks = 1;
}
}
return numberOfWeeks;
},
/**
* return the accommodation end date for this course
*
* @return Date The accommodation end date date
*/
getAccommodationEndDate : function() {
return new Date(this._courseEndDate);
},
/**
* return the course type
*
* @return string the course type
*/
getCourseType : function () {
return this._courseType;
},
/**
* return the parent
*
* @return SchoolCourses The parent
*/
getParent : function() {
return this._parent;
},
/**
* Set the element container of this course
*
* @param element
*/
setElement : function(element){
this._element = element;
},
/**
* Set the element container of this course
*
* @return  The element containing this course
*/
getElement : function(){
return this._element;
},
/**
* delete this course
*
*/
deleteCourse : function (){
this.fireEvent('delete');
}
});/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @event Event refreshPrice This is fired when the price have to be recalculated
* @event Event refreshAccommodations This is fired when a course is modified and the accommodations have to be refreshed
* @event Event heightModified This is fired when a course is added or deleted
*
* @param FixedDatesCourseListItemDTO schoolFixedCoursesDTO The DTO containing all the fixed courses informations
* @param VariableDatesCourseListItemDTO schoolVariableCoursesDTO The DTO containing all the variable courses informations
* @param Object translateDTO The DTO containing all the translations for this class
* @param School parent The parent
**/
var SchoolCourses = new Class({
Implements: Events,
/**
* This is the initialization function
*
*/
initialize : function(schoolFixedCoursesDTO, schoolVariableCoursesDTO, schoolOptionalCoursesDTO, oSchool) {
this._schoolFixedCoursesDTO = schoolFixedCoursesDTO;
this._schoolVariableCoursesDTO = schoolVariableCoursesDTO;
this._schoolOptionalCoursesDTO = schoolOptionalCoursesDTO;
this._courses = new Array();
this._optionalCourses = new Array();
this._school = this._parent = oSchool;
this._booking = this._school.getParent();
},
/**
* Return the school component
*/
getSchool : function () {
return this._school;
},
/**
* Returns an object containing the informations for the calculation price
*
* @return Object The fixed course informations needed to calculate the price
*/
getFixedCoursesDTO : function() {
var schoolDTO = new Array();
this._courses.each(function(item, index) {
if (item.getCourseType() == 'fixed' && item.isComplete()) {
schoolDTO.push(item.getDTO());
}
});
return schoolDTO;
},
/**
* Returns an object containing the informations for the calculation price
*
* @return Object the variable course informations needed to calculate the price
*/
getVariableCoursesDTO : function() {
var schoolDTO = new Array();
this._courses.each(function(item, index) {
if (item.getCourseType() == 'variable' && item.isComplete()) {
schoolDTO.push(item.getDTO());
}
});
return schoolDTO;
},
/**
* Returns an object containing the informations for the calculation price
*
* @return Object the variable course informations needed to calculate the price
*/
getOptionalCoursesDTO : function() {
var schoolDTO = new Array();
this._optionalCourses.each(function(item, index) {
if ( item.isComplete()) {
schoolDTO.push(item.getDTO());
}
});
return schoolDTO;
},
/**
*
* Populate the fixed courses from a dto
*
* @param Array The fixed course informations needed to populate
*/
populateCoursesFromDTO : function(fixedCoursesToPopulateDTO, variableCoursesToPopulateDTO, optionalCoursesToPopulateDTO) {
// push every courses into one array
var coursesDTO = new Array();
if (fixedCoursesToPopulateDTO) {
fixedCoursesToPopulateDTO.each(function(item, index) {
item.courseType = "fixed";
coursesDTO.push(item);
});
}
if (variableCoursesToPopulateDTO) {
variableCoursesToPopulateDTO.each(function(item, index) {
item.courseType = "variable";
coursesDTO.push(item);
});
}
if (optionalCoursesToPopulateDTO) {
optionalCoursesToPopulateDTO.each(function(item, index) {
item.courseType = "optional";
coursesDTO.push(item);
});
}
// sort the courses by startdates
coursesDTO.sort(function(x1, x2) {
return ((new Date()).fromIso(x1.startDate) - (new Date()).fromIso(x2.startDate));
});
coursesDTO.each(function(item, index) {
var course;
switch(item.courseType){
case 'variable':
course = this._schoolVariableCoursesDTO.filter(function(itemDTO, indexDTO) {
return itemDTO.id == item.id;
}.bind(this));
break;
case 'fixed':
course = this._schoolFixedCoursesDTO.filter(function(itemDTO, indexDTO) {
return itemDTO.id == item.id;
}.bind(this));
break;
case 'optional':
course = this._schoolOptionalCoursesDTO.filter(function(itemDTO, indexDTO) {
return itemDTO.id == item.id;
}.bind(this));
break;
}
// if a course has been found
if (course[0]) {
var newCourse = this.addCourse(course[0], item, true);
}
}.bind(this));
},
/**
* Return the course information dto from its id
*
* @param integer courseId The course id
*/
getCourseInformationDTO : function (courseId) {
var course = this._schoolVariableCoursesDTO.filter(function(itemDTO, indexDTO) {
return itemDTO.id == courseId;
}.bind(this));
if(!course.length)
course = this._schoolFixedCoursesDTO.filter(function(itemDTO, indexDTO) {
return itemDTO.id == courseId;
}.bind(this));
if(!course.length)
course = this._schoolOptionalCoursesDTO.filter(function(itemDTO, indexDTO) {
return itemDTO.id == courseId;
}.bind(this));
return course.length ? course[0] : null;
},
/**
* Refresh all the courses
*
*/
refreshCourses : function() {
var availableOptionalCourses = this.getValidOptionalCourses();
// remove the optionals course not allowed to be there
this._optionalCourses.each(function(item, index) {
if (!availableOptionalCourses.contains(parseInt(item.getCourseDetails().id))) {
this._optionalCourses.erase(item);
}else {
var modified = false;
var validStartDates = this.getValidOptionalCourseStartDate(item);
if (validStartDates.length && !validStartDates.contains(item.getCourseStartDate())) {
item.setCourseStartDate(validStartDates[0]);
modified = true;
}
var validDurations = this.getValidOptionalCourseDuration(item);
if (!validDurations.contains(item.getCourseDuration()) && !item.getCourseDetails().oneDayCourse ) {
item.setCourseDuration(validDurations.getLast());
modified = true;
}
if(!validStartDates.length &&  !validDurations.length){
this._courses.erase(item);
}
//                if (modified) item.refreshOptionalCourseData();
}
}.bind(this));
this.refreshCoursesList();
if(this.optionalCoursesList){
this.refreshVisualOptionalCoursesList(this.optionalCoursesList);
}
this._courses.each(function(item, index) {
item.refreshLists();
});
this.fireEvent('heightModified');
},
/**
* Refresh the courses list
*
* @param SchoolList list the list we want to populate
*
*/
refreshCoursesList : function() {
this._validCourses = this.getValidCourses();
},
/**
* Return the valid courses list
*
*/
getValidCoursesList : function () {
if(!this._validCourses)
this._validCourses = new Array();
return this._validCourses;
},
/**
* return the list of available optional course
*
* @return integer[] Array of optional course IDs
*/
getValidOptionalCourses : function() {
var completedCourses = this.getCompletedCourses();
var availableOptionalCourses = new Array();
for (var i = 0; i < completedCourses.length; i++) {
if (completedCourses[i].getCourseDetails().type != 'optional') {
for (var j = 0; j < completedCourses[i].getCourseDetails().availableOptionalCourses.length; j++) {
availableOptionalCourses.include(completedCourses[i].getCourseDetails().availableOptionalCourses[j]);
}
}
}
var validOptionalCourses = [];
if(completedCourses.length){
var startDate = completedCourses[0]._courseStartDate;
var endDate = completedCourses[completedCourses.length-1]._courseEndDate;
this._schoolOptionalCoursesDTO.each(function(optCourse){
if(availableOptionalCourses.contains(parseInt(optCourse.id))){
var valid = false;
optCourse.startDates.each(function(start){
if(start >= startDate && start <= endDate){
valid = true;
}
}.bind(this));
if(valid)
validOptionalCourses.push(parseInt(optCourse.id));
}
}.bind(this));
}
return validOptionalCourses;
},
/**
* Add a selected course
*
* @param Object course The course DTO
*/
addCourse : function(course, dto, noEvent) {
var newCourse;
switch (course.type) {
case 'fixedDate' :
newCourse = new SchoolFixedCourseObject(course, this);
this._courses.push(newCourse);
break;
case 'variableDate' :
newCourse = new SchoolVariableCourseObject(course, this);
this._courses.push(newCourse);
break;
case 'optional' :
newCourse = new SchoolOptionalCourseObject(course, this);
this._optionalCourses.push(newCourse);
break;
}
newCourse.addEvent('deleteCourse', function(e) {
this.deleteCourse(newCourse);
this.onCourseUpdated();
}.bind(this));
if(dto){
newCourse.populateFromDTO(dto);
}
this.fireEvent('courseAdded', newCourse);
newCourse.addEvent('courseUpdated', this.onCourseUpdated.bind(this));
if(!noEvent){
newCourse.fireEvent('courseUpdated');
this.fireEvent('heightModified');
}
return newCourse;
},
/**
* Add a course from its ID
*
* @param integer courseId the course id
*/
addCourseFromId : function(courseId, dto, noEvent, courseType) {
var courseItem = [];
if (!courseType || courseType == 'variableDates' || courseType == 'variableDate') {
courseItem = this._schoolVariableCoursesDTO.filter(function (item, index) {
return item.id == courseId;
}.bind(this));
}
if (!courseType || courseType == 'fixedDates' || courseType == 'fixedDate') {
if (!courseItem.length) {
courseItem = this._schoolFixedCoursesDTO.filter(function (item, index) {
return  item.id == courseId;
}.bind(this));
}
}
if (!courseType || courseType == 'optional') {
if (!courseItem.length) {
courseItem = this._schoolOptionalCoursesDTO.filter(function (item, index) {
return  item.id == courseId;
}.bind(this));
}
}
if(courseItem.length) {
return this.addCourse(courseItem[0], dto, noEvent);
}
return null;
},
/**
* Delete the course given in parameter
*
* @param SchoolCourseObject course The course to delete
*/
deleteCourse : function(course) {
if (course.getCourseType() == 'optional') {
if (course.isComplete()) {
this._optionalCourses.erase(course);
this.onCourseUpdated();
} else {
this._optionalCourses.erase(course);
}
} else {
if (course.isComplete()) {
this._courses.erase(course);
this.onCourseUpdated();
} else {
this._courses.erase(course);
}
}
this.fireEvent('coursesUpdated');
},
/**
* Remove all the courses
*/
removeAllCourses : function (includeOptional, noEvent) {
if(includeOptional)this._optionalCourses = new Array();
this._courses = new Array();
if(!noEvent){
this.onCourseUpdated();
this.fireEvent('coursesUpdated');
}
},
/**
* Called when a course is modified
*/
onCourseUpdated : function() {
this.refreshCourses();
this.fireEvent('coursesUpdated');
},
/**
* Returns all the courses stocked in the DTO
*
* @return Array An array containing all the courses
*/
getCourses : function(noFilter) {
var validCourses = new Array();
// retrieving the valid fixed courses
this._schoolFixedCoursesDTO.each(function(item, index) {
// check if there are course type filter
if(noFilter || this.isValidFromFilter(item)){
validCourses.include(item);
}
}.bind(this));
// retrieving the variable courses
this._schoolVariableCoursesDTO.each(function(item, index) {
// check if there are course type filter
if(noFilter || this.isValidFromFilter(item)){
validCourses.include(item);
}
}.bind(this));
validCourses = validCourses.sort(this.sortFunction);
return validCourses;
},
/**
* Check if the given course is valid with the booking filters
*
* @param course
* @return {Boolean}
*/
isValidFromFilter : function(course){
if(!this._booking.getCourseTypesFilter().length || this._booking.getCourseTypesFilter().indexOf(parseInt(course.courseTypeId)) != -1){
if(this._booking.getLevelFilter() == 0 ||
(this._booking.getLevelFilter() >= course.minimumAcceptedLevel && this._booking.getLevelFilter() <= course.maximumAcceptedLevel)){
return true;
}
}
return false
},
/**
* Returns all the optional courses stored in the DTO
*
* @return Array An array containing all the courses
*/
getOptionalCourses : function() {
var validCourses = new Array();
// retrieving the valid optional courses
this._schoolOptionalCoursesDTO.each(function(item, index) {
validCourses.include(item);
});
return validCourses;
},
/**
* This method returns the number of completed courses
*
* @param Boolean ignoreOptionalCourses true if it shouldnt return the optional courses
*
* @return SchoolCourseObject The courses completed
*/
getCompletedCourses : function(ignoreOptionalCourses) {
var completedCourses = new Array();
if (this._courses.length) {
this._courses.each(function(item, index) {
if (item.isComplete()) {
completedCourses.push(item);
}
});
}
if (!ignoreOptionalCourses && this._optionalCourses.length) {
this._optionalCourses.each(function(item, index) {
if (item.isComplete()) {
completedCourses.push(item);
}
});
}
return completedCourses;
},
/**
* Return all the valid courses added in the cart
*
* @param Boolean bIgnoreOptional True to ignore the optional courses
*
* @return SchoolCourse[] Return an array of valid course in the cart
*/
getValidAddedCourses : function(bIgnoreOptional) {
var validCourses = new Array();
for (var i = 0; i < this._courses.length; i++) {
if (this._courses[i].isComplete()) {
if(!bIgnoreOptional || this._courses[i].getCourseDetails().type != 'optional'){
validCourses.push(this._courses[i]);
}
}
}
if(!bIgnoreOptional){
for (var i = 0; i < this._optionalCourses.length; i++) {
if (this._optionalCourses[i].isComplete()) {
validCourses.push(this._optionalCourses[i]);
}
}
}
return validCourses;
},
/**
* Returns the valid courses for a given period based on a courseObject
*
* @param SchoolCourseObject course The course object, if empty we consider that we want to add a new course
* @param boolean optionalCourse True to check only the optional courses
*
* @return Array Return an array of course DTO (by default the one that is given in parameter)
*/
getValidCourses : function(course, optionalCourse) {
var startDate = new Date();
var endDate = new Date(startDate.getFullYear() + 10, startDate.getMonth(), startDate.getDate());
var validCourses = new Array();
// initializing the start and end dates
// if we want to add a new course
if (!course) {
if (this._courses.length > 0 && this._courses.getLast()._courseEndDate) {
startDate = this._courses.getLast()._courseEndDate;
}
// if we want to replace a course
} else {
// if the course is not the last one and there are more than one course
if (course != this._courses.getLast() && this._courses.length > 1) {
if (this._courses[this._courses.indexOf(course) + 1]._courseStartDate)endDate = this._courses[this._courses.indexOf(course) + 1]._courseStartDate;
}
// if this courses is not the first
if (this._courses.indexOf(course) > 0) {
if (this._courses[this._courses.indexOf(course) - 1]._courseEndDate)startDate = this._courses[this._courses.indexOf(course) - 1]._courseEndDate;
}
}
if (!optionalCourse) {
// retrieving the valid fixed courses
this._schoolFixedCoursesDTO.each(function(item, index) {
var courseItem = item;
if(this.isValidFromFilter(item)){
item.sessions.each(function(item, index) {
//alert(item.courseStartDate);
if (item.courseStartDate >= startDate && item.courseEndDate <= endDate) {
validCourses.include(courseItem);
}
});
}
}.bind(this));
// retrieving the valid variable courses
this._schoolVariableCoursesDTO.each(function(item, index) {
var itemDuration = item.duration[0];
var courseItem = item;
if (this.isValidFromFilter(item)) {
item.startDates.each(function (item, index) {
var itemEndDate = new Date(item.getFullYear(), item.getMonth(), item.getDate() + itemDuration * 7);
if (item >= startDate && itemEndDate <= endDate) {
validCourses.include(courseItem);
}
}.bind(this));
}
}.bind(this));
} else {
// retrieving the valid optional courses
this._schoolOptionalCoursesDTO.each(function(item, index) {
if(this.isValidFromFilter(item)){
// need to recalulcate the start date for the optional courses
var completedCourses = this.getCompletedCourses();
if (completedCourses.length) {
var startDate = completedCourses[0].getCourseStartDate();
} else {
var startDate = new Date();
}
for (var i = 0; i < completedCourses.length; i++) {
if (completedCourses[i].getCourseDetails().availableOptionalCourses && completedCourses[i].getCourseDetails().availableOptionalCourses.contains(parseInt(item.id))) {
startDate = completedCourses[i].getCourseStartDate();
break;
}
}
var itemDuration = parseInt(item.duration[0]);
var courseItem = item;
item.startDates.each(function (itemStartDate, index) {
var itemEndDate = new Date(itemStartDate.getFullYear(), itemStartDate.getMonth(), (itemStartDate.getDate() + itemDuration * 7));
if (itemStartDate >= startDate && itemEndDate <= endDate) {
validCourses.include(courseItem);
}
}.bind(this));
}
}.bind(this));
}
// if there is no course available and we are not on the add course list,
//  we just add the one that is already there
if (!validCourses.length && course) {
var courseItem = this._schoolVariableCoursesDTO.filter(function(item, index) {
return item.id == course._courseDetails.id;
}.bind(this));
if (!courseItem.length) {
courseItem = this._schoolFixedCoursesDTO.filter(function(item, index) {
return  item.id == course._courseDetails.id;
}.bind(this));
}
validCourses.include(courseItem[0]);
}
// sort the list
validCourses = validCourses.sort(this.sortFunction);
return validCourses;
},
/**
* todo add the course type filter
* @param originalCourse
* @param newCourseId
* @param courseType
* @return {*}
*/
replaceCourse : function(originalCourse, newCourseId, courseType){
if(originalCourse.getCourseType() == 'optional'){
var courseIndex = this._optionalCourses.indexOf(originalCourse);
}else {
var courseIndex = this._courses.indexOf(originalCourse);
}
var dto = originalCourse.getDTO();
if (courseIndex != -1) {
var courseItem = this._schoolVariableCoursesDTO.filter(function(item, index) {
return item.id == newCourseId;
}.bind(this));
if (!courseItem.length) {
courseItem = this._schoolFixedCoursesDTO.filter(function(item, index) {
return  item.id == newCourseId;
}.bind(this));
}
if (!courseItem.length) {
courseItem = this._schoolOptionalCoursesDTO.filter(function(item, index) {
return  item.id == newCourseId;
}.bind(this));
}
if (courseItem.length) {
courseItem = courseItem[0];
var newCourse;
switch (courseItem.type) {
case 'fixedDate' :
newCourse = new SchoolFixedCourseObject(courseItem, this);
break;
case 'variableDate' :
newCourse = new SchoolVariableCourseObject(courseItem, this);
break;
case 'optional' :
newCourse = new SchoolOptionalCourseObject(courseItem, this);
break;
}
if(newCourse){
if(originalCourse.getCourseType() == 'optional'){
this._optionalCourses.splice(courseIndex, 1, newCourse);
} else {
this._courses.splice(courseIndex, 1, newCourse);
}
newCourse.addEvent('courseUpdated', this.onCourseUpdated.bind(this));
newCourse.addEvent('deleteCourse', function(e) {
this.deleteCourse(newCourse);
this.onCourseUpdated();
}.bind(this));
this.fireEvent('courseAdded', newCourse);
newCourse.populateFromDTO(dto);
newCourse.fireEvent('courseUpdated');
this.fireEvent('heightModified');
return newCourse;
}
}
}
},
/**
* function called to sort an array of valid courses
*
* @param courseDTO a The first course to compare
* @param courseDTO b The second course to compare
*/
sortFunction : function(a, b) {
return a.position - b.position;
},
/**
* Returns the valid courses date session for a given period based on a courseObject
*
* @param SchoolCoursesObject course The course object, if empty we consider that we want to add a new course
*
* @return Array An array of validSessions
*/
getValidFixedCourseSession : function(course) {
var validSessions = new Array();
var startDate = new Date();
var endDate = new Date(startDate.getFullYear() + 10, startDate.getMonth(), startDate.getDate());
// if the course is not the last one and there are more than one course
if (course != this._courses.getLast() && this._courses.length > 1) {
for (var i = this._courses.indexOf(course) + 1; i < this._courses.length; i++) {
if (this._courses[i]._courseStartDate)endDate = this._courses[i]._courseStartDate;
}
}
// if this courses is not the first
if (this._courses.indexOf(course) > 0) {
for (var i = this._courses.indexOf(course) - 1; i >= 0; i--) {
if (this._courses[i].getCourseDetails().type != 'optional' && this._courses[i]._courseEndDate)startDate = this._courses[i]._courseEndDate;
}
}
// retrieve the valid sessions
course._courseDetails.sessions.each(function(item, index) {
if (item.courseStartDate >= startDate && item.courseEndDate <= endDate) {
validSessions.include(item);
}
}.bind(this));
return validSessions;
},
/**
* Returns the valid courses date session for a given period based on a courseObject
*
* @param SchoolCoursesObject course The course object, if empty we consider that we want to add a new course
*
* @return Array An Array of valid start date
*/
getValidVariableCourseStartDate : function(course) {
var validStartDates = new Array();
var startDate = new Date();
var endDate = new Date(startDate.getFullYear() + 10, startDate.getMonth(), startDate.getDate());
var minimalEndDate = '';
var duration = course._courseDuration;
var availabilities = course.getCourseDetails().availabilities;
if (!duration) {
duration = course._courseDetails.duration[0];
}
// if the course is not the last one and there are more than one course
if (course != this._courses.getLast() && this._courses.length > 1) {
for (var i = this._courses.indexOf(course) + 1; i < this._courses.length; i++) {
if (this._courses[i].getCourseDetails().type != 'optional' && this._courses[i]._courseStartDate)endDate = this._courses[i]._courseStartDate;
}
}
// if this courses is not the first
if (this._courses.indexOf(course) > 0) {
for (var i = 0; i < this._courses.indexOf(course); i++) {
if (this._courses[i].getCourseDetails().type != 'optional' && this._courses[i]._courseEndDate)startDate = this._courses[i]._courseEndDate;
}
}
course._courseDetails.startDates.each(function(item, index) {
// the minimal end date is the date minus the minimal course duration
minimalEndDate = new Date(item.getFullYear(), item.getMonth(), item.getDate() + 7 * duration);
if (item >= startDate && minimalEndDate <= endDate) {
var year = item.getFullYear();
var week = item.getWeek();
if (course.getCourseDetails().courseStartDay == 7) {
if (week > 1)week = week + 1;
}
// check the availabilities for this start date
var toAdd = true;
if (availabilities) {
for (var i = 0; i < duration; i++) {
if (week > 52) {
var lastWeek = (new Date(item.getFullYear(), 12, 31)).getWeek();
if (week < lastWeek) {
week = week - lastWeek;
year = year + 1;
}
}
if (availabilities[year + '' + week] == 0) {
toAdd = false;
}
week++;
}
}
if (toAdd) {
validStartDates.push(item);
}
}
}.bind(this));
return validStartDates;
},
/**
* Returns the valid courses date session for a given period based on a courseObject
*
* @param SchoolCoursesObject course The course object, if empty we consider that we want to add a new course
*
* @return Array An Array of valid start date
*/
getValidOptionalCourseStartDate : function(course) {
var validStartDates = new Array();
var periods = new Array();
var sameCoursesPeriods = new Array();
var addedCourses = this.getValidAddedCourses();
var sameCourses = new Array();
var availabilities =  course.getCourseDetails().availabilities;
var additionalDays = course.getCourseDetails().additionalDays;
// check every course to calculate the start date and end date validity
for (var i = 0; i < addedCourses.length; i++) {
if (addedCourses[i].getCourseDetails().type != 'optional' && addedCourses[i].getCourseDetails().availableOptionalCourses.contains(parseInt(course.getCourseDetails().id))) {
var period = new Object();
period.startDate = (new Date()).fromIso(addedCourses[i].getDTO().startDate);
period.endDate = (new Date()).fromIso(addedCourses[i].getDTO().endDate);
if(additionalDays){
period.endDate.setDate(period.endDate.getDate() + additionalDays);
}
periods.push(period);
}
if (addedCourses[i].getCourseDetails().type == 'optional' && addedCourses[i].getCourseDetails().id == course.getCourseDetails().id && addedCourses[i] != course) {
sameCourses.push(addedCourses[i]);
}
}
if (sameCourses.length) {
for (var i = 0; i < sameCourses.length; i++) {
var period = new Object();
period.startDate = sameCourses[i]._courseStartDate;
period.endDate = sameCourses[i]._courseEndDate;
sameCoursesPeriods.push(period);
}
}
var minimalEndDate = '';
var duration = course._courseDuration;
if (!duration) {
duration = course._courseDetails.duration[0];
}
course._courseDetails.startDates.each(function(item, index) {
// check if the same courses is already added
var toAdd = true;
if (sameCoursesPeriods.length) {
for (var j = 0; j < sameCoursesPeriods.length; j++) {
if ((item >= sameCoursesPeriods[j].startDate && item <= sameCoursesPeriods[j].endDate)) {
toAdd = false;
}
}
}
if (toAdd) {
// the minimal end date is the date minus the minimal course duration
if(course.getCourseDetails().oneDayCourse){
minimalEndDate = new Date(item.getFullYear(), item.getMonth(), item.getDate());
} else {
minimalEndDate = new Date(item.getFullYear(), item.getMonth(), item.getDate() + 7 * duration);
}
minimalEndDate = course.getOptionalCourseEndDate(minimalEndDate);
for (var i = 0; i < periods.length; i++) {
if (item >= periods[i].startDate && minimalEndDate <= periods[i].endDate) {
var year = item.getFullYear();
var week = item.getWeek();
if (course.getCourseDetails().courseStartDay == 7) {
if (week > 1)week = week + 1;
}
// check the availabilities for this start date
var toAdd = true;
if (availabilities) {
for (var i = 0; i < duration; i++) {
if (week > 52) {
var lastWeek = (new Date(item.getFullYear(), 12, 31)).getWeek();
if (week < lastWeek) {
week = week - lastWeek;
year = year + 1;
}
}
if (availabilities[year + '' + week] == 0) {
toAdd = false;
}
week++;
}
}
// if the item can be added
if (toAdd) {
validStartDates.include(item);
break;
}
}
}
}
}.bind(this));
return validStartDates;
},
/**
* Returns the valid courses date session for a given period based on a courseObject
*
* @param SchoolCoursesObject course The course object, if empty we consider that we want to add a new course
*
* @return Array An array of valid durations
*/
getValidOptionalCourseDuration : function(course) {
var validDurations = new Array();
var sameCoursesPeriods = new Array();
var addedCourses = this.getValidAddedCourses();
var sameCourses = new Array();
var periods = new Array();
var availabilities = course.getCourseDetails().availabilities;
var additionalDays = course.getCourseDetails().additionalDays;
if(addedCourses.length){
for (var i = 0; i < addedCourses.length; i++) {
if (addedCourses[i].getCourseDetails().type != 'optional' && addedCourses[i].getCourseDetails().availableOptionalCourses.contains(parseInt(course.getCourseDetails().id))) {
var period = new Object();
period.startDate = (new Date()).fromIso(addedCourses[i].getDTO().startDate);
period.endDate = (new Date()).fromIso(addedCourses[i].getDTO().endDate);
if(additionalDays){
period.endDate.setDate(period.endDate.getDate() + additionalDays);
}
periods.push(period);
}
if (addedCourses[i].getCourseDetails().type == 'optional' && addedCourses[i].getCourseDetails().id == course.getCourseDetails().id && addedCourses[i] != course) {
sameCourses.push(addedCourses[i]);
}
}
if (sameCourses.length) {
for (var i = 0; i < sameCourses.length; i++) {
var period = new Object();
period.startDate = sameCourses[i]._courseStartDate;
period.endDate = sameCourses[i]._courseEndDate;
sameCoursesPeriods.push(period);
}
}
var optionEndDate = '';
var week = -1;
var year = -1;
if (course.getCourseStartDate()) {
week = course.getCourseStartDate().getWeek();
year = course.getCourseStartDate().getFullYear();
if(course.getCourseDetails().courseStartDay== 7){
if(week > 1)week = week+1;
}
}
// for each duration
for (var i = 0; i < (course.getCourseDetails().duration.length); i++) {
var value = course.getCourseDetails().duration[i];
// check if there is an availability
if (week == -1 || !availabilities ||
availabilities[year + '' + (week + parseInt(value) - 1)] != 0) {
var toAdd = true;
// the minimal end date is the date minus the minimal course duration
var daysPerWeek = (course.getCourseDetails().oneDayCourse ? 1 : 7);
if (course._courseStartDate) {
optionEndDate = new Date(course._courseStartDate.getFullYear(), course._courseStartDate.getMonth(), course._courseStartDate.getDate() + daysPerWeek * value);
optionEndDate = course.getOptionalCourseEndDate(optionEndDate);
} else {
optionEndDate = new Date(periods[0].startDate.getFullYear(), periods[0].startDate.getMonth(), periods[0].startDate.getDate() + daysPerWeek * value);
}
// check each periods of courses it can be attached
for (var j = 0; j < periods.length; j++) {
if (course._courseStartDate >= periods[j].startDate && optionEndDate <= periods[j].endDate || !course._courseStartDate) {
// and check the end date meets the right range.
for (var k = 0; k < sameCoursesPeriods.length; k++) {
if (optionEndDate >= sameCoursesPeriods[k].startDate && optionEndDate < sameCoursesPeriods[k].endDate) {
toAdd = false;
}
}
if (toAdd) {
validDurations.include(value);
break;
}
}
}
// if there is no more availability, the valid durations stops here
}else{
break;
}
}
}
return validDurations;
},
/**
* Returns the valid courses date session for a given period based on a courseObject
*
* @param SchoolCoursesObject course The course object, if empty we consider that we want to add a new course
*
* @return Array An array of valid durations
*/
getValidVariableCourseDuration : function(course) {
var validDurations = new Array();
var startDate = new Date();
var endDate = new Date(startDate.getFullYear() + 10, startDate.getMonth(), startDate.getDate());
var calculatedEndDate;
var availabilities = course.getCourseDetails().availabilities;
// if the course is not the last one and there are more than one course
if (course != this._courses.getLast() && this._courses.length > 1) {
for (var i = this._courses.indexOf(course) + 1; i < this._courses.length; i++) {
if (this._courses[i].getCourseDetails().type != 'optional' && this._courses[i]._courseStartDate)endDate = this._courses[i]._courseStartDate;
}
}
// if this courses is not the first
if (this._courses.indexOf(course) > 0) {
for (var i = this._courses.indexOf(course) - 1; i >= 0; i--) {
if (this._courses[i]._courseEndDate)startDate = this._courses[i]._courseEndDate;
}
}
var week = -1;
var year = -1;
if (course.getCourseStartDate()) {
week = course.getCourseStartDate().getWeek();
year = course.getCourseStartDate().getFullYear();
if(course.getCourseDetails().courseStartDay == 7){
if(week > 1)week = week+1;
}
}
// find the default start date, usefull when the start date is not the same as the course information one
if(course.getCourseStartDate()){
var defaultCourseStartDate = new Date(course.getCourseStartDate());
// if the course start day is not the same as the given in the course informations
if (defaultCourseStartDate && course.getCourseDetails().courseStartDay && course.getCourseDetails().courseStartDay !=  defaultCourseStartDate.getDay()) {
var courseStartDayUp = (defaultCourseStartDate.getDay() == 0 ? 7 : defaultCourseStartDate.getDay());
var courseStartDayDown = courseStartDayUp;
var iter = 0;
while (course.getCourseDetails().courseStartDay != courseStartDayDown && course.getCourseDetails().courseStartDay != courseStartDayUp) {
if (courseStartDayDown == 0)courseStartDayDown = 7;
if (courseStartDayUp == 8)courseStartDayUp = 1;
courseStartDayDown--;
courseStartDayUp++;
iter++;
}
if (courseStartDayDown == course.getCourseDetails().courseStartDay) {
defaultCourseStartDate.setDate(defaultCourseStartDate.getDate() - iter);
} else {
defaultCourseStartDate.setDate(defaultCourseStartDate.getDate() + iter);
}
}
}
for (var i = 0; i < (course.getCourseDetails().duration.length); i++) {
var value = course.getCourseDetails().duration[i];
// the minimal end date is the date minus the minimal course duration
if (defaultCourseStartDate) {
calculatedEndDate = new Date(defaultCourseStartDate.getFullYear(), defaultCourseStartDate.getMonth(), defaultCourseStartDate.getDate() + 7 * value);
} else {
calculatedEndDate = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate() + 7 * value);
}
if (defaultCourseStartDate >= startDate && calculatedEndDate <= endDate || !defaultCourseStartDate) {
var currentYear = calculatedEndDate.getFullYear();
var currentWeek = calculatedEndDate.getWeek();
if(course.getCourseDetails().courseStartDay == 7){
if(currentWeek > 1)currentWeek = currentWeek+1;
}
if(currentWeek > 52){
currentWeek = currentWeek - 52;
currentYear = currentYear + 1;
}
if(currentWeek > 1)
currentWeek = currentWeek - 1;
if (currentWeek == -1 || !availabilities ||
availabilities[currentYear + '' + (currentWeek)] != 0) {
validDurations.push(value);
} else {
break;
}
}
}
return validDurations;
},
/**
* Returns the accommodation start date
*
* @optionalparam AccommodationDTO accommodation The Accommodation DTO
*
* @return Date The  possible start date
*/
getAccommodationStartDate : function() {
var newDate = null;
this._courses.each(function(item, index) {
// check if the course is complete
if (item.isComplete()) {
// if it is a fixed Course
if (item.getCourseType() == 'fixed') {
// if this accommodation is available with this course
if (newDate > item.getSession().accommodationStartDate || newDate == null) {
newDate = new Date(item.getSession().accommodationStartDate);
}
// if it's a variable course
} else {
// if this accommodation is available with this course
// the new date is set
if (newDate > item._courseStartDate || newDate == null) {
newDate = new Date(item._courseStartDate);
}
}
}
}.bind(this));
return newDate;
},
/**
* Returns the maximal final date
*
* @return Date The end date for the accommodations
*/
getAccommodationEndDate : function() {
var endDate = new Date();
if (this._courses.length) {
for (var i = 0; i < this._courses.length; i++) {
if (this._courses[i].getCourseDetails().type != 'optional' && this._courses[i].isComplete()) {
if (this._courses[i].getCourseType() == 'fixed') {
endDate = new Date(this._courses[i].getSession().accommodationEndDate);
} else {
endDate = new Date(this._courses[i]._courseEndDate);
}
}
}
}
return endDate;
},
/**
*  Returns the available accommodations
*
* @return Array of integer The Accommodation IDs
*/
getAvailableAccommodations : function() {
var availableAccommodations = new Array();
for (var i = 0; i < this._courses.length; i++) {
availableAccommodations = availableAccommodations.combine(this._courses[i].getAvailableAccommodations());
}
return availableAccommodations;
},
/**
* Return an array containing the mandatory accommodations with their start and end date
*
* @return Array of MandatoryAccommodationsDTO
*/
getMandatoryAccommodations : function() {
var mandatoryAccommodations = new Array();
for (var i = 0; i < this._courses.length; i++) {
if (this._courses[i].isComplete() && this._courses[i].getCourseDetails().accommodationMandatory == true) {
var mandatoryAccommodation = new Object();
mandatoryAccommodation.courseId = this._courses[i].getCourseDetails().id;
mandatoryAccommodation.ids = this._courses[i].getCourseDetails().availableAccommodations;
mandatoryAccommodation.startDate = this._courses[i].getDTO().startDate;
mandatoryAccommodation.endDate = this._courses[i].getDTO().endDate;
mandatoryAccommodations.push(mandatoryAccommodation);
}
}
return mandatoryAccommodations;
},
/**
* Return true if the transfer is mandatory
*
* @return boolean true if the transfers are mandatory
*/
getIsTransferMandatory : function() {
var mandatory = false;
for (var i = 0; i < this._courses.length; i++) {
if (this._courses[i].getCourseDetails().transferMandatory == true) {
mandatory = true;
}
}
return mandatory;
},
/**
* Returns the parent of this object
*
* @return SchoolPriceEstimation The parent object
*/
getParent : function() {
return this._parent;
}
});/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param TransferListItemDTO schoolTransfersDTO the transfers informations
* @param SchoolPriceEstimation parent the parent
**/
var SchoolCurrencySelector = new Class({
Implements: Events,
/**
* This is the initialization function
* @param TranslateDTO translateDTO The DTO containing the translations
* @param String defaultCurrency The default currency
* @param Array currenciesList An array containing all the currencies
* @param Object parent The parent
*/
initialize : function(translateDTO, defaultCurrency, currenciesList,parent) {
this._translateDTO = translateDTO;
this._parent = parent;
this._classNameBase = this._parent.getClassNameBase();
this._currencyAvailableList = currenciesList;
this._currency = defaultCurrency;
},
/**
* Return the available currencies
*/
getAvailableCurrencies : function () {
return this._currencyAvailableList;
},
/**
* return the visual transfer container
*/
getHtml : function() {
this._currencyContainer = new Element('div', {'class': this._classNameBase + 'Section' + " " + this._classNameBase + 'Currency-Section'});
this._currencyTitle = new Element('div', {'class': this._classNameBase + 'Section-Title'});
this._currencyButtons = new Element('div', {'class': this._classNameBase + 'Button' + ' ' + this._classNameBase + 'AddButton'});
this._currencyTitle.inject(this._currencyContainer);
//        this._currencyButtons.inject(this._currencyContainer);
this._currencyTitle.set("html", this._translateDTO.title);
this._currencyButtons.set("html", this._currency);
this._currencyButtons.inject(this._currencyContainer);
this._currencyList = new SchoolList(this._currencyButtons, this._classNameBase);
this.createVisualList(this._currencyList);
this._currencyList.getHtml().inject(this._parent.getListContainer());
this._currencyList.initializeEvents();
return this._currencyContainer;
},
/**
* Generate the list of currency
*
* @param SchoolList list The list to update
*/
createVisualList : function(list) {
for (var i = 0; i < this._currencyAvailableList.length; i++) {
list.addItem(this._currencyAvailableList[i].currencyCode, this._currencyAvailableList[i].currencyName + " (" + this._currencyAvailableList[i].currencyCode  + ")");
}
list.getListItems().each(function(item, index) {
item.setIsValid(true);
item.setIsSelectionnable(true);
if (item.getValue() == this._currency) {
this._currencyButtons.set("html", item.getHtmlValue());
list.setSelectedItem(item);
}
item.getHtml().addEvent('click', function(event) {
this._currencyButtons.set("html", item.getHtmlValue());
this._currency = item.getValue();
this._parent.fireEvent('refreshPrice');
list.setSelectedItem(item);
}.bind(this));
}.bind(this));
},
/**
* Returns the number of currency
*
* @return number The number of currency
*/
getCurrency : function() {
return this._currency;
},
/**
* Set the currency
*
* @param String currency the currency code to use
*/
setCurrency : function(currency) {
if (currency != this._currency) {
this._currency = currency;
if(this._parent.getSchools() && this._parent.getSchools().length){
this.fireEvent('refreshPrice');
}
}
}
});/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @event Event deleteCourse This event is fired when a course is deleted
*
* @param FixedDatesCourseListItemDTO courseDetails The object containing the course informations
* @param Object translateDTO The object containing all the translations for this class
* @param SchoolCourses parent The parent
*
**/
var SchoolFixedCourseObject = new Class({
Extends: SchoolCourseObject,
/**
* This is the initialization function
*
*/
initialize : function(courseDetails, schoolCourses) {
this.parent(courseDetails, schoolCourses, 'fixed');
this._session = -1;
this.type = 'fixed';
// the course start dates
this._sessionsObjects = new Array();
this._startDatesObject = new Array();
var addedStartDates = new Array();
var addedDurations = new Array();
this._courseDetails.sessions.each(function(session, index) {
var startDateName = session.courseStartDate.format(this._dateFormat, this._monthsTranslations);
this._sessionsObjects.push({id : index , name : startDateName});
if(!addedStartDates.contains(session.courseStartDate.getTime())){
this._startDatesObject.push({id : session.courseStartDate.getTime() + '', name : startDateName});
addedStartDates.push(session.courseStartDate.getTime())
}
var duration = this.getCalculatedDuration(session.courseStartDate, session.courseEndDate)
if(!addedDurations.contains(parseInt(duration))){
addedDurations.push(parseInt(duration));
}
// we store the dates values so the list is populated with the right object not the value stored in the dom object
}.bind(this));
addedDurations.sort(function(a,b){return a-b});
this._durations = [];
for(var i = 0; i < addedDurations.length; i++) {
this._durations.push({id : addedDurations[i], name : addedDurations[i]});
}
},
render : function () {
// start date template
var startDatesListTemplate = this._booking.getSchoolCourseStartDatesListTemplate(this._courseStartDate, this);
if(startDatesListTemplate){
$p(startDatesListTemplate.getSelector(), this._element).render(this._sessionsObjects, startDatesListTemplate.getDirective());
// the start dates list
if(this._startDatesList)
this._startDatesList.removeEvents('change');
this._startDatesList = this._booking.populateList(this._element.getElement(startDatesListTemplate.getSelector()));
if(this._session){
this._startDatesList.setValue(this._courseDetails.sessions.indexOf(this._session));
}
this._startDatesList.addEvent('change', function(e) {
var sessionIndex = parseInt(this._startDatesList.getValue());
if(this._courseDetails.sessions[sessionIndex]) {
this._session = this._courseDetails.sessions[sessionIndex];
}
this.refreshCourseData();
}.bind(this));
}
this.refreshCourseData();
},
/**
* Set the session
*
* @param session
*/
setSession : function(session) {
this._session = session;
this.refreshCourseData();
},
/**
* return the current session
*
* @return  session
*/
getSession : function() {
return this._session;
},
/**
* Set the session from its index
*/
setSessionIndex : function(sessionIndex){
if(this._courseDetails.sessions[sessionIndex]) {
this._session = this._courseDetails.sessions[sessionIndex];
}
this.refreshCourseData();
},
/*
updateDescriptions : function() {
if (this._manager.getDescription && this._manager.getDescription('course', this._courseDetails.courseTypeId) && this._manager.getDescription('course', this._courseDetails.courseTypeId) != '') {
this._moreInfo.setAttribute('title', this._courseDetails.name + ' :: ' + this._manager.getDescription('course', this._courseDetails.courseTypeId));
this._moreInfo.setStyle('display', '');
if (!this._descriptionToolTip) {
this._descriptionToolTip = new SynergeeTips(this._moreInfo, {'className':'Booking-Tool-Tip Tool-tip', 'hideDelay':1000, 'fixed':true, 'closeButton':true, 'maxHeight':280});
} else {
this._descriptionToolTip.build(this._moreInfo);
}
}
},
*/
/**
* return the accommodation end date for this course
*
* @return Date The accommodation end date date
*/
getAccommodationEndDate : function() {
return this._courseDetails.sessions[this._session].accommodationEndDate;
},
/**
* Populate the fixed course from a dto
*
* @param Object The fixed course informations needed to populate
*/
populateFromDTO : function(toPopulateDTO) {
this._session = null;
var startDate = toPopulateDTO.startDate;
var endDate = toPopulateDTO.endDate;
if(!(startDate instanceof Date)){
startDate = (new Date()).fromIso(startDate);
}
if(!(endDate instanceof Date)){
endDate = (new Date()).fromIso(endDate);
}
// Set the variables for this course
this._courseStartDate = startDate;
this._courseEndDate = endDate;
this._courseDuration = this.getCalculatedDuration(this._courseStartDate, this._courseEndDate);
// if the session hasn't been found (when the course is populated)
this._courseDetails.sessions.each(function(item, index) {
if (item.courseStartDate.format(this._dateFormat, this._monthsTranslations) == this._courseStartDate.format(this._dateFormat, this._monthsTranslations) &&
item.courseEndDate.format(this._dateFormat, this._monthsTranslations) == this._courseEndDate.format(this._dateFormat, this._monthsTranslations)) {
this._session = item;
}
}.bind(this));
if (!this._session) {
var diff = -1;
for (var i = 0; i < this._courseDetails.sessions.length; i++) {
var session = this._courseDetails.sessions[i];
if (i == 0 || diff > Math.abs(this._courseStartDate.getTime() - session.courseStartDate.getTime())) {
diff = Math.abs(this._courseStartDate.getTime() - session.courseStartDate.getTime());
this._session = session;
}
}
}
this.refreshCourseData();
},
/**
* Refresh all the lists of this course
*
*/
refreshLists : function() {
this.refreshVisualValidCoursesList(this._coursesValidList);
this.refreshVisualFixedCourseStartDates(this._schoolCourses.getValidFixedCourseSession(this));
},
getCourseValidSessions  : function () {
return this._schoolCourses.getValidFixedCourseSession(this);
},
/**
* Refresh the values when the user change a course information
*
* @param Object values The new values
*/
refreshCourseData : function() {
// Set the variables for this course
this._courseStartDate = this._session.courseStartDate;
this._courseEndDate = this._session.courseEndDate;
this._courseDuration = this.getCalculatedDuration(this._courseStartDate, this._courseEndDate);
// if the session hasn't been found (when the course is populated)
if (this._session == -1) {
this._courseDetails.sessions.each(function(item, index) {
if (item.courseStartDate.format(this._dateFormat, this._monthsTranslations) == this._courseStartDate.format(this._dateFormat, this._monthsTranslations) &&
item.courseEndDate.format(this._dateFormat, this._monthsTranslations) == this._courseEndDate.format(this._dateFormat, this._monthsTranslations)) {
this._session = item;
}
}.bind(this));
}
this.refreshLists();
this.fireEvent('courseUpdated');
},
/**
* Replace the courses and find the best possible dates
*
* @param Number newCourseId  The new course ID
*/
replaceCourse : function(newCourse) {
this._durationValue.removeClass(this._classNameBase + 'Section-Item-Info-ValueNotEditable');
if (newCourse.type ==  'fixed') {
if (this.isComplete()) {
this._courseDetails = newCourse;
this._durationValue.addClass(this._classNameBase + 'Section-Item-Info-ValueNotEditable');
this.refreshCourseData(this.getNewFixedCourseDates(this._courseStartDate));
this._title.set("html", this._courseDetails.name);
this._subTitle.set("html", this._courseDetails.subtitle);
this.reGenerateLists();
this._parent.fireEvent('refreshCourses');
} else {
this._schoolSessionsList.getHtml().destroy();
this._coursesValidList.getHtml().destroy();
this._parent.replaceCourse(this, newCourse);
}
} else {
this._schoolSessionsList.getHtml().destroy();
this._coursesValidList.getHtml().destroy();
if (this.isComplete()) {
var values = new Object();
values.startDate = this._courseStartDate;
values.duration = this._courseDuration;
this._parent.replaceCourse(this, newCourse, values);
} else {
this._parent.replaceCourse(this, newCourse);
}
}
},
/**
* Returns the best dates for the new fixed courses based on the old one
*
* @param Date oldStartDate The date of the replaced course
*
* @return Object The new dates session.
*/
getNewFixedCourseDates : function(oldStartDate) {
if (oldStartDate) {
var dateDifference;
var newDates;
this._courseDetails.sessions.each(function(item, index) {
if (dateDifference > Math.abs(oldStartDate - item.courseStartDate) || !dateDifference) {
dateDifference = Math.abs(oldStartDate - item.courseStartDate);
newDates = item;
}
}.bind(this));
return newDates;
}
},
/**
* Refresh a list of course that have possible dates between this courseStartDate and courseEndDate
*
* @param SchoolList list The valid courses list
*
*/
refreshVisualValidCoursesList : function(list) {
var validCourses = this._parent.getValidCourses(this);
//todo remove
//        list.getListItems().each(function(item, index) {
//            if (validCourses.indexOf(item.getValue()) != -1) {
//                item.setIsValid(true);
//            } else {
//                item.setIsValid(false);
//            }
//        }.bind(this));
},
/**
* Refresh a list from an array of a fixed course start dates
*
* @param Array validStartDates An array of Date containing the validated startDates
* @param SchoolList list The valid courses list
*/
refreshVisualFixedCourseStartDates : function(validSessions) {
this._validSessionsIndexList = new Array();
validSessions.each(function(item, index){
this._validSessionsIndexList.push(this._courseDetails.sessions.indexOf(item) + '');
}.bind(this));
},
/**
* Return the valid course sessions index
*
* @return integer[] The valid index
*/
getValidCourseSessions : function () {
if(!this._validSessionsIndexList)
this._validSessionsIndexList = new Array();
return this._validSessionsIndexList;
},
/**
* Return the valid course start dates
*/
getValidCourseStartDates:function () {
var validSessions = this.getValidCourseSessions();
var validStartDates = [];
var startDatesAdded = [];
validSessions.each(function(item, index){
var session = this._courseDetails.sessions[item];
if(!startDatesAdded.contains(session.courseStartDate.getTime())){
validStartDates.push(session.courseStartDate);
startDatesAdded.push(session.courseStartDate.getTime() + '');
}
}.bind(this));
return startDatesAdded;
},
/**
* Return the valid course start dates
*/
getCourseValidDurations:function () {
var validDurations = [];
if (this._courseStartDate) {
this._courseDetails.sessions.each(function (session, index) {
if (session.courseStartDate.getTime() == this._courseStartDate.getTime()) {
validDurations.push(this.getCalculatedDuration(session.courseStartDate, session.courseEndDate) + '');
}
}.bind(this));
}
return validDurations;
},
setCourseStartDate : function (startDate) {
var sessions = [];
// Set the variables for this course
//        this._courseEndDate = endDate;
//        this._courseDuration = this.getCalculatedDuration(this._courseStartDate, this._courseEndDate);
// if the session hasn't been found (when the course is populated)
this._courseDetails.sessions.each(function(item, index) {
if (item.courseStartDate.format(this._dateFormat, this._monthsTranslations) == startDate.format(this._dateFormat, this._monthsTranslations)) {
sessions.push(item);
}
}.bind(this));
if(sessions.length == 1){
this.setSession(sessions[0]);
}
if(sessions.length > 1){
this._courseStartDate = startDate;
this._courseEndDate = null;
this._courseDuration = null;
this._session = null;
this.fireEvent('courseUpdated');
}
},
setCourseDuration : function (duration) {
var sessions = [];
if(this._courseStartDate){
// if the session hasn't been found (when the course is populated)
this._courseDetails.sessions.each(function(item, index) {
if (item.courseStartDate.format(this._dateFormat, this._monthsTranslations) == this._courseStartDate.format(this._dateFormat, this._monthsTranslations)) {
if(this.getCalculatedDuration(item.courseStartDate, item.courseEndDate) == parseInt(duration)){
sessions.push(item);
}
}
}.bind(this));
if(sessions.length){
this.setSession(sessions[0]);
}
}
},
getCourseDuration : function (){
if(this._courseDuration){
return this._courseDuration;
}
return null;
},
/**
* Return the duration in weeks.
* To perform the calculation we count the number of time the week day of the start date
* can be repeated between the start date and the end date.
*
* @param date startDate The start date
* @param date endDate The end date
* @return integer the number of weeks
*/
getCalculatedDuration : function(startDate, endDate) {
var numberOfWeeks = 0;
if(startDate && endDate){
var refStartDate = startDate.getTime();
var refEndDate = endDate.getTime() - (86400 * 3 *1000) ;
while (refStartDate <= refEndDate) {
numberOfWeeks++;
refStartDate += (86400 * 7 * 1000);
}
if(numberOfWeeks == 0 && startDate.getTime() < endDate.getTime() ){
numberOfWeeks = 1;
}
}
return numberOfWeeks;
}
/**
* This function remove the course from the list
*
*/
/* removeCourse : function() {
this._schoolSessionsList.getHtml().destroy();
this._coursesValidList.getHtml().destroy();
this._container.destroy();
}*/
});
/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param number id the insurance ID
* @param char name the insurance name
* @param SchoolInsurances parent
**/
var SchoolInsuranceObject = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(id, insuranceDTO, parent) {
this._insuranceDTO = insuranceDTO;
this._insuranceName = this._insuranceDTO.name;
this._type = this._insuranceDTO.type;
this._insuranceId = id;
this._parent = parent;
this._isMandatory = false;
this._insuranceStartDate = null;
this._insuranceEndDate = null;
this._insuranceDuration = null;
this._booking = this._parent.getParent();
this._booking.addEvent('descriptionsUpdated', function() {
this.updateDescriptions();
}.bind(this));
},
getInsuranceId : function () {
return this._insuranceId;
},
/**
* Return this insurance informations DTO
*
* @return InsuranceDTO The insurance informations DTO
*/
getInsuranceInformations : function() {
return this._insuranceDTO;
},
updateDescriptions : function() {
if (this._booking.getDescription && this._booking.getDescription('insurance', this._insuranceId) && this._booking.getDescription('insurance', this._insuranceId) != '') {
this._descriptionText = this._booking.getDescription('insurance', this._insuranceId);
} else {
this._descriptionText = null;
}
},
getDescriptionText : function () {
return this._descriptionText;
},
/**
* Returns an object containing the informations for the calculation price
*
* @return object The informations needed to calculate the price
*/
getDTO : function() {
var insuranceDTO = new Object();
insuranceDTO.id = this._insuranceId;
return insuranceDTO;
},
/**
* Return the insurance ID
*
* @return integer The Insurance ID
*/
getInsuranceId : function() {
return parseInt(this._insuranceId);
},
/**
* Populate the insurance from a dto
*/
populateFromDTO : function(toPopulateDTO) {
// there is nothing to populate in fact
},
/**
* Set the mandatory information of this insurance
*
* @param Boolean mandatory True if the insurance is mandatory
*/
setMandatory : function(mandatory) {
this._isMandatory = mandatory;
},
/**
* Return true if the insurance is mandatory
*/
getMandatory : function() {
return this._isMandatory;
},
/**
* Set the dates of the insurance
* @param Date startDate The Start Date
* @param Date endDate The End Date
*/
setDates : function(startDate, endDate, duration) {
this._insuranceStartDate = startDate;
if (endDate)endDate.setDate(endDate.getDate() + this._insuranceDTO.additionalMandatoryDays);
this._insuranceEndDate = endDate;
this._insuranceDuration = duration + this._insuranceDTO.additionalMandatoryDays;
},
/**
* Return date the insurance start date
*/
getInsuranceStartDate : function() {
return this._insuranceStartDate;
},
/**
* Return date the insurance end date
*/
getInsuranceEndDate : function() {
return this._insuranceEndDate;
},
/**
* Return date the insurance duration
*/
getInsuranceDuration : function() {
return this._insuranceDuration;
},
/**
* Return true if the insurance is complete
*
* @return Boolean Return true if complete
*/
isComplete : function() {
if (this._insuranceStartDate && this._insuranceEndDate && this._insuranceDuration)return true;
return false;
},
/**
* Returns the HTML structure of this course
*
* @return Element the insurance element
*/
getHtml : function() {
return this._container;
},
/**
* Generate the list of insurances for this school
*
* @param schoolList list The list to update
*/
getVisualInsuranceList : function(list) {
list.clear();
this._parent._schoolInsurancesDTO.each(function(item, index) {
list.addItem(item.id, item.name);
list.getListItems().getLast().getHtml().addEvent('click', function() {
this.setInsurance(item);
}.bind(this));
}.bind(this));
if (!this._parent._schoolInsurancesDTO || this._parent.getParent().getCompletedCourses().length > 0) {
list.getListItems().getLast().setIsValid(false);
} else {
list.getListItems().getLast().setIsValid(true);
}
this.refreshVisualInsuranceList();
},
/**
* refresh the list of insurances for this school
*
*/
refreshVisualInsuranceList : function() {
this._parent.refreshVisualInsuranceList(this._insuranceList);
},
/**
* Replace the insurance with the one given in parameter
*
* @param Object insurance The insurance DTO
*/
setInsurance : function(insurance) {
this._insuranceName = insurance.name;
this._insuranceId = insurance.id;
this._type = insurance.type;
this._insuranceDTO = insurance;
this._title.set("html", this._insuranceName);
this.updateDescriptions();
// the check will be done again by the insurances container
this.setMandatory(false);
this.fireEvent('refreshPrice');
},
/**
* Generate the options list for a insurance
* and set an event on each option
*
* @param SchoolList The list to update
*/
getVisualInsuranceOptionsList : function(list) {
list.clear();
list.getListItems().each(function(item, index) {
item.getHtml().addClass(this._classNameBase + 'List-Item');
item.getHtml().addEvent('click', function() {
this._parent.getParent().fireEvent('refreshPrice');
}.bind(this));
}.bind(this));
},
/**
* Set the element container of this insurance
*
* @param element
*/
setElement : function(element){
this._element = element;
},
/**
* Set the element container of this insurance
*
* @return  The element containing this insurance
*/
getElement : function(){
return this._element;
},
/**
* Remove the insurance from the list
*
*/
removeInsurance : function() {
this.fireEvent('deleteInsurance');
}
});
/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param InsuranceListItemDTO schoolInsurancesDTO the insurances informations
* @param Object translateDTO The DTO containing the translations for this class
* @param SchoolPriceEstimation parent the parent
**/
var SchoolInsurances = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(schoolInsurancesDTO, parent) {
this._schoolInsurancesDTO = new Hash();
if (schoolInsurancesDTO) {
for (var i = 0; i < schoolInsurancesDTO.length; i++) {
this._schoolInsurancesDTO.set(schoolInsurancesDTO[i].id, schoolInsurancesDTO[i]);
}
}
this._booking = this._parent = parent;
this._insurances = new Array();
this._mandatoryInsurances = new Array();
this._defaultCheckedInsurances = new Array();
this._schoolInsurancesDTO.each(function(item, index) {
if (item.mandatory) {
this._mandatoryInsurances.push(item);
}
if (item.defaultChecked) {
this._defaultCheckedInsurances.push(item);
}
}.bind(this));
},
/**
* Return the insurances list
*
* @return BiHash The insurances DTO
*/
getInsurances : function() {
return this._schoolInsurancesDTO;
},
/**
* Return the added insurances list
*
* @return InsuranceObject[] The insurances
*/
getAddedInsurances : function() {
return this._insurances;
},
/**
* Returns an array containing the informations for the calculation price
*
* @return Array The informations needed to calculate the price
*/
getDTO : function() {
var insurancesDTO = new Array();
this._insurances.each(function(item, index) {
if (item.isComplete()) {
insurancesDTO.push(item.getDTO());
}
}.bind(this));
return insurancesDTO;
},
/**
* Populate the insurances from a dto
*/
populateFromDTO : function(toPopulateDTO) {
if (toPopulateDTO) {
toPopulateDTO.each(function(item, index) {
this.addInsurance(item.id, true);
}.bind(this));
}
},
/**
* Refresh the list of insurances for this school
*
* @param schoolList list The list to update
* @param availabilityItem boolean True if there is a last item to be used
*/
refreshInsuranceList : function() {
this._validInsurances = new Array();
this._schoolInsurancesDTO.each(function(insurance, key) {
if (this._duration && (insurance.availableFrom <= this._duration) && !this.isAdded(key)) {
this._validInsurances.push(insurance);
}
}.bind(this));
},
/**
* Return the valid insurance list
*/
getValidInsurancesList : function() {
this.refreshInsurances();
return this._validInsurances;
},
/**
* Refresh the value of the insurances (all insurances have the same duration)
*/
refreshValues : function() {
this._startDate = this.getInsuranceStartDate();
this._endDate = this.getInsuranceEndDate();
this._courseStartDate = this.getInsuranceStartDate(true);
this._courseEndDate = this.getInsuranceEndDate(true);
this._duration = this.getCalculatedDuration(this._startDate, this._endDate);
this._courseDuration = this.getCalculatedDuration(this._courseStartDate, this._courseEndDate);
if (this._insurances) {
for (var i = 0; i < this._insurances.length; i++) {
// Check if the insurance is calculated from the duration of the course
if(this._insurances[i].getInsuranceInformations().durationCalculationMethod == 1){
this._insurances[i].setDates(new Date(this._courseStartDate), new Date(this._courseEndDate), this._courseDuration);
} else {
this._insurances[i].setDates(new Date(this._startDate), new Date(this._endDate), this._duration);
}
}
}
},
/**
* Return the insurance start date calculated from the course and accommodation
*/
getInsuranceStartDate : function(courseOnly) {
if(!courseOnly)courseOnly = false;
return this._booking.getStartDate(courseOnly);
},
/**
* Return the insurance start date calculated from the course and accommodation
*/
getInsuranceCourseStartDate : function(courseOnly) {
if(!courseOnly)courseOnly = false;
return this._booking.getCourseStartDate(courseOnly);
},
/**
* Return the insurance end date calculated from the course and accommodation
*/
getInsuranceEndDate : function() {
return this._booking.getEndDate();
},
/**
* Return the durationof the insurance
*/
getInsuranceDuration : function() {
return this._duration;
},
/**
* Return the duration in days.
* To perform the calculation we count the number of time the week day of the start date
* can be repeated between the start date and the end date.
*
* @param date startDate The start date
* @param date endDate The end date
* @return integer the number of Days
*/
getCalculatedDuration : function(startDate, endDate) {
var numberOfDays = 0;
// The number of milliseconds in one day
var oneDay = 1000 * 60 * 60 * 24;
if (startDate && endDate && startDate.getTime && endDate.getTime) {
startDate = startDate.getTime();
endDate = endDate.getTime() + 1000;
var differenceMs = Math.abs(startDate - endDate)
numberOfDays = (Math.round(differenceMs/oneDay)) + 1;
}
return numberOfDays;
},
/**
* Refresh the insurances lists
*
*/
refreshInsurances : function() {
this.refreshValues();
this.refreshInsuranceList();
// refresh every insurance list for the added insurances
this._mandatoryInsurances.each(function(item, index) {
if (!this.isAdded(item.id)) {
var minDuration = (parseInt(item.mandatoryFrom) ? parseInt(item.mandatoryFrom) : 0);
var addedInsurance = null;
if (this._duration > 0 && minDuration <= this._duration) {
this.addInsurance(item.id);
if (addedInsurance = this.getAddedInsurance(item.id)) {
addedInsurance.setMandatory(true);
}
}
} else {
var minDuration = (parseInt(item.mandatoryFrom) ? parseInt(item.mandatoryFrom) : 0);
if (minDuration > this._duration) {
if (addedInsurance = this.getAddedInsurance(item.id)) {
addedInsurance.setMandatory(false);
}
}
}
}.bind(this));
// removing the insurances that are not meeting the min weeks
this._insurances.each(function(item, index){
if(this._schoolInsurancesDTO.get(item.getInsuranceId()).availableFrom > this._duration){
this.deleteInsurance(item);
}
}.bind(this));
// remove all the insurance if there are no more course on the quote
if (this._booking.getCompletedCourses().length == 0) {
this.deleteAllInsurance();
}else {
this._defaultCheckedInsurances.each(function(item, index) {
if (!this.isAdded(item.id)) {
this.addInsurance(item.id);
}
this._defaultCheckedInsurances.erase(item);
}.bind(this))
}
},
/**
* Add a insurance from its ID
*
* @param Number id The insurance id
*/
addInsurance : function(id, noTest) {
if (this._schoolInsurancesDTO.has(id) && (this._schoolInsurancesDTO.get(id).availableFrom <= this._duration  || noTest)  && !this.isAdded(id)) {
var schoolInsurance = this._schoolInsurancesDTO.get(id);
var newInsurance = new SchoolInsuranceObject(schoolInsurance.id, schoolInsurance, this);
this._insurances.push(newInsurance);
newInsurance.addEvent('insuranceUpdated', this.refreshPrice.bind(this));
newInsurance.addEvent('deleteInsurance', function(e) {
this.deleteInsurance(newInsurance);
}.bind(this));
this.fireEvent('insuranceAdded', newInsurance);
this.refreshPrice();
return newInsurance;
}
return null;
},
/**
* Refresh the price
*/
refreshPrice: function() {
this.fireEvent('insurancesUpdated');
},
/**
* Returns the parent of this object
*
* @return SchoolPriceEstimation The parent
*/
getParent : function() {
return this._parent;
},
/**
* Display or hide the add button
*
* @param boolean isDisplayed True to display the hide button
*/
displayAddButton : function(isDisplayed) {
},
/**
* Returns the number of completed insurances
*
* @return Number The number of completed insurances
*/
getCompletedInsurances : function() {
return this._insurances;
},
/**
* Test if the insurance is added
*
* @param insuranceId integer The insurance id
*
* @return Boolean True if the insurance is added
*/
isAdded : function (insuranceId) {
insuranceId = parseInt(insuranceId);
if (this._insurances) {
for (var i = 0; i < this._insurances.length; i++) {
if (this._insurances[i].getInsuranceId() == insuranceId)return true;
}
}
return false;
},
/**
* Return the added insurance from its id
* @param insuranceId integer The insurance ID
*
* @return SchoolInsuranceObject The insurance object
*
*/
getAddedInsurance : function (insuranceId) {
if (this._insurances) {
for (var i = 0; i < this._insurances.length; i++) {
if (this._insurances[i].getInsuranceId() == parseInt(insuranceId))return this._insurances[i];
}
}
return false;
},
/**
* Delete the given insurance
*
* @param SchoolInsuranceObject insurance The insurance to delete
*/
deleteInsurance : function(insurance) {
this._insurances.erase(insurance);
this.fireEvent('insurancesUpdated');
},
/**
* Delete the given insurance
*
* @param SchoolInsuranceObject insurance The insurance to delete
*/
deleteAllInsurance : function(insurance) {
if(this._insurances.length){
this._insurances = new Array();
this.fireEvent('insurancesUpdated');
}
}
});/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param Element button the button attached to the list
*
**/
var SchoolList = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(button, classNameBase) {
this._classNameBase = classNameBase;
this._listItems = new Array();
this._button = button;
this._list = this.setHtml();
this._list.setStyle('display', 'none');
this._selectedItem = -1;
this._buttonEventInitialized = false;
},
/**
* return true if the event on the list button is initialized
*
* @return boolean True if initialized
*/
getButtonEventInitialized : function() {
return this._buttonEventInitialized;
},
/**
*  Create the HTML structure
*
* @return Element The div container
*/
setHtml : function() {
return new Element('div', {'class': this._classNameBase + 'List', 'style':'display:none'});
},
/**
* This method add an element to the list
*
* @param Object value The value of the item
* @param String html The html value of the item
*/
addItem : function(value, html) {
var newItem = new SchoolListItem(value, html, this._classNameBase);
this._listItems.push(newItem);
newItem._listItem.inject(this._list);
},
/**
* This method add an element to the list
*
* @param SchoolListItem listItem The school list item
*/
addListItem : function(schooListItem) {
this._listItems.push(schooListItem);
schooListItem._listItem.inject(this._list);
},
/**
* this method clear the list
*/
clear : function() {
this._listItems = new Array();
this._list = this.setHtml();
},
/**
* This method return the list html formatted
*
* @return Element The list element
*/
getHtml : function() {
return this._list;
},
/**
* This method return the listItem Array
*
* @return Array The array containing all the list items
*/
getListItems : function() {
return this._listItems;
},
/**
* Returns the valid list items
*
* @return SchoolListItem[] The valid list items
*/
getValidListItems : function() {
var listItems = new Array();
this._listItems.each(function(item, index) {
if (item.getIsValid()) {
listItems.push(item);
}
});
return listItems;
},
/**
* Returns the height of a valid Item
*
* @return Number The height of a valid Item
*/
getItemHeight : function() {
var test = false;
var height = 0;
var i = 0;
while (!test) {
height = this._listItems[i].getHtml().getCoordinates().height;
test = this._listItems[i].getIsValid();
i++;
}
return height;
},
/**
* Returns the height of a list
*
* @return Number The height of this school list
*/
getListHeight : function() {
var height = 0;
this._listItems.each(function(item, index) {
if (item.getIsValid()) {
height = height + item.getHtml().getCoordinates().height;
}
});
return height;
},
/**
* This method set the item the list has to scroll to
*
* @param SchoolListItem item The item the list has to scroll to
*/
setSelectedItem : function(item) {
if (this._selectedItem != -1){
if(this._listItems[this._selectedItem])this._listItems[this._selectedItem].getHtml().removeProperty('id');
}
this._selectedItem = this._listItems.indexOf(item);
if(this._listItems[this._selectedItem])this._listItems[this._selectedItem].getHtml().setProperty('id', 'firstValid');
},
/**
* This method return the selected item
*
* @return SchoolListItem item The selected item
*/
getSelectedItem : function() {
if (this._selectedItem != -1){
if(this._listItems[this._selectedItem])
return this._listItems[this._selectedItem];
}
return null;
},
/**
*  Initialize the main events for this list
*/
initializeEvents : function() {
var myTimer = -1;
// add the mouseleave event on the list
this.getHtml().addEvent('mouseleave', function() {
myTimer = this.getHtml().setStyles.delay(500, this.getHtml(), {'visibility':'hidden','display':'none', 'z-index':0});
}.bind(this));
this.getHtml().addEvent('mouseover', function() {
clearTimeout(myTimer);
});
this._listItems.each(function(item, index) {
item.getHtml().addEvent('click', function() {
if (!(item.getValue() == 'category' || item.getValue() == 'error' )) {
this.getHtml().setStyle('visibility', 'hidden');
this.getHtml().setStyle('display', 'none');
this.getHtml().setStyle('height', '0px');
}
}.bind(this));
}.bind(this));
// add an event on the button to appear the list
this.addButtonEvent();
},
/**
* Return the list button
*/
getButton : function(){
return this._button;
},
/**
* add the event on the list button
*/
addButtonEvent : function(){
if(!this._buttonEventInitialized)this._button.addEvent('click', this.buttonClickEvent.bind(this));
this._buttonEventInitialized = true;
},
/**
*  remove the button event for this button
*/
removeButtonEvent : function() {
if(this._buttonEventInitialized)this._button.removeEvent('click', this.buttonClickEvent.bind(this));
if(this._buttonEventInitialized)this._button.removeEvents();
this._buttonEventInitialized = false;
},
/**
* method to call when the button is clicked
*/
buttonClickEvent : function() {
// add an event on the button to appear the list
var scroll = new Fx.Scroll(this._list);
var listHeight = parseInt(this.getHtml().getStyle('max-height'));
if (!listHeight) listHeight = 165;
this.getHtml().setStyle('height', '0px');
if(Browser.ie6){
this.getHtml().setStyle('width', this._button.getCoordinates().width + 'px');
}else{
this.getHtml().setStyle('min-width', this._button.getCoordinates().width + 'px');
}
this.setPosition();
this.getHtml().setStyle('display', 'block');
// the list is scrolled to the first valid element
if (this.getHtml().getElements('div[id=firstValid]')[0]) {
scroll.toElement(this.getHtml().getElements('div[id=firstValid]')[0]);
}
// set the overflow-y auto if the list is taller than the list
if (this.getListHeight() > listHeight) {
this.getHtml().setStyle('overflow-y', 'auto');
if(Browser.ie6){
this.getHtml().setStyle('width', (parseInt(this.getHtml().getStyle('width')) + 15) + 'px');
}else{
this.getHtml().setStyle('min-width', (parseInt(this.getHtml().getStyle('width')) + 30) + 'px');
}
//            new Fx.Style(this.getHtml(), 'height', {transition : Fx.Transitions.Quart.easeOut}).start('0', (listHeight) + 'px');
new Fx.Tween(this.getHtml(),{
duration: 100,
property: 'height'
}).start(0, listHeight);
} else {
new Fx.Tween(this.getHtml(),{
duration: 100,
property: 'height'
}).start(0, this.getListHeight());
//            new Fx.Style(this.getHtml(), 'height', {transition : Fx.Transitions.Quart.easeOut}).start('0', this.getListHeight() + 'px');
}
this.getHtml().setStyle('visibility', 'visible');
this.getHtml().setStyle('z-index', 1000);
},
deleteList : function(){
this.removeButtonEvent();
if(this.getHtml().getParent())this.getHtml().destroy();
},
/**
* This function Set the list position relative to its button
*
*/
setPosition : function() {
this._list.setStyle('left', this._button.getCoordinates().left);
this._list.setStyle('top', this._button.getCoordinates().top);
}
});﻿/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param Object value The value of the item
* @param String html The visual value of the item
*
**/
var SchoolListItem = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(value, html, classNameBase) {
this._classNameBase = classNameBase;
this._value = value;
this._html = html;
this.setVisualHtml();
this._isValid = true;
this._isSelectionnable = true;
},
/**
* Create the HTHML part of the item
*/
setVisualHtml : function() {
this._listItem = new Element('div');
this._listItem.set("html", this._html);
this._listItem.listItemContainer = this;
},
/**
* set the new html value
*
* @param html String the new html value
*/
setHtml : function(html){
this._html = html;
this._listItem.set("html", this._html);
},
/**
* Returns the element
*
* @return Element The list item
*/
getHtml : function() {
return this._listItem;
},
/**
* Returns the html value of the item
*
* @return Element The Html view of the item
*/
getHtmlValue : function() {
return this._html;
},
/**
* Returns the element value
*
* @return Object The item value
*/
getValue : function() {
return this._value;
},
/**
*  Set the validity of a list item
*
* @param Boolean validity The new validity of the list item
*/
setIsValid : function(validity) {
if (validity) {
this.getHtml().setStyle('display', 'block');
} else {
this.getHtml().setStyle('display', 'none');
}
this._isValid = validity;
},
/**
* Returns true if the item is valid
*
* @return Boolean Return true if the item is valid
*/
getIsValid : function() {
return this._isValid;
},
/**
* This method set if an item is selectionnable or not
*
* @param Boolean value Set true if the item is selectionnable
*/
setIsSelectionnable : function(value) {
if (value) {
this.getHtml().addClass(this._classNameBase + 'List-Item');
this.getHtml().removeClass(this._classNameBase + 'NotSelectionable');
} else {
this.getHtml().addClass(this._classNameBase + 'NotSelectionable');
this.getHtml().removeClass(this._classNameBase + 'List-Item');
}
this._isSelectionnable = value;
},
/**
* Returns true if the item is selectionnable
*
* @return Boolean Returns true if the item is selectionnable
*/
getIsSelectionnable : function() {
return this._isSelectionnable;
}
});/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @event Event deleteCourse This event is fired when a course is deleted
*
* @param FixedDatesCourseListItemDTO courseDetails courseDetails The object containing the course informations
* @param Object translateDTO The object containing all the translations for this class
* @param SchoolCourses parent The parent
*
**/
var SchoolOptionalCourseObject = new Class({
Extends: SchoolCourseObject,
/**
* This is the initialization function
*
*/
initialize : function(courseDetails, schoolCourses) {
this.parent(courseDetails, schoolCourses, 'optional');
this.type = 'optional';
// the course start dates
this._startDatesObjects = new Array();
this._courseDetails.startDates.each(function(item, index) {
this._startDatesObjects.push({id : item.getTime() , name : item.format(this._dateFormat, this._monthsTranslations)});
// we store the dates values so the list is populated with the right object not the value stored in the dom object
}.bind(this));
// the course durations
this._durations = new Array();
this._courseDetails.duration.each(function(item, index) {
this._durations.push({id : item , name : item });
}.bind(this));
this.refreshCourseData();
},
/**
* Populate the variable course from a dto
*
* @param Object The fixed course informations needed to populate
*/
populateFromDTO : function(toPopulateDTO) {
var startDate = toPopulateDTO.startDate;
var endDate = toPopulateDTO.endDate;
if(!(startDate instanceof Date)){
startDate = (new Date()).fromIso(startDate);
}
if(!(endDate instanceof Date)){
endDate = (new Date()).fromIso(endDate);
}
this._courseStartDate = startDate;
this._courseEndDate = endDate;
this._courseDuration = this.getCalculatedDuration(this._courseStartDate, this._courseEndDate);
// If the start and the duration are set,  the end date is calculated
if (this._courseStartDate && this._courseDuration) {
this._courseEndDate = this.getOptionalCourseEndDate();
}
// when the user selects only the start date, the duration is set with the minimal value
if (this._courseStartDate && !this._courseDuration) {
this._courseDuration = this._courseDetails.duration[0];
this._courseEndDate = this.getOptionalCourseEndDate();
}
this.refreshCourseData();
},
/**
* Refresh all the lists of this course
*
*/
refreshLists : function() {
// add the list of courses that fit the start and end Date
this.refreshVisualValidCoursesList(this._coursesValidList);
// initialize the start date list and its event
this.refreshCourseStartDatesList(this._schoolCourses.getValidOptionalCourseStartDate(this));
// initialize the duration list and its event
this.refreshCourseDurationList(this._schoolCourses.getValidOptionalCourseDuration(this));
},
/**
* Generate all the lists of this course
*
*/
reGenerateLists : function() {
// remove old list if exists
if (this._coursesValidList._listItems.length) {
this._coursesValidList.getHtml().destroy();
this._coursesValidList.clear();
}
if (this._schoolStartList._listItems.length) {
this._schoolStartList.getHtml().destroy();
this._schoolStartList.clear();
}
if (this._schoolDurationList._listItems.length) {
this._schoolDurationList.getHtml().destroy();
this._schoolDurationList.clear();
}
this.getVisualValidCoursesList(this._coursesValidList);
this._coursesValidList.getHtml().inject(this._listContainer);
this._coursesValidList.initializeEvents();
this.getVisualOptionalCourseStartDates(this._courseDetails.startDates, this._schoolStartList);
this.refreshVisualOptionalCourseStartDates(this._parent.getValidOptionalCourseStartDate(this), this._schoolStartList);
this._schoolStartList.getHtml().inject(this._listContainer);
this._schoolStartList.initializeEvents();
this.getVisualOptionalCourseDurationList(this._courseDetails.duration, this._schoolDurationList);
this.refreshVisualOptionalCourseDurationList(this._parent.getValidOptionalCourseDuration(this), this._schoolDurationList);
this._schoolDurationList.getHtml().inject(this._listContainer);
this._schoolDurationList.initializeEvents();
},
/**
* Refresh the course data
*
*/
refreshCourseData : function() {
// If the start and the duration are set,  the end date is calculated
if (this._courseStartDate && this._courseDuration) {
this._courseEndDate = this.getOptionalCourseEndDate();
}
// when the user selects only the start date
if (this._courseStartDate && !this._courseDuration) {
if (this._courseDuration) {
this._courseEndDate = this.getOptionalCourseEndDate();
} else {
this.refreshCourseDurationList(this._schoolCourses.getValidOptionalCourseDuration(this));
}
}
this.refreshLists();
// refresh the course list
this.fireEvent('courseUpdated');
},
/**
*  Returns the endDate of a variable course
*
* @param Date endDate The enddate to calculate
* @return Date The variable course end date
*/
getOptionalCourseEndDate : function(endDate) {
if ((this._courseDetails.courseStartDay == this._courseDetails.courseEndDay && this._courseDetails.oneDayCourse)) {
if (!endDate)endDate = this._courseStartDate;
} else {
if (!endDate)endDate = new Date(this._courseStartDate.getFullYear(), this._courseStartDate.getMonth(), this._courseStartDate.getDate() + this._courseDuration * 7);
var endDateDay = endDate.getDay();
if (endDateDay == 0) endDateDay = 7;
while (endDateDay != this._courseDetails.courseEndDay) {
endDate.setDate(endDate.getDate() - 1);
endDateDay = endDate.getDay();
if (endDateDay == 0) endDateDay = 7;
}
}
return endDate;
},
/**
* Replace the courses and find the best possible dates
*
* @param Object The new course DTO
*/
replaceCourse : function(newCourse) {
this._durationValue.removeClass(this._classNameBase + 'Section-Item-Info-Value');
this._schoolStartList.getHtml().destroy();
this._schoolDurationList.getHtml().destroy();
this._coursesValidList.getHtml().destroy();
if (this.isComplete()) {
this._parent.replaceCourse(this, newCourse, this._courseStartDate);
} else {
this._parent.replaceCourse(this, newCourse);
}
this.updateDescriptions();
},
/**
* this method find the best dates and duration for the new variable course based on the old one
*
* @param Date oldStartDate the date of the replaced course
* @param number oldDuration the duration of the replaced course
*/
replaceOptionalCourseDates : function(oldStartDate, oldDuration) {
// if the start date is already set
if (oldStartDate) {
var dateDifference = null;
var newDate;
this._courseDetails.startDates.each(function(item, index) {
if (dateDifference > Math.abs(oldStartDate.getTime() - item.getTime()) || dateDifference == null) {
dateDifference = Math.abs(oldStartDate.getTime() - item.getTime());
newDate = item;
}
});
// if there is already a duration set
if (oldDuration) {
var durationDifference = null;
var newDuration;
this._courseDetails.duration.each(function(item, index) {
if (durationDifference > Math.abs(oldDuration - item) || durationDifference == null) {
durationDifference = Math.abs(oldDuration - item);
newDuration = item;
}
});
this._courseDuration = newDuration;
if (this._courseDuration > 1) {
this._durationValue.set("html", this._translateDTO.weeks.replace("%0", this._courseDuration));
} else {
this._durationValue.set("html", this._translateDTO.week.replace("%0", this._courseDuration));
}
}
this._courseStartDate = newDate;
//            this.refreshOptionalCourseData();
}
},
/**
* Refresh a list of course that have possible dates between this courseStartDate and courseEndDate
*
* @param SchoolList list The courses list
*
*/
refreshVisualValidCoursesList : function() {
this._validCourses = this._parent.getValidCourses(this, true);
},
/**
* Return the valid courses that could replace this one
*/
getValidCourses : function () {
if (!this._validCourses)
this._validCourses = new Array();
return this._validCourses;
},
/**
* this function refresh a list from an array of a course start dates
*
* @param Array validStartDates an array of Date containing the validated startDates
* @param SchoolList list the valid courses list
*/
refreshCourseStartDatesList : function(validStartDates) {
this._validStartDatesList = new Array();
for (var i = 0; i < validStartDates.length; i++) {
this._validStartDatesList.push(validStartDates[i]);
}
},
/**
* Return a list with the valid start dates time stamp
*
* @return string[] The valid start dates
*/
getCourseValidStartDates : function () {
if (!this._validStartDatesList)
this._validStartDatesList = new Array();
return this._validStartDatesList;
},
/**
* Return a list with the valid start dates time stamp
*
* @return string[] The valid start dates
*/
getCourseValidStartDatesTime : function () {
if (!this._validStartDatesList)
this._validStartDatesList = new Array();
var validStartDatesTimeList = new Array();
for (var i = 0; i < this._validStartDatesList.length; i++) {
validStartDatesTimeList.push(this._validStartDatesList[i].getTime());
}
return validStartDatesTimeList;
},
/**
* Return a list with the valid start dates time stamp
*
* @return string[] The valid start dates
*/
getCourseValidDurations : function () {
if (!this._validDurationsList)
this._validDurationsList = new Array();
return this._validDurationsList;
},
/**
* This function return a formated list from an array of duration
*
* @param Array validDurations An array of number containing the validated durations
* @param SchoolList list The list to refresh
*/
refreshCourseDurationList : function(validDurations) {
this._validDurationsList = validDurations;
},
/**
*  set the course duration
*
* @param integer duration The course duration
*/
setCourseDuration : function(duration) {
this._courseDuration = duration;
this.refreshCourseData();
},
/**
*  set the course start date
*
* @param Date startDate The course start date
*/
setCourseStartDate : function(startDate) {
if(startDate && startDate.isValid && startDate.isValid()){
this._courseStartDate = startDate;
}else {
this._courseStartDate = null;
}
this.refreshCourseData();
}
/**
* Remove the course from the list
*
*/
/*removeCourse : function() {
this._schoolStartList.getHtml().destroy();
this._schoolDurationList.getHtml().destroy();
this._coursesValidList.getHtml().destroy();
this._container.destroy();
}*/
});
/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param TransferListItemDTO schoolTransfersDTO the transfers informations
* @param SchoolPriceEstimation parent the parent
**/
var SchoolParticipants = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(translateDTO, enabled, parent) {
this._translateDTO = translateDTO;
this._enabled = enabled;
this._parent = parent;
this._classNameBase = this._parent.getClassNameBase();
this._participant = 1;
},
/**
* return the visual transfer container
*/
getHtml : function() {
this._participantContainer = new Element('div', {'class': this._classNameBase + 'Section Participants-Section'});
this._participantTitle = new Element('div', {'class': this._classNameBase + 'Section-Title'});
this._participantButtons = new Element('div', {'class': this._classNameBase + 'Button' + ' ' + this._classNameBase + 'AddButton'});
this._participantTitle.inject(this._participantContainer);
this._participantTitle.set("html", this._translateDTO.title);
this._participantButtons.set("html", '1');
this._participantButtons.inject(this._participantContainer);
this._participantsList = new SchoolList(this._participantButtons, this._classNameBase);
this.createVisualList(this._participantsList);
this._participantsList.getHtml().inject(this._parent.getListContainer());
this._participantsList.initializeEvents();
if(!this._enabled){
this._participantContainer.setStyle('display', 'none');
}
return this._participantContainer;
},
/**
* Generate the list of participants
*
* @param SchoolList list The list to update
*/
createVisualList : function(list) {
for (var i = 1; i < 50; i++) {
if(i > 1){
list.addItem(i, i);
} else{
list.addItem(i, i);
}
}
list.getListItems().each(function(item, index) {
item.setIsValid(true);
item.setIsSelectionnable(true);
if (item.getValue() == this._participant) {
list.setSelectedItem(item);
}
item.getHtml().addEvent('click', function(event) {
this._participantButtons.set("html", item.getHtmlValue());
this._participant = item.getValue();
this._parent.refreshAccommodations();
this._parent.fireEvent('refreshPrice');
list.setSelectedItem(item);
}.bind(this));
}.bind(this));
},
/**
* Returns the number of participant
*
* @return number The number of participants
*/
getParticipant : function() {
if(!this._participant)this._participant = 1;
return this._participant;
},
setParticipant : function(participants){
this._participant = participants;
if(this._parent.widgetMode)
this._participantButtons.set("html", participants);
//        this._participantButtons.set("html", participants + (participants > 1? " "+this._translateDTO.participants : " "+this._translateDTO.participant));
}
});/**
* This class is used for the price calculation functionnalities DTO
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
**/
var SchoolPriceCalculatorDTO = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function() {
this.dateFormat = '';
this.schoolInformations = '';
this.schoolFixedDatesCourses = new Array();
this.schoolVariableDatesCourses = new Array();
this.schoolOptionalCourses = new Array();
this.schoolAccommodations = new Array();
this.schoolTransfers = new Array();
}
});
/**
* This class is used for the price calculation functionnalities
*
**/
var SchoolInformationDTO = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(schoolGroupID, vacationAndHolidays) {
this.schoolGroupID = schoolGroupID;
this.vacationAndHolidays = vacationAndHolidays;
}
});
/**
* This class is used for the price calculation functionnalities
*
**/
var FixedDatesCourseListItemDTO = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(id, name, sessions, subtitle, accommodationMandatory, transferMandatory, availableAccommodations, position, availableOptionalCourses, courseTypeId, price, minimumAcceptedLevel, maximumAcceptedLevel,prices) {
this.id = id;
this.type = 'fixedDate';
this.name = name;
this.sessions = sessions;
this.subtitle = subtitle;
this.accommodationMandatory = accommodationMandatory;
this.transferMandatory = transferMandatory;
this.availableAccommodations = availableAccommodations;
this.position = position;
this.availableOptionalCourses = availableOptionalCourses;
this.courseTypeId = courseTypeId;
this.price = price;
this.prices = prices;
this.minimumAcceptedLevel = minimumAcceptedLevel;
this.maximumAcceptedLevel = maximumAcceptedLevel;
}
});
/**
* This class is used for the price calculation functionnalities
*
**/
var FixedDatesCourseSessionListItemDTO = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(courseStartDate, courseEndDate, accommodationStartDate, accommodationEndDate) {
this.courseStartDate = courseStartDate;
this.courseEndDate = courseEndDate;
this.accommodationStartDate = accommodationStartDate;
this.accommodationEndDate = accommodationEndDate;
}
});
/**
* This class is used for the price calculation functionnalities
*
**/
var VariableDatesCourseListItemDTO = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(id, name, startDates, duration, courseStartDay, courseEndDay, subtitle,  accommodationMandatory, transferMandatory, availableAccommodations, position, availableOptionalCourses, availabilities, courseTypeId, price, minimumAcceptedLevel, maximumAcceptedLevel,prices) {
this.id = id;
this.type = 'variableDate';
this.name = name;
this.startDates = startDates;
this.duration = duration;
this.courseStartDay = courseStartDay;
this.courseEndDay = courseEndDay;
this.subtitle = subtitle;
this.accommodationMandatory = accommodationMandatory;
this.transferMandatory = transferMandatory;
this.availableAccommodations = availableAccommodations;
this.position = position;
this.availableOptionalCourses = availableOptionalCourses;
this.availabilities = availabilities;
this.courseTypeId = courseTypeId;
this.price = price;
this.prices = prices;
this.minimumAcceptedLevel = minimumAcceptedLevel;
this.maximumAcceptedLevel = maximumAcceptedLevel;
}
});
/**
* This class is used for the price calculation functionnalities
*
**/
var OptionalCourseListItemDTO = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(id, name, startDates, duration, courseStartDay, courseEndDay, subtitle, position, availabilities, oneDayCourse, courseTypeId, additionalDays, price, minimumAcceptedLevel, maximumAcceptedLevel, prices) {
this.id = id;
this.type = 'optional';
this.name = name;
this.startDates = startDates;
this.duration = duration;
this.courseStartDay = courseStartDay;
this.courseEndDay = courseEndDay;
this.subtitle = subtitle;
this.position = position;
this.availabilities = availabilities;
this.oneDayCourse = oneDayCourse;
this.courseTypeId = courseTypeId;
this.additionalDays = (additionalDays ? parseInt(additionalDays) : 0);
this.price = price;
this.prices = prices;
this.minimumAcceptedLevel = minimumAcceptedLevel;
this.maximumAcceptedLevel = maximumAcceptedLevel;
}
});
/**
* This class is used for the price calculation functionnalities
*
**/
var AccommodationOptionListItemDTO = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(id, name, minimumDurationInWeeks, minimumNumberOfPeople, accommodationStartDay, accommodationEndDay, availabilities, noAccommodation, additionalNightsBefore, additionalNightsAfter, additionalNightsOnBounds,price, prices) {
this.id = id;
this.name = name;
this.minimumDurationInWeeks = minimumDurationInWeeks;
this.minimumNumberOfPeople = minimumNumberOfPeople;
this.accommodationStartDay = accommodationStartDay;
this.accommodationEndDay = accommodationEndDay;
this.availabilities = availabilities;
this.noAccommodation = noAccommodation;
this.additionalNightsBefore = parseInt(additionalNightsBefore);
this.additionalNightsAfter = parseInt(additionalNightsAfter);
this.additionalNightsOnBounds = parseInt(additionalNightsOnBounds);
this.price = price;
this.prices = prices;
}
});
/**
* This class is used for the price calculation functionnalities
*
**/
var AccommodationListItemDTO = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(id, name, option, availableTransfers, accommodationType ) {
this.id = id;
this.name = name;
this.option = option;
this.availableTransfers = availableTransfers;
this.accommodationType = accommodationType ? parseInt(accommodationType): 0;
}
});
/**
* This class is used for the price calculation functionnalities
*
**/
var TransferListItemDTO = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(id, name, type) {
this.id = id;
this.name = name;
this.type = type;
}
});
/**
* This class is used for the price calculation functionalities
*
**/
var InsuranceListItemDTO = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(id, name,defaultChecked, mandatory, availableFrom, mandatoryFrom, additionalMandatoryDays, durationCalculationMethod) {
this.id = parseInt(id);
this.name = name;
this.defaultChecked = (defaultChecked ? parseInt(defaultChecked) : 0);
this.mandatory =  (mandatory ? parseInt(mandatory) : 0);
this.availableFrom = (availableFrom ? parseInt(availableFrom) : 0);
this.mandatoryFrom = (mandatoryFrom ? parseInt(mandatoryFrom) : 0);
this.additionalMandatoryDays = (additionalMandatoryDays ? parseInt(additionalMandatoryDays) : 0);
this.durationCalculationMethod = (durationCalculationMethod ? parseInt(durationCalculationMethod) : 0);
}
});
/**
* This class is used for the price calculation functionalities
*
**/
var PromotionListItemDTO = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(id, name, triggers) {
this.id = parseInt(id);
this.name = name;
this.triggers = triggers;
}
});
﻿/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param TransferListItemDTO schoolTransfersDTO the transfers informations
* @param Object translateDTO The DTO containing the translations for this class
* @param SchoolPriceEstimation parent the parent
**/
var SchoolSection = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(schoolItemsDTO, parent) {
this._schoolItemsDTO = schoolItemsDTO;
this._parent = parent;
this._items = new Array();
},
/**
* Returns an array containing the informations for the calculation price
*
* @return Array The informations needed to calculate the price
*/
getDTO : function() {
var dto = new Array();
this._items.each(function(item, index) {
if (item.isComplete()) {
dto.push(item.getDTO());
}
});
return dto;
},
/**
* Retrieve the list of transfers for this school
*
* @param schoolList list The list to update
*/
getVisualItemsList : function(list) {
list.clear();
this._schoolItemsDTO.each(function(item, index) {
list.addItem(item.id, item.name);
list.getListItems().getLast().getHtml().addEvent('click', function() {
this.addItem(item.id);
}.bind(this));
}.bind(this));
list.addItem('error', this._translateDTO.notAvailable);
if (this._parent.getCompletedCourses().length > 0) {
if (this._schoolTransfersDTO.length) {
list.getListItems().getLast().setIsValid(false);
} else {
list.getListItems().getLast().setHtml(this._translateDTO.noTransfers);
list.getListItems().getLast().setIsValid(true);
}
} else {
list.getListItems().getLast().setHtml(this._translateDTO.notAvailable);
list.getListItems().getLast().setIsValid(true);
}
this.refreshVisualTransferList(list);
},
/**
* Returns the parent of this object
*
* @return SchoolPriceEstimation The parent
*/
getParent : function() {
return this._parent;
},
addItem : function(id){
//        var schoolItemsDTO = this._schoolItemsDTO.copy();
var schoolItemsDTO = Array.from(this._schoolItemsDTO.copy);
var schoolItem = schoolItemsDTO.filter(function(item, index) {
return item.id == id;
}.bind(this));
/*   var newItem = new SchoolItemObject(schoolItem[0].id, schoolItem[0].name, schoolItem[0].type, this._translateDTO.itemObject, this);
this._items.push(newItem);
newItem.createVisualItem();
this._items.getLast().getHtml().inject(this._addedItemsList);
newItem.addEvent('deleteItem', function(e) {
this.deleteItem(newItem);
}.bind(this));
this._parent.fireEvent('heightModified');
this._parent.fireEvent('refreshPrice');
*/
},
/**
* Display or hide the add button
*
* @param boolean isDisplayed True to display the hide button
*/
displayAddButton : function(isDisplayed) {
if (isDisplayed) {
this._addButton.setStyle('display', '');
} else {
this._addButton.setStyle('display', 'none');
}
},
/**
* Returns the number of completed transfers
*
* @return Number The number of completed transfers
*/
getCompletedItems : function() {
return this._items.length;
},
/**
* Delete the given transfer
*
* @param SchoolItemObject transfer The transfer to delete
*/
deleteItem : function(item) {
this._items.erase(item);
this._parent.fireEvent('heightModified');
this._parent.fireEvent('refreshPrice');
}
});/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param TransferListItemDTO schoolTransfersDTO the transfers informations
* @param Object translateDTO The DTO containing the translations for this class
* @param SchoolPriceEstimation parent the parent
**/
var SchoolPromotions = new Class({
Extends: SchoolSection,
/**
* This is the initialization function
*/
initialize : function(schoolItemsDTO, parent) {
this.parent(schoolItemsDTO, parent);
this.promotions = new Array();
},
validateCode : function(code){
if (!this._xhrValidate) {
this._xhrValidate = new Request({
url : this._parent.getBookingJsonUrl(),
onSuccess: function(e) {
if (parseInt(e)) {
// we can add the promotion
for (var i = 0; i < this.promotions.length; i++) {
if (this.promotions[i] == parseInt(e)) {
this.fireEvent('promotionAlreadyAdded');
this.fireEvent('promotionsUpdated');
return 0;
}
}
this.promotions.push(parseInt(e));
this.fireEvent('promotionAdded');
this.fireEvent('promotionsUpdated');
} else {
this.fireEvent('invalidCode');
}
}.bind(this)
});
}
if(code != ''){
this._xhrValidate.send('step=validateCode&code=' + code);
}
},
/**
* Return the DTO
*/
getDTO : function(){
return this.promotions;
},
/**
* Populate the transfers from a dto
*/
populateFromDTO : function(toPopulateDTO) {
if (toPopulateDTO) {
this.promotions = toPopulateDTO;
}
}
});/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param number id the transfer ID
* @param char name the transfer name
* @param integer type the transfer type    1 : One way only   2 : round trip  3 : all   4 : way back only
* @param SchoolTransfers parent
**/
var SchoolTransferObject = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(id, name, type, parent) {
//        this._translateDTO = translateDTO;
this._transferName = name;
this._type = type;
this._transferId = id;
this._parent = parent;
this._isMandatory = false;
var options = this.getOptions();
if(options.length){
this.setTransferType(options[0].id);
}
// transferType 1 for round trip, 2 for one way at arrival , 3 for one way to departure
// the transfer value type
this._value = 1;
},
/**
* Returns an object containing the informations for the calculation price
*
* @return object The informations needed to calculate the price
*/
getDTO : function() {
var transferDTO = new Object();
transferDTO.id = this._transferId;
transferDTO.roundTrip = this._transferTypeData;
transferDTO.transferType = this._transferTypeData;
return transferDTO;
},
/**
* Set this transfer mandatory
*
* @param Boolean isMandatory True if the transfer is mandatory
*/
setIsMandatoryTransfer : function(isMandatory) {
this._isMandatory = isMandatory;
this.fireEvent('transferUpdated');
},
/**
* Get this transfer mandatory
*
* @return Boolean True if mandatory
*/
getIsMandatoryTransfer : function() {
return this._isMandatory;
},
/**
* Return the transfer ID
*
* @return integer The Transfer ID
*/
getTransferId : function() {
return parseInt(this._transferId);
},
/**
* Populate the transfer from a dto
*/
populateFromDTO : function(toPopulateDTO) {
this.setTransferType(parseInt(toPopulateDTO.roundTrip));
},
/**
* Return true if the transfer is complete
*
* @return Boolean Return true if complete
*/
isComplete : function() {
if (this._value != -1) {
return true;
}
},
/**
* Return the transfer type
*/
getTransferType : function () {
return this._transferTypeData;
},
/**
* Return the transfer type
*
* set the transfer type
* @param integer transferType (0 : onef way, 1 : roundtrip, 2 : way back
*
*/
setTransferType : function (transferType) {
this._transferTypeData = transferType;
this.fireEvent('transferUpdated');
},
/**
* Return an array containing the options
*/
getOptions : function() {
var options = new Array();
if (this._type != 2 && this._type != 4) {
options.push({'id': 0 , 'name' : 'oneWay'});
}
if (this._type != 2 && this._type != 1) {
options.push({'id': 2 , 'name' : 'wayBack'});
}
if (this._type != 1 && this._type != 4) {
options.push({'id': 1 , 'name' : 'roundTrip'});
}
return options;
},
/**
* refresh the list of transfers for this school
*
*/
refreshVisualTransferList : function() {
this._parent.refreshVisualTransferList(this._transferList);
},
/**
* Replace the transfer with the one given in parameter
*
* @param Object transfer The transfer DTO
*/
setTransfer : function(transfer) {
this._transferName = transfer.name;
this._transferId = transfer.id;
this._type = transfer.type;
this._title.set("html", this._transferName);
if (this._transfersList._listItems.length) {
this._transfersList.getHtml().destroy();
this._transfersList.clear();
}
this.getVisualTransferOptionsList(this._transfersList);
this._transfersList.getHtml().inject(this._parent.getParent().getListContainer());
this._transfersList.initializeEvents();
if(this._transfersList.getValidListItems().length == 1){
this._transferTypeValue.setAttribute('class', this._classNameBase + 'Section-Item-Info-ValueNotEditable');
this._transfersList.removeButtonEvent();
this._transfersList.getValidListItems()[0].getHtml().fireEvent('click');
}else{
this._transferTypeValue.setAttribute('class', this._classNameBase + 'Section-Item-Info-Value');
this._transfersList.addButtonEvent();
}
//        this._parent.getParent().fireEvent('refreshPrice');
},
/**
* Set the element container of this transfer
*
* @param element
*/
setElement : function(element){
this._element = element;
},
/**
* Set the element container of this transfer
*
* @return  The element containing this transfer
*/
getElement : function(){
return this._element;
},
/**
* Remove the transfer from the list
*
*/
removeTransfer : function() {
this.fireEvent('deleteTransfer');
}
});
/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @param TransferListItemDTO schoolTransfersDTO the transfers informations
* @param Object translateDTO The DTO containing the translations for this class
* @param SchoolPriceEstimation parent the parent
**/
var SchoolTransfers = new Class({
Implements: Events,
/**
* This is the initialization function
*/
initialize : function(schoolTransfersDTO, parent) {
this._schoolTransfersDTO = schoolTransfersDTO;
this._translateDTO = {};
this._school = this._parent = parent;
this._booking = this._school.getBooking();
this._transfers = new Array();
},
/**
* Returns an array containing the informations for the calculation price
*
* @return Array The informations needed to calculate the price
*/
getDTO : function() {
var transfersDTO = new Array();
this._transfers.each(function(item, index) {
if (item.isComplete()) {
transfersDTO.push(item.getDTO());
}
});
return transfersDTO;
},
/**
* Populate the transfers from a dto
*/
populateFromDTO : function(toPopulateDTO) {
if (toPopulateDTO) {
toPopulateDTO.each(function(item, index) {
var transfer;
transfer = this._schoolTransfersDTO.filter(function(itemDTO, indexDTO) {
return itemDTO.id == item.id;
}.bind(this));
if (transfer[0]) {
for(var i = 0; i < this._transfers.length; i++){
if(parseInt(this._transfers[i]._transferId) == parseInt(transfer[0].id)){
this._transfers[i].removeTransfer();
this._transfers.erase(this._transfers[i]);
i--;
}
}
this.addTransfer(item.id);
if(this._transfers.length){
this._transfers[this._transfers.length - 1].populateFromDTO(item);
}
}
}.bind(this));
}
},
/**
* Retrieve the list of transfers for this school
*
* @param schoolList list The list to update
*/
getVisualTransferList : function(list) {
list.clear();
this._schoolTransfersDTO.each(function(item, index) {
list.addItem(item.id, item.name);
list.getListItems().getLast().getHtml().addEvent('click', function() {
this.addTransfer(item.id);
}.bind(this));
}.bind(this));
list.addItem('error', this._translateDTO.notAvailable);
if (this._school.getCompletedCourses().length > 0) {
if (this._schoolTransfersDTO.length) {
list.getListItems().getLast().setIsValid(false);
} else {
list.getListItems().getLast().setHtml(this._translateDTO.noTransfers);
list.getListItems().getLast().setIsValid(true);
}
} else {
list.getListItems().getLast().setHtml(this._translateDTO.notAvailable);
list.getListItems().getLast().setIsValid(true);
}
this.refreshVisualTransferList(list);
},
/**
* Refresh the list of transfers for this school
*
* @param schoolList list The list to update
*/
refreshVisualTransferList : function(list) {
this._availableTransfers = this._school.getAvailableTransfers();
},
getValidTransfersList : function () {
if(!this._availableTransfers)
this._availableTransfers = new Array();
return this._availableTransfers;
},
/**
* Return the added transfers
*
* @return TransferObject[] The added transfers
*/
getTransfers : function() {
return this._transfers;
},
/**
* Refresh the transfers lists
*
*/
refreshTransfers : function() {
var transfNb = this._transfers.length;
this.refreshVisualTransferList(this._transfersList);
if (this._school.getCompletedCourses().length == 0) {
for (var i = this._transfers.length - 1; i >= 0; i--) {
this._transfers[i].removeTransfer();
this._transfers.erase(this._transfers[i]);
}
this._school.fireEvent('heightModified');
if (transfNb > 0)this._school.fireEvent('refreshPrice');
} else {
// get the available transfers List
var availableTransfers = this._school.getAvailableTransfers();
// remove the unavailable transfers
this._transfers.each(function(item) {
if (!availableTransfers.contains(item.getTransferId())) {
item.removeTransfer();
this._transfers.erase(item);
}
}.bind(this));
// add transfers if they are mandatory
if (!this._transfers.length && availableTransfers.length && this._school.getIsTransferMandatory()) {
var transfersToAdd = this._schoolTransfersDTO.filter(function(item) {
return  availableTransfers.contains(parseInt(item.id));
});
if (transfersToAdd.length) {
this.addTransfer(transfersToAdd[0].id);
} else {
this.addTransfer(availableTransfers[0]);
}
this._transfers[0].setIsMandatoryTransfer(true);
} else {
if (this._school.getIsTransferMandatory() && this._transfers.length) {
this._transfers[0].setIsMandatoryTransfer(true);
}
}
}
if (!this._school.getIsTransferMandatory()) {
for (var i = 0; i < this._transfers.length; i++) {
this._transfers[i].setIsMandatoryTransfer(false);
}
}
this.fireEvent('transfersUpdated');
/*
if ((this._school.getIsTransferMandatory() == true && this._transfers.length) || this._school.getCompletedAccommodations().length == 0) {
this.displayAddButton(false);
} else {
this.displayAddButton(true);
}*/
},
/**
* Add a transfer from its ID
*
* @param Integer id The transfer id
*/
addTransfer : function(id) {
var schoolTransfersDTO = Array.from(this._schoolTransfersDTO);
var schoolTransfer = schoolTransfersDTO.filter(function(item, index) {
return item.id == id;
}.bind(this));
var newTransfer = new SchoolTransferObject(schoolTransfer[0].id, schoolTransfer[0].name, schoolTransfer[0].type, this);
this._transfers.push(newTransfer);
newTransfer.addEvent('deleteTransfer', function(e) {
this.deleteTransfer(newTransfer);
}.bind(this));
newTransfer.addEvent('transferUpdated', function(e) {
this.fireEvent('transfersUpdated');
}.bind(this));
this.refreshTransfers();
this.fireEvent('transferAdded', newTransfer);
return newTransfer;
},
replaceTransfer : function(oldTransfer, newTransferId){
var oldTransferIndex = this._transfers.indexOf(oldTransfer);
var dto = oldTransfer.getDTO();
var schoolTransfersDTO = Array.from(this._schoolTransfersDTO);
var schoolTransfer = schoolTransfersDTO.filter(function(item, index) {
return item.id == newTransferId;
}.bind(this));
var newTransfer = new SchoolTransferObject(schoolTransfer[0].id, schoolTransfer[0].name, schoolTransfer[0].type, this);
this._transfers.splice(oldTransferIndex, 1, newTransfer);
newTransfer.addEvent('deleteTransfer', function(e) {
this.deleteTransfer(newTransfer);
}.bind(this));
newTransfer.addEvent('transferUpdated', function(e) {
this.fireEvent('transfersUpdated');
}.bind(this));
newTransfer.populateFromDTO(dto);
this.refreshTransfers();
this.fireEvent('transferAdded', newTransfer);
return newTransfer;
},
/**
* Returns the parent of this object
*
* @return SchoolPriceEstimation The parent
*/
getParent : function() {
return this._school;
},
/**
* Display or hide the add button
*
* @param boolean isDisplayed True to display the hide button
*/
displayAddButton : function(isDisplayed) {
if (isDisplayed) {
//            this._transferAddButton.setStyle('display', '');
} else {
//            this._transferAddButton.setStyle('display', 'none');
}
},
/**
* Returns the number of completed transfers
*
* @return Number The number of completed transfers
*/
getCompletedTransfers : function() {
return this._transfers.length;
},
/**
* Delete the given transfer
*
* @param SchoolTransferObject transfer The transfer to delete
*/
deleteTransfer : function(transfer) {
this._transfers.erase(transfer);
this.fireEvent('transfersUpdated');
},
/**
* Remove all transfers
*/
removeAllTransfers : function () {
this._transfers = new Array();
this.fireEvent('transfersUpdated');
}
});/**
* This class is used for the price estimation functionnalities
*
* @author Yves-André Giroud <yves-andre.giroud@pyrameed.com>
* @copyright Copyright 2007 Pyrameed
*
* @event Event deleteCourse This event is fired when a course is deleted
*
* @param FixedDatesCourseListItemDTO courseDetails courseDetails The object containing the course informations
* @param Object translateDTO The object containing all the translations for this class
* @param SchoolCourses parent The parent
*
**/
var SchoolVariableCourseObject = new Class({
Extends: SchoolCourseObject,
/**
* This is the initialization function
*
*/
initialize : function(courseDetails, schoolCourses) {
this.parent(courseDetails, schoolCourses, 'variable');
this.type = 'variable';
// the course start dates
this._startDatesObjects = new Array();
this._courseDetails.startDates.each(function(item, index) {
this._startDatesObjects.push({id : item.getTime() , name : item.format(this._dateFormat, this._monthsTranslations)});
// we store the dates values so the list is populated with the right object not the value stored in the dom object
}.bind(this));
// the course durations
this._durations = new Array();
this._courseDetails.duration.each(function(item, index) {
this._durations.push({id : item , name : item });
}.bind(this));
},
/**
* set the course duration
*
* @param integer duration The duration
*/
setCourseDuration : function(duration){
this._courseDuration = duration;
this.refreshCourseData();
},
/**
* set the course startDate
*
* @param integer duration The duration
*/
setCourseStartDate : function(startDate){
this._courseStartDate = startDate;
this.refreshCourseData();
},
/**
* Refresh the course end date template
*/
refreshCourseEndDateTemplate : function () {
//        var courseEndDateTemplate = this._booking.getSchoolCourseEndDateTemplate(this);
//        if(courseEndDateTemplate && this._element){
//            var endDate = this._courseEndDate ? this._courseEndDate.format(this._dateFormat , this._monthsTranslations) : '';
//            $p(courseEndDateTemplate.getSelector(), this._element).render({date : endDate}, courseEndDateTemplate.getDirective());
//        }
},
/**
* Populate the variable course from a dto
*
* @param Object The fixed course informations needed to populate
*/
populateFromDTO : function(toPopulateDTO) {
this.refreshLists();
var validStartDates = this.getCourseValidStartDatesTime();
if (validStartDates.length) {
var startDate = toPopulateDTO.startDate;
var endDate = toPopulateDTO.endDate;
if(!(startDate instanceof Date)){
startDate = (new Date()).fromIso(startDate);
}
if(!(endDate instanceof Date)){
endDate = (new Date()).fromIso(endDate);
}
var duration = this.getCalculatedDuration(startDate, endDate);
// find the best start date
if (validStartDates.contains(startDate.getTime())) {
this._courseStartDate = startDate;
} else {
var startDateTime = startDate.getTime();
var diff = -1;
for (var i = 0; i < validStartDates.length; i ++) {
if (diff == -1 || diff > Math.abs(parseInt(validStartDates[i]) - startDateTime)) {
this._courseStartDate = new Date(parseInt(validStartDates[i]));
diff = Math.abs(parseInt(validStartDates[i]) - startDateTime);
}
}
}
// find the best duration
this.refreshLists();
if (duration) {
var validDurations = this.getCourseValidDurations();
if (validDurations.contains(duration + '')) {
this._courseDuration = duration;
} else {
var diff = -1;
for (var i = 0; i < validDurations.length; i ++) {
if (diff == -1 || diff > Math.abs(parseInt(validDurations[i]) - duration)) {
this._courseDuration = validDurations[i];
diff = Math.abs(parseInt(validDurations[i]) - duration);
}
}
}
}
}
this.refreshCourseData();
},
/**
* Refresh all the lists of this course
*
*/
refreshLists : function() {
// add the list of courses that fit the start and end Date
this.refreshVisualValidCoursesList(this._coursesValidList);
// initialize the start date list and its event
this.refreshCourseStartDatesList(this._schoolCourses.getValidVariableCourseStartDate(this));
// initialize the duration list and its event
this.refreshCourseDurationList(this._schoolCourses.getValidVariableCourseDuration(this));
// check if the buttons must be activated or not
/* if(this._schoolStartList.getValidListItems().length == 1 ){
this._startDateValue.addClass(this._classNameBase + 'Section-Item-Info-ValueNotEditable');
this._startDateValue.removeClass(this._classNameBase + 'Section-Item-Info-Value');
this._schoolStartList.removeButtonEvent();
if(this._courseStartDate != this._schoolStartList.getValidListItems()[0].getValue())this._schoolStartList.getValidListItems()[0].getHtml().fireEvent('click');
}else{
this._startDateValue.removeClass(this._classNameBase + 'Section-Item-Info-ValueNotEditable');
this._startDateValue.addClass(this._classNameBase + 'Section-Item-Info-Value');
this._schoolStartList.addButtonEvent();
}
if(this._schoolDurationList.getValidListItems().length == 1 ){
this._durationValue.addClass(this._classNameBase + 'Section-Item-Info-ValueNotEditable');
this._durationValue.removeClass(this._classNameBase + 'Section-Item-Info-Value');
this._schoolDurationList.removeButtonEvent();
if(parseInt(this._courseDuration) != parseInt(this._schoolDurationList.getValidListItems()[0].getValue()))this._schoolDurationList.getValidListItems()[0].getHtml().fireEvent('click');
}else{
this._durationValue.addClass(this._classNameBase + 'Section-Item-Info-Value');
this._durationValue.removeClass(this._classNameBase + 'Section-Item-Info-ValueNotEditable');
this._schoolDurationList.addButtonEvent();
}*/
},
/**
* Refresh the course data
*
*/
refreshCourseData : function() {
// If the start and the duration are set,  the end date is calculated
if (this._courseStartDate && this._courseDuration) {
this._courseEndDate = this.getVariableCourseEndDate();
}
// when the user selects only the start date
if (this._courseStartDate && !this._courseDuration) {
if(this._courseDuration){
this._courseEndDate = this.getVariableCourseEndDate();
}else {
this.refreshCourseDurationList(this._schoolCourses.getValidVariableCourseDuration(this));
}
}
this.fireEvent('courseUpdated');
},
/**
*  Returns the endDate of a variable course
*
*  @return Date The variable course end date
*/
getVariableCourseEndDate : function() {
var endDate = new Date(this._courseStartDate.getFullYear(), this._courseStartDate.getMonth(), this._courseStartDate.getDate() + this._courseDuration * 7 + 2);
var endDateDay = endDate.getDay();
if (endDateDay == 0) endDateDay = 7;
while (endDateDay != this._courseDetails.courseEndDay) {
endDate.setDate(endDate.getDate() - 1);
endDateDay = endDate.getDay();
if (endDateDay == 0) endDateDay = 7;
}
return endDate;
},
/**
* Replace the courses and find the best possible dates
*
* @param Object The new course DTO
*/
replaceCourse : function(newCourse) {
this._durationValue.removeClass(this._classNameBase + 'Section-Item-Info-Value');
if (newCourse.type != 'fixed') {
if (this.isComplete()) {
this._courseDetails = newCourse;
this._durationValue.addClass(this._classNameBase + 'Section-Item-Info-Value');
this.replaceVariableCourseDates(this._courseStartDate, this._courseDuration);
this._title.set("html", this._courseDetails.name);
this._subTitle.set("html", this._courseDetails.subtitle);
this.reGenerateLists();
this.fireEvent('courseUpdated');
} else {
this._schoolStartList.getHtml().destroy();
this._schoolDurationList.getHtml().destroy();
this._coursesValidList.getHtml().destroy();
this._schoolCourses.replaceCourse(this, newCourse);
}
} else {
this._schoolStartList.getHtml().destroy();
this._schoolDurationList.getHtml().destroy();
this._coursesValidList.getHtml().destroy();
if (this.isComplete()) {
this._schoolCourses.replaceCourse(this, newCourse, this._courseStartDate);
} else {
this._schoolCourses.replaceCourse(this, newCourse);
}
}
this.updateDescriptions();
},
/**
* this method find the best dates and duration for the new variable course based on the old one
*
* @param Date oldStartDate the date of the replaced course
* @param number oldDuration the duration of the replaced course
*/
replaceVariableCourseDates : function(oldStartDate, oldDuration) {
// if the start date is already set
if (oldStartDate) {
var dateDifference = null;
var newDate;
this._courseDetails.startDates.each(function(item, index) {
if (dateDifference > Math.abs(oldStartDate.getTime() - item.getTime()) || dateDifference == null) {
dateDifference = Math.abs(oldStartDate.getTime() - item.getTime());
newDate = item;
}
});
// if there is already a duration set
if (oldDuration) {
var durationDifference = null;
var newDuration;
this._courseDetails.duration.each(function(item, index) {
if (durationDifference > Math.abs(oldDuration - item) || durationDifference == null) {
durationDifference = Math.abs(oldDuration - item);
newDuration = item;
}
});
this._courseDuration = newDuration;
if (this._courseDuration > 1) {
this._durationValue.set("html", this._translateDTO.weeks.replace("%0", this._courseDuration));
} else {
this._durationValue.set("html", this._translateDTO.week.replace("%0", this._courseDuration));
}
}
this._courseStartDate = newDate;
this._startDateValue.set("html", newDate.format(this._dateFormat , this._monthsTranslations));
this.refreshCourseData();
}
},
/**
* Refresh a list of course that have possible dates between this courseStartDate and courseEndDate
*
* @param SchoolList list The courses list
*   todo
*/
refreshVisualValidCoursesList : function(list) {
var validCourses = this._schoolCourses.getValidCourses(this);
/*
list.getListItems().each(function(item, index) {
if (validCourses.indexOf(item.getValue()) != -1) {
item.setIsValid(true);
} else {
item.setIsValid(false);
}
}.bind(this));*/
},
/**
* this function refresh a list from an array of a course start dates
*
* @param Array validStartDates an array of Date containing the validated startDates
* @param SchoolList list the valid courses list
*/
refreshCourseStartDatesList : function(validStartDates) {
this._validStartDatesList = new Array();
for(var i = 0; i < validStartDates.length; i++){
this._validStartDatesList.push(validStartDates[i]);
}
},
/**
* Return a list with the valid start dates time stamp
*
* @return string[] The valid start dates
*/
getCourseValidStartDates : function (){
if(!this._validStartDatesList)
this._validStartDatesList = new Array();
return this._validStartDatesList;
},
/**
* Return a list with the valid start dates time stamp
*
* @return string[] The valid start dates
*/
getCourseValidStartDatesTime : function (){
if(!this._validStartDatesList)
this._validStartDatesList = new Array();
var validStartDateTime = new Array();
for(var i = 0; i < this._validStartDatesList.length; i++){
validStartDateTime.push(this._validStartDatesList[i].getTime());
}
return validStartDateTime;
},
/**
* Return a list with the valid start dates time stamp
*
* @return string[] The valid start dates
*/
getCourseValidDurations : function (){
if(!this._validDurationsList)
this._validDurationsList = new Array();
return this._validDurationsList;
},
/**
* This function return a formated list from an array of duration
*
* @param Array validDurations An array of number containing the validated durations
* @param SchoolList list The list to refresh
*/
refreshCourseDurationList : function(validDurations) {
this._validDurationsList = validDurations;
},
/**
* Returns false is it's not a fixed course
*
* @return Boolean Return false because it's a variable course
*/
getIsFixedCourse : function() {
return false;
}
});
