/*
FGQ - 02/17/2011 Added date functions to control behavior of dropdowns 
*/
	
	    function FromDateChange() {
	        var d_dy = document.getElementById('depart_day');
	        var d_mo = document.getElementById('depart_month');
	        var d_yr = document.getElementById('depart_year');
	        var r_dy = document.getElementById('return_day');
	        var r_mo = document.getElementById('return_month');
	        var r_yr = document.getElementById('return_year');
	        var d_dy_cmp = d_dy.selectedIndex;
	        var d_mo_cmp = d_mo.selectedIndex;
	        var d_yr_cmp = d_yr.value;
	        var r_dy_cmp = r_dy.selectedIndex;
	        var r_mo_cmp = r_mo.selectedIndex;
	        var r_yr_cmp = r_yr.value;
            
	        if (d_dy_cmp < 10) {
	            d_dy_cmp = '0' + d_dy_cmp;
	        }

	        if (d_mo_cmp < 10) {
	            d_mo_cmp = '0' + d_mo_cmp;
	        }

	        if (r_dy_cmp < 10) {
	            r_dy_cmp = '0' + r_dy_cmp;
	        }

	        if (r_mo_cmp < 10) {
	            r_mo_cmp = '0' + r_mo_cmp;
	        }

	        var cmp1 = d_yr_cmp + d_mo_cmp + d_dy_cmp;
	        var cmp2 = r_yr_cmp + r_mo_cmp + r_dy_cmp;
	        //alert("cmp1:" + cmp1 + " cmp2:" + cmp2);
	        var change = 'n';
	        //if departure date is later than return date, change the return date
	        if (d_yr_cmp + d_mo_cmp + d_dy_cmp > r_yr_cmp + r_mo_cmp + r_dy_cmp) {
	            change = 'y';
	            //alert("to be changed");
	        }
	        var d_dy_index = d_dy.selectedIndex;
	        
	        var r_dy_index = r_dy.selectedIndex;
	        
	        //change return year to match depart year
	        var dyr_value = d_yr.value;
	        if (change == 'y') {
	            r_yr.value = d_yr.value;
	        }

	        //alert("Made it to FromDateChange");
	        //populate depart day dropdown
	        var fm_index = d_mo.selectedIndex;
	        var fm_value = d_mo.options[fm_index].value;
	        var max_days = 31;
	        //alert("depart_month_index:" + fm_index + " depart_month_Value: " + fm_value + " depart_year_Value: " + dyr_value);
	        //change return month selected index
	        if (change == 'y') {
	            r_mo.selectedIndex = fm_index;
	        }
	        
	        if ((fm_index == 4) || (fm_index == 6) || (fm_index == 9) || (fm_index == 11)) {
	            max_days = 30;
	        }
	        else if (fm_index == 2) {
	            // Check for leap year
	            if (((dyr_value % 4 == 0) && (dyr_value % 100 != 0)) || (dyr_value % 400 == 0)) { // leap year
	                max_days = 29;
	            }
	            else {
	                max_days = 28;
	            }
	        }
	        //alert("max days:" + max_days);
	        if (d_dy_index > max_days) {
	            d_dy_index = max_days;
	        }
	        //change return day dropdown selected
	        if (change == 'y') {
	            if (r_dy_index > max_days) {
	                r_dy_index = max_days;
	            }
	        }

	        //clear the day of month dropdowns
            var c;
            var op = d_dy.options.length;
            //alert('depart length:' + op);
            for (c = op; c >= 0; c--) {
                d_dy.options.remove(c);
            }

            //clear the return date day of month dropdowns
            if (change == 'y') {
                op = r_dy.options.length;
                for (c = op; c >= 0; c--) {
                    r_dy.options.remove(c);
                }
            }
            
	        for (c = 0; c <= max_days; c++) {
	            
	            if (c == 0) {
	                addOption(d_dy, ' ', ' ');
	                //populate the return date day of month dropdown
	                if (change == 'y') {
	                    addOption(r_dy, ' ', ' ');
	                }
	            }
	            else {
	                addOption(d_dy, c, c);
	                //populate the return date day of month dropdown
	                if (change == 'y') {
	                    addOption(r_dy, c, c);
	                }
	            }
	        }
	       //alert("d index:" + d_dy_index + " r index:" + r_dy_index);
	        d_dy.selectedIndex = d_dy_index;
	        if (change == 'y') {
	            //alert("trying to activate change handler");
	            r_mo.fireEvent("onchange");
	            //if we're changing the date and the depart day is greater than the return day, 
	            //  change the return day to equal the depart day.
	            if (d_dy_index > r_dy_index) {
	                r_dy_index = d_dy_index;
	            }
	            r_dy.selectedIndex = r_dy_index;
	        }

	    }

	    function ToDateChange() {
	        var r_dy = document.getElementById('return_day');
	        var r_mo = document.getElementById('return_month');
	        var r_yr = document.getElementById('return_year');

	        var r_dy_index = r_dy.selectedIndex;
	        var ryr_value = r_yr.value;

	        //alert("Made it to ToDateChange");
	        var max_days = 31;
	        var fm_index = r_mo.selectedIndex;
	        var fm_value = r_mo.options[fm_index].value;

	        if ((fm_index == 4) || (fm_index == 6) || (fm_index == 9) || (fm_index == 11)) {
	            max_days = 30;
	        }
	        else if (fm_index == 2) {
	            // Check for leap year
	            if (((ryr_value % 4 == 0) && (ryr_value % 100 != 0)) || (ryr_value % 400 == 0)) { // leap year
	                max_days = 29;
	            }
	            else {
	                max_days = 28;
	            }
	        }
	        //alert("max days:" + max_days);
	        if (r_dy_index > max_days) {
	            r_dy_index = max_days;
	        }

	        //clear the dropdowns
	        var c;
	        var op = r_dy.options.length;
	        op = r_dy.options.length;
	        for (c = op; c >= 0; c--) {
	            r_dy.options.remove(c);
	        }

	        for (c = 0; c <= max_days; c++) {

	            if (c == 0) {
	                addOption(r_dy, ' ', ' ');
	            }
	            else {
	                addOption(r_dy, c, c);
	            }
	        }

	        r_dy.selectedIndex = r_dy_index;

	    } 

        function addOption(selectbox,text,value )
        {
            var optn = document.createElement("OPTION");
            optn.text = text;
            optn.value = value;
            selectbox.options.add(optn);
        }

        function SetupDates() {
            var d_dy = document.getElementById('depart_day');
            var d_mo = document.getElementById('depart_month');
            var d_yr = document.getElementById('depart_year');
            var r_dy = document.getElementById('return_day');
            var r_mo = document.getElementById('return_month');
            var r_yr = document.getElementById('return_year');
            var d_dy_index = d_dy.selectedIndex;

            var r_dy_index = r_dy.selectedIndex;

            var dyr_value = d_yr.value;
            var ryr_value = r_yr.value;

            //alert("Made it to SetupDates");
            //populate depart day dropdown
            var fm_index = d_mo.selectedIndex;
            var tm_index = r_mo.selectedIndex;
            var fm_value = d_mo.options[fm_index].value;
            var max_days = 31;
            var t_max_days = 31;
            //alert("depart_month_index:" + fm_index + " depart_month_Value: " + fm_value + " depart_year_Value: " + dyr_value);
            //change return month selected index

            if ((fm_index == 4) || (fm_index == 6) || (fm_index == 9) || (fm_index == 11)) {
                max_days = 30;
            }
            else if (fm_index == 2) {
                // Check for leap year
                if (((dyr_value % 4 == 0) && (dyr_value % 100 != 0)) || (dyr_value % 400 == 0)) { // leap year
                    max_days = 29;
                }
                else {
                    max_days = 28;
                }
            }

            if ((tm_index == 4) || (tm_index == 6) || (tm_index == 9) || (tm_index == 11)) {
                t_max_days = 30;
            }
            else if (tm_index == 2) {
                // Check for leap year
                if (((ryr_value % 4 == 0) && (ryr_value % 100 != 0)) || (ryr_value % 400 == 0)) { // leap year
                    t_max_days = 29;
                }
                else {
                    t_max_days = 28;
                }
            }

            //clear the day of month dropdowns
            var c;
            var op = d_dy.options.length;
            //alert('depart length:' + op);
            
            for (c = op; c >= 0; c--) {
                d_dy.options.remove(c);
            }

            //clear the return date day of month dropdowns

            op = r_dy.options.length;
            for (c = op; c >= 0; c--) {
                r_dy.options.remove(c);
            }


            for (c = 0; c <= max_days; c++) {

                if (c == 0) {
                    addOption(d_dy, ' ', ' ');
                }
                else {
                    addOption(d_dy, c, c);
                    //populate the return date day of month dropdown
                }
            }
            //alert("d index:" + d_dy_index + " r index:" + r_dy_index);
            d_dy.selectedIndex = d_dy_index;

            for (c = 0; c <= t_max_days; c++) {

                if (c == 0) {
                    addOption(r_dy, ' ', ' ');
                }
                else {
                    addOption(r_dy, c, c);
                    //populate the return date day of month dropdown
                }
            }
            r_dy.selectedIndex = r_dy_index;





        }




        function CCardLength() {
            //alert('made it to ccardlength');
            var textCardNum = document.getElementById('txtCCardNumber');
            var goodlen = 0;
            if (textCardNum != null) {
                var ccardtype = document.getElementById('ddCCardType');
                var ccardvalue = ccardtype.options[ccardtype.selectedIndex].value;
                //alert('ccardvalue=' + ccardvalue);
                if (ccardvalue == 'AM') {
                    goodlen = 15;
                }
                else {
                    goodlen = 16;
                }
                var yval = new String();
                yval = textCardNum.value;
                var iposspace = yval.indexOf(' ');
                var iposdash = yval.indexOf('-');
                yval = yval.replace(' ', '');
                yval = yval.replace('-', '');
                if (iposdash == '-1' & iposspace == '-1') {
                }
                else {
                    textCardNum.value = yval;
                }
                //textCardNum.value = yval;
                //alert('yval=' + yval);
                var lofCC = yval.length;
                //alert('lofCC=' + lofCC);
                if (lofCC == goodlen) {
                    // it's 16, show the green check and erase the red x
                    var imgX = document.getElementById('imgXMark');
                    if (imgX != null) {
                        imgX.style.display = "none";
                    }
                    var imgCheck = document.getElementById('imgCheckMark');
                    if (imgCheck != null) {
                        imgCheck.style.display = "";
                    }
                }
                else {
                    if (lofCC == '0') {
                        // it's 0 get rid of them both
                        var imgX = document.getElementById('imgXMark');
                        if (imgX != null) {
                            imgX.style.display = "none";
                        }
                        var imgCheck = document.getElementById('imgCheckMark');
                        if (imgCheck != null) {
                            imgCheck.style.display = "none";
                        }

                    }
                    else {
                        //it's not zero and it's not 16...show the x
                        var imgX = document.getElementById('imgXMark');
                        if (imgX != null) {
                            imgX.style.display = "";
                        }

                        var imgCheck = document.getElementById('imgCheckMark');
                        if (imgCheck != null) {
                            imgCheck.style.display = "none";
                        }

                    }
                }
            }
            //var lblLength = document.getElementById('lblCCardNumberLen');
            //lblLength.innerHTML = lofCC;
        }
        function Same_Name_Copy() {
            //alert('made it to Same Name Copy');

            var textFN_From = document.getElementById('txtFirstName');
            var textLN_From = document.getElementById('txtLastName');
            //alert('firstname:' + textFN_From.value);
            //alert('lastname:' + textLN_From.value);
            var FN_To = document.getElementById('txtFirstNameOnCCard');
            var LN_To = document.getElementById('txtLastNameOnCCard');
            //alert('firstname to:' + FN_To.value);
            FN_To.value = textFN_From.value;
            LN_To.value = textLN_From.value;

        }
        function Signon_MiniScreen() {
            var pnlMiniScreen = document.getElementById('pnlSignon');
            var pnlErrorMiniScreen = document.getElementById('pnlLoginerror');
            var lblSignon = document.getElementById('lblSignon');
            if (pnlErrorMiniScreen != null) {
                pnlErrorMiniScreen.style.display = "none";
            }
            pnlMiniScreen.style.display = "";
            lblSignon.style.display = "";
        }
        function Clear_MiniScreen() {
            var pnlMiniScreen = document.getElementById('pnlSignon');
            pnlMiniScreen.style.display = "none";
        }
	


