//MooTools, My Object Oriented Javascript Tools. Copyright (c) 2006 Valerio Proietti, <http://mad4milk.net>, MIT Style License.

var Class = function(properties){
    var klass = function(){
        for (var p in this){
            if (this[p]) this[p]._proto_ = this;
        }
        if (arguments[0] != 'noinit' && this.initialize) return this.initialize.apply(this, arguments);
    };
    klass.extend = this.extend;
    klass.implement = this.implement;
    klass.prototype = properties;
    return klass;
};

Class.empty = function(){};

Class.create = function(properties){
    return new Class(properties);
};

Class.prototype = {

    extend: function(properties){
        var pr0t0typ3 = new this('noinit');
        for (var property in properties){
            var previous = pr0t0typ3[property];
            var current = properties[property];
            if (previous && previous != current) current = previous.parentize(current) || current;
            pr0t0typ3[property] = current;
        }
        return new Class(pr0t0typ3);
    },

    implement: function(properties){
        for (var property in properties) this.prototype[property] = properties[property];
    }

};

Object.extend = function(){
    var args = arguments;
    if (args[1]) args = [args[0], args[1]];
    else args = [this, args[0]];
    for (var property in args[1]) args[0][property] = args[1][property];
    return args[0];
};

Object.Native = function(){
    for (var i = 0; i < arguments.length; i++) arguments[i].extend = Class.prototype.implement;
};

new Object.Native(Function, Array, String, Number);

Function.extend({

    parentize: function(current){
        var previous = this;
        return function(){
            this.parent = previous;
            return current.apply(this, arguments);
        };
    }

});

Function.extend({

    pass: function(args, bind){
        var fn = this;
        if ($type(args) != 'array') args = [args];
        return function(){
            return fn.apply(bind || fn._proto_ || fn, args);
        };
    },

    bind: function(bind){
        var fn = this;
        return function(){
            return fn.apply(bind, arguments);
        };
    },

    bindAsEventListener: function(bind){
        var fn = this;
        return function(event){
            fn.call(bind, event || window.event);
            return false;
        };
    },

    delay: function(ms, bind){
        return setTimeout(this.bind(bind || this._proto_ || this), ms);
    },

    periodical: function(ms, bind){
        return setInterval(this.bind(bind || this._proto_ || this), ms);
    }

});

function $clear(timer){
    clearTimeout(timer);
    clearInterval(timer);
    return null;
};

function $type(obj){
    if (!obj) return false;
    var type = false;
    if (obj instanceof Function) type = 'function';
    else if (obj.nodeName){
        if (obj.nodeType == 3 && !/\S/.test(obj.nodeValue)) type = 'textnode';
        else if (obj.nodeType == 1) type = 'element';
    }
    else if (obj instanceof Array) type = 'array';
    else if (typeof obj == 'object') type = 'object';
    else if (typeof obj == 'string') type = 'string';
    else if (typeof obj == 'number' && isFinite(obj)) type = 'number';
    return type;
};

var Chain = new Class({

    chain: function(fn){
        this.chains = this.chains || [];
        this.chains.push(fn);
        return this;
    },

    callChain: function(){
        if (this.chains && this.chains.length) this.chains.splice(0, 1)[0].delay(10, this);
    },

    clearChain: function(){
        this.chains = [];
    }

});

if (!Array.prototype.forEach){

    Array.prototype.forEach = function(fn, bind){
        for(var i = 0; i < this.length ; i++) fn.call(bind, this[i], i);
    };
}

Array.extend({

    each: Array.prototype.forEach,

    copy: function(){
        var newArray = [];
        for (var i = 0; i < this.length; i++) newArray.push(this[i]);
        return newArray;
    },

    remove: function(item){
        for (var i = 0; i < this.length; i++){
            if (this[i] == item) this.splice(i, 1);
        }
        return this;
    },

    test: function(item){
        for (var i = 0; i < this.length; i++){
            if (this[i] == item) return true;
        };
        return false;
    },

    extend: function(newArray){
        for (var i = 0; i < newArray.length; i++) this.push(newArray[i]);
        return this;
    },

    associate: function(keys){
        var newArray = [];
        for (var i =0; i < this.length; i++) newArray[keys[i]] = this[i];
        return newArray;
    }

});

function $A(array){
    return Array.prototype.copy.call(array);
};

String.extend({

    test: function(regex, params){
        return this.match(new RegExp(regex, params));
    },
    toInt: function(){
        return parseInt(this);
    },

    camelCase: function(){
        return this.replace(/-\D/gi, function(match){
            return match.charAt(match.length - 1).toUpperCase();
        });
    },
    capitalize: function(){
        return this.toLowerCase().replace(/\b[a-z]/g, function(match){
            return match.toUpperCase();
        });
    },

    trim: function(){
        return this.replace(/^\s*|\s*$/g, '');
    },

    clean: function(){
        return this.replace(/\s\s/g, ' ').trim();
    },

    rgbToHex: function(array){
        var rgb = this.test('([\\d]{1,3})', 'g');
        if (rgb[3] == 0) return 'transparent';
        var hex = [];
        for (var i = 0; i < 3; i++){
            var bit = (rgb[i]-0).toString(16);
            hex.push(bit.length == 1 ? '0'+bit : bit);
        }
        var hexText = '#'+hex.join('');
        if (array) return hex;
        else return hexText;
    },

    hexToRgb: function(array){
        var hex = this.test('^[#]{0,1}([\\w]{1,2})([\\w]{1,2})([\\w]{1,2})$');
        var rgb = [];
        for (var i = 1; i < hex.length; i++){
            if (hex[i].length == 1) hex[i] += hex[i];
            rgb.push(parseInt(hex[i], 16));
        }
        var rgbText = 'rgb('+rgb.join(',')+')';
        if (array) return rgb;
        else return rgbText;
    }

});

Number.extend({

    toInt: function(){
        return this;
    }

});

var Element = new Class({

    initialize: function(el){
        if ($type(el) == 'string') el = document.createElement(el);
        return $(el);
    },

    inject: function(el, where){
        el = $(el) || new Element(el);
        switch(where){
            case "before": $(el.parentNode).insertBefore(this, el); break;
            case "after": {
                    if (!el.getNext()) $(el.parentNode).appendChild(this);
                    else $(el.parentNode).insertBefore(this, el.getNext());
            } break;
            case "inside": el.appendChild(this); break;
        }
        return this;
    },

    injectBefore: function(el){
        return this.inject(el, 'before');
    },

    injectAfter: function(el){
        return this.inject(el, 'after');
    },

    injectInside: function(el){
        return this.inject(el, 'inside');
    },

    adopt: function(el){
        this.appendChild($(el) || new Element(el));
        return this;
    },

    remove: function(){
        this.parentNode.removeChild(this);
    },

    clone: function(contents){
        return $(this.cloneNode(contents || true));
    },

    replaceWith: function(el){
        var el = $(el) || new Element(el);
        this.parentNode.replaceChild(el, this);
        return el;
    },

    appendText: function(text){
        if (this.getTag() == 'style' && window.ActiveXObject) this.styleSheet.cssText = text;
        else this.appendChild(document.createTextNode(text));
        return this;
    },

    hasClass: function(className){
        return !!this.className.test("\\b"+className+"\\b");
    },

    addClass: function(className){
        if (!this.hasClass(className)) this.className = (this.className+' '+className.trim()).clean();
        return this;
    },

    removeClass: function(className){
        if (this.hasClass(className)) this.className = this.className.replace(className.trim(), '').clean();
        return this;
    },

    toggleClass: function(className){
        if (this.hasClass(className)) return this.removeClass(className);
        else return this.addClass(className);
    },

    setStyle: function(property, value){
        if (property == 'opacity') this.setOpacity(parseFloat(value));
        else this.style[property.camelCase()] = value;
        return this;
    },

    setStyles: function(source){
        if ($type(source) == 'object') {
            for (var property in source) this.setStyle(property, source[property]);
        } else if ($type(source) == 'string') {
            if (window.ActiveXObject) this.cssText = source;
            else this.setAttribute('style', source);
        }
        return this;
    },

    setOpacity: function(opacity){
        if (opacity == 0){
            if(this.style.visibility != "hidden") this.style.visibility = "hidden";
        } else {
            if(this.style.visibility != "visible") this.style.visibility = "visible";
        }
        if (window.ActiveXObject) this.style.filter = "alpha(opacity=" + opacity*100 + ")";
        this.style.opacity = opacity;
        return this;
    },

    getStyle: function(property){
        var proPerty = property.camelCase();
        var style = this.style[proPerty] || false;
        if (!style) {
            if (document.defaultView) style = document.defaultView.getComputedStyle(this,null).getPropertyValue(property);
            else if (this.currentStyle) style = this.currentStyle[proPerty];
        }
        if (style && ['color', 'backgroundColor', 'borderColor'].test(proPerty) && style.test('rgb')) style = style.rgbToHex();
        return style;
    },

    addEvent: function(action, fn){
        this[action+fn] = fn.bind(this);
        if (this.addEventListener) this.addEventListener(action, fn, false);
        else this.attachEvent('on'+action, this[action+fn]);
        var el = this;
        if (this != window) Unload.functions.push(function(){
            el.removeEvent(action, fn);
            el[action+fn] = null;
        });
        return this;
    },

    removeEvent: function(action, fn){
        if (this.removeEventListener) this.removeEventListener(action, fn, false);
        else this.detachEvent('on'+action, this[action+fn]);
        return this;
    },

    getBrother: function(what){
        var el = this[what+'Sibling'];
        while ($type(el) == 'textnode') el = el[what+'Sibling'];
        return $(el);
    },

    getPrevious: function(){
        return this.getBrother('previous');
    },

    getNext: function(){
        return this.getBrother('next');
    },

    getFirst: function(){
        var el = this.firstChild;
        while ($type(el) == 'textnode') el = el.nextSibling;
        return $(el);
    },

    getLast: function(){
        var el = this.lastChild;
        while ($type(el) == 'textnode')
        el = el.previousSibling;
        return $(el);
    },

    setProperty: function(property, value){
        var el = false;
        switch(property){
            case 'class': this.className = value; break;
            case 'style': this.setStyles(value); break;
            case 'name': if (window.ActiveXObject && this.getTag() == 'input'){
                el = $(document.createElement('<input name="'+value+'" />'));
                $A(this.attributes).each(function(attribute){
                    if (attribute.name != 'name') el.setProperty(attribute.name, attribute.value);

                });
                if (this.parentNode) this.replaceWith(el);
            };
            default: this.setAttribute(property, value);
        }
        return el || this;
    },

    setProperties: function(source){
        for (var property in source) this.setProperty(property, source[property]);
        return this;
    },

    setHTML: function(html){
        this.innerHTML = html;
        return this;
    },

    getProperty: function(property){
        return this.getAttribute(property);
    },

    getTag: function(){
        return this.tagName.toLowerCase();
    },

    getOffset: function(what){
        what = what.capitalize();
        var el = this;
        var offset = 0;
        do {
            offset += el['offset'+what] || 0;
            el = el.offsetParent;
        } while (el);
        return offset;
    },

    getTop: function(){
        return this.getOffset('top');
    },

    getLeft: function(){
        return this.getOffset('left');
    },

    getValue: function(){
        var value = false;
        switch(this.getTag()){
            case 'select': value = this.getElementsByTagName('option')[this.selectedIndex].value; break;
            case 'input': if ( (this.checked && ['checkbox', 'radio'].test(this.type)) || (['hidden', 'text', 'password'].test(this.type)) )
                value = this.value; break;
            case 'textarea': value = this.value;
        }
        return value;
    }

});

new Object.Native(Element);

Element.extend({
    hasClassName: Element.prototype.hasClass,
    addClassName: Element.prototype.addClass,
    removeClassName: Element.prototype.removeClass,
    toggleClassName: Element.prototype.toggleClass
});

function $Element(el, method, args){
    if ($type(args) != 'array') args = [args];
    return Element.prototype[method].apply(el, args);
};

function $(el){
    if ($type(el) == 'string') el = document.getElementById(el);
    if ($type(el) == 'element'){
        if (!el.extend){
            Unload.elements.push(el);
            el.extend = Object.extend;
            el.extend(Element.prototype);
        }
        return el;
    } else return false;
};

window.addEvent = document.addEvent = Element.prototype.addEvent;
window.removeEvent = document.removeEvent = Element.prototype.removeEvent;

var Unload = {

    elements: [], functions: [], vars: [],

    unload: function(){
        Unload.functions.each(function(fn){
            fn();
        });

        window.removeEvent('unload', window.removeFunction);

        Unload.elements.each(function(el){
            for(var p in Element.prototype){
                window[p] = null;
                document[p] = null;
                el[p] = null;
            }
            el.extend = null;
        });
    }

};

window.removeFunction = Unload.unload;
window.addEvent('unload', window.removeFunction);
var Fx = fx = {};

Fx.Base = new Class({

    setOptions: function(options){
        this.options = Object.extend({
            onStart: Class.empty,
            onComplete: Class.empty,
            transition: Fx.Transitions.sineInOut,
            duration: 500,
            unit: 'px',
            wait: true,
            fps: 50
        }, options || {});
    },

    step: function(){
        var time = new Date().getTime();
        // BEGIN change
        if (time < this.time + this.options.duration + this.pauseOffset){
            this.cTime = time - this.time - this.pauseOffset;
        // END change
            this.setNow();
        } else {
            this.options.onComplete.pass(this.element, this).delay(10);
            this.isComplete = true;
            this.clearTimer();
            this.callChain();
            this.now = this.to;
        }
        this.increase();
    },

    set: function(to){
        this.now = to;
        this.increase();
        return this;
    },

    setNow: function(){
        this.now = this.compute(this.from, this.to);
    },

    compute: function(from, to){
        return this.options.transition(this.cTime, from, (to - from), this.options.duration);
    },

    custom: function(from, to){
        if (!this.options.wait) this.clearTimer();
        if (this.timer) return;
        this.options.onStart.pass(this.element, this).delay(10);
        this.from = from;
        this.to = to;
        this.time = new Date().getTime();
        // BEGIN change
        this.isStarted = true;
        this.isComplete = false;
        this.pauseOffset = 0;
        this.set(from);
        // END change
        this.timer = this.step.periodical(Math.round(1000/this.options.fps), this);
        return this;
    },

    clearTimer: function(){
        this.timer = $clear(this.timer);
        // BEGIN change
        if (!this.stopTime && !this.isComplete) {
            this.stopTime = new Date().getTime();
        }
        // END change
        return this;
    },

    // BEGIN change
    start: function(from, to) { this.custom(from, to); },
    stop: function() { this.clearTimer(); },

    resume: function(){
        if (this.stopTime && !this.isComplete) {
            this.pauseOffset += new Date().getTime() - this.stopTime;
            this.stopTime = 0;
            this.timer = this.step.periodical(Math.round(1000/this.options.fps), this);
        }
        return this;
    },
    // END change

    setStyle: function(element, property, value){
        element.setStyle(property, value + this.options.unit);
    }

});

Fx.Base.implement(new Chain);

Fx.Style = Fx.Base.extend({

    initialize: function(el, property, options){
        this.element = $(el);
        this.setOptions(options);
        this.property = property.camelCase();
    },

    hide: function(){
        return this.set(0);
    },

    goTo: function(val){
        return this.custom(this.now || 0, val);
    },

    increase: function(){
        this.setStyle(this.element, this.property, this.now);
    }

});

Fx.Styles = Fx.Base.extend({

    initialize: function(el, options){
        this.element = $(el);
        this.setOptions(options);
        this.now = {};
    },

    setNow: function(){
        for (var p in this.from) this.now[p] = this.compute(this.from[p], this.to[p]);
    },

    custom: function(objFromTo){
        if (this.timer && this.options.wait) return;
        var from = {};
        var to = {};
        for (var p in objFromTo){
            from[p] = objFromTo[p][0];
            to[p] = objFromTo[p][1];
        }
        return this.parent(from, to);
    },

    increase: function(){
        for (var p in this.now) this.setStyle(this.element, p, this.now[p]);
    }

});