/*
DatePicker v5.4 by frequency-decoder.com

Released under a creative commons Attribution-Share Alike 3.0 Unported license (http://creativecommons.org/licenses/by-sa/3.0/)

Please credit frequency-decoder in any derivative work - thanks.
        
You are free:
        
* to Share — to copy, distribute and transmit the work
* to Remix — to adapt the work
            
Under the following conditions:

* Attribution — You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).      
* Share Alike — If you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license.
*/



        var datePickerController = (function datePickerController() {

            var debug = false,
            isOpera = Object.prototype.toString.call(window.opera) === "[object Opera]",
            isMoz = /mozilla/.test(navigator.userAgent.toLowerCase()) && !/(compatible|webkit)/.test(navigator.userAgent.toLowerCase()),
            languageInfo = parseUILanguage(),
            datePickers = {},
            uniqueId = 0,
            weeksInYearCache = {},
            localeImport = false,
            nbsp = String.fromCharCode(160),
            describedBy = "",
            nodrag = false,
            buttonTabIndex = true,
            returnLocaleDate = false,
            mouseWheel = true,
            cellFormat = "d-sp-F-sp-Y",
            titleFormat = "F-sp-d-cc-sp-Y",
            formatParts = isOpera ? ["placeholder"] : ["placeholder", "sp-F-sp-Y"],
            dividors = ["dt", "sl", "ds", "cc", "sp"],
            dvParts = "dt|sl|ds|cc|sp",
            dParts = "d|j",
            mParts = "m|n|M|F",
            yParts = "Y|y",
            kbEvent = false,
            bespokeTitles = {},
            finalOpacity = 100,
            validFmtRegExp = /^((sp|dt|sl|ds|cc)|([d|D|l|j|N|w|S|W|M|F|m|n|t|Y|y]))(-((sp|dt|sl|ds|cc)|([d|D|l|j|N|w|S|W|M|F|m|n|t|Y|y])))*$/,
            rangeRegExp = /^((\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01]))$/,
            wcDateRegExp = /^(((\d\d\d\d)|(\*\*\*\*))((0[1-9]|1[012])|(\*\*))(0[1-9]|[12][0-9]|3[01]))$/;

            (function() {
                var scriptFiles = document.getElementsByTagName('script'),
                    scriptInner = String(scriptFiles[scriptFiles.length - 1].innerHTML).replace(/[\n\r\s\t]+/g, " ").replace(/^\s+/, "").replace(/\s+$/, ""),
                    json = parseJSON(scriptInner);

                if (typeof json === "object" && !("err" in json)) {
                    affectJSON(json);
                };

                if (typeof (fdLocale) != "object") {
                    var head = document.getElementsByTagName("head")[0] || document.documentElement,
                            loc = scriptFiles[scriptFiles.length - 1].src.substr(0, scriptFiles[scriptFiles.length - 1].src.lastIndexOf("/")) + "/lang/",
                            script;

                    for (var i = 0; i < languageInfo.length; i++) {
                        script = document.createElement('script');
                        script.type = "text/javascript";
                        script.src = loc + languageInfo[i] + ".js";
                        script.charSet = "utf-8";

                        /*@cc_on
                        /*@if(@_win32)
                        var bases = document.getElementsByTagName('base');
                        if (bases.length && bases[0].childNodes.length) {
                            bases[0].appendChild(script);
                        } else {
                            document.getElementsByTagName('head')[0].appendChild(script);
                        };
                        bases = null;
                        @else @*/
                                head.appendChild(script);
                                /*@end
                        @*/
                    };
                    script = null;
                } else {
                    returnLocaleDate = true;
                };
            })();

            function parseUILanguage() {
                var languageTag = document.getElementsByTagName('html')[0].getAttribute('lang') || document.getElementsByTagName('html')[0].getAttribute('xml:lang');

                if (!languageTag) {
                    languageTag = "en";
                } else {
                    languageTag = languageTag.toLowerCase();
                };

                return languageTag.search(/^([a-z]{2,3})-([a-z]{2})$/) != -1 ? [languageTag.match(/^([a-z]{2,3})-([a-z]{2})$/)[1], languageTag] : [languageTag];
            };

            function affectJSON(json) {
                if (typeof json !== "object") { return; };
                for (key in json) {
                    value = json[key];
                    switch (key.toLowerCase()) {
                        case "lang":
                            if (value.search(/^[a-z]{2,3}(-([a-z]{2}))?$/i) != -1) {
                                languageInfo = [value.toLowerCase()];
                                returnLocaleDate = true;
                            };
                            break;
                        case "nodrag":
                            nodrag = !!value;
                            break;
                        case "buttontabindex":
                            buttonTabIndex = !!value;
                            break;
                        case "mousewheel":
                            mouseWheel = !!value;
                            break;
                        case "cellformat":
                            if (typeof value == "string" && value.match(validFmtRegExp)) {
                                parseCellFormat(value);
                            };
                            break;
                        case "titleformat":
                            if (typeof value == "string" && value.match(validFmtRegExp)) {
                                titleFormat = value;
                            };
                            break;
                        case "describedby":
                            if (typeof value == "string") {
                                describedBy = value;
                            };
                            break;
                        case "finalopacity":
                            if (typeof value == 'number' && (+value > 20 && +value <= 100)) {
                                finalOpacity = parseInt(value, 10);
                            };
                            break;
                        case "bespoketitles":
                            bespokeTitles = {};
                            for (var dt in value) {
                                bespokeTitles[dt] = value[dt];
                            };
                    };
                };
            };

            function parseCellFormat(value) {
                if (isOpera) {
                    // Don't use hidden text for opera due to focus outline problems               
                    formatParts = ["placeholder"];
                    cellFormat = "j-sp-F-sp-Y";
                    return;
                };

                // I'm sure this could be done with a regExp and a split in one line... seriously...
                var parts = value.split("-"),
                    fullParts = [],
                    tmpParts = [],
                    part;

                for (var pt = 0; pt < parts.length; pt++) {
                    part = parts[pt];
                    if (part == "j" || part == "d") {
                        if (tmpParts.length) {
                            fullParts.push(tmpParts.join("-"));
                            tmpParts = [];
                        };
                        fullParts.push("placeholder");
                    } else {
                        tmpParts.push(part);
                    };
                };

                if (tmpParts.length) {
                    fullParts.push(tmpParts.join("-"));
                };

                if (!fullParts.length || fullParts.length > 3) {
                    formatParts = ["placeholder", "sp-F-sp-Y"];
                    cellFormat = "j-sp-F-sp-Y";
                    return;
                };

                formatParts = fullParts;
                cellFormat = value;
            };

            function pad(value, length) {
                length = length || 2;
                return "0000".substr(0, length - Math.min(String(value).length, length)) + value;
            };

            function addEvent(obj, type, fn) {
                try {
                    if (obj.attachEvent) {
                        obj["e" + type + fn] = fn;
                        obj[type + fn] = function() { obj["e" + type + fn](window.event); };
                        obj.attachEvent("on" + type, obj[type + fn]);
                    } else {
                        obj.addEventListener(type, fn, true);
                    };
                } catch (err) { }
            };

            function removeEvent(obj, type, fn) {
                try {
                    if (obj.detachEvent) {
                        obj.detachEvent("on" + type, obj[type + fn]);
                        obj[type + fn] = null;
                    } else {
                        obj.removeEventListener(type, fn, true);
                    };
                } catch (err) { };
            };

            function stopEvent(e) {
                e = e || document.parentWindow.event;
                if (e.stopPropagation) {
                    e.stopPropagation();
                    e.preventDefault();
                };
                /*@cc_on
                @if(@_win32)
                e.cancelBubble = true;
                e.returnValue = false;
                @end
                @*/
                return false;
            };

            function parseJSON(str) {
                // Check we have a String
                if (typeof str !== 'string' || str == "") { return {}; };
                try {
                    // Does a JSON (native or not) Object exist                              
                    if (typeof JSON === "object" && JSON.parse) {
                        return window.JSON.parse(str);
                        // Genious code taken from: http://kentbrewster.com/badges/                                                      
                    } else if (/lang|buttontabindex|mousewheel|cellformat|titleformat|nodrag|describedby/.test(str.toLowerCase())) {
                        var f = Function(['var document,top,self,window,parent,Number,Date,Object,Function,',
                                        'Array,String,Math,RegExp,Image,ActiveXObject;',
                                        'return (', str.replace(/<\!--.+-->/gim, '').replace(/\bfunction\b/g, 'function­'), ');'].join(''));
                        return f();
                    };
                } catch (e) { };

                if (debug) {
                    throw "Could not parse the JSON object";
                };

                return { "err": "Could not parse the JSON object" };
            };

            function setARIARole(element, role) {
                if (element && element.tagName) {
                    element.setAttribute("role", role);
                };
            };

            function setARIAProperty(element, property, value) {
                if (element && element.tagName) {
                    element.setAttribute("aria-" + property, value);
                };
            };

            // The datePicker object itself 
            function datePicker(options) {
                this.dateSet = null;
                this.timerSet = false;
                this.visible = false;
                this.fadeTimer = null;
                this.timer = null;
                this.yearInc = 0;
                this.monthInc = 0;
                this.dayInc = 0;
                this.mx = 0;
                this.my = 0;
                this.x = 0;
                this.y = 0;
                this.created = false;
                this.disabled = false;
                this.opacity = 0;
                this.opacityTo = 99;
                this.inUpdate = false;
                this.kbEventsAdded = false;
                this.fullCreate = false;
                this.selectedTD = null;
                this.cursorTD = null;
                this.cursorDate = options.cursorDate ? options.cursorDate : "",
                this.date = options.cursorDate ? new Date(+options.cursorDate.substr(0, 4), +options.cursorDate.substr(4, 2) - 1, +options.cursorDate.substr(6, 2)) : new Date();
                this.defaults = {};
                this.dynDisabledDates = {};
                this.firstDayOfWeek = localeImport.firstDayOfWeek;
                this.interval = new Date();
                this.clickActivated = false;
                this.noFocus = true;
                this.kbEvent = false;
                this.disabledDates = false;
                this.enabledDates = false;
                this.delayedUpdate = false;
                this.bespokeTitles = {};

                for (var thing in options) {
                    if (thing.search(/callbacks|formElements|formatMasks/) != -1) continue;
                    this[thing] = options[thing];
                };

                /*@cc_on
                @if(@_win32)
                this.iePopUp = null;
                this.isIE7 = false;
                @end
                @*/

                /*@cc_on
                @if(@_jscript_version <= 5.7)
                this.isIE7 = document.documentElement && typeof document.documentElement.style.maxHeight != "undefined";
                @end
                @*/

                for (var i = 0, prop; prop = ["callbacks", "formElements", "formatMasks"][i]; i++) {
                    this[prop] = {};
                    for (var thing in options[prop]) {
                        this[prop][thing] = options[prop][thing];
                    };
                };

                // Adjust time to stop daylight savings madness on windows
                this.date.setHours(5);

                this.changeHandler = function() {
                    o.setDateFromInput();
                    o.callback("dateset", o.createCbArgObj());
                };
                this.createCbArgObj = function() {
                    return this.dateSet ? { "id": this.id, "date": this.dateSet, "dd": pad(this.date.getDate()), "mm": pad(this.date.getMonth() + 1), "yyyy": this.date.getFullYear()} : { "id": this.id, "date": null, "dd": null, "mm": null, "yyyy": null };
                };
                this.getScrollOffsets = function() {
                    if (typeof (window.pageYOffset) == 'number') {
                        //Netscape compliant
                        return [window.pageXOffset, window.pageYOffset];
                    } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
                        //DOM compliant
                        return [document.body.scrollLeft, document.body.scrollTop];
                    } else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
                        //IE6 standards compliant mode
                        return [document.documentElement.scrollLeft, document.documentElement.scrollTop];
                    };
                    return [0, 0];
                };
                this.reposition = function() {
                    if (!o.created || o.staticPos) { return; };

                    o.div.style.visibility = "hidden";
                    o.div.style.left = o.div.style.top = "0px";
                    o.div.style.display = "block";

                    var osh = o.div.offsetHeight,
                            osw = o.div.offsetWidth,
                            elem = document.getElementById('fd-but-' + o.id),
                            pos = o.truePosition(elem),
                            trueBody = (document.compatMode && document.compatMode != "BackCompat") ? document.documentElement : document.body,
                            sOffsets = o.getScrollOffsets(),
                            scrollTop = sOffsets[1],
                            scrollLeft = sOffsets[0],
                            fitsBottom = parseInt(trueBody.clientHeight + scrollTop) > parseInt(osh + pos[1] + elem.offsetHeight + 2),
                            fitsTop = parseInt(pos[1] - (osh + elem.offsetHeight + 2)) > parseInt(scrollTop);

                    o.div.style.visibility = "visible";

                    o.div.style.left = Number(parseInt(trueBody.clientWidth + scrollLeft) < parseInt(osw + pos[0]) ? Math.abs(parseInt((trueBody.clientWidth + scrollLeft) - osw)) : pos[0]) + "px";
                    o.div.style.top = (fitsBottom || !fitsTop) ? Math.abs(parseInt(pos[1] + elem.offsetHeight + 2)) + "px" : Math.abs(parseInt(pos[1] - (osh + 2))) + "px";
                    /*@cc_on
                    @if(@_jscript_version <= 5.7)
                    if (o.isIE7) return;
                    o.iePopUp.style.top = o.div.style.top;
                    o.iePopUp.style.left = o.div.style.left;
                    o.iePopUp.style.width = osw + "px";
                    o.iePopUp.style.height = (osh - 2) + "px";
                    @end
                    @*/
                };
                this.removeOldFocus = function() {
                    var td = document.getElementById(o.id + "-date-picker-hover");
                    if (td) {
                        try {
                            td.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", "-1");
                            td.tabIndex = -1;
                            td.className = td.className.replace(/date-picker-hover/, "");
                            td.id = "";
                            td.onblur = null;
                            td.onfocus = null;
                        } catch (err) { };
                    };
                };
                this.addAccessibleDate = function() {
                    var td = document.getElementById(o.id + "-date-picker-hover");

                    if (td && !(td.getElementsByTagName("span").length)) {
                        var ymd = td.className.match(/cd-([\d]{4})([\d]{2})([\d]{2})/),
                                    noS = (td.className.search(/date-picker-unused|out-of-range|day-disabled|no-selection|not-selectable/) != -1),
                                    spn = document.createElement('span'),
                                    spnC;

                        spn.className = "fd-screen-reader"; ;

                        while (td.firstChild) td.removeChild(td.firstChild);

                        if (noS) {
                            spnC = spn.cloneNode(false);
                            spnC.appendChild(document.createTextNode(getTitleTranslation(13)));
                            td.appendChild(spnC);
                        };

                        for (var pt = 0, part; part = formatParts[pt]; pt++) {
                            if (part == "placeholder") {
                                td.appendChild(document.createTextNode(+ymd[3]));
                            } else {
                                spnC = spn.cloneNode(false);
                                spnC.appendChild(document.createTextNode(printFormattedDate(new Date(ymd[1], +ymd[2] - 1, ymd[3]), part, true)));
                                td.appendChild(spnC);
                            };
                        };
                    };
                };
                this.setNewFocus = function() {
                    var td = document.getElementById(o.id + "-date-picker-hover");
                    if (td) {
                        try {
                            td.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", "0");
                            td.tabIndex = 0;

                            td.className = td.className.replace(/date-picker-hover/, "") + " date-picker-hover";
                            if (!this.clickActivated) {
                                td.onblur = o.onblur;
                                td.onfocus = o.onfocus;
                            };

                            if (!isOpera && !this.clickActivated) o.addAccessibleDate();

                            if (!this.noFocus && !this.clickActivated) {
                                setTimeout(function() { try { td.focus(); } catch (err) { }; }, 0);
                            };
                        } catch (err) { };
                    };
                };
                this.setCursorDate = function(yyyymmdd) {
                    if (String(yyyymmdd).search(/^([0-9]{8})$/) != -1) {
                        this.date = new Date(+yyyymmdd.substr(0, 4), +yyyymmdd.substr(4, 2) - 1, +yyyymmdd.substr(6, 2));
                        this.cursorDate = yyyymmdd;

                        if (this.staticPos) {
                            this.updateTable();
                        };
                    };
                };
                this.updateTable = function(noCallback) {
                    if (!o || o.inUpdate || !o.created) return;

                    o.inUpdate = true;
                    o.removeOldFocus();

                    if (o.timerSet && !o.delayedUpdate) {
                        if (o.monthInc) {
                            var n = o.date.getDate(),
                                            d = new Date(o.date);

                            d.setDate(2);
                            d.setMonth(d.getMonth() + o.monthInc * 1);
                            d.setDate(Math.min(n, daysInMonth(d.getMonth(), d.getFullYear())));

                            o.date = new Date(d);
                        } else {
                            o.date.setDate(Math.min(o.date.getDate() + o.dayInc, daysInMonth(o.date.getMonth() + o.monthInc, o.date.getFullYear() + o.yearInc)));
                            o.date.setMonth(o.date.getMonth() + o.monthInc);
                            o.date.setFullYear(o.date.getFullYear() + o.yearInc);
                        };
                    };

                    o.outOfRange();
                    if (!o.noToday) { o.disableTodayButton(); };
                    o.showHideButtons(o.date);

                    var cd = o.date.getDate(),
                            cm = o.date.getMonth(),
                            cy = o.date.getFullYear(),
                            cursorDate = (String(cy) + pad(cm + 1) + pad(cd)),
                            tmpDate = new Date(cy, cm, 1);

                    tmpDate.setHours(5);

                    var dt, cName, td, i, currentDate, cellAdded, col, currentStub, abbr, bespokeRenderClass, spnC, dateSetD,
                        weekDayC = (tmpDate.getDay() + 6) % 7,
                        firstColIndex = (((weekDayC - o.firstDayOfWeek) + 7) % 7) - 1,
                        dpm = daysInMonth(cm, cy),
                        today = new Date(),
                        stub = String(tmpDate.getFullYear()) + pad(tmpDate.getMonth() + 1),
                        cellAdded = [4, 4, 4, 4, 4, 4],
                        lm = new Date(cy, cm - 1, 1),
                        nm = new Date(cy, cm + 1, 1),
                        daySub = daysInMonth(lm.getMonth(), lm.getFullYear()),
                        stubN = String(nm.getFullYear()) + pad(nm.getMonth() + 1),
                        stubP = String(lm.getFullYear()) + pad(lm.getMonth() + 1),
                        weekDayN = (nm.getDay() + 6) % 7,
                        weekDayP = (lm.getDay() + 6) % 7,
                        today = today.getFullYear() + pad(today.getMonth() + 1) + pad(today.getDate()),
                        spn = document.createElement('span');

                    o.firstDateShown = !o.constrainSelection && o.fillGrid && (0 - firstColIndex < 1) ? String(stubP) + (daySub + (0 - firstColIndex)) : stub + "01";
                    o.lastDateShown = !o.constrainSelection && o.fillGrid ? stubN + pad(41 - firstColIndex - dpm) : stub + String(dpm);
                    o.currentYYYYMM = stub;

                    bespokeRenderClass = o.callback("redraw", { id: o.id, dd: pad(cd), mm: pad(cm + 1), yyyy: cy, firstDateDisplayed: o.firstDateShown, lastDateDisplayed: o.lastDateShown }) || {};
                    dts = o.getDates(cy, cm + 1);

                    o.checkSelectedDate();

                    dateSetD = (o.dateSet != null) ? o.dateSet.getFullYear() + pad(o.dateSet.getMonth() + 1) + pad(o.dateSet.getDate()) : false;
                    spn.className = "fd-screen-reader";

                    if (this.selectedTD != null) {
                        setARIAProperty(this.selectedTD, "selected", false);
                        this.selectedTD = null;
                    };

                    for (var curr = 0; curr < 42; curr++) {
                        row = Math.floor(curr / 7);
                        td = o.tds[curr];
                        spnC = spn.cloneNode(false);

                        while (td.firstChild) td.removeChild(td.firstChild);

                        if ((curr > firstColIndex && curr <= (firstColIndex + dpm)) || o.fillGrid) {
                            currentStub = stub;
                            weekDay = weekDayC;
                            dt = curr - firstColIndex;
                            cName = [];
                            selectable = true;

                            if (dt < 1) {
                                dt = daySub + dt;
                                currentStub = stubP;
                                weekDay = weekDayP;
                                selectable = !o.constrainSelection;
                                cName.push("month-out");
                            } else if (dt > dpm) {
                                dt -= dpm;
                                currentStub = stubN;
                                weekDay = weekDayN;
                                selectable = !o.constrainSelection;
                                cName.push("month-out");
                            };

                            weekDay = (weekDay + dt + 6) % 7;

                            cName.push("day-" + localeDefaults.dayAbbrs[weekDay].toLowerCase());

                            currentDate = currentStub + String(dt < 10 ? "0" : "") + dt;

                            if (o.rangeLow && +currentDate < +o.rangeLow || o.rangeHigh && +currentDate > +o.rangeHigh) {
                                td.className = "out-of-range";
                                td.title = "";
                                td.appendChild(document.createTextNode(dt));
                                if (o.showWeeks) { cellAdded[row] = Math.min(cellAdded[row], 2); };
                            } else {
                                if (selectable) {
                                    td.title = titleFormat ? printFormattedDate(new Date(+String(currentStub).substr(0, 4), +String(currentStub).substr(4, 2) - 1, +dt), titleFormat, true) : "";
                                    cName.push("cd-" + currentDate + " yyyymm-" + currentStub + " mmdd-" + currentStub.substr(4, 2) + pad(dt));
                                } else {
                                    td.title = titleFormat ? getTitleTranslation(13) + " " + printFormattedDate(new Date(+String(currentStub).substr(0, 4), +String(currentStub).substr(4, 2) - 1, +dt), titleFormat, true) : "";
                                    cName.push("yyyymm-" + currentStub + " mmdd-" + currentStub.substr(4, 2) + pad(dt) + " not-selectable");
                                };

                                if (currentDate == today) { cName.push("date-picker-today"); };

                                if (dateSetD == currentDate) {
                                    cName.push("date-picker-selected-date");
                                    setARIAProperty(td, "selected", "true");
                                    this.selectedTD = td;
                                };

                                if (o.disabledDays[weekDay] || dts[currentDate] == 0) { cName.push("day-disabled"); if (titleFormat && selectable) { td.title = getTitleTranslation(13) + " " + td.title; }; }

                                if (currentDate in bespokeRenderClass) { cName.push(bespokeRenderClass[currentDate]); }

                                if (o.highlightDays[weekDay]) { cName.push("date-picker-highlight"); };

                                if (cursorDate == currentDate) {
                                    td.id = o.id + "-date-picker-hover";
                                };

                                td.appendChild(document.createTextNode(dt));
                                td.className = cName.join(" ");

                                if (o.showWeeks) {
                                    cellAdded[row] = Math.min(cName[0] == "month-out" ? 3 : 1, cellAdded[row]);
                                };
                            };
                        } else {
                            td.className = "date-picker-unused";
                            td.appendChild(document.createTextNode(nbsp));
                            td.title = "";
                        };

                        if (o.showWeeks && curr - (row * 7) == 6) {
                            while (o.wkThs[row].firstChild) o.wkThs[row].removeChild(o.wkThs[row].firstChild);
                            o.wkThs[row].appendChild(document.createTextNode(cellAdded[row] == 4 && !o.fillGrid ? nbsp : getWeekNumber(cy, cm, curr - firstColIndex - 6)));
                            o.wkThs[row].className = "date-picker-week-header" + (["", "", " out-of-range", " month-out", ""][cellAdded[row]]);
                        };
                    };

                    var span = o.titleBar.getElementsByTagName("span");
                    while (span[0].firstChild) span[0].removeChild(span[0].firstChild);
                    while (span[1].firstChild) span[1].removeChild(span[1].firstChild);
                    span[0].appendChild(document.createTextNode(getMonthTranslation(cm, false) + nbsp));
                    span[1].appendChild(document.createTextNode(cy));

                    if (o.timerSet) {
                        o.timerInc = 50 + Math.round(((o.timerInc - 50) / 1.8));
                        o.timer = window.setTimeout(o.updateTable, o.timerInc);
                    };

                    o.inUpdate = o.delayedUpdate = false;
                    o.setNewFocus();
                };

                this.destroy = function() {

                    if (document.getElementById("fd-but-" + this.id)) {
                        document.getElementById("fd-but-" + this.id).parentNode.removeChild(document.getElementById("fd-but-" + this.id));
                    };

                    if (!this.created) { return; };

                    // Cleanup for Internet Explorer
                    removeEvent(this.table, "mousedown", o.onmousedown);
                    removeEvent(this.table, "mouseover", o.onmouseover);
                    removeEvent(this.table, "mouseout", o.onmouseout);
                    removeEvent(document, "mousedown", o.onmousedown);
                    removeEvent(document, "mouseup", o.clearTimer);

                    if (window.addEventListener && !window.devicePixelRatio) {
                        try {
                            window.removeEventListener('DOMMouseScroll', this.onmousewheel, false);
                        } catch (err) { };
                    } else {
                        removeEvent(document, "mousewheel", this.onmousewheel);
                        removeEvent(window, "mousewheel", this.onmousewheel);
                    };
                    o.removeOnFocusEvents();
                    clearTimeout(o.fadeTimer);
                    clearTimeout(o.timer);

                    /*@cc_on
                    @if(@_jscript_version <= 5.7)
                    if (!o.staticPos && !o.isIE7) {
                        try {
                            o.iePopUp.parentNode.removeChild(o.iePopUp);
                            o.iePopUp = null;
                        } catch (err) { };
                    };
                    @end
                    @*/

                    if (this.div && this.div.parentNode) {
                        this.div.parentNode.removeChild(this.div);
                    };

                    o = null;
                };
                this.resizeInlineDiv = function() {
                    o.div.style.width = o.table.offsetWidth + "px";
                    o.div.style.height = o.table.offsetHeight + "px";
                };
                this.create = function() {

                    if (document.getElementById("fd-" + this.id)) return;

                    this.noFocus = true;

                    function createTH(details) {
                        var th = document.createElement('th');
                        if (details.thClassName) th.className = details.thClassName;
                        if (details.colspan) {
                            /*@cc_on
                            /*@if (@_win32)
                            th.setAttribute('colSpan', details.colspan);
                            @else @*/
                                        th.setAttribute('colspan',details.colspan);
                                        /*@end
                            @*/
                        };
                        /*@cc_on
                        /*@if (@_win32)
                        th.unselectable = "on";
                        /*@end@*/
                        return th;
                    };
                    function createThAndButton(tr, obj) {
                        for (var i = 0, details; details = obj[i]; i++) {
                            var th = createTH(details);
                            tr.appendChild(th);
                            var but = document.createElement('span');
                            but.className = details.className;
                            but.id = o.id + details.id;
                            but.appendChild(document.createTextNode(details.text || o.nbsp));
                            but.title = details.title || "";
                            /*@cc_on
                            /*@if(@_win32)
                            th.unselectable = but.unselectable = "on";
                            /*@end@*/
                            th.appendChild(but);
                        };
                    };

                    this.div = document.createElement('div');
                    this.div.id = "fd-" + this.id;
                    this.div.className = "datePicker";

                    // Attempt to hide the div from screen readers during content creation
                    this.div.style.visibility = "hidden";
                    this.div.style.display = "none";

                    // Set the ARIA describedby property if the required block available
                    if (this.describedBy && document.getElementById(this.describedBy)) {
                        setARIAProperty(this.div, "describedby", this.describedBy);
                    };

                    // Set the ARIA labelled property if the required label available
                    if (this.labelledBy) {
                        setARIAProperty(this.div, "labelledby", this.labelledBy.id);
                    };

                    var tr, row, col, tableHead, tableBody, tableFoot;

                    this.table = document.createElement('table');
                    this.table.className = "datePickerTable";
                    this.table.onmouseover = this.onmouseover;
                    this.table.onmouseout = this.onmouseout;
                    this.table.onclick = this.onclick;

                    if (this.staticPos) {
                        this.table.onmousedown = this.onmousedown;
                    };

                    this.div.appendChild(this.table);

                    var dragEnabledCN = !this.dragDisabled ? " drag-enabled" : "";

                    if (!this.staticPos) {
                        this.div.style.visibility = "hidden";
                        this.div.className += dragEnabledCN;
                        document.getElementsByTagName('body')[0].appendChild(this.div);

                        /*@cc_on
                        @if(@_jscript_version <= 5.7)

                        if (!this.isIE7) {
                            this.iePopUp = document.createElement('iframe');
                            this.iePopUp.src = "javascript:'<html></html>';";
                            this.iePopUp.setAttribute('className', 'iehack');
                            // Remove iFrame from tabIndex                                        
                            this.iePopUp.setAttribute("tabIndex", -1);
                            // Hide it from ARIA aware technologies
                            setARIARole(this.iePopUp, "presentation");
                            setARIAProperty(this.iePopUp, "hidden", "true");
                            this.iePopUp.scrolling = "no";
                            this.iePopUp.frameBorder = "0";
                            this.iePopUp.name = this.iePopUp.id = this.id + "-iePopUpHack";
                            document.body.appendChild(this.iePopUp);
                        };

                        @end
                        @*/

                        // Aria "hidden" property for non active popup datepickers
                        setARIAProperty(this.div, "hidden", "true");
                    } else {
                        elem = document.getElementById(this.positioned ? this.positioned : this.id);
                        if (!elem) {
                            this.div = null;
                            if (debug) throw this.positioned ? "Could not locate a datePickers associated parent element with an id:" + this.positioned : "Could not locate a datePickers associated input with an id:" + this.id;
                            return;
                        };

                        this.div.className += " static-datepicker";

                        if (this.positioned) {
                            elem.appendChild(this.div);
                        } else {
                            elem.parentNode.insertBefore(this.div, elem.nextSibling);
                        };

                        if (this.hideInput) {
                            for (var elemID in this.formElements) {
                                elem = document.getElementById(elemID);
                                if (elem) {
                                    elem.className += " fd-hidden-input";
                                };
                            };
                        };

                        setTimeout(this.resizeInlineDiv, 300);
                    };

                    // ARIA Grid role
                    setARIARole(this.div, "grid");

                    if (this.statusFormat) {
                        tableFoot = document.createElement('tfoot');
                        this.table.appendChild(tableFoot);
                        tr = document.createElement('tr');
                        tr.className = "date-picker-tfoot";
                        tableFoot.appendChild(tr);
                        this.statusBar = createTH({ thClassName: "date-picker-statusbar" + dragEnabledCN, colspan: this.showWeeks ? 8 : 7 });
                        tr.appendChild(this.statusBar);
                        this.updateStatus();
                    };

                    tableHead = document.createElement('thead');
                    this.table.appendChild(tableHead);

                    tr = document.createElement('tr');
                    setARIARole(tr, "presentation");

                    tableHead.appendChild(tr);

                    // Title Bar
                    this.titleBar = createTH({ thClassName: "date-picker-title" + dragEnabledCN, colspan: this.showWeeks ? 8 : 7 });

                    tr.appendChild(this.titleBar);
                    tr = null;

                    var span = document.createElement('span');
                    span.appendChild(document.createTextNode(nbsp));
                    span.className = "month-display" + dragEnabledCN;
                    this.titleBar.appendChild(span);

                    span = document.createElement('span');
                    span.appendChild(document.createTextNode(nbsp));
                    span.className = "year-display" + dragEnabledCN;
                    this.titleBar.appendChild(span);

                    span = null;

                    tr = document.createElement('tr');
                    setARIARole(tr, "presentation");
                    tableHead.appendChild(tr);

                    createThAndButton(tr, [
                        { className: "prev-but prev-year", id: "-prev-year-but", text: "\u00AB", title: getTitleTranslation(2) },
                        { className: "prev-but prev-month", id: "-prev-month-but", text: "\u2039", title: getTitleTranslation(0) },
                        { colspan: this.showWeeks ? 4 : 3, className: "today-but", id: "-today-but", text: getTitleTranslation(4) },
                        { className: "next-but next-month", id: "-next-month-but", text: "\u203A", title: getTitleTranslation(1) },
                        { className: "next-but next-year", id: "-next-year-but", text: "\u00BB", title: getTitleTranslation(3) }
                        ]);

                    tableBody = document.createElement('tbody');
                    this.table.appendChild(tableBody);

                    var colspanTotal = this.showWeeks ? 8 : 7,
                            colOffset = this.showWeeks ? 0 : -1,
                            but, abbr;

                    for (var rows = 0; rows < 7; rows++) {
                        row = document.createElement('tr');

                        if (rows != 0) {
                            // ARIA Grid role
                            setARIARole(row, "row");
                            tableBody.appendChild(row);
                        } else {
                            tableHead.appendChild(row);
                        };

                        for (var cols = 0; cols < colspanTotal; cols++) {
                            if (rows === 0 || (this.showWeeks && cols === 0)) {
                                col = document.createElement('th');
                            } else {
                                col = document.createElement('td');
                                setARIAProperty(col, "describedby", this.id + "-col-" + cols + (this.showWeeks ? " " + this.id + "-row-" + rows : ""));
                                setARIAProperty(col, "selected", "false");
                            };

                            /*@cc_on@*/
                            /*@if(@_win32)
                            col.unselectable = "on";
                            /*@end@*/

                            row.appendChild(col);
                            if ((this.showWeeks && cols > 0 && rows > 0) || (!this.showWeeks && rows > 0)) {
                                setARIARole(col, "gridcell");
                            } else {
                                if (rows === 0 && cols > colOffset) {
                                    col.className = "date-picker-day-header";
                                    col.scope = "col";
                                    setARIARole(col, "columnheader");
                                    col.id = this.id + "-col-" + cols;
                                } else {
                                    col.className = "date-picker-week-header";
                                    col.scope = "row";
                                    setARIARole(col, "rowheader");
                                    col.id = this.id + "-row-" + rows;
                                };
                            };
                        };
                    };

                    col = row = null;

                    this.ths = this.table.getElementsByTagName('thead')[0].getElementsByTagName('tr')[2].getElementsByTagName('th');
                    for (var y = 0; y < colspanTotal; y++) {
                        if (y == 0 && this.showWeeks) {
                            this.ths[y].appendChild(document.createTextNode(getTitleTranslation(6)));
                            this.ths[y].title = getTitleTranslation(8);
                            continue;
                        };

                        if (y > (this.showWeeks ? 0 : -1)) {
                            but = document.createElement("span");
                            but.className = "fd-day-header";
                            /*@cc_on@*/
                            /*@if(@_win32)
                            but.unselectable = "on";
                            /*@end@*/
                            this.ths[y].appendChild(but);
                        };
                    };

                    but = null;

                    this.trs = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
                    this.tds = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('td');
                    this.butPrevYear = document.getElementById(this.id + "-prev-year-but");
                    this.butPrevMonth = document.getElementById(this.id + "-prev-month-but");
                    this.butToday = document.getElementById(this.id + "-today-but");
                    this.butNextYear = document.getElementById(this.id + "-next-year-but");
                    this.butNextMonth = document.getElementById(this.id + "-next-month-but");

                    if (this.noToday) {
                        this.butToday.style.display = "none";
                    };

                    if (this.showWeeks) {
                        this.wkThs = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('th');
                        this.div.className += " weeks-displayed";
                    };

                    tableBody = tableHead = tr = createThAndButton = createTH = null;

                    if (this.rangeLow && this.rangeHigh && (this.rangeHigh - this.rangeLow < 7)) { this.equaliseDates(); };

                    this.updateTableHeaders();
                    this.created = true;
                    this.updateTable();

                    if (this.staticPos) {
                        this.visible = true;
                        this.opacity = this.opacityTo = this.finalOpacity;
                        this.div.style.visibility = "visible";
                        this.div.style.display = "block";
                        this.noFocus = true;
                        this.fade();
                    } else {
                        this.reposition();
                        this.div.style.visibility = "visible";
                        this.fade();
                        this.noFocus = true;
                    };

                    this.callback("domcreate", { "id": this.id });
                };
                this.fade = function() {
                    window.clearTimeout(o.fadeTimer);
                    o.fadeTimer = null;
                    var diff = Math.round(o.opacity + ((o.opacityTo - o.opacity) / 4));
                    o.setOpacity(diff);
                    if (Math.abs(o.opacityTo - diff) > 3 && !o.noFadeEffect) {
                        o.fadeTimer = window.setTimeout(o.fade, 50);
                    } else {
                        o.setOpacity(o.opacityTo);
                        if (o.opacityTo == 0) {
                            o.div.style.display = "none";
                            o.div.style.visibility = "hidden";
                            setARIAProperty(o.div, "hidden", "true");
                            o.visible = false;
                        } else {
                            setARIAProperty(o.div, "hidden", "false");
                            o.visible = true;
                        };
                    };
                };
                this.trackDrag = function(e) {
                    e = e || window.event;
                    var diffx = (e.pageX ? e.pageX : e.clientX ? e.clientX : e.x) - o.mx;
                    var diffy = (e.pageY ? e.pageY : e.clientY ? e.clientY : e.Y) - o.my;
                    o.div.style.left = Math.round(o.x + diffx) > 0 ? Math.round(o.x + diffx) + 'px' : "0px";
                    o.div.style.top = Math.round(o.y + diffy) > 0 ? Math.round(o.y + diffy) + 'px' : "0px";
                    /*@cc_on
                    @if(@_jscript_version <= 5.7)
                    if (o.staticPos || o.isIE7) return;
                    o.iePopUp.style.top = o.div.style.top;
                    o.iePopUp.style.left = o.div.style.left;
                    @end
                    @*/
                };
                this.stopDrag = function(e) {
                    var b = document.getElementsByTagName("body")[0];
                    b.className = b.className.replace(/fd-drag-active/g, "");
                    removeEvent(document, 'mousemove', o.trackDrag, false);
                    removeEvent(document, 'mouseup', o.stopDrag, false);
                    o.div.style.zIndex = 9999;
                };
                this.onmousedown = function(e) {
                    e = e || document.parentWindow.event;
                    var el = e.target != null ? e.target : e.srcElement,
                            origEl = el,
                            hideDP = true,
                            reg = new RegExp("^fd-(but-)?" + o.id + "$");

                    o.mouseDownElem = null;

                    // Are we within the wrapper div or the button    
                    while (el) {
                        if (el.id && el.id.length && el.id.search(reg) != -1) {
                            hideDP = false;
                            break;
                        };
                        try { el = el.parentNode; } catch (err) { break; };
                    };

                    // If not, then ...     
                    if (hideDP) {
                        hideAll();
                        return true;
                    };

                    if ((o.div.className + origEl.className).search('fd-disabled') != -1) { return true; };

                    // We check the mousedown events on the buttons
                    if (origEl.id.search(new RegExp("^" + o.id + "(-prev-year-but|-prev-month-but|-next-month-but|-next-year-but)$")) != -1) {

                        o.mouseDownElem = origEl;

                        addEvent(document, "mouseup", o.clearTimer);
                        addEvent(origEl, "mouseout", o.clearTimer);

                        var incs = {
                            "-prev-year-but": [0, -1, 0],
                            "-prev-month-but": [0, 0, -1],
                            "-next-year-but": [0, 1, 0],
                            "-next-month-but": [0, 0, 1]
                        },
                                    check = origEl.id.replace(o.id, ""),
                                    dateYYYYMM = Number(o.date.getFullYear() + pad(o.date.getMonth() + 1));

                        o.timerInc = 800;
                        o.timerSet = true;
                        o.dayInc = incs[check][0];
                        o.yearInc = incs[check][1];
                        o.monthInc = incs[check][2];
                        o.accellerator = 1;

                        if (!(o.currentYYYYMM == dateYYYYMM)) {
                            if ((o.currentYYYYMM < dateYYYYMM && (o.yearInc == -1 || o.monthInc == -1)) || (o.currentYYYYMM > dateYYYYMM && (o.yearInc == 1 || o.monthInc == 1))) {
                                o.delayedUpdate = false;
                                o.timerInc = 1200;
                            } else {
                                o.delayedUpdate = true;
                                o.timerInc = 800;
                            };
                        };

                        o.updateTable();

                        return stopEvent(e);

                    } else if (el.className.search("drag-enabled") != -1) {
                        o.mx = e.pageX ? e.pageX : e.clientX ? e.clientX : e.x;
                        o.my = e.pageY ? e.pageY : e.clientY ? e.clientY : e.Y;
                        o.x = parseInt(o.div.style.left);
                        o.y = parseInt(o.div.style.top);
                        addEvent(document, 'mousemove', o.trackDrag, false);
                        addEvent(document, 'mouseup', o.stopDrag, false);
                        var b = document.getElementsByTagName("body")[0];
                        b.className = b.className.replace(/fd-drag-active/g, "") + " fd-drag-active";
                        o.div.style.zIndex = 10000;

                        return stopEvent(e);
                    };
                    return true;
                };
                this.onclick = function(e) {
                    if (o.opacity != o.opacityTo || o.disabled) return stopEvent(e);

                    e = e || document.parentWindow.event;
                    var el = e.target != null ? e.target : e.srcElement;

                    while (el.parentNode) {
                        // Are we within a valid i.e. clickable TD node  
                        if (el.tagName && el.tagName.toLowerCase() == "td") {

                            if (el.className.search(/cd-([0-9]{8})/) == -1 || el.className.search(/date-picker-unused|out-of-range|day-disabled|no-selection|not-selectable/) != -1) return stopEvent(e);

                            var cellDate = el.className.match(/cd-([0-9]{8})/)[1];
                            o.date = new Date(cellDate.substr(0, 4), cellDate.substr(4, 2) - 1, cellDate.substr(6, 2));
                            o.dateSet = new Date(o.date);
                            o.noFocus = true;
                            o.callback("dateset", { "id": o.id, "date": o.dateSet, "dd": o.dateSet.getDate(), "mm": o.dateSet.getMonth() + 1, "yyyy": o.dateSet.getFullYear() });
                            o.returnFormattedDate();
                            o.hide();

                            o.stopTimer();

                            break;
                            // Today button pressed             
                        } else if (el.id && el.id == o.id + "-today-but") {
                            o.date = new Date();
                            o.updateTable();
                            o.stopTimer();
                            break;
                            // Day headers clicked, change the first day of the week      
                        } else if (el.className.search(/date-picker-day-header/) != -1) {
                            var cnt = o.showWeeks ? -1 : 0,
                                        elem = el;

                            while (elem.previousSibling) {
                                elem = elem.previousSibling;
                                if (elem.tagName && elem.tagName.toLowerCase() == "th") cnt++;
                            };

                            o.firstDayOfWeek = (o.firstDayOfWeek + cnt) % 7;
                            o.updateTableHeaders();
                            break;
                        };
                        try { el = el.parentNode; } catch (err) { break; };
                    };

                    return stopEvent(e);
                };

                this.show = function(autoFocus) {
                    if (this.staticPos) { return; };

                    var elem, elemID;
                    for (elemID in this.formElements) {
                        elem = document.getElementById(this.id);
                        if (!elem || (elem && elem.disabled)) { return; };
                    };

                    this.noFocus = true;

                    // If the datepicker doesn't exist in the dom  
                    if (!this.created || !document.getElementById('fd-' + this.id)) {
                        this.created = false;
                        this.fullCreate = false;
                        this.create();
                        this.fullCreate = true;
                    } else {
                        this.setDateFromInput();
                        this.reposition();
                    };

                    this.noFocus = !!!autoFocus;

                    if (this.noFocus) {
                        this.clickActivated = true;
                        addEvent(document, "mousedown", this.onmousedown);
                        if (mouseWheel) {
                            if (window.addEventListener && !window.devicePixelRatio) window.addEventListener('DOMMouseScroll', this.onmousewheel, false);
                            else {
                                addEvent(document, "mousewheel", this.onmousewheel);
                                addEvent(window, "mousewheel", this.onmousewheel);
                            };
                        };
                    } else {
                        this.clickActivated = false;
                    };

                    this.opacityTo = this.finalOpacity;
                    this.div.style.display = "block";

                    /*@cc_on
                    @if(@_jscript_version <= 5.7)
                    if (!o.isIE7) {
                        this.iePopUp.style.width = this.div.offsetWidth + "px";
                        this.iePopUp.style.height = this.div.offsetHeight + "px";
                        this.iePopUp.style.display = "block";
                    };
                    @end
                    @*/

                    this.setNewFocus();
                    this.fade();
                    var butt = document.getElementById('fd-but-' + this.id);
                    if (butt) { butt.className = butt.className.replace("dp-button-active", "") + " dp-button-active"; };
                };
                this.hide = function() {
                    if (!this.visible || !this.created || !document.getElementById('fd-' + this.id)) return;

                    this.kbEvent = false;

                    o.div.className = o.div.className.replace("datepicker-focus", "");

                    this.stopTimer();
                    this.removeOnFocusEvents();
                    this.clickActivated = false;

                    // Update status bar                                
                    if (this.statusBar) { this.updateStatus(getTitleTranslation(9)); };

                    this.noFocus = true;
                    this.setNewFocus();

                    if (this.staticPos) {
                        return;
                    };

                    var butt = document.getElementById('fd-but-' + this.id);
                    if (butt) butt.className = butt.className.replace("dp-button-active", "");

                    removeEvent(document, "mousedown", this.onmousedown);

                    if (mouseWheel) {
                        if (window.addEventListener && !window.devicePixelRatio) {
                            try { window.removeEventListener('DOMMouseScroll', this.onmousewheel, false); } catch (err) { };
                        } else {
                            removeEvent(document, "mousewheel", this.onmousewheel);
                            removeEvent(window, "mousewheel", this.onmousewheel);
                        };
                    };

                    /*@cc_on
                    @if(@_jscript_version <= 5.7)
                    if (!this.isIE7) { this.iePopUp.style.display = "none"; };
                    @end
                    @*/

                    this.opacityTo = 0;
                    this.fade();
                };
                this.onblur = function(e) {
                    o.hide();
                };
                this.onfocus = function(e) {
                    o.noFocus = false;
                    o.div.className = o.div.className.replace("datepicker-focus", "") + " datepicker-focus";
                    o.addOnFocusEvents();
                };
                this.onmousewheel = function(e) {
                    e = e || document.parentWindow.event;
                    var delta = 0;

                    if (e.wheelDelta) {
                        delta = e.wheelDelta / 120;
                        if (isOpera && window.opera.version() < 9.2) delta = -delta;
                    } else if (e.detail) {
                        delta = -e.detail / 3;
                    };

                    var n = o.date.getDate(),
                            d = new Date(o.date),
                            inc = delta > 0 ? 1 : -1;

                    d.setDate(2);
                    d.setMonth(d.getMonth() + inc * 1);
                    d.setDate(Math.min(n, daysInMonth(d.getMonth(), d.getFullYear())));

                    if (o.outOfRange(d)) { return stopEvent(e); };

                    o.date = new Date(d);

                    o.updateTable();

                    if (o.statusBar) { o.updateStatus(printFormattedDate(o.date, o.statusFormat, true)); };

                    return stopEvent(e);
                };
                this.onkeydown = function(e) {
                    o.stopTimer();
                    if (!o.visible) return false;

                    e = e || document.parentWindow.event;
                    var kc = e.keyCode ? e.keyCode : e.charCode;

                    if (kc == 13) {
                        // RETURN/ENTER: close & select the date
                        var td = document.getElementById(o.id + "-date-picker-hover");
                        if (!td || td.className.search(/cd-([0-9]{8})/) == -1 || td.className.search(/no-selection|out-of-range|day-disabled/) != -1) {
                            return stopEvent(e);
                        };
                        o.dateSet = new Date(o.date);
                        o.callback("dateset", o.createCbArgObj());
                        o.returnFormattedDate();
                        o.hide();
                        return stopEvent(e);
                    } else if (kc == 27) {
                        // ESC: close, no date selection 
                        if (!o.staticPos) {
                            o.hide();
                            return stopEvent(e);
                        };
                        return true;
                    } else if (kc == 32 || kc == 0) {
                        // SPACE: goto today's date 
                        o.date = new Date();
                        o.updateTable();
                        return stopEvent(e);
                    } else if (kc == 9) {
                        // TAB: close, no date selection & focus on btton - popup only                                      
                        if (!o.staticPos) {
                            return stopEvent(e);
                        };
                        return true;
                    };

                    // Internet Explorer fires the keydown event faster than the JavaScript engine can
                    // update the interface. The following attempts to fix this.

                    /*@cc_on
                    @if(@_win32)
                    if (new Date().getTime() - o.interval.getTime() < 50) { return stopEvent(e); };
                    o.interval = new Date();
                    @end
                    @*/

                    if (isMoz) {
                        if (new Date().getTime() - o.interval.getTime() < 50) { return stopEvent(e); };
                        o.interval = new Date();
                    };

                    if ((kc > 49 && kc < 56) || (kc > 97 && kc < 104)) {
                        if (kc > 96) kc -= (96 - 48);
                        kc -= 49;
                        o.firstDayOfWeek = (o.firstDayOfWeek + kc) % 7;
                        o.updateTableHeaders();
                        return stopEvent(e);
                    };

                    if (kc < 33 || kc > 40) return true;

                    var d = new Date(o.date), tmp, cursorYYYYMM = o.date.getFullYear() + pad(o.date.getMonth() + 1);

                    // HOME: Set date to first day of current month
                    if (kc == 36) {
                        d.setDate(1);
                        // END: Set date to last day of current month                                 
                    } else if (kc == 35) {
                        d.setDate(daysInMonth(d.getMonth(), d.getFullYear()));
                        // PAGE UP & DOWN                                   
                    } else if (kc == 33 || kc == 34) {
                        var inc = (kc == 34) ? 1 : -1;

                        // CTRL + PAGE UP/DOWN: Moves to the same date in the previous/next year
                        if (e.ctrlKey) {
                            d.setFullYear(d.getFullYear() + inc * 1);
                            // PAGE UP/DOWN: Moves to the same date in the previous/next month                                            
                        } else {
                            var n = o.date.getDate();

                            d.setDate(2);
                            d.setMonth(d.getMonth() + inc * 1);
                            d.setDate(Math.min(n, daysInMonth(d.getMonth(), d.getFullYear())));
                        };
                        // LEFT ARROW                                    
                    } else if (kc == 37) {
                        d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() - 1);
                        // RIGHT ARROW
                    } else if (kc == 39 || kc == 34) {
                        d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() + 1);
                        // UP ARROW                                        
                    } else if (kc == 38) {
                        d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() - 7);
                        // DOWN ARROW                                        
                    } else if (kc == 40) {
                        d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() + 7);
                    };

                    if (o.outOfRange(d)) { return stopEvent(e); };
                    o.date = d;

                    if (o.statusBar) {
                        o.updateStatus(o.getBespokeTitle(o.date.getFullYear(), o.date.getMonth() + 1, o.date.getDate()) || printFormattedDate(o.date, o.statusFormat, true));
                    };

                    var t = String(o.date.getFullYear()) + pad(o.date.getMonth() + 1) + pad(o.date.getDate());

                    if (e.ctrlKey || (kc == 33 || kc == 34) || t < o.firstDateShown || t > o.lastDateShown) {
                        o.updateTable();
                        /*@cc_on
                        @if(@_win32)
                        o.interval = new Date();
                        @end
                        @*/
                    } else {
                        if (!o.noToday) { o.disableTodayButton(); };
                        o.removeOldFocus();

                        for (var i = 0, td; td = o.tds[i]; i++) {
                            if (td.className.search("cd-" + t) == -1) {
                                continue;
                            };
                            o.showHideButtons(o.date);
                            td.id = o.id + "-date-picker-hover";
                            o.setNewFocus();
                            break;
                        };
                    };

                    return stopEvent(e);
                };
                this.onmouseout = function(e) {
                    e = e || document.parentWindow.event;
                    var p = e.toElement || e.relatedTarget;
                    while (p && p != this) try { p = p.parentNode } catch (e) { p = this; };
                    if (p == this) return false;
                    if (o.currentTR) {
                        o.currentTR.className = "";
                        o.currentTR = null;
                    };

                    if (o.statusBar) {
                        o.updateStatus(o.getBespokeTitle(o.date.getFullYear(), o.date.getMonth() + 1, o.date.getDate()) || printFormattedDate(o.date, o.statusFormat, true));
                    };
                };
                this.onmouseover = function(e) {
                    e = e || document.parentWindow.event;
                    var el = e.target != null ? e.target : e.srcElement;
                    while (el.nodeType != 1) { el = el.parentNode; };

                    if (!el || !el.tagName) { return; };

                    var statusText = getTitleTranslation(9);
                    switch (el.tagName.toLowerCase()) {
                        case "td":
                            if (el.className.search(/date-picker-unused|out-of-range/) != -1) {
                                statusText = getTitleTranslation(9);
                            } if (el.className.search(/cd-([0-9]{8})/) != -1) {
                                o.stopTimer();
                                var cellDate = el.className.match(/cd-([0-9]{8})/)[1];

                                o.removeOldFocus();
                                el.id = o.id + "-date-picker-hover";
                                o.setNewFocus();

                                o.date = new Date(+cellDate.substr(0, 4), +cellDate.substr(4, 2) - 1, +cellDate.substr(6, 2));
                                if (!o.noToday) { o.disableTodayButton(); };

                                statusText = o.getBespokeTitle(+cellDate.substr(0, 4), +cellDate.substr(4, 2), +cellDate.substr(6, 2)) || printFormattedDate(o.date, o.statusFormat, true);
                            };
                            break;
                        case "th":
                            if (!o.statusBar) { break; };
                            if (el.className.search(/drag-enabled/) != -1) {
                                statusText = getTitleTranslation(10);
                            } else if (el.className.search(/date-picker-week-header/) != -1) {
                                var txt = el.firstChild ? el.firstChild.nodeValue : "";
                                statusText = txt.search(/^(\d+)$/) != -1 ? getTitleTranslation(7, [txt, txt < 3 && o.date.getMonth() == 11 ? getWeeksInYear(o.date.getFullYear()) + 1 : getWeeksInYear(o.date.getFullYear())]) : getTitleTranslation(9);
                            };
                            break;
                        case "span":
                            if (!o.statusBar) { break; };
                            if (el.className.search(/drag-enabled/) != -1) {
                                statusText = getTitleTranslation(10);
                            } else if (el.className.search(/day-([0-6])/) != -1) {
                                var day = el.className.match(/day-([0-6])/)[1];
                                statusText = getTitleTranslation(11, [getDayTranslation(day, false)]);
                            } else if (el.className.search(/prev-year/) != -1) {
                                statusText = getTitleTranslation(2);
                            } else if (el.className.search(/prev-month/) != -1) {
                                statusText = getTitleTranslation(0);
                            } else if (el.className.search(/next-year/) != -1) {
                                statusText = getTitleTranslation(3);
                            } else if (el.className.search(/next-month/) != -1) {
                                statusText = getTitleTranslation(1);
                            } else if (el.className.search(/today-but/) != -1 && el.className.search(/disabled/) == -1) {
                                statusText = getTitleTranslation(12);
                            };
                            break;
                        default:
                            statusText = "";
                    };
                    while (el.parentNode) {
                        el = el.parentNode;
                        if (el.nodeType == 1 && el.tagName.toLowerCase() == "tr") {
                            if (o.currentTR) {
                                if (el == o.currentTR) break;
                                o.currentTR.className = "";
                            };
                            el.className = "dp-row-highlight";
                            o.currentTR = el;
                            break;
                        };
                    };
                    if (o.statusBar && statusText) { o.updateStatus(statusText); };
                };
                this.clearTimer = function() {
                    o.stopTimer();
                    o.timerInc = 800;
                    o.yearInc = 0;
                    o.monthInc = 0;
                    o.dayInc = 0;

                    removeEvent(document, "mouseup", o.clearTimer);
                    if (o.mouseDownElem != null) {
                        removeEvent(o.mouseDownElem, "mouseout", o.clearTimer);
                    };
                    o.mouseDownElem = null;
                };

                var o = this;

                this.setDateFromInput();

                if (this.staticPos) {
                    this.create();
                } else {
                    this.createButton();
                };

                (function() {
                    var elemID, elem;

                    for (elemID in o.formElements) {
                        elem = document.getElementById(elemID);
                        if (elem && elem.tagName && elem.tagName.search(/select|input/i) != -1) {
                            addEvent(elem, "change", o.changeHandler);
                        };

                        if (!elem || elem.disabled == true) {
                            o.disableDatePicker();
                        };
                    };
                })();


                // We have fully created the datepicker...
                this.fullCreate = true;


            };
            datePicker.prototype.addButtonEvents = function(but) {
                function buttonEvent(e) {
                    e = e || window.event;

                    var inpId = this.id.replace('fd-but-', ''),
                            dpVisible = isVisible(inpId),
                            autoFocus = false,
                            kbEvent = datePickers[inpId].kbEvent;

                    if (kbEvent) {
                        datePickers[inpId].kbEvent = false;
                        return;
                    };

                    if (e.type == "keydown") {
                        datePickers[inpId].kbEvent = true;
                        var kc = e.keyCode != null ? e.keyCode : e.charCode;
                        if (kc != 13) return true;
                        if (dpVisible) {
                            this.className = this.className.replace("dp-button-active", "");
                            hideAll();
                            return stopEvent(e);
                        };
                        autoFocus = true;
                    } else {
                        datePickers[inpId].kbEvent = false;
                    };

                    this.className = this.className.replace("dp-button-active", "");

                    if (!dpVisible) {
                        this.className += " dp-button-active";
                        hideAll(inpId);
                        showDatePicker(inpId, autoFocus);
                    } else {
                        hideAll();
                    };

                    return stopEvent(e);
                };

                but.onkeydown = buttonEvent;
                but.onclick = buttonEvent;

                if (!buttonTabIndex || this.bespokeTabIndex === false) {
                    but.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", "-1");
                    but.tabIndex = -1;
                    but.onkeydown = null;
                    removeEvent(but, "keydown", buttonEvent);
                } else {
                    but.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", this.bespokeTabIndex);
                    but.tabIndex = this.bespokeTabIndex;
                };
            };

            datePicker.prototype.createButton = function() {

                if (this.staticPos || document.getElementById("fd-but-" + this.id)) { return; };

                var inp = document.getElementById(this.id),
                    span = document.createElement('span'),
                    but = document.createElement('a');

                but.href = "#" + this.id;
                but.className = "date-picker-control";
                but.title = getTitleTranslation(5);
                but.id = "fd-but-" + this.id;

                span.appendChild(document.createTextNode(nbsp));
                but.appendChild(span);

                span = document.createElement('span');
                span.className = "fd-screen-reader";
                span.appendChild(document.createTextNode(but.title));
                but.appendChild(span);

                // Set the ARIA role to be "button"
                setARIARole(but, "button");

                // Set a "haspopup" ARIA property - should this not be a list if ID's????
                setARIAProperty(but, "haspopup", true);

                if (this.positioned && document.getElementById(this.positioned)) {
                    document.getElementById(this.positioned).appendChild(but);
                } else {
                    inp.parentNode.insertBefore(but, inp.nextSibling);
                };

                this.addButtonEvents(but);

                but = null;

                this.callback("dombuttoncreate", { id: this.id });
            };
            datePicker.prototype.setBespokeTitles = function(titles) {
                this.bespokeTitles = titles;
            };
            datePicker.prototype.addBespokeTitles = function(titles) {
                for (var dt in titles) {
                    this.bespokeTitles[dt] = titles[dt];
                };
            };
            datePicker.prototype.getBespokeTitle = function(y, m, d) {
                var dt, dtFull, yyyymmdd = y + String(pad(m)) + pad(d);

                // Try this datepickers bespoke titles
                for (dt in this.bespokeTitles) {
                    dtFull = dt.replace(/^(\*\*\*\*)/, y).replace(/^(\d\d\d\d)(\*\*)/, "$1" + pad(m));
                    if (dtFull == yyyymmdd) return this.bespokeTitles[dt];
                };

                // Try the generic bespoke titles
                for (dt in bespokeTitles) {
                    dtFull = dt.replace(/^(\*\*\*\*)/, y).replace(/^(\d\d\d\d)(\*\*)/, "$1" + pad(m));
                    if (dtFull == yyyymmdd) return bespokeTitles[dt];
                };

                return false;
            };
            datePicker.prototype.returnSelectedDate = function() {
                return this.dateSet;
            };
            datePicker.prototype.setRangeLow = function(range) {
                this.rangeLow = (String(range).search(/^(\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])$/) == -1) ? false : range;
                if (!this.inUpdate) this.setDateFromInput();
            };
            datePicker.prototype.setRangeHigh = function(range) {
                this.rangeHigh = (String(range).search(/^(\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])$/) == -1) ? false : range;
                if (!this.inUpdate) this.setDateFromInput();
            };
            datePicker.prototype.setDisabledDays = function(dayArray) {
                if (!dayArray.length || dayArray.length != 7 || dayArray.join("").search(/^([0|1]{7})$/) == -1) {
                    if (debug) {
                        throw "Invalid values located when attempting to call setDisabledDays";
                    };
                    return false;
                };
                this.disabledDays = dayArray;
                if (!this.inUpdate) this.setDateFromInput();
            };
            datePicker.prototype.setDisabledDates = function(dateObj) {
                this.disabledDates = {};
                this.addDisabledDates(dateObj);
            };
            datePicker.prototype.setEnabledDates = function(dateObj) {
                this.enabledDates = {};
                this.addEnabledDates(dateObj);
            };
            datePicker.prototype.addDisabledDates = function(dateObj) {
                this.enabledDates = false;
                this.disabledDates = this.disabledDates || {};

                var startD;
                for (startD in dateObj) {
                    if ((String(startD).search(wcDateRegExp) != -1 && dateObj[startD] == 1) || (String(startD).search(rangeRegExp) != -1 && String(dateObj[startD]).search(rangeRegExp) != -1)) {
                        this.disabledDates[startD] = dateObj[startD];
                    };
                };

                if (!this.inUpdate) this.setDateFromInput();
            };
            datePicker.prototype.addEnabledDates = function(dateObj) {
                this.disabledDates = false;
                this.enabledDates = this.enabledDates || {};

                var startD;
                for (startD in dateObj) {
                    if ((String(startD).search(wcDateRegExp) != -1 && dateObj[startD] == 1) || (String(startD).search(rangeRegExp) != -1 && String(dateObj[startD]).search(rangeRegExp) != -1)) {
                        this.enabledDates[startD] = dateObj[startD];
                    };
                };

                if (!this.inUpdate) this.setDateFromInput();
            };
            datePicker.prototype.setSelectedDate = function(yyyymmdd) {
                if (String(yyyymmdd).search(wcDateRegExp) == -1) {
                    return false;
                };

                var match = yyyymmdd.match(rangeRegExp),
                    dt = new Date(+match[2], +match[3] - 1, +match[4]);

                if (!dt || isNaN(dt) || !this.canDateBeSelected(dt)) {
                    return false;
                };

                this.dateSet = new Date(dt);

                if (!this.inUpdate) this.updateTable();

                this.callback("dateset", this.createCbArgObj());
                this.returnFormattedDate();
            };
            datePicker.prototype.checkSelectedDate = function() {
                if (this.dateSet && !this.canDateBeSelected(this.dateSet)) {
                    this.dateSet = null;
                };
                if (!this.inUpdate) this.updateTable();
            };
            datePicker.prototype.addOnFocusEvents = function() {
                if (this.kbEventsAdded || this.noFocus) {
                    return;
                };

                addEvent(document, "keypress", this.onkeydown);
                addEvent(document, "mousedown", this.onmousedown);

                /*@cc_on
                @if(@_win32)
                removeEvent(document, "keypress", this.onkeydown);
                addEvent(document, "keydown", this.onkeydown);
                @end
                @*/
                if (window.devicePixelRatio) {
                    removeEvent(document, "keypress", this.onkeydown);
                    addEvent(document, "keydown", this.onkeydown);
                };
                this.noFocus = false;
                this.kbEventsAdded = true;
            };
            datePicker.prototype.removeOnFocusEvents = function() {

                if (!this.kbEventsAdded) { return; };

                removeEvent(document, "keypress", this.onkeydown);
                removeEvent(document, "keydown", this.onkeydown);
                removeEvent(document, "mousedown", this.onmousedown);

                this.kbEventsAdded = false;
            };
            datePicker.prototype.stopTimer = function() {
                this.timerSet = false;
                window.clearTimeout(this.timer);
            };
            datePicker.prototype.setOpacity = function(op) {
                this.div.style.opacity = op / 100;
                this.div.style.filter = 'alpha(opacity=' + op + ')';
                this.opacity = op;
            };
            datePicker.prototype.getDates = function(y, m) {
                var dpm = daysInMonth(m - 1, y),
                    obj = {},
                    dds = this.getGenericDates(y, m, false),
                    eds = this.getGenericDates(y, m, true),
                    dts = y + pad(m);

                for (var i = 1; i <= dpm; i++) {
                    dt = dts + "" + pad(i);

                    if (dds) {
                        obj[dt] = (dt in dds) ? 0 : 1;
                    } else if (eds) {
                        obj[dt] = (dt in eds) ? 1 : 0;
                    } else {
                        obj[dt] = 1;
                    };
                };

                return obj;
            };
            datePicker.prototype.getGenericDates = function(y, m, enabled) {
                var deDates = enabled ? this.enabledDates : this.disabledDates;

                if (!deDates) {
                    return false;
                };

                m = pad(m);

                var obj = {},
                    lower = this.firstDateShown,
                    upper = this.lastDateShown,
                    dt1, dt2, rngLower, rngUpper;

                if (!upper || !lower) {
                    lower = this.firstDateShown = y + pad(m) + "01";
                    upper = this.lastDateShown = y + pad(m) + pad(daysInMonth(m, y));
                };

                for (dt in deDates) {
                    dt1 = dt.replace(/^(\*\*\*\*)/, y).replace(/^(\d\d\d\d)(\*\*)/, "$1" + m);
                    dt2 = deDates[dt];

                    if (dt2 == 1) {
                        if (Number(dt1.substr(0, 6)) >= +String(this.firstDateShown).substr(0, 6)
                                   &&
                                   Number(dt1.substr(0, 6)) <= +String(this.lastDateShown).substr(0, 6)) {
                            obj[dt1] = 1;
                        };
                        continue;
                    };

                    // Range
                    if (+String(this.firstDateShown).substr(0, 6) >= Number(dt1.substr(0, 6))
                           &&
                           +String(this.lastDateShown).substr(0, 6) <= Number(dt2.substr(0, 6))) {
                        // Same month
                        if (Number(dt1.substr(0, 6)) == Number(dt2.substr(0, 6))) {
                            for (var i = dt1; i <= dt2; i++) {
                                obj[i] = 1;
                            };
                            continue;
                        };

                        // Different months but we only want this month
                        rngLower = Number(dt1.substr(0, 6)) == +String(this.firstDateShown).substr(0, 6) ? dt1 : lower;
                        rngUpper = Number(dt2.substr(0, 6)) == +String(this.lastDateShown).substr(0, 6) ? dt2 : upper;
                        for (var i = +rngLower; i <= +rngUpper; i++) {
                            obj[i] = 1;
                        };
                    };
                };
                return obj;
            };
            datePicker.prototype.truePosition = function(element) {
                var pos = this.cumulativeOffset(element);
                if (isOpera) { return pos; };
                var iebody = (document.compatMode && document.compatMode != "BackCompat") ? document.documentElement : document.body,
                    dsocleft = document.all ? iebody.scrollLeft : window.pageXOffset,
                    dsoctop = document.all ? iebody.scrollTop : window.pageYOffset,
                    posReal = this.realOffset(element);
                return [pos[0] - posReal[0] + dsocleft, pos[1] - posReal[1] + dsoctop];
            };
            datePicker.prototype.realOffset = function(element) {
                var t = 0, l = 0;
                do {
                    t += element.scrollTop || 0;
                    l += element.scrollLeft || 0;
                    element = element.parentNode;
                } while (element);
                return [l, t];
            };
            datePicker.prototype.cumulativeOffset = function(element) {
                var t = 0, l = 0;
                do {
                    t += element.offsetTop || 0;
                    l += element.offsetLeft || 0;
                    element = element.offsetParent;
                } while (element);
                return [l, t];
            };
            datePicker.prototype.equaliseDates = function() {
                var clearDayFound = false, tmpDate;
                for (var i = this.rangeLow; i <= this.rangeHigh; i++) {
                    tmpDate = String(i);
                    if (!this.disabledDays[new Date(tmpDate.substr(0, 4), tmpDate.substr(6, 2), tmpDate.substr(4, 2)).getDay() - 1]) {
                        clearDayFound = true;
                        break;
                    };
                };
                if (!clearDayFound) { this.disabledDays = [0, 0, 0, 0, 0, 0, 0] };
            };
            datePicker.prototype.outOfRange = function(tmpDate) {

                if (!this.rangeLow && !this.rangeHigh) { return false; };
                var level = false;
                if (!tmpDate) {
                    level = true;
                    tmpDate = this.date;
                };

                var d = pad(tmpDate.getDate()),
                    m = pad(tmpDate.getMonth() + 1),
                    y = tmpDate.getFullYear(),
                    dt = String(y) + String(m) + String(d);

                if (this.rangeLow && +dt < +this.rangeLow) {
                    if (!level) { return true; };
                    this.date = new Date(this.rangeLow.substr(0, 4), this.rangeLow.substr(4, 2) - 1, this.rangeLow.substr(6, 2), 5, 0, 0);
                    return false;
                };
                if (this.rangeHigh && +dt > +this.rangeHigh) {
                    if (!level) { return true; };
                    this.date = new Date(this.rangeHigh.substr(0, 4), this.rangeHigh.substr(4, 2) - 1, this.rangeHigh.substr(6, 2), 5, 0, 0);
                };
                return false;
            };
            datePicker.prototype.canDateBeSelected = function(tmpDate) {
                if (!tmpDate) return false;

                var d = pad(tmpDate.getDate()),
                    m = pad(tmpDate.getMonth() + 1),
                    y = tmpDate.getFullYear(),
                    dt = String(y) + String(m) + String(d),
                    dd = this.getDates(y, m),
                    wd = tmpDate.getDay() == 0 ? 7 : tmpDate.getDay();

                if ((this.rangeLow && +dt < +this.rangeLow) || (this.rangeHigh && +dt > +this.rangeHigh) || (dd[dt] == 0) || this.disabledDays[wd - 1]) {
                    return false;
                };

                return true;
            };
            datePicker.prototype.updateStatus = function(msg) {
                while (this.statusBar.firstChild) { this.statusBar.removeChild(this.statusBar.firstChild); };

                if (msg && this.statusFormat.search(/-S|S-/) != -1 && msg.search(/([0-9]{1,2})(st|nd|rd|th)/) != -1) {
                    msg = msg.replace(/([0-9]{1,2})(st|nd|rd|th)/, "$1<sup>$2</sup>").split(/<sup>|<\/sup>/);
                    var dc = document.createDocumentFragment();
                    for (var i = 0, nd; nd = msg[i]; i++) {
                        if (/^(st|nd|rd|th)$/.test(nd)) {
                            var sup = document.createElement("sup");
                            sup.appendChild(document.createTextNode(nd));
                            dc.appendChild(sup);
                        } else {
                            dc.appendChild(document.createTextNode(nd));
                        };
                    };
                    this.statusBar.appendChild(dc);
                } else {
                    this.statusBar.appendChild(document.createTextNode(msg ? msg : getTitleTranslation(9)));
                };
            };
            datePicker.prototype.setDateFromInput = function() {
                //alert("made it to set date from input");
                var origDateSet = this.dateSet,
                    m = false,
                    dt, elemID, elem, elemFmt, d, y, elemVal;

                this.dateSet = null;

                for (elemID in this.formElements) {
                    elem = document.getElementById(elemID);

                    if (!elem) {
                        return;
                    };

                    elemVal = String(elem.value);
                    elemFmt = this.formElements[elemID];
                    dt = false;

                    if (!(elemVal == "")) {
                        for (var i = 0, fmt; fmt = this.formatMasks[elemID][i]; i++) {
                            dt = parseDateString(elemVal, fmt);
                            if (dt) {
                                break;
                            };
                        };
                    };

                    if (dt) {
                        if (elemFmt.search(new RegExp('[' + dParts + ']')) != -1) {
                            //console.Log("located d part " + elemFmt + " : " + dt.getDate());
                            d = dt.getDate();
                        };
                        if (elemFmt.search(new RegExp('[' + mParts + ']')) != -1) {
                            //console.Log("located m part " + elemFmt + " : " + dt.getMonth());                                       
                            m = dt.getMonth();
                        };
                        if (elemFmt.search(new RegExp('[' + yParts + ']')) != -1) {
                            //console.Log("located y part " + elemFmt + " : " + dt.getFullYear());
                            y = dt.getFullYear()
                        };
                    };
                };

                dt = false;

                if (d && !(m === false) && y) {
                    if (+d > daysInMonth(+m, +y)) {
                        d = daysInMonth(+m, +y);
                        dt = false;
                    } else {
                        dt = new Date(+y, +m, +d);
                    };
                };

                if (!dt || isNaN(dt)) {
                    var newDate = new Date(y || new Date().getFullYear(), !(m === false) ? m : new Date().getMonth(), 1);
                    this.date = this.cursorDate ? new Date(+this.cursorDate.substr(0, 4), +this.cursorDate.substr(4, 2) - 1, +this.cursorDate.substr(6, 2)) : new Date(newDate.getFullYear(), newDate.getMonth(), Math.min(+d || new Date().getDate(), daysInMonth(newDate.getMonth(), newDate.getFullYear())));

                    this.date.setHours(5);
                    this.outOfRange();
                    //this.callback("dateset", this.createCbArgObj());  
                    this.updateTable();
                    return;
                };


                dt.setHours(5);
                this.date = new Date(dt);
                this.outOfRange();

                if (dt.getTime() == this.date.getTime() && this.canDateBeSelected(this.date)) {
                    this.dateSet = new Date(this.date);
                };

                //this.callback("dateset", this.createCbArgObj()); 
                if (this.fullCreate) this.updateTable();
                this.returnFormattedDate(true);
            };
            datePicker.prototype.setSelectIndex = function(elem, indx) {
                for (var opt = elem.options.length - 1; opt >= 0; opt--) {
                    if (elem.options[opt].value == indx) {
                        elem.selectedIndex = opt;
                        return;
                    };
                };
            };
            datePicker.prototype.returnFormattedDate = function(noFocus) {
                if (!this.dateSet) {
                    return;
                };

                var d = pad(this.dateSet.getDate()),
                    m = pad(this.dateSet.getMonth() + 1),
                    y = this.dateSet.getFullYear(),
                    el = false,
                    elemID, elem, elemFmt, fmtDate;

                noFocus = !!noFocus;

                for (elemID in this.formElements) {
                    elem = document.getElementById(elemID);

                    if (!elem) return;

                    if (!el) el = elem;

                    elemFmt = this.formElements[elemID];

                    fmtDate = printFormattedDate(this.dateSet, elemFmt, returnLocaleDate);
                    if (elem.tagName.toLowerCase() == "input") {
                        elem.value = fmtDate;
                    } else {
                        this.setSelectIndex(elem, fmtDate);
                    };
                };

                if (this.staticPos) {
                    this.noFocus = true;
                    this.updateTable();
                    this.noFocus = false;
                };

                if (this.fullCreate) {
                    if (el.type && el.type != "hidden" && !noFocus) { el.focus(); };
                };
            };
            datePicker.prototype.disableDatePicker = function() {
                if (this.disabled) return;

                if (this.staticPos) {
                    this.removeOnFocusEvents();
                    this.removeOldFocus();
                    this.noFocus = true;
                    this.div.className = this.div.className.replace(/dp-disabled/, "") + " dp-disabled";
                    this.table.onmouseover = this.table.onclick = this.table.onmouseout = this.table.onmousedown = null;
                    removeEvent(document, "mousedown", this.onmousedown);
                    removeEvent(document, "mouseup", this.clearTimer);
                } else {
                    if (this.visible) this.hide();
                    var but = document.getElementById("fd-but-" + this.id);
                    if (but) {
                        but.className = but.className.replace(/dp-disabled/, "") + " dp-disabled";
                        // Set a "disabled" ARIA state
                        setARIAProperty(but, "disabled", true);
                        but.onkeydown = but.onclick = function() { return false; };
                        but.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", "-1");
                        but.tabIndex = -1;
                    };
                };

                clearTimeout(this.timer);
                this.disabled = true;
            };
            datePicker.prototype.enableDatePicker = function() {
                if (!this.disabled) return;

                if (this.staticPos) {
                    this.removeOldFocus();
                    this.noFocus = true;
                    this.updateTable();
                    this.div.className = this.div.className.replace(/dp-disabled/, "");
                    this.disabled = false;
                    this.table.onmouseover = this.onmouseover;
                    this.table.onmouseout = this.onmouseout;
                    this.table.onclick = this.onclick;
                    this.table.onmousedown = this.onmousedown;
                } else {
                    var but = document.getElementById("fd-but-" + this.id);
                    if (but) {
                        but.className = but.className.replace(/dp-disabled/, "");
                        // Reset the "disabled" ARIA state
                        setARIAProperty(but, "disabled", false);
                        this.addButtonEvents(but);
                    };
                };

                this.disabled = false;
            };
            datePicker.prototype.disableTodayButton = function() {
                var today = new Date();
                this.butToday.className = this.butToday.className.replace("fd-disabled", "");
                if (this.outOfRange(today) || (this.date.getDate() == today.getDate() && this.date.getMonth() == today.getMonth() && this.date.getFullYear() == today.getFullYear())) {
                    this.butToday.className += " fd-disabled";
                };
            };
            datePicker.prototype.updateTableHeaders = function() {
                var colspanTotal = this.showWeeks ? 8 : 7,
                    colOffset = this.showWeeks ? 1 : 0,
                    d, but;

                for (var col = colOffset; col < colspanTotal; col++) {
                    d = (this.firstDayOfWeek + (col - colOffset)) % 7;
                    this.ths[col].title = getDayTranslation(d, false);

                    if (col > colOffset) {
                        but = this.ths[col].getElementsByTagName("span")[0];
                        while (but.firstChild) { but.removeChild(but.firstChild); };
                        but.appendChild(document.createTextNode(getDayTranslation(d, true)));
                        but.title = this.ths[col].title;
                        but.className = but.className.replace(/day-([0-6])/, "") + " day-" + d;
                        but = null;
                    } else {
                        while (this.ths[col].firstChild) { this.ths[col].removeChild(this.ths[col].firstChild); };
                        this.ths[col].appendChild(document.createTextNode(getDayTranslation(d, true)));
                    };

                    this.ths[col].className = this.ths[col].className.replace(/date-picker-highlight/g, "");
                    if (this.highlightDays[d]) {
                        this.ths[col].className += " date-picker-highlight";
                    };
                };

                if (this.created) { this.updateTable(); }
            };
            datePicker.prototype.callback = function(type, args) {
                if (!type || !(type in this.callbacks)) {
                    return false;
                };

                var ret = false;
                for (var func = 0; func < this.callbacks[type].length; func++) {
                    ret = this.callbacks[type][func](args || this.id);
                };
                return ret;
            };
            datePicker.prototype.showHideButtons = function(tmpDate) {
                if (!this.butPrevYear) { return; };

                var tdm = tmpDate.getMonth(),
                    tdy = tmpDate.getFullYear();

                if (this.outOfRange(new Date((tdy - 1), tdm, daysInMonth(+tdm, tdy - 1)))) {
                    if (this.butPrevYear.className.search(/fd-disabled/) == -1) {
                        this.butPrevYear.className += " fd-disabled";
                    };
                    if (this.yearInc == -1) this.stopTimer();
                } else {
                    this.butPrevYear.className = this.butPrevYear.className.replace(/fd-disabled/g, "");
                };

                if (this.outOfRange(new Date(tdy, (+tdm - 1), daysInMonth(+tdm - 1, tdy)))) {
                    if (this.butPrevMonth.className.search(/fd-disabled/) == -1) {
                        this.butPrevMonth.className += " fd-disabled";
                    };
                    if (this.monthInc == -1) this.stopTimer();
                } else {
                    this.butPrevMonth.className = this.butPrevMonth.className.replace(/fd-disabled/g, "");
                };

                if (this.outOfRange(new Date((tdy + 1), +tdm, 1))) {
                    if (this.butNextYear.className.search(/fd-disabled/) == -1) {
                        this.butNextYear.className += " fd-disabled";
                    };
                    if (this.yearInc == 1) this.stopTimer();
                } else {
                    this.butNextYear.className = this.butNextYear.className.replace(/fd-disabled/g, "");
                };

                if (this.outOfRange(new Date(tdy, +tdm + 1, 1))) {
                    if (this.butNextMonth.className.search(/fd-disabled/) == -1) {
                        this.butNextMonth.className += " fd-disabled";
                    };
                    if (this.monthInc == 1) this.stopTimer();
                } else {
                    this.butNextMonth.className = this.butNextMonth.className.replace(/fd-disabled/g, "");
                };
            };
            var localeDefaults = {
                fullMonths: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
                monthAbbrs: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
                fullDays: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
                dayAbbrs: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
                titles: ["Previous month", "Next month", "Previous year", "Next year", "Today", "Show Calendar", "wk", "Week [[%0%]] of [[%1%]]", "Week", "Select a date", "Click \u0026 Drag to move", "Display \u201C[[%0%]]\u201D first", "Go to Today\u2019s date", "Disabled date :"],
                firstDayOfWeek: 0,
                imported: false
            };
            var joinNodeLists = function() {
                if (!arguments.length) { return []; }
                var nodeList = [];
                for (var i = 0; i < arguments.length; i++) {
                    for (var j = 0, item; item = arguments[i][j]; j++) {
                        nodeList[nodeList.length] = item;
                    };
                };
                return nodeList;
            };
            var cleanUp = function() {
                var dp, fe;
                for (dp in datePickers) {
                    for (fe in datePickers[dp].formElements) {
                        if (!document.getElementById(fe)) {
                            datePickers[dp].destroy();
                            datePickers[dp] = null;
                            delete datePickers[dp];
                            break;
                        }
                    };
                };
            };
            var hideAll = function(exception) {
                var dp;
                for (dp in datePickers) {
                    if (!datePickers[dp].created || (exception && exception == datePickers[dp].id)) continue;
                    datePickers[dp].hide();
                };
            };
            var hideDatePicker = function(inpID) {
                if (inpID in datePickers) {
                    if (!datePickers[inpID].created || datePickers[inpID].staticPos) return;
                    datePickers[inpID].hide();
                };
            };
            var showDatePicker = function(inpID, autoFocus) {
                if (!(inpID in datePickers)) return false;

                datePickers[inpID].clickActivated = !!!autoFocus;
                datePickers[inpID].show(autoFocus);
                return true;
            };
            var destroy = function(e) {
                e = e || window.event;

                // Don't remove datepickers if it's a pagehide/pagecache event (webkit et al)
                if (e.persisted) {
                    return;
                };

                for (dp in datePickers) {
                    datePickers[dp].destroy();
                    datePickers[dp] = null;
                    delete datePickers[dp];
                };
                datePickers = null;

                removeEvent(window, 'unload', datePickerController.destroy);
            };
            var destroySingleDatePicker = function(id) {
                if (id && (id in datePickers)) {
                    datePickers[id].destroy();
                    datePickers[id] = null;
                    delete datePickers[id];
                };
            };
            var getTitleTranslation = function(num, replacements) {
                replacements = replacements || [];
                if (localeImport.titles.length > num) {
                    var txt = localeImport.titles[num];
                    if (replacements && replacements.length) {
                        for (var i = 0; i < replacements.length; i++) {
                            txt = txt.replace("[[%" + i + "%]]", replacements[i]);
                        };
                    };
                    return txt.replace(/[[%(\d)%]]/g, "");
                };
                return "";
            };
            var getDayTranslation = function(day, abbreviation) {
                var titles = localeImport[abbreviation ? "dayAbbrs" : "fullDays"];
                return titles.length && titles.length > day ? titles[day] : "";
            };
            var getMonthTranslation = function(month, abbreviation) {
                var titles = localeImport[abbreviation ? "monthAbbrs" : "fullMonths"];
                return titles.length && titles.length > month ? titles[month] : "";
            };
            var daysInMonth = function(nMonth, nYear) {
                nMonth = (nMonth + 12) % 12;
                return (((0 == (nYear % 4)) && ((0 != (nYear % 100)) || (0 == (nYear % 400)))) && nMonth == 1) ? 29 : [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][nMonth];
            };

            var getWeeksInYear = function(Y) {
                if (Y in weeksInYearCache) {
                    return weeksInYearCache[Y];
                };
                var X1, X2, NW;
                with (X1 = new Date(Y, 0, 4)) {
                    setDate(getDate() - (6 + getDay()) % 7);
                };
                with (X2 = new Date(Y, 11, 28)) {
                    setDate(getDate() + (7 - getDay()) % 7);
                };
                weeksInYearCache[Y] = Math.round((X2 - X1) / 604800000);
                return weeksInYearCache[Y];
            };

            var getWeekNumber = function(y, m, d) {
                var d = new Date(y, m, d, 0, 0, 0);
                var DoW = d.getDay();
                d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
                var ms = d.valueOf(); // GMT
                d.setMonth(0);
                d.setDate(4); // Thu in Week 1
                return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
            };

            var printFormattedDate = function(date, fmt, useImportedLocale) {
                if (!date || isNaN(date)) { return ""; };

                var parts = fmt.split("-"),
                      str = [],
                        d = date.getDate(),
                        D = date.getDay(),
                        m = date.getMonth(),
                        y = date.getFullYear(),
                    flags = {
                        "sp": " ",
                        "dt": ".",
                        "sl": "/",
                        "ds": "-",
                        "cc": ",",
                        "d": pad(d),
                        "D": useImportedLocale ? localeImport.dayAbbrs[D == 0 ? 6 : D - 1] : localeDefaults.dayAbbrs[D == 0 ? 6 : D - 1],
                        "l": useImportedLocale ? localeImport.fullDays[D == 0 ? 6 : D - 1] : localeDefaults.fullDays[D == 0 ? 6 : D - 1],
                        "j": d,
                        "N": D == 0 ? 7 : D,
                        "w": D,
                        "W": getWeekNumber(y, m, d),
                        "M": useImportedLocale ? localeImport.monthAbbrs[m] : localeDefaults.monthAbbrs[m],
                        "F": useImportedLocale ? localeImport.fullMonths[m] : localeDefaults.fullMonths[m],
                        "m": pad(m + 1),
                        "n": m + 1,
                        "t": daysInMonth(m, y),
                        "y": String(y).substr(2, 2),
                        "Y": y,
                        "S": ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
                    };

                for (var pt = 0, part; part = parts[pt]; pt++) {
                    str.push(!(part in flags) ? "" : flags[part]);
                };

                return str.join("");
            };
            var parseDateString = function(str, fmt) {
                var d = false,
                    m = false,
                    y = false,
                    now = new Date(),
                    parts = fmt.replace(/-sp(-sp)+/g, "-sp").split("-"),
                    divds = { "dt": ".", "sl": "/", "ds": "-", "cc": "," },
                    str = "" + str;

                loopLabel:
                for (var pt = 0, part; part = parts[pt]; pt++) {
                    if (str.length == 0) { return false; };

                    switch (part) {
                        // Dividers - be easy on them all i.e. accept them all when parsing...                                 
                        case "sp":
                        case "dt":
                        case "sl":
                        case "ds":
                        case "cc":
                            str = str.replace(/^(\s|\.|\/|,|-){1,}/, "");
                            break;
                        // DAY 
                        case "d": // Day of the month, 2 digits with leading zeros (01 - 31)
                        case "j": // Day of the month without leading zeros (1 - 31)  
                            // Accept both when parsing                                                          
                            if (str.search(/^(3[01]|[12][0-9]|0?[1-9])/) != -1) {
                                d = +str.match(/^(3[01]|[12][0-9]|0?[1-9])/)[0];
                                str = str.substr(str.match(/^(3[01]|[12][0-9]|0?[1-9])/)[0].length);
                                break;
                            } else {
                                return "";
                            };
                        case "D": // A textual representation of a day, three letters (Mon - Sun)
                        case "l": // A full textual representation of the day of the week (Monday - Sunday)
                            // Accept English & imported locales and both modifiers                                                  
                            l = localeDefaults.fullDays.concat(localeDefaults.dayAbbrs);
                            if (localeImport.imported) {
                                l = l.concat(localeImport.fullDays).concat(localeImport.dayAbbrs);
                            };

                            for (var i = 0; i < l.length; i++) {
                                if (new RegExp("^" + l[i], "i").test(str)) {
                                    str = str.substr(l[i].length);
                                    continue loopLabel;
                                };
                            };

                            break;
                        case "N": // ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0) 1 (for Monday) through 7 (for Sunday)
                        case "w": // Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
                            if (str.search(part == "N" ? /^([1-7])/ : /^([0-6])/) != -1) {
                                str = str.substr(1);

                            };
                            break;
                        case "S": // English ordinal suffix for the day of the month, 2 characters: st, nd, rd or th
                            if (str.search(/^(st|nd|rd|th)/i) != -1) {
                                str = str.substr(2);
                            };
                            break;
                        // WEEK 
                        case "W": // ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0): 1 - 53
                            if (str.search(/^([1-9]|[1234[0-9]|5[0-3])/) != -1) {
                                str = str.substr(str.match(/^([1-9]|[1234[0-9]|5[0-3])/)[0].length);
                            };
                            break;
                        // MONTH 
                        case "M": // A short textual representation of a month, three letters
                        case "F": // A full textual representation of a month, such as January or March
                            // Accept English & imported locales and both modifiers                                                    
                            l = localeDefaults.fullMonths.concat(localeDefaults.monthAbbrs);
                            if (localeImport.imported) {
                                l = l.concat(localeImport.fullMonths).concat(localeImport.monthAbbrs);
                            };
                            for (var i = 0; i < l.length; i++) {
                                if (str.search(new RegExp("^" + l[i], "i")) != -1) {
                                    str = str.substr(l[i].length);
                                    m = ((i + 12) % 12);
                                    continue loopLabel;
                                };
                            };
                            return "";
                        case "m": // Numeric representation of a month, with leading zeros
                        case "n": // Numeric representation of a month, without leading zeros
                            // Accept either when parsing
                            l = /^(1[012]|0?[1-9])/;
                            if (str.search(l) != -1) {
                                m = +str.match(l)[0] - 1;
                                str = str.substr(str.match(l)[0].length);
                                break;
                            } else {
                                return "";
                            };
                        case "t": // Number of days in the given month: 28 through 31
                            if (str.search(/2[89]|3[01]/) != -1) {
                                str = str.substr(2);
                                break;
                            };
                            break;
                        // YEAR 
                        case "Y": // A full numeric representation of a year, 4 digits
                            if (str.search(/^(\d{4})/) != -1) {
                                y = str.substr(0, 4);
                                str = str.substr(4);
                                break;
                            } else {
                                return "";
                            };
                        case "y": // A two digit representation of a year - be easy on four figure dates though                                                
                            if (str.search(/^(\d{4})/) != -1) {
                                y = str.substr(0, 4);
                                str = str.substr(4);
                                break;
                            } else if (str.search(/^(0[0-9]|[1-9][0-9])/) != -1) {
                                y = str.substr(0, 2);
                                y = +y < 50 ? '20' + "" + String(y) : '19' + "" + String(y);
                                str = str.substr(2);
                                break;
                            } else return "";

                        default:
                            return "";
                    };
                };

                if (!(str == "") || (d === false && m === false && y === false)) {
                    return false;
                };

                m = m === false ? 11 : m;
                y = y === false ? now.getFullYear() : y;
                d = d === false ? daysInMonth(+m, +y) : d;

                if (d > daysInMonth(+m, +y)) {
                    return false;
                };

                var tmpDate = new Date(y, m, d);

                return !tmpDate || isNaN(tmpDate) ? false : tmpDate;
            };
            var findLabelForElement = function(element) {
                var label;
                if (element.parentNode && element.parentNode.tagName.toLowerCase() == "label") lebel = element.parentNode;
                else {
                    var labelList = document.getElementsByTagName('label');
                    // loop through label array attempting to match each 'for' attribute to the id of the current element
                    for (var lbl = 0; lbl < labelList.length; lbl++) {
                        // Internet Explorer requires the htmlFor test
                        if ((labelList[lbl]['htmlFor'] && labelList[lbl]['htmlFor'] == element.id) || (labelList[lbl].getAttribute('for') == element.id)) {
                            label = labelList[lbl];
                            break;
                        };
                    };
                };

                if (label && !label.id) { label.id = element.id + "_label"; };
                return label;
            };
            var updateLanguage = function() {
                if (typeof (window.fdLocale) == "object") {
                    localeImport = {
                        titles: fdLocale.titles,
                        fullMonths: fdLocale.fullMonths,
                        monthAbbrs: fdLocale.monthAbbrs,
                        fullDays: fdLocale.fullDays,
                        dayAbbrs: fdLocale.dayAbbrs,
                        firstDayOfWeek: ("firstDayOfWeek" in fdLocale) ? fdLocale.firstDayOfWeek : 0,
                        imported: true
                    };
                } else if (!localeImport) {
                    localeImport = localeDefaults;
                };
            };
            var loadLanguage = function() {
                updateLanguage();
                for (dp in datePickers) {
                    if (!datePickers[dp].created) continue;
                    datePickers[dp].updateTable();
                };
            };
            var checkElem = function(elem) {
                return !(!elem || !elem.tagName || !((elem.tagName.toLowerCase() == "input" && (elem.type == "text" || elem.type == "hidden")) || elem.tagName.toLowerCase() == "select"));
            };
            var addDatePicker = function(options) {

                updateLanguage();

                if (!options.formElements) {
                    if (debug) throw "No form elements stipulated within initialisation parameters";
                    return;
                };

                options.id = (options.id && (options.id in options.formElements)) ? options.id : "";
                options.formatMasks = {};

                var testParts = [dParts, mParts, yParts],
                    partsFound = [0, 0, 0],
                    tmpPartsFound,
                    matchedPart,
                    newParts,
                    indParts,
                    fmt,
                    fmtBag,
                    fmtParts,
                    newFormats,
                    myMin,
                    myMax;

                for (var elemID in options.formElements) {
                    elem = document.getElementById(elemID);

                    if (!checkElem(elem)) {
                        if (debug) throw "The element with and id of '" + elemID + "' is of the wrong type or does not exist within the DOM";
                        return false;
                    };

                    if (!options.id) options.id = elemID;

                    fmt = options.formElements[elemID];

                    if (!(fmt.match(validFmtRegExp))) {
                        if (debug) throw "The element with and id of '" + elemID + "' has the following incorrect date format assigned to it: " + fmt;
                        return false;
                    };

                    fmtBag = [fmt];

                    if (options.dateFormats && (elemID in options.dateFormats) && options.dateFormats[elemID].length) {
                        newFormats = [];

                        for (var f = 0, bDft; bDft = options.dateFormats[elemID][f]; f++) {
                            if (!(bDft.match(validFmtRegExp))) {
                                if (debug) throw "The element with and id of '" + elemID + "' has the following incorrect date format assigned to it within the dateFormats parameter: " + bDft;
                                return false;
                            };

                            newFormats.push(bDft);
                        };

                        fmtBag = fmtBag.concat(newFormats);
                    };

                    tmpPartsFound = [0, 0, 0];

                    for (var i = 0, testPart; testPart = testParts[i]; i++) {
                        if (fmt.search(new RegExp('(' + testPart + ')')) != -1) {
                            partsFound[i] = tmpPartsFound[i] = 1;

                            // Create the date format strings to check against later for text input elements
                            if (elem.tagName.toLowerCase() == "input") {
                                matchedPart = fmt.match(new RegExp('(' + testPart + ')'))[0];
                                newParts = String(matchedPart + "|" + testPart.replace(new RegExp("(" + matchedPart + ")"), "")).replace("||", "|");
                                indParts = newParts.split("|");
                                newFormats = [];

                                for (var z = 0, bFmt; bFmt = fmtBag[z]; z++) {
                                    for (var x = 0, indPart; indPart = indParts[x]; x++) {
                                        if (indPart == matchedPart) continue;
                                        newFormats.push(bFmt.replace(new RegExp('(' + testPart + ')(-|$)', 'g'), indPart + "-").replace(/-$/, ""));
                                    };
                                };

                                fmtBag = fmtBag.concat(newFormats);
                            };
                        };
                    };

                    options.formatMasks[elemID] = fmtBag.concat();

                    if (elem.tagName.toLowerCase() == "select") {
                        myMin = myMax = 0;

                        // If we have a selectList, then try to parse the higher and lower limits 
                        var selOptions = elem.options;

                        // Check the yyyymmdd 
                        if (tmpPartsFound[0] && tmpPartsFound[1] && tmpPartsFound[2]) {
                            var yyyymmdd,
                                            cursorDate = false;

                            // Remove the disabledDates parameter
                            if ("disabledDates" in options) {
                                delete (options.disabledDates);
                            };

                            // Dynamically calculate the available "enabled" dates
                            options.enabledDates = {};

                            for (i = 0; i < selOptions.length; i++) {
                                for (var f = 0, fmt; fmt = fmtBag[f]; f++) {
                                    dt = parseDateString(selOptions[i].value, fmt /*options.formElements[elemID]*/);
                                    if (dt) {
                                        yyyymmdd = dt.getFullYear() + "" + pad(dt.getMonth() + 1) + "" + pad(dt.getDate());

                                        if (!cursorDate) cursorDate = yyyymmdd;

                                        options.enabledDates[yyyymmdd] = 1;

                                        if (!myMin || Number(yyyymmdd) < myMin) {
                                            myMin = yyyymmdd;
                                        };

                                        if (!myMax || Number(yyyymmdd) > myMax) {
                                            myMax = yyyymmdd;
                                        };

                                        break;
                                    };
                                };
                            };

                            // Automatically set cursor to first available date (if no bespoke cursorDate was set);                                        
                            if (!options.cursorDate && cursorDate) options.cursorDate = cursorDate;

                        } else if (tmpPartsFound[1] && tmpPartsFound[2]) {
                            var yyyymm;

                            for (i = 0; i < selOptions.length; i++) {
                                for (var f = 0, fmt; fmt = fmtBag[f]; f++) {
                                    dt = parseDateString(selOptions[i].value, fmt /*options.formElements[elemID]*/);
                                    if (dt) {
                                        yyyymm = dt.getFullYear() + "" + pad(dt.getMonth() + 1);

                                        if (!myMin || Number(yyyymm) < myMin) {
                                            myMin = yyyymm;
                                        };

                                        if (!myMax || Number(yyyymm) > myMax) {
                                            myMax = yyyymm;
                                        };

                                        break;
                                    };
                                };
                            };

                            // Round the min & max values to be used as rangeLow & rangeHigh
                            myMin += "" + "01";
                            myMax += "" + daysInMonth(+myMax.substr(4, 2) - 1, +myMax.substr(0, 4));

                        } else if (tmpPartsFound[2]) {
                            var yyyy;

                            for (i = 0; i < selOptions.length; i++) {
                                for (var f = 0, fmt; fmt = fmtBag[f]; f++) {
                                    dt = parseDateString(selOptions[i].value, fmt /*options.formElements[elemID]*/);
                                    if (dt) {
                                        yyyy = dt.getFullYear();
                                        if (!myMin || Number(yyyy) < myMin) {
                                            myMin = yyyy;
                                        };

                                        if (!myMax || Number(yyyy) > myMax) {
                                            myMax = yyyy;
                                        };

                                        break;
                                    };
                                };
                            };

                            // Round the min & max values to be used as rangeLow & rangeHigh
                            myMin += "0101";
                            myMax += "1231";
                        };

                        if (myMin && (!options.rangeLow || (+options.rangeLow < +myMin))) options.rangeLow = myMin;
                        if (myMax && (!options.rangeHigh || (+options.rangeHigh > +myMin))) options.rangeHigh = myMax;
                    };
                };

                if (!(partsFound[0] && partsFound[1] && partsFound[2])) {
                    if (debug) throw "Could not find all of the required date parts for element: " + elem.id;
                    return false;
                };

                var opts = {
                    formElements: options.formElements,
                    // Form element id
                    id: options.id,
                    // Format masks 
                    formatMasks: options.formatMasks,
                    // Non popup datepicker required
                    staticPos: !!(options.staticPos),
                    // Position static datepicker or popup datepicker's button
                    positioned: options.positioned && document.getElementById(options.positioned) ? options.positioned : "",
                    // Ranges stipulated in YYYYMMDD format       
                    rangeLow: options.rangeLow && String(options.rangeLow).search(rangeRegExp) != -1 ? options.rangeLow : "",
                    rangeHigh: options.rangeHigh && String(options.rangeHigh).search(rangeRegExp) != -1 ? options.rangeHigh : "",
                    // Status bar format
                    statusFormat: options.statusFormat && String(options.statusFormat).search(validFmtRegExp) != -1 ? options.statusFormat : "",
                    // No fade in/out effect
                    noFadeEffect: !!(options.staticPos) ? true : !!(options.noFadeEffect),
                    // No drag functionality
                    dragDisabled: nodrag || !!(options.staticPos) ? true : !!(options.dragDisabled),
                    // Bespoke tabindex for this datePicker (or it's activation button)
                    bespokeTabIndex: options.bespokeTabindex && typeof options.bespokeTabindex == 'number' ? parseInt(options.bespokeTabindex, 10) : 0,
                    // Bespoke titles
                    bespokeTitles: options.bespokeTitles || {},
                    // Final opacity 
                    finalOpacity: options.finalOpacity && typeof options.finalOpacity == 'number' && (options.finalOpacity > 20 && options.finalOpacity <= 100) ? parseInt(+options.finalOpacity, 10) : (!!(options.staticPos) ? 100 : finalOpacity),
                    // Do we hide the form elements on datepicker creation
                    hideInput: !!(options.hideInput),
                    // Do we hide the "today" button
                    noToday: !!(options.noTodayButton),
                    // Do we show week numbers
                    showWeeks: !!(options.showWeeks),
                    // Do we fill the entire grid with dates                                                  
                    fillGrid: !!(options.fillGrid),
                    // Do we constrain selection of dates outside the current month
                    constrainSelection: "constrainSelection" in options ? !!(options.constrainSelection) : true,
                    // The date to set the initial cursor to
                    cursorDate: options.cursorDate && String(options.cursorDate).search(rangeRegExp) != -1 ? options.cursorDate : "",
                    // Locate label to set the ARIA labelled-by property
                    labelledBy: findLabelForElement(elem),
                    // Have we been passed a describedBy to set the ARIA decribed-by property...
                    describedBy: (options.describedBy && document.getElementById(options.describedBy)) ? options.describedBy : describedBy && document.getElementById(describedBy) ? describedBy : "",
                    // Callback functions
                    callbacks: options.callbackFunctions ? options.callbackFunctions : {},
                    // Days of the week to highlight (normally the weekend)
                    highlightDays: options.highlightDays && options.highlightDays.length && options.highlightDays.length == 7 ? options.highlightDays : [0, 0, 0, 0, 0, 1, 1],
                    // Days of the week to disable
                    disabledDays: options.disabledDays && options.disabledDays.length && options.disabledDays.length == 7 ? options.disabledDays : [0, 0, 0, 0, 0, 0, 0]
                };

                if (options.disabledDates) {
                    if (options.enabledDates) delete (options.enabledDates);
                    opts.disabledDates = {};
                    var startD;
                    for (startD in options.disabledDates) {
                        if ((String(startD).search(wcDateRegExp) != -1 && options.disabledDates[startD] == 1) || (String(startD).search(rangeRegExp) != -1 && String(options.disabledDates[startD]).search(rangeRegExp) != -1)) {
                            opts.disabledDates[startD] = options.disabledDates[startD];
                        };
                    };
                } else if (options.enabledDates) {
                    var startD;
                    opts.enabledDates = {};
                    for (startD in options.enabledDates) {
                        if ((String(startD).search(wcDateRegExp) != -1 && options.enabledDates[startD] == 1) || (String(startD).search(rangeRegExp) != -1 && String(options.enabledDates[startD]).search(rangeRegExp) != -1)) {
                            opts.enabledDates[startD] = options.enabledDates[startD];
                        };
                    };
                };

                datePickers[options.id] = new datePicker(opts);
                datePickers[options.id].callback("create", datePickers[options.id].createCbArgObj());
            };

            // Used by the button to dictate whether to open or close the datePicker
            var isVisible = function(id) {
                return (!id || !(id in datePickers)) ? false : datePickers[id].visible;
            };

            addEvent(window, 'unload', destroy);

            return {
                // General event functions...
                addEvent: function(obj, type, fn) { return addEvent(obj, type, fn); },
                removeEvent: function(obj, type, fn) { return removeEvent(obj, type, fn); },
                stopEvent: function(e) { return stopEvent(e); },
                // Show a single popup datepicker
                show: function(inpID) { return showDatePicker(inpID, false); },
                // Hide a popup datepicker
                hide: function(inpID) { return hideDatePicker(inpID); },
                // Create a new datepicker
                createDatePicker: function(options) { addDatePicker(options); },
                // Destroy a datepicker (remove events and DOM nodes)               
                destroyDatePicker: function(inpID) { destroySingleDatePicker(inpID); },
                // Check datePicker form elements exist, if not, destroy the datepicker
                cleanUp: function() { cleanUp(); },
                // Pretty print a date object according to the format passed in               
                printFormattedDate: function(dt, fmt, useImportedLocale) { return printFormattedDate(dt, fmt, useImportedLocale); },
                // Update the internal date using the form element value
                setDateFromInput: function(inpID) { if (!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setDateFromInput(); },
                // Set low and high date ranges
                setRangeLow: function(inpID, yyyymmdd) { if (!inpID || !(inpID in datePickers)) { return false; }; datePickers[inpID].setRangeLow(yyyymmdd); },
                setRangeHigh: function(inpID, yyyymmdd) { if (!inpID || !(inpID in datePickers)) { return false; }; datePickers[inpID].setRangeHigh(yyyymmdd); },
                // Set bespoke titles for a datepicker instance
                setBespokeTitles: function(inpID, titles) { if (!inpID || !(inpID in datePickers)) { return false; }; datePickers[inpID].setBespokeTitles(titles); },
                // Add bespoke titles for a datepicker instance
                addBespokeTitles: function(inpID, titles) { if (!inpID || !(inpID in datePickers)) { return false; }; datePickers[inpID].addBespokeTitles(titles); },
                // Attempt to parse a valid date from a date string using the passed in format
                parseDateString: function(str, format) { return parseDateString(str, format); },
                // Change global configuration parameters
                setGlobalVars: function(json) { affectJSON(json); },
                setSelectedDate: function(inpID, yyyymmdd) { if (!inpID || !(inpID in datePickers)) { return false; }; datePickers[inpID].setSelectedDate(yyyymmdd); },
                // Is the date valid for selection i.e. not outside ranges etc
                dateValidForSelection: function(inpID, dt) { if (!inpID || !(inpID in datePickers)) return false; return datePickers[inpID].canDateBeSelected(dt); },
                // Add disabled and enabled dates
                addDisabledDates: function(inpID, dts) { if (!inpID || !(inpID in datePickers)) return false; datePickers[inpID].addDisabledDates(dts); },
                setDisabledDates: function(inpID, dts) { if (!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setDisabledDates(dts); },
                addEnabledDates: function(inpID, dts) { if (!inpID || !(inpID in datePickers)) return false; datePickers[inpID].addEnabledDates(dts); },
                setEnabledDates: function(inpID, dts) { if (!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setEnabledDates(dts); },
                // Disable and enable the datepicker
                disable: function(inpID) { if (!inpID || !(inpID in datePickers)) return false; datePickers[inpID].disableDatePicker(); },
                enable: function(inpID) { if (!inpID || !(inpID in datePickers)) return false; datePickers[inpID].enableDatePicker(); },
                // Set the cursor date
                setCursorDate: function(inpID, yyyymmdd) { if (!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setCursorDate(yyyymmdd); },
                // Whats the currently selected date
                getSelectedDate: function(inpID) { return (!inpID || !(inpID in datePickers)) ? false : datePickers[inpID].returnSelectedDate(); },
                // Attempt to update the language (causes a redraw of all datepickers on the page)
                loadLanguage: function() { loadLanguage(); },
                // Set the debug level i.e. throw errors or fail silently
                setDebug: function(dbg) { debug = !!(dbg); }
            };
        })();