Element.extend({

    effect: function(property, options){
        return new Fx.Style(this, property, options);
    },

    effects: function(options){
        return new Fx.Styles(this, options);
    }

});

Fx.Transitions = {
    linear: function(t, b, c, d){
        return c*t/d + b;
    },
    sineInOut: function(t, b, c, d){
        return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
    }

};

var Ajax = ajax = new Class({

    setOptions: function(options){
        this.options = {
            method: 'post',
            postBody: null,
            async: true,
            onComplete: Class.empty,
            onStateChange: Class.empty,
            update: null,
            evalScripts: false
        };
        Object.extend(this.options, options || {});
    },

    initialize: function(url, options){
        this.setOptions(options);
        this.url = url;
        this.transport = this.getTransport();
    },

    request: function(){
        this.transport.open(this.options.method, this.url, this.options.async);
        this.transport.onreadystatechange = this.onStateChange.bind(this);
        if (this.options.method == 'post'){
            this.transport.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
            if (this.transport.overrideMimeType) this.transport.setRequestHeader('Connection', 'close');
        }
        switch($type(this.options.postBody)){
            case 'element': this.options.postBody = $(this.options.postBody).toQueryString(); break;
            case 'object': this.options.postBody = Object.toQueryString(this.options.postBody);
        }
        if($type(this.options.postBody) == 'string') this.transport.send(this.options.postBody);
        else this.transport.send(null);
        return this;
    },

    onStateChange: function(){
        this.options.onStateChange.delay(10, this);
        if (this.transport.readyState == 4 && this.transport.status == 200){
            if (this.options.update) $(this.options.update).setHTML(this.transport.responseText);
            this.options.onComplete.pass([this.transport.responseText, this.transport.responseXML], this).delay(20);
            if (this.options.evalScripts) this.evalScripts.delay(30, this);
            this.transport.onreadystatechange = Class.empty;
            this.callChain();
        }
    },

    evalScripts: function(){
        if(scripts = this.transport.responseText.match(/<script[^>]*?>[\S\s]*?<\/script>/g)){
            scripts.each(function(script){
                eval(script.replace(/^<script[^>]*?>/, '').replace(/<\/script>$/, ''));
            });
        }
    },

    getTransport: function(){
        if (window.XMLHttpRequest) return new XMLHttpRequest();
        else if (window.ActiveXObject) return new ActiveXObject('Microsoft.XMLHTTP');
    }

});

// BEGIN change
Ajax.implement({
    initialize: function(url, options){
        this.setOptions(options);
        this.url = url;
        this.transport = this.getTransport();
        this.tried = 0;
    },
    responseIsSuccess: function(status){
        try {
            if(this.transport.readyState != 4 ||
                 this.transport.status == "undefined" ||
                (this.transport.status < 200 || this.transport.status >= 300))
                    return false;
            return true;
        } catch(e) {
            return false;
        }
    },
    responseIsFailure: function(){
        return !this.responseIsSuccess();
    },
    onStateChange: function(){
        if (this.transport.readyState == 4 && this.responseIsSuccess()){
            if (this.options.update) $(this.options.update).setHTML(this.transport.responseText);
            this.options.onComplete.pass([this.transport.responseText, this.transport.responseXML], this).delay(20);
            if (this.options.evalScripts) this.evalScripts.delay(30, this);
            this.transport.onreadystatechange = Class.empty;
            this.callChain();
        } else if(this.transport.readyState == 4 && this.responseIsFailure()) {
            if($type(this.options.onFailure)=='function') this.options.onFailure.pass(this.transport, this).delay(20);
        }
    }
});
// END change

Ajax.implement(new Chain);

Object.toQueryString = function(source){
    var queryString = [];
    for (var property in source) queryString.push(encodeURIComponent(property)+'='+encodeURIComponent(source[property]));
    return queryString.join('&');
};

Element.extend({

    send: function(options){
        options = Object.extend(options, {postBody: this.toQueryString(), method: 'post'});
        return new Ajax(this.getProperty('action'), options).request();
    },

    toQueryString: function(){
        var queryString = [];
        $A(this.getElementsByTagName('*')).each(function(el){
            var name = $(el).name;
            var value = el.getValue();
            if (value && name) queryString.push(encodeURIComponent(name)+'='+encodeURIComponent(value));
        });
        return queryString.join('&');
    }

});

Fx.Transitions = {
    linear: function(t, b, c, d){
        return c*t/d + b;
    },
    quadIn: function(t, b, c, d){
        return c*(t/=d)*t + b;
    },
    quadOut: function(t, b, c, d){
        return -c *(t/=d)*(t-2) + b;
    },
    quadInOut: function(t, b, c, d){
        if ((t/=d/2) < 1) return c/2*t*t + b;
        return -c/2 * ((--t)*(t-2) - 1) + b;
    },
    cubicIn: function(t, b, c, d){
        return c*(t/=d)*t*t + b;
    },
    cubicOut: function(t, b, c, d){
        return c*((t=t/d-1)*t*t + 1) + b;
    },
    cubicInOut: function(t, b, c, d){
        if ((t/=d/2) < 1) return c/2*t*t*t + b;
        return c/2*((t-=2)*t*t + 2) + b;
    },
    quartIn: function(t, b, c, d){
        return c*(t/=d)*t*t*t + b;
    },
    quartOut: function(t, b, c, d){
        return -c * ((t=t/d-1)*t*t*t - 1) + b;
    },
    quartInOut: function(t, b, c, d){
        if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
        return -c/2 * ((t-=2)*t*t*t - 2) + b;
    },
    quintIn: function(t, b, c, d){
        return c*(t/=d)*t*t*t*t + b;
    },
    quintOut: function(t, b, c, d){
        return c*((t=t/d-1)*t*t*t*t + 1) + b;
    },
    quintInOut: function(t, b, c, d){
        if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
        return c/2*((t-=2)*t*t*t*t + 2) + b;
    },
    sineIn: function(t, b, c, d){
        return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
    },
    sineOut: function(t, b, c, d){
        return c * Math.sin(t/d * (Math.PI/2)) + b;
    },
    sineInOut: function(t, b, c, d){
        return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
    },
    expoIn: function(t, b, c, d){
        return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
    },
    expoOut: function(t, b, c, d){
        return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
    },
    expoInOut: function(t, b, c, d){
        if (t==0) return b;
        if (t==d) return b+c;
        if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
        return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
    },
    circIn: function(t, b, c, d){
        return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
    },
    circOut: function(t, b, c, d){
        return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
    },
    circInOut: function(t, b, c, d){
        if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
        return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
    },
    elasticIn: function(t, b, c, d, a, p){
        if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3; if (!a) a = 1;
        if (a < Math.abs(c)){ a=c; var s=p/4; }
        else var s = p/(2*Math.PI) * Math.asin(c/a);
        return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
    },
    elasticOut: function(t, b, c, d, a, p){
        if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3; if (!a) a = 1;
        if (a < Math.abs(c)){ a=c; var s=p/4; }
        else var s = p/(2*Math.PI) * Math.asin(c/a);
        return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
    },
    elasticInOut: function(t, b, c, d, a, p){
        if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5); if (!a) a = 1;
        if (a < Math.abs(c)){ a=c; var s=p/4; }
        else var s = p/(2*Math.PI) * Math.asin(c/a);
        if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
        return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
    },
    backIn: function(t, b, c, d, s){
        if (!s) s = 1.70158;
        return c*(t/=d)*t*((s+1)*t - s) + b;
    },
    backOut: function(t, b, c, d, s){
        if (!s) s = 1.70158;
        return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
    },
    backInOut: function(t, b, c, d, s){
        if (!s) s = 1.70158;
        if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
        return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
    },
    bounceIn: function(t, b, c, d){
        return c - Fx.Transitions.bounceOut (d-t, 0, c, d) + b;
    },
    bounceOut: function(t, b, c, d){
        if ((t/=d) < (1/2.75)){
            return c*(7.5625*t*t) + b;
        } else if (t < (2/2.75)){
            return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
        } else if (t < (2.5/2.75)){
            return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
        } else {
            return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
        }
    },
    bounceInOut: function(t, b, c, d){
        if (t < d/2) return Fx.Transitions.bounceIn(t*2, 0, c, d) * .5 + b;
        return Fx.Transitions.bounceOut(t*2-d, 0, c, d) * .5 + c*.5 + b;
    }

};
/*window.onerror = function(msg, url, line){
  alert('Error details' + '\nMessage:' + msg + '\nURL:' + url + '\nLine:' + line);
}*/
// dependency: mootools.js
if (typeof(console) == "undefined") {
    var console = {};
    if (!console.log) {
        console.log = Class.empty();
    }
}

function it_UtilsClass() {
    this.isDateBefore = isDateBefore;
    this.delegate = delegate;
    this.delegateOnlyCustomArgs = delegateOnlyCustomArgs;
    this.exists = exists;
    this.getProperty = getProperty;
    this.createParentDiv = createParentDiv;

    /** only tests the days, not the hours */
    function isDateBefore(baseDate, testedDate) {
        if (testedDate.getFullYear() != baseDate.getFullYear()) { return testedDate.getFullYear() < baseDate.getFullYear(); }
        if (testedDate.getMonth() != baseDate.getMonth()) { return testedDate.getMonth() < baseDate.getMonth(); }
        if (testedDate.getDate() != baseDate.getDate()) { return testedDate.getDate() < baseDate.getDate(); }
    }

    /**
     * . inspired by Tween.js
     * @param scopeObj
     * @param methodNameOrFunction
     * @param arguments (optional): passed when the function is called
     */
    function delegate(scopeObj, functionOrName) {
        var usedFunction = null;
        if (functionOrName instanceof String) {
            usedFunction = scopeObj[functionOrName];
        } else { // functionOrName is a function
            usedFunction = functionOrName;
        }
        var a = new Array();
        for(var i = 2 ; i < arguments.length ; i++){
            a[i - 2] = arguments[i];
        }
        return function() {
            var args = ArrayUtils.concat(arguments, a);
            usedFunction.apply(scopeObj, args);
        };
    }

    function delegateOnlyCustomArgs(scopeObj, functionOrName) {
        var usedFunction = null;
        if (functionOrName instanceof String) {
            usedFunction = scopeObj[functionOrName];
        } else { // functionOrName is a function
            usedFunction = functionOrName;
        }
        var a = new Array();
        for(var i = 2 ; i < arguments.length ; i++){
            a[i - 2] = arguments[i];
        }
        return function() {
            usedFunction.apply(scopeObj, a);
        };
    }

    function exists(element, propertyName) { //:boolean
        if (element == null) {
            return false;
        }
        return typeof(element[propertyName]) != "undefined";
    }

    function getProperty(element, propertyName, defaultValue) {
        if (element == null) {
            return defaultValue;
        }
        var value = element[propertyName];
        return value != null ? value : defaultValue;
    }

    function createParentDiv(div) {
        div = $(div);
        return new Element("div").injectBefore(div).adopt(div);
    }
}
var it_Utils = new it_UtilsClass();

//----------------------------------------------------------------//
//--------------------------Position Utils------------------------//
//----------------------------------------------------------------//
function PositionUtilsClass() {
    this.toPx = toPx;
    this.mouseX = 0;
    this.mouseY = 0;
    this.scrollX = 0;
    this.scrollY = 0;
    this.calculatePos = calculatePos;
    this.setPosition = setPosition;
    this.calculateAndSetPos = calculateAndSetPos;

    var self = this;
    var useDocumentElement = (document.documentElement != null) && (document.documentElement.scrollLeft != null);

    document.onmousemove = _updateMousePos;
    window.onscroll = _updateScrollPos; // avoids flickers that occur if put in updateMousePos

    function _updateMousePos(e) {
        var evt;
        if (window.event != null) {
            evt = event;
        } else {
            evt = e;
        }
        _updateMousePosXInDoc(evt);
        _updateMousePosYInDoc(evt);
    }

    function _updateMousePosXInDoc(evt) { // event is a keyword in IE
        if (evt.pageX) {
            // usually enough
            self.mouseX = evt.pageX;
        } else if (evt.clientX) {
            self.mouseX = evt.clientX + self.scrollX;
        } else {
            self.mouseX = 0;
        }
    }

    function _updateMousePosYInDoc(evt) {
        if (evt.pageY) {
            self.mouseY = evt.pageY;
        } else if (evt.clientY) {
            self.mouseY = evt.clientY + self.scrollY;
        } else {
            self.mouseY = 0;
        }
    }

    function _updateScrollPos() {
        if (document.documentElement.scrollTop) {
            self.scrollX = document.documentElement.scrollLeft;
            self.scrollY = document.documentElement.scrollTop;
        } else {
            self.scrollX = document.body.scrollLeft;
            self.scrollY = document.body.scrollTop;
        }
    }

    function toPx(str) {
        return "" + str + "px";
    }

    // . options (all optional, and $ is used):
    //         . anchorX, anchorY, anchor (anchorX and anchorY)
    //         . offsetX (default 0), offsetY (default 0)
    //         . alignmentX (only with anchorY)
    //             . with targetDiv: "left", "center"
    //            . with anchorX: "anchorXLeft", "anchorXCenter"
    // . targetDiv is the div (or its name) that contains the popup
    function calculatePos(targetDiv, options) {
        var posX, posY;
        if (options != null) {
            // div renferences
            targetDiv   = $(targetDiv);
            if (options.anchor) {
                options.anchorX = options.anchor;
                options.anchorY = options.anchor;
            }
            var anchorX = $(options.anchorX);
            var anchorY = $(options.anchorY);
            // posX
            if (anchorX) {
                posX = anchorX.getLeft() + anchorX.offsetWidth;
            } else {
                posX = this.mouseX;
            }
            if (options.offsetX) {
                posX += options.offsetX;
            }
            // posY
            if (anchorY) {
                posY = anchorY.getTop() + anchorY.offsetHeight;
                if (options.alignmentX) {
                    if (options.alignmentX == "center") {
                        posX -= targetDiv.offsetWidth / 2;
                    } else if (options.alignmentX == "left") {
                        posX -= targetDiv.offsetWidth;
                    } else if (options.alignmentX == "anchorXCenter") {
                        posX -= anchorX.offsetWidth / 2;
                    } else if (options.alignmentX == "anchorXLeft") {
                        posX -= anchorX.offsetWidth;
                    }
                }
            } else {
                posY = this.mouseY;
            }
            if (options.offsetY) {
                posY += options.offsetY;
            }
        } else {
            posX = this.mouseX + 10; // + 10 by security
            posY = this.mouseY + 10;
        }
        // pos security
        if (posX < 0) {
            posX = 0;
        }
        if (posY < 0) {
            posY = 0;
        }
        return {posX: posX, posY: posY};
    }

    function setPosition(element, left, top) {
        var el = $(element);
        if (el.offsetParent) {
            var parentEl = $(el.offsetParent);
            left -= parentEl.getLeft();
            top -= parentEl.getTop();
        }
        el.style.left = left + "px";
        el.style.top = top + "px";
    }

    function calculateAndSetPos(targetElement, options) {
        var pos = this.calculatePos(targetElement, options);
        this.setPosition(targetElement, pos.posX, pos.posY);
    }
}
// simulates static methods
var PositionUtils = new PositionUtilsClass();


//----------------------------------------------------------//
//-----------------------Array Classes----------------------//
//----------------------------------------------------------//
// usefull because 'in' and 'delete' operators have limitation (no delete obj[key])
// concat is ok (IE4+)
function ArrayUtilsClass() {
    this.add = push;
    this.addAll = addAll;
    this.concat = concat;
    this.clone = clone;
    this.push = push;
    this.unshift = unshift;
    this.remove = remove;
    this.indexOf = indexOf;
    this.contains = contains;

    function push(array, value) {
        array[array.length] = value;
    }
    function addAll(destArray, sourceArray) {
        for (var i = 0; i < sourceArray.length; i++) {
            this.push(destArray, sourceArray[i]);
        }
    }
    function concat(array1, array2) { // :Array
        var ret = new Array();
        this.addAll(ret, array1);
        this.addAll(ret, array2);
        return ret;
    }
    function clone(array) { // :Array
        var newArray = new Array(array.length);
        this.addAll(newArray, array);
        return newArray;
    }
    function unshift(array, value) {
        var lg = array.length;
        var currentValue = value;
        var i = 0;
        for (; i < lg; i++) {
            var nextValue = array[i];
            array[i] = currentValue;
            currentValue = nextValue;
        }
        array[i] = currentValue;
    }
    /** removes the element and offsets the end of the array to fill the blank **/
    function remove(array, element) {
        var elementFound = false;
        var length = array.length;
        for (var i = 0; i < length; i++) {
            if (!elementFound) {
                if (array[i] == element) {
                    elementFound = true;
                }
            } else {
                array[i-1] = array[i];
            }
        }
        if (elementFound && length != 0) {
            array.length = array.length - 1;
        }
    }
    function indexOf(array, element) { // :int
        for (var i = 0; i < array.length; i++) {
            if (array[i] == element) {
                return i;
            }
        }
        return -1;
    }
    function contains(array, element) { // :boolean
        return indexOf(array, element) != -1;
    }
}
// simulates static methods
var ArrayUtils = new ArrayUtilsClass();

// arrayOut is optional
function ArrayBuilder(arrayOut) {
    if (arrayOut == null) {
        this.arrayOut = new Array();
    } else {
        this.arrayOut = arrayOut;
    }

    this.push = push;
    this.unshift = unshift;

    // the return this allows the user to write push(...).push(...)
    function push(value) {
        ArrayUtils.push(this.arrayOut, value);
        return this;
    }

    function unshift(value) {
        ArrayUtils.unshift(this.arrayOut, value);
        return this;
    }
}

//----------------------------------------------------------//
//----------------------String Classes----------------------//
//----------------------------------------------------------//
function StringUtilsClass() {
    this.isEmpty = isEmpty;
    this.trim = trim;
    this.startsWith = startsWith;
    this.endsWith = endsWith;

    function isEmpty(str) {
        return str == undefined || str == null || str.length == 0;
    }

    function trim(value, maxSize, options) {
        if (maxSize != null && value.length > maxSize) {
            var withDots = (options != null && options.withDots);
            var withSpan = (options != null && options.withSpan);
            var strBuilder = new StringBuilder();
            if (withSpan) {
                strBuilder.append('<span title="').append(value).append('">');
            }
            strBuilder.append(withDots ? value.substring(0, maxSize - 3) : value.substring(0, maxSize));
            if (withDots) {
                strBuilder.append("...");
            }
            if (withSpan) {
                strBuilder.append("</span>")
            }
            return strBuilder.toString();
        } else {
            return value;
        }
    }

    function startsWith(bigStr, smallStr) {
        return _startsWithInternal(bigStr, smallStr, 0);
    }

    function endsWith(bigStr, smallStr) {
        return _startsWithInternal(bigStr, smallStr, bigStr.length - smallStr.length);
    }

    // private static
    function _startsWithInternal(bigStr, smallStr, offset) {
        if (smallStr.length + offset > bigStr.length) {
            return false;
        }
        for (var i = 0; i < smallStr.length; i++) {
            if (bigStr.charAt(i + offset) != smallStr.charAt(i)) {
                return false;
            }
        }
        return true;
    }
}
var StringUtils = new StringUtilsClass();

function StringBuilder() {
    this.arrayBuilder = new ArrayBuilder(new Array());

    this.append = append;
    this.a = append;
    this.appendIfNotEmpty = appendIfNotEmpty;
    this.appendArray = appendArray;
    this.toString = toString;

    var trimOptions = { withDots:true, withSpan:true };

    function append(value, maxSize) {
        if (maxSize != null) {
            value = StringUtils.trim(value, maxSize, trimOptions);
        }
        this.arrayBuilder.push(value);
        return this;
    }
    function appendIfNotEmpty(value, maxSize) {
        if (!StringUtils.isEmpty(value)) {
            return this.append(value, maxSize);
        } else {
            return this;
        }
    }
    function appendArray(array, maxSize) {
        if (array == null) {
            return this;
        }
        var strBuilder = new StringBuilder();
        for (var i=0; i<array.length; i++) {
            if (i>0) {
                strBuilder.append(", ");
            }
            strBuilder.append(array[i]);
        }
        var value = strBuilder.toString();
        value = StringUtils.trim(value, maxSize, trimOptions);
        this.append(value);
        return this;
    }
    function toString() {
        return this.arrayBuilder.arrayOut.join("");
    }
}
//---------------------------------------------------------//

/*
example of data (it handles array and indexation):
            var selectData = new Array();
            selectData[0] = [ "FR", "France", [
                [ "PAR", "Paris" ],
                [ "LYS", "Lyon" ]
            ]];
            selectData.FR = selectData[0];
            selectData[1] = [ "IT", "Italie", [
                [ "BLQ", "Bologne" ],
                [ "MIL", "Milan" ],
                [ "ROM", "Rome" ]
            ]];
            selectData.IT = selectData[1];
*/
function it_SelectClass() {
    this.fill = fill;

    function fill(selectDivOrName, data, selectedCode) {
        var select = $(selectDivOrName);
        select.options.length = 0;
        for (var i = 0; i < data.length; i++) {
            var valuePair = data[i];
            select.options[i] = new Option(valuePair[1], valuePair[0]); // text and code
            if (selectedCode != null && valuePair[0] == selectedCode) {
                select.selectedIndex = i;
            }
        }
    }
}
var it_Select = new it_SelectClass();

function SelectPair(masterSelect, slaveSelect, data) {
    var master = $(masterSelect);
    var slave = $(slaveSelect);
    var data = data;

    this.initMaster = initMaster;
    this.updateSlave = updateSlave;

    function initMaster(masterSelCode, slaveSelCode) {
        it_Select.fill(master, data, masterSelCode); // only uses the first 2 strings
        master.onchange = it_Utils.delegate(this, this.updateSlave);
        updateSlave(slaveSelCode);
    }
    /**
     * selectedCode can be null
     */
    function updateSlave(selectedCode) {
        slave.options.length = 0;
        var masterValue = master.options[master.selectedIndex].value;
        var slaveData = data[masterValue][2]; // after the master code and text
        it_Select.fill(slave, slaveData, selectedCode);
    }
}

/**
 * masterServiceName can be null (if the options are put by a jsp)
 */
function it_AjaxSelectPair(masterSelect, slaveSelect, serviceUrl, masterServiceName, slaveServiceName) {
    var master = $(masterSelect);
    var slace = $(slaveSelect);

    this.initMaster = initMaster;
    this.updateSlave = updateSlave;

    function initMaster() {
        master.onchange = it_Utils.delegate(this, this.updateSlave);
    }
    function updateSlave() {

    }
}

function it_AjaxClass() {
    /** static (some private methods are still made properties so that they can be overridden */
    this.sendValue = sendValue;
    this._onComplete = _onComplete;
    this._showMessage = _showMessage;
    this._showError = _showError;

    /**
     * serviceUrl: must be absolute
     */
    function sendValue(serviceUrl, commandName, options, param1Name, param1Value, param2Name, param2Value) {
        options = Object.extend({callback: Class.empty}, options || {});
        var params = new StringBuilder();
        _addParam(params, "cmd", commandName);
        if (!StringUtils.isEmpty(param1Name)) {
            params.a("&");
            _addParam(params, param1Name, param1Value);
            if (!StringUtils.isEmpty(param2Name)) {
                params.a("&");
                _addParam(params, param2Name, param2Value);
            }
        }
        new Ajax(serviceUrl,
            {postBody: params.toString(),
            onComplete: it_Utils.delegate(this, this._onComplete, options.callback),
            onFailure: it_Utils.delegateOnlyCustomArgs(this, this._showError, "Erreur inconnue")})
        .request();
    }

    /** static */
    function _addParam(builder, name, value) {
        builder.a(encodeURIComponent(name)).a("=").a(encodeURIComponent(value));
    }

    function _onComplete(responseText, responseXml, callback) {
        var response = eval('(' + responseText + ')');
        if (it_Utils.exists(response, "error")) {
            this._showError(response.error);
            return;
        }
        if (it_Utils.exists(response, "error")) {
            this._showMessage(response.message);
        }
        callback(response.content);
    }

    function _showMessage(message) {
        alert(message); // may change in the future (with a status bar for instance)
    }
    function _showError(errorMessage) {
        alert("Erreur: " + errorMessage);
    }
}
var it_Ajax = new it_AjaxClass();

//---------------------------------------------------------------//
//------------------------------Flash-----------------------------//
//----------------------------------------------------------------//
function it_writeFlash(path, id, width, height) {
    var source = '<OBJECT id="' + id + '" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0" width="' + width + '" height="' + height + '" align="middle">\n'
    + '<param name="allowScriptAccess" value="never" />\n'
    + '<PARAM name="movie" value="' + path + '" />\n'
    + '<param name="loop" value="false" />\n'
    + '<param name="menu" value="false" />\n'
    + '<param name="quality" value="high" />\n'
    + '<param name="scale" value="noscale" />\n'
    + '<param name="salign" value="lt" />\n'
    + '<param name="wmode" value="transparent" />\n'
    + '<embed name="' + id + '" swLiveConnect="false" src="' + path + '" wmode="transparent" loop="false" menu="false" quality="high" scale="noscale" salign="lt" width="' + width + '" height="' + height + '" align="top" allowScriptAccess="never" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />\n'
    + '</OBJECT>\n';
    document.write(source);
}

var it_IE = (navigator.appName == 'Microsoft Internet Explorer');

/** async loading of frames (IE bug) */
function it_frameLoaderClass() {
    var frameNames = new Array();
    var locations = new Array();

    this.add = add;
    this.loadFrames = loadFrames;
    this._loadFramesImpl = _loadFramesImpl;

    function add(frameName, location) {
        ArrayUtils.push(frameNames, frameName);
        ArrayUtils.push(locations, location);
    }

    function loadFrames() {
        if (it_IE && parent.frames.length > 1) { // using IE and in a frame
            setTimeout(it_Utils.delegate(this, _loadFramesImpl), 2000);
        } else {
            this._loadFramesImpl();
        }
    }

    function _loadFramesImpl() {
        for (var i = 0; i < frameNames.length; i++) {
            // console.log("frame named: " + frameNames[i] + ", " + frames[frameNames[i]])
            frames[frameNames[i]].location.href = locations[i];
        }
    }
}
var it_frameLoader = new it_frameLoaderClass();
var DAYSOFW = new Array("Di","Lu","Ma","Me","Je","Ve","Sa");
var DAYSOFWK = new Array("Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi");
var MONTHS = new Array("Janvier","F&eacute;vrier","Mars","Avril","Mai","Juin","Juillet","Ao&ucirc;t","Septembre","Octobre","Novembre","D&eacute;cembre");
var SHRTM = new Array('','Jan.','Fev.','Mars','Avril','Mai','Juin','Juil.','Aout','Sept.','Oct.','Nov.','Dec.' );


//----------------------------------------------------------------//
//------------------------Date Utils Objects----------------------//
//----------------------------------------------------------------//
// this way all date fields start from 1
function DateUtilsClass() {
    this.getDay = getDay;
    this.getMonth = getMonth;
    this.getYear = getYear;
    this.setDay = setDay;
    this.setMonth = setMonth;
    this.setYear = setYear;
    this.addYear = addYear;
    this.newDate = newDate;
    this.padZero = padZero;
    this.getDayStr = getDayStr;
    this.getMonthStr = getMonthStr;
    this.getComparInt = getComparInt;
    this.equals = equals;
    this.getDaysInMonth = getDaysInMonth;
    this.clone = clone;

    function getDay(date) {    return date.getDate(); }
    function getMonth(date) { return date.getMonth()+1;    }
    function getYear(date) { return date.getFullYear();    }
    function setDay(date, day) { date.setDate(day); return date; }
    function setMonth(date, month) { date.setMonth(month - 1); return date; }
    function setYear(date, year) { date.setFullYear(year); return date; }
    function addYear(date, yearOffset) { date.setFullYear(date.getFullYear() + yearOffset); return date; }
    function newDate(year, month, day) { return new Date(year, month - 1, day);    }
    function padZero(num) { return ((num <= 9) ? ("0" + num) : num); }
    function getDayStr(date) { return padZero(getDay(date)); }
    function getMonthStr(date) { return padZero(getMonth(date)); }
    function getComparInt(date, useYears) {
        if (useYears == null) useYears = true;
        if (useYears) {
            return parseInt('' + getYear(date) + getMonthStr(date) + getDayStr(date), 10);
        } else {
            return parseInt('' + getMonthStr(date) + getDayStr(date), 10);
        }
    }
    function equals(date1, date2, useYears) {
        return getComparInt(date1, useYears) == getComparInt(date2, useYears);
    }
    function getDaysInMonth(m, y) {
        monthdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        if (m != 2) {
            return monthdays[m];
        } else {
            return ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0 ? 29 : 28);
        }
    }
    function clone(date) { var newDate = new Date(); newDate.setTime(date.getTime()); return newDate; }

}
// simulates static methods
var DateUtils = new DateUtilsClass();

// dateListener is a function: argument: sourceCompoundDate
//--------------------------------------------------//
//----------------CompoundDate Object---------------//
//--------------------------------------------------//
function CompoundDate(objName, formName, options) {
    if (formName === null) {
        formName = "form[0]";
    }
    this.today = new Date();
    this.options = Object.extend({
            objectsToHide: [],
            anchorElement: null,
            imgDir: "img",
            fromDate: this.today,
            toDate: DateUtils.newDate(DateUtils.getYear(this.today) + 1, DateUtils.getMonth(this.today), DateUtils.getDay(this.today))
        }, options || {});
    /* Properties */
    this.objName         = objName;
    if(this.year < 2000) this.year += 1900; //for Netscape
    this.yearComboRange = 1; // we only show this and the next year
    this.formName         = formName;
    this.dayObj            = '';
    this.monthObj        = '';
    this.yearObj        = '';
    this.monthNames        = SHRTM;
    this.dateListener   = null;

    /* Public Methods */
    this.setDateListener    = setDateListener;
    this.setToToday            = setToToday;
    this.getDate            = getDate;
    this.setDate            = setDate;
    this.setDateParts        = setDateParts;
    // the getxxx methods all start from 1
    this.getSelYear            = getSelYear;
    this.getSelMonth        = getSelMonth;
    this.getSelDay            = getSelDay;

    this.showCalendar       = showCalendar;
    // called by select boxes
    this.fieldChanged       = fieldChanged;
    // called by the calendar
    this.calendarHidden     = calendarHidden;

    /* Private Methods */
    this._initOptions        = _initOptions;
    this._writeYearOptions     = _writeYearOptions;
    this._writeMonthOptions = _writeMonthOptions;
    this._writeDayOptions     = _writeDayOptions;
    this._updateDayOfWeek    = _updateDayOfWeek;
    this._setVisibility     = _setVisibility;
    this._adjustDaysInMonthFromForm    = _adjustDaysInMonthFromForm;
    this._adjustDaysInMonth    = _adjustDaysInMonth;

    /* Constructor Code */
    this.dayObj = eval("document." + this.formName + ".elements['" + this.objName + ".day']");
    this.monthObj = eval("document." + this.formName + ".elements['" + this.objName + ".month']");
    this.yearObj = eval("document." + this.formName + ".elements['" + this.objName + ".year']");
    this.dayOfWeekObj = document.getElementById(this.objName + ".dayOfWeek");

    this._initOptions();
    this.setToToday();
    this._adjustDaysInMonthFromForm();
    this._updateDayOfWeek();

    // preload images
    var imgUp = new Image(25,25);
    imgUp.src = this.options.imgDir + '/calendar/up.gif';
    var imgDown = new Image(25,25);
    imgDown.src = this.options.imgDir + '/calendar/down.gif';

    //--------------------------public Methods-----------------------//
    function setDateListener(dateListener) {
        this.dateListener = dateListener;
    }
    function calendarHidden() {
        this._setVisibility(true);
        if (typeof(updateFormVisibility) != "undefined") {
            updateFormVisibility();
        }
    }
    function getSelYear() {
        return this.yearObj[this.yearObj.selectedIndex].value;
    }
    function getSelMonth() {
        return this.monthObj[this.monthObj.selectedIndex].value;
    }
    function getSelDay() {
        return this.dayObj[this.dayObj.selectedIndex].value;;
    }
    // Set the date boxes to today's date
    function setToToday() {
        this.setDateParts(DateUtils.getYear(this.today), DateUtils.getMonth(this.today), DateUtils.getDay(this.today));
    }
    function getDate() {
        return DateUtils.newDate(this.getSelYear(), this.getSelMonth(), this.getSelDay());
    }
    function setDate(date) {
        this.setDateParts(DateUtils.getYear(date), DateUtils.getMonth(date), DateUtils.getDay(date));
    }
    /**
    * . Set the date boxes to specific date
    * . called at the init and by the calendar
    * @param integer year
    * @param integer month
    * @param integer day
    */
    function setDateParts( year, month, day ) {
        this._adjustDaysInMonth(month, year); // must be called first
        this.dayObj[day-1].selected = true;
        this.monthObj[month-1].selected = true;
        for(i = 0; i < this.yearObj.length; i++ ) {
            if( this.yearObj[i].value == year )
                this.yearObj[i].selected = true;
        }
        this._updateDayOfWeek();
        if (this.dateListener != null) {
            this.dateListener(this);
        }
    }

    function fieldChanged() {
        this._updateDayOfWeek();
        this._adjustDaysInMonthFromForm();
        if (this.dateListener != null) {
            this.dateListener(this);
        }
    }

    function showCalendar(event) {
        this._setVisibility(false);
        g_Calendar.show(event, this);
    }

    //-----------------------private Methods-----------------------//
    function _updateDayOfWeek(){
        // all parts start from 1
        var date = DateUtils.newDate(this.getSelYear(), this.getSelMonth(), this.getSelDay());
        var dayOfWeek = date.getDay();
        this.dayOfWeekObj.innerHTML = DAYSOFWK[dayOfWeek];
    }
    function _setVisibility(visible) {
        var visibleStr = visible ? "visible" : "hidden";
        for (var i = 0; i < this.options.objectsToHide.length; i++) {
            this.options.objectsToHide[i].style.visibility = visibleStr;
        }
    }

    function _adjustDaysInMonthFromForm() {
        var month = this.monthObj[this.monthObj.selectedIndex].value;
        var year = this.yearObj[this.yearObj.selectedIndex].value;
        this._adjustDaysInMonth(month, year);
    }
    function _adjustDaysInMonth(month, year) {
        var daysForThisSelection = DateUtils.getDaysInMonth(month, year);
        var prevDaysInSelection = this.dayObj.length;

        if (prevDaysInSelection > daysForThisSelection) {
            for (i=0; i<(prevDaysInSelection - daysForThisSelection); i++) {
                this.dayObj.options[this.dayObj.options.length - 1] = null
            }
        }
        if (daysForThisSelection > prevDaysInSelection) {
            var prevLastDay = this.dayObj.options.length;
            for( i = prevLastDay+1; i <= daysForThisSelection; i++ ) {
                var newOption = new Option( i, i );
                var optionsColl = this.dayObj.options;
                optionsColl[optionsColl.length] = newOption;
            }
        }
        if (this.dayObj.selectedIndex < 0)
            this.dayObj.selectedIndex == 0;
    }

    function _initOptions() {
        this._writeYearOptions();
        this._writeMonthOptions();
        this._writeDayOptions();
    }

    function _writeYearOptions() {
        var minYear = DateUtils.getYear(this.options.fromDate);
        var maxYear = DateUtils.getYear(this.options.toDate);
        for( i = minYear; i <= maxYear; i++) {
            var newOption = new Option(i, i);
            var optionsColl = this.yearObj.options;
            optionsColl[optionsColl.length] = newOption;
        }
    }
    function _writeMonthOptions() {
        for( i=1; i <= 12; i++ ) {
            var newOption = new Option( this.monthNames[i], i );
            var optionsColl = this.monthObj.options;
            optionsColl[optionsColl.length] = newOption;
        }
    }
    function _writeDayOptions() {
        for( i=1; i <= 31; i++ ) {
            var newOption = new Option( i, i );
            var optionsColl = this.dayObj.options;
            optionsColl[optionsColl.length] = newOption;
        }
    }
}


//--------------------------------------------------//
//----------------------Calendar--------------------//
//--------------------------------------------------//
    // standard browser sniffer class
    function Browser(){
      this.dom = document.getElementById?1:0;
      this.ie4 = (document.all && !this.dom)?1:0;
      this.ns4 = (document.layers && !this.dom)?1:0;
      this.ns6 = (this.dom && !document.all)?1:0;
      this.ie5 = (this.dom && document.all)?1:0;
      this.ok = this.dom || this.ie4 || this.ns4;
      this.platform = navigator.platform;
    }
    var browser = new Browser();

    var timeoutDelay = 500; // milliseconds before disappear
    var g_startDay = 1// 0=sunday, 1=monday

    // used by timeout auto hide functions
    var timeoutId = false;

    var builder = new StringBuilder();
    builder.a('<div id="calendarContainer" style="position:absolute; left: 100px; top: 100px; width: 124px; height: 132px;')
        .a('clip:rect(0px 124px 132px 0px); visibility : hidden; z-index : 4; background-color : #ffffff;" ')
        .a(' onmouseout="calendarTimeout();" onmouseover="if (timeoutId) clearTimeout(timeoutId);"></div>');
    document.write(builder.toString());

    var g_Calendar;  // global to hold the calendar reference, set by constructor

    function calendarTimeout() {
        timeoutId = setTimeout('g_Calendar.hide();',timeoutDelay);
    }

    // constructor for calendar class
    // shared singleton
    function Calendar() {
        g_Calendar = this;
        this.daysOfWeek = DAYSOFW;
        this.months = MONTHS;
        this.daysInMonth = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
        this.containerLayer = document.getElementById('calendarContainer');
    }

     Calendar.prototype.getFirstDOM = function() {
        var date = new Date();
        DateUtils.setDay(date, 1);
        DateUtils.setMonth(date, this.month);
        DateUtils.setYear(date, this.year);
        return date.getDay(); // day of week
    }

    Calendar.prototype.updatePopupContent = function() {
        var builder = new StringBuilder();
        builder.a('<form id="calendarForm" onSubmit="this.year.blur();return false;"><table width="100%" border="0" cellspacing="0" cellpadding="2" class="calBorderColor"><tr><td valign="top"><table width="100%" border="0" cellspacing="0" cellpadding="1" class="calBgColor">')
            .a('<tr><td width="60%" class="cal" align="left">')
            .a('<table border="0" cellspacing="0" cellpadding="0"><tr><td><a href="javascript: g_Calendar.changeMonth(-1);" onmouseover="window.status=\'Mois pr&eacute;c&eacute;dent\';return true;" onmouseout="window.status=\'\';return true;"><img name="calendar" src="').a(this.compoundDate.options.imgDir).a('/calendar/down.gif" width="8" height="12" border="0" alt=""></a></td><td class="cal" width="100%" align="center">').a(this.months[this.month - 1]).a('</td><td class="cal"><a href="javascript: g_Calendar.changeMonth(+1);" onmouseover="window.status=\'Mois suivant\';return true;" onmouseout="window.status=\'\';return true;"><img name="calendar" src="').a(this.compoundDate.options.imgDir).a('/calendar/up.gif" width="8" height="12" border="0" alt=""></a></td></tr></table>')
              .a('</td><td width="40%" align="right" class="cal">')
              .a('<table border="0" cellspacing="0" cellpadding="0"><tr><td class="cal"><a href="javascript: g_Calendar.changeYear(-1);" onmouseover="window.status=\'Ann&eacute;e pr&eacute;c&eacute;dente\';return true;" onmouseout="window.status=\'\';return true;"><img name="calendar" src="').a(this.compoundDate.options.imgDir).a('/calendar/down.gif" width="8" height="12" border="0" alt=""></a></td><td class="cal" width="100%" align="center">').a(this.year).a('</td><td class="cal"><a href="javascript: g_Calendar.changeYear(+1);" onmouseover="window.status=\'Ann&eacute;e suivante\';return true;" onmouseout="window.status=\'\';return true;"><img name="calendar" src="').a(this.compoundDate.options.imgDir).a('/calendar/up.gif" width="8" height="12" border="0" alt=""></a></td></tr></table>')
              .a('</td></tr></table>');

        var iCount = 1;
        var iFirstDOM = (7+this.getFirstDOM()-g_startDay)%7; // to prevent calling it in a loop
        var iDaysInMonth = DateUtils.getDaysInMonth(this.month, this.year);

        builder.a('<table width="100%" border="0" cellspacing="0" cellpadding="1" class="calBgColor"><tr>');
        for (var i=0;i<7;i++){
            builder.a('<td align="center" class="calDaysColor">').a(this.daysOfWeek[(g_startDay+i)%7]).a('</td>');
        }
        builder.a('</tr>');
        var fromComparInt = DateUtils.getComparInt(this.compoundDate.options.fromDate);
        var toComparInt = DateUtils.getComparInt(this.compoundDate.options.toDate);
        var iCount = 1;
        var cellDate = DateUtils.newDate(this.year, this.month, iCount);
        var cellComparInt = DateUtils.getComparInt(cellDate);
        for (var j=1;j<=6;j++) {
            builder.a('<tr>');
            for (var i=1;i<=7;i++) {
                builder.a('<td width="16" align="center" ');
                if ( (7*(j-1) + i)>=iFirstDOM+1  && iCount <= iDaysInMonth) {
                    if (this.day == iCount && this.year == this.compoundDate.getSelYear() && this.month == this.compoundDate.getSelMonth()) {
                        builder.a('class="calHighlightColor"');
                    } else {
                        if (i==7-g_startDay || i==((7-g_startDay)%7)+1) {
                            builder.a('class="calWeekend"');
                        } else {
                            builder.a('class="cal"');
                        }
                    }
                    builder.a('>');
                    if (cellComparInt >= fromComparInt && cellComparInt <= toComparInt) {
                        builder.a('<a class="cal" href="javascript: g_Calendar.clickDay(').a(iCount).a(');" onmouseover="window.status=\'').a(iCount).a(' ').a(this.months[this.month - 1]).a(' ').a(this.year).a('\';return true;" onmouseout="window.status=\'\';return true;">').a(iCount).a('</a>');
                    } else {
                        builder.a('<span class="disabled">').a(iCount).a('</span>');
                    }
                    iCount++;
                    DateUtils.setDay(cellDate, iCount);
                    cellComparInt = DateUtils.getComparInt(cellDate);
                } else {
                    if  (i==7-g_startDay || i==((7-g_startDay)%7)+1) {
                        builder.a('class="calWeekend"');
                    } else {
                        builder.a('class="cal"');
                    }
                    builder.a('>&nbsp;');
                }
                builder.a('</td>');
            }
            builder.a('</tr>');
        }
        builder.a('</table></td></tr></table></form>');
        this.containerLayer.innerHTML = builder.toString();
    }
    Calendar.prototype.changeYear = function(incr){
       (incr==1)?this.year++:this.year--;
       this.updatePopupContent();
    }
    Calendar.prototype.changeMonth = function(incr){
        if (this.month == 12 && incr == 1){
            this.month = 1;
            this.year++;
        } else {
            if (this.month==1 && incr==-1){
                this.month = 12;
                this.year--;
            } else {
                (incr == 1) ? this.month++ : this.month--;
            }
        }
       this.updatePopupContent();
    }

    Calendar.prototype.clickDay = function(day){
       this.compoundDate.setDateParts(this.year, this.month, day);
       this.hide();
    }

    Calendar.prototype.show = function(event, compoundDate) {
        if (this.containerLayer.style.visibility=='visible') {
            this.containerLayer.style.visibility='hidden';
            this.compoundDate.calendarHidden();
            return;
        }

        if (compoundDate.options.anchorElement) {
            var anchor = compoundDate.options.anchorElement;
            var leftPos = anchor.getLeft() + anchor.offsetWidth;
            var topPos = anchor.getTop();
            PositionUtils.setPosition(this.containerLayer, leftPos, topPos);
        } else {
            PositionUtils.setPosition(this.containerLayer,
                PositionUtils.mouseX + 5, PositionUtils.mouseY - 15);
        }

        // values init using the compoundDate
        this.compoundDate = compoundDate;
        this.month = compoundDate.getSelMonth();
        this.day = compoundDate.getSelDay();
        this.year = compoundDate.getSelYear();
           this.updatePopupContent();

        this.containerLayer.style.visibility='visible';
    }

    Calendar.prototype.hide = function() {
        this.containerLayer.style.visibility='hidden';
        if (this.compoundDate != null) {
            this.compoundDate.calendarHidden();
            this.compoundDate = null;
        }
    }

    function handleDocumentClick(e){
      if (browser.ie4 || browser.ie5) e = window.event;

      if (browser.ns6){
          if (g_Calendar != null){
            var bTest = (e.pageX > parseInt(g_Calendar.containerLayer.style.left,10) && e.pageX <  (parseInt(g_Calendar.containerLayer.style.left,10)+125) && e.pageY < (parseInt(g_Calendar.containerLayer.style.top,10)+125) && e.pageY > parseInt(g_Calendar.containerLayer.style.top,10));
            if (e.target.id.toLowerCase().indexOf("calendarimg") == -1 && e.target.name!='month'  && e.target.name!='year' && e.target.name!='calendar' && e.target.className != "cal" && !bTest){
              g_Calendar.hide();
            }
        }
      }
      if (browser.ie4 || browser.ie5){
          if (g_Calendar != null){
        // if user clicked inside the calendar & outside a valid date, it doesn't disappear
       var bTest = (e.x > parseInt(g_Calendar.containerLayer.style.left,10) && e.x <  (parseInt(g_Calendar.containerLayer.style.left,10)+125) && e.y < (parseInt(g_Calendar.containerLayer.style.top,10)+125) && e.y > parseInt(g_Calendar.containerLayer.style.top,10));
        if (e.srcElement.id.toLowerCase().indexOf("calendarimg") == -1 && e.srcElement.name!='month' && e.srcElement.name!='year' && e.srcElement.className != "cal" && !bTest & typeof(e.srcElement)!='object'){
          g_Calendar.hide();
         }
        }
      }
      if (browser.ns4) g_Calendar.hide();
    }

      // Finally licked extending native date object;
      Date.isLeapYear = function(year){ if (year%4==0 && ((year%100!=0) || (year%400==0))) return true; else return false; }
      Date.daysInYear = function(year){ if (Date.isLeapYear(year)) return 366; else return 365;}
      var DAY = 1000*60*60*24;
      Date.prototype.addDays = function(num){
        return new Date((num*DAY)+this.valueOf());
      }
    window.onloadCal=function(){
      new Calendar();
    }
    window.document.onclick=handleDocumentClick;
