
var datePickerController;

(function() {

datePicker.isSupported = typeof document.createElement != "undefined" &&
        typeof document.documentElement != "undefined" &&
        typeof document.documentElement.offsetWidth == "number";

datePicker.months = [
        "January",   "February", "March",    "April",
        "May",       "June",     "July",     "August",
        "September", "October",  "November", "December"];
datePicker.fullDay = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
datePicker.daysPerMonth = [31,28,31,30,31,30,31,31,30,31,30,31];

datePicker.dateFormats = [
        new RegExp(/([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})/),
        new RegExp(/([0-9]{1,2})\-([0-9]{1,2})\-([0-9]{4})/)
        ];

datePicker.titles = ["Previous month", "Next month"];
datePicker.getDaysPerMonth = function (nMonth, nYear) {
        nMonth = (nMonth + 12) % 12;
        var res = datePicker.daysPerMonth[nMonth];
        if (nMonth == 1) {
                res += nYear % 4 == 0 && !(nYear % 400 == 0) ? 1 : 0;
        }
        return res;
};

function datePicker ( elem, dateFormat, dividor, firstDayOfWeek, highlightDays) {

        if(!elem) return;

        this._elem              = elem;
        this._created           = false;
        this._visible           = false;
        this._date              = new Date();
        this._europeanFormat    = dateFormat;
        this._dividor           = dividor ? '-' : '/';
        this._firstDayOfWeek    = firstDayOfWeek ? firstDayOfWeek : 0;
        this._matrix            = [[],[],[],[],[],[],[]];
        this._highlightDays     = highlightDays;
        this._div;
        this._table;

        var o = this;

        /* Opera woes: Opera overrides all CTRL + arrow key combinations with their own "Spatial Navigation" feature.
           This makes the next/previous month/year key combinations break and I can find no way around it apart
           from redefining the keys used (I like CTRL + arrow though) */
           
        o.events = {
                onkeydown: function (e) {
                        if (e == null) e = document.parentWindow.event;
                        var kc = e.keyCode != null ? e.keyCode : e.charCode;

                        if ( kc == 13 ) {
                                // Return
                                o.returnFormattedDate();
                                o.hide();

                                e.cancelBubble = true;
                                if (e.stopPropagation) e.stopPropagation();
                                return false;
                        }
                        
                        if ( kc < 32 || kc > 40 ) return true;

                        var d = new Date( o._date ).valueOf();
                        
                        if ( kc == 37 ) {
                                // ctrl + left = previous month
                                if( e.ctrlKey ) {
                                        var d = new Date( o._date );
                                        d.setDate( Math.min(d.getDate(), datePicker.getDaysPerMonth(d.getMonth() - 1,d.getFullYear())) ); // no need to catch dec -> jan for the year
                                        d.setMonth( d.getMonth() - 1 );
                                } else {
                                        d -= 24 * 60 * 60 * 1000;
                                }
                        } else if ( kc == 39 ) {
                                // ctrl + right = next month
                                 if( e.ctrlKey ) {
                                        var d = new Date( o._date );
                                        d.setDate( Math.min(d.getDate(), datePicker.getDaysPerMonth(d.getMonth() + 1,d.getFullYear())) ); // no need to catch dec -> jan for the year
                                        d.setMonth( d.getMonth() + 1 );
                                } else {
                                        d += 24 * 60 * 60 * 1000;
                                }
                        } else if ( kc == 38 ) {
                                // ctrl + up = next year
                                if( e.ctrlKey ) {
                                        var d = new Date( o._date );
                                        d.setYear( d.getFullYear() + 1 );
                                // up = previous week
                                } else {
                                        d -= 7 * 24 * 60 * 60 * 1000;
                                }
                        } else if ( kc == 40 ) {
                                // ctrl + down = previous year
                                if( e.ctrlKey ) {
                                        var d = new Date( o._date );
                                        d.setYear( d.getFullYear() - 1 );
                                // down = next week
                                } else {
                                        d += 7 * 24 * 60 * 60 * 1000;
                                }
                        }

                        o._date =  new Date( d );
                        o.updateTable();

                        e.cancelBubble = true;
                        if (e.stopPropagation) e.stopPropagation();

                        return false;
                },
                onmousedown: function(e) {
                        if ( e == null ) e = document.parentWindow.event;
                        var el = e.target != null ? e.target : e.srcElement;
                        if(el.tagName.toUpperCase() == "BUTTON" || (el.tagName.toUpperCase() == "TD" && el.className != "")) return false;

                        datePickerController.hideAll();
                },
                onmouseover: function(e) { this.className += " date-picker-hover"; },
                onmouseout: function(e)  { this.className = this.className.replace(/date-picker-hover/g, ''); },
                onclick: function (e) {
                        if ( e == null ) e = document.parentWindow.event;
                        var el = e.target != null ? e.target : e.srcElement;
                        while ( el.nodeType != 1 ) el = el.parentNode;

                        var d = new Date( o._date );
                        var n = Number( el.firstChild.data );

                        if(isNaN(n)) {
                                o.hide();
                                return;
                        }

                        d.setDate( n );
                        o._date = d;

                        o.returnFormattedDate();
                        o.hide();
                }
        }

        o.resize = function() {
                if(!o._created || !o._elem) return;

                var pos = findPosition(o._elem);
                o._div.style.left = pos[0] + "px";
                o._div.style.top  = pos[1] + (o._elem.offsetHeight + 1) + "px";

                o._div.style.width = o._table.style.width = o._elem.offsetWidth + "px";
        }
        
        o.create = function() {

                if(o._created) return;

                /* Internet explorer requires either an iframe or a proprietary popup object
                   to sit below the date-pickers div in order for other form controls like
                   checkboxs, selectboxs etc to not 'show through'
                */

                if(document.all && !window.opera && window.createPopup) {
                        o._iePopUp = document.createElement('iframe');
                        o._iePopUp.src = "";
                        o._iePopUp.setAttribute('className','iehack');
                        o._iePopUp.scrolling="no"
                        o._iePopUp.frameBorder="0"
                        document.body.appendChild(o._iePopUp);
                }
                
                o._div = document.createElement('div');
                o._div.style.zIndex = 9999;
                var tableBody = document.createElement('tbody');
                var tableHead = document.createElement('thead');
                var nbsp = String.fromCharCode( 160 );
                
                o._table = document.createElement('table');
                o._div.className= "datePicker"

                var tr = document.createElement('tr');
                var th = document.createElement('th');
                th.setAttribute('text-align','left');

                var tmpelem = document.createElement('button');
                tmpelem.setAttribute("type", "button");
                tmpelem.appendChild(document.createTextNode(nbsp));
                tmpelem.className = "prev-button";
                tmpelem.title = datePicker.titles[0];
                
                tmpelem.onclick = function(e) {
                        var d = new Date( o._date );
                        d.setDate( Math.min(d.getDate(), datePicker.getDaysPerMonth(d.getMonth() - 1,d.getFullYear())) ); // no need to catch dec -> jan for the year
                        d.setMonth( d.getMonth() - 1 );
                        o._date = d;
                        o.updateTable();
                        this.blur();
                }
                th.appendChild( tmpelem );
                tr.appendChild( th );

                o._titleBar = document.createElement('th');
                o._titleBar.setAttribute('colSpan','5');

                tr.appendChild( o._titleBar );

                th = document.createElement('th');
                th.setAttribute('text-align','right');
                var tmpelem = document.createElement('button');
                tmpelem.setAttribute("type", "button");
                tmpelem.appendChild(document.createTextNode(nbsp));
                tmpelem.className = "next-button";
                tmpelem.title = datePicker.titles[1];
                
                tmpelem.onclick = function(e) {
                        var d = new Date( o._date );
                        d.setDate( Math.min(d.getDate(), datePicker.getDaysPerMonth(d.getMonth() + 1,d.getFullYear())) ); // no need to catch dec -> jan for the year
                        d.setMonth( d.getMonth() + 1 );
                        o._date = d;
                        o.updateTable();
                        this.blur();
                }
                th.appendChild( tmpelem );
                tr.appendChild( th );
                tableHead.appendChild(tr);

                var row, col;

                weekDays = new Array(7);
                for ( i = 0; i < 7; i++) {
                        weekDays[i] = datePicker.fullDay[ (i + ( 7 - o._firstDayOfWeek)) % 7].charAt(0);
                }

                for(var rows = 0; rows < 7; rows++) {
                        row = document.createElement('tr')
                        for(var cols = 0; cols < 7; cols++) {
                                col = (rows == 0) ? document.createElement('th') : document.createElement('td');
                                if(rows != 0) {
                                        col.appendChild(document.createTextNode(nbsp));
                                } else {
                                        col.appendChild(document.createTextNode(weekDays[cols]));
                                        col.title = datePicker.fullDay[cols];
                                        col.className = "date-picker-day-header";
                                }

                                row.appendChild(col);
                        }
                        if(rows != 0) tableBody.appendChild(row);
                        else          tableHead.appendChild(row);
                }
                o._table.appendChild( tableHead );
                o._table.appendChild( tableBody );
                o._div.appendChild( o._table );
                o._created = true;

                document.getElementsByTagName('body')[0].appendChild( o._div );
        }

        o.setDateFromInput = function() {
                // ([0-9]{1,2})[-,/]([0-9]{1,2})[-,/](([0-9]{2})|([0-9]{4}))
                
                var date = o._elem.value;
                
                if(date.match(/[0-9]{4}/)) {
                        if(date > 1900 && date < 2030) {
                                date = '01' + o._dividor + '06' + o._dividor + date;
                        }
                }
                        
                var dates = date.split(o._dividor);
                
                if(dates.length != 3) {
                        o._date = new Date();
                        return;
                }
                

                var dt;
                if(o._europeanFormat) dt = dates[0] + "/" + dates[1] + "/" + dates[2]; // mm/dd/yyyy
                else                  dt = dates[0] + "/" + dates[1] + "/" + dates[2]; // mm/dd/yyyy
                
                if(new Date( dt ) == 'Invalid Date'){
                        o._date = new Date();
                        return;
                }

                o._date.setMonth(o._europeanFormat ? dates[1]-1 : dates[0]-1);
                o._date.setYear(dates[2]);
                o._date.setDate(o._europeanFormat ? dates[0] : dates[1]);
        }

        o.returnFormattedDate = function() {
                var d           = (o._date.getDate() < 10) ? "0" + o._date.getDate() : o._date.getDate();
                var m           = ((o._date.getMonth() + 1) < 10) ? "0" + (o._date.getMonth() + 1) : o._date.getMonth() + 1;
                var yyyy        = o._date.getFullYear();

                if(o._europeanFormat) o._elem.value = m + o._dividor + d + o._dividor + yyyy;
                else                  o._elem.value = m + o._dividor + d + o._dividor + yyyy;

                o._elem.focus();
                // Setting the input value with javascript does not invoke the onchange event
                // handler so we do it ourselves (in case other scripts have a handle on it).
                if(o._elem.onchange) o._elem.onchange();
        }

        // Credit where credit's due:
        
        // Most of the logic for this method from the webfx date-picker
        // http://webfx.eae.net/
        
        o.updateTable = function() {

                var i;
                var str = "";
                var rows = 6;
                var cols = 7;
                var currentWeek = 0;
                var nbsp = String.fromCharCode( 160 );

                var cells = new Array( rows );
                o._matrix = new Array( rows );
                for ( i = 0; i < rows; i++ ) {
                        cells[i] = new Array( cols );
                        o._matrix[i] = new Array( cols );
                }

                // Set the tmpDate to this month

                var tmpDate = new Date( o._date.getFullYear(), o._date.getMonth(), 1 );

                var today = new Date();

                // titleBar

                var titleText = datePicker.months[o._date.getMonth()] + nbsp + o._date.getFullYear();
                while(o._titleBar.firstChild) o._titleBar.removeChild(o._titleBar.firstChild);
                o._titleBar.appendChild(document.createTextNode(titleText));

                //
                for ( i = 1; i < 32; i++ ) {
                        tmpDate.setDate( i );
                        // convert to ISO, Monday is 0 and 6 is Sunday
                        var weekDay = ( tmpDate.getDay() + 6 ) % 7;

                        var colIndex = ( weekDay + (o._firstDayOfWeek) + 7 ) % 7;
                        if ( tmpDate.getMonth() == o._date.getMonth() ) {

                                var isToday = tmpDate.getDate() == today.getDate() &&
                                              tmpDate.getMonth() == today.getMonth() &&
                                              tmpDate.getFullYear() == today.getFullYear();

                                cells[currentWeek][colIndex] = { text: "", className: "" };

                                if ( o._date.getDate() == tmpDate.getDate())
                                        cells[currentWeek][colIndex].className += " date-picker-selected";
                                if ( isToday )
                                        cells[currentWeek][colIndex].className += " date-picker-today";
                                for( d = 0 ;d < o._highlightDays.length; d++ ) {
                                        if(colIndex == o._highlightDays[d]) {
                                                cells[currentWeek][colIndex].className += " date-picker-highlight";
                                        }
                                }
                                cells[currentWeek][colIndex].text = o._matrix[currentWeek][colIndex] = tmpDate.getDate();
                                if ( colIndex == 6 ) currentWeek++;
                        }
                }

                var trs = o._table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');

                var tmpCell;

                for ( var y = 0; y < rows; y++ ) {
                        var tds = trs[y].getElementsByTagName('td');
                        for (var x = 0; x < cols; x++) {
                                tmpCell = tds[x];

                                while(tmpCell.firstChild) tmpCell.removeChild(tmpCell.firstChild);
                                if ( typeof cells[y][x] != "undefined" ) {
                                        tmpCell.className = cells[y][x].className;
                                        tmpCell.appendChild(document.createTextNode(cells[y][x].text));
                                        tmpCell.onmouseover = o.events.onmouseover;
                                        tmpCell.onmouseout = o.events.onmouseout;
                                        tmpCell.onclick = o.events.onclick;
                                } else {
                                        tmpCell.className = "";
                                        tmpCell.onmouseover = tmpCell.onmouseout = null;
                                        tmpCell.onclick = function() { o.hide(); }
                                        tmpCell.appendChild(document.createTextNode(nbsp));
                                }
                        }
                }

        }
        
        o.init = function() {
                o.resize();
                o.setDateFromInput();
                o.ieHack(true);
                // Comment the next line should you NOT wish to use the prototype libraries
                new Effect2.Fade(o._div, { from:0.0, to:0, duration:0.0 });
        }
        o.ieHack = function(cleanup) {
                // IE hack
                if(o._iePopUp) {
                        o._iePopUp.style.display = "block";
                        o._iePopUp.style.top = (o._div.offsetTop + 2) + "px";
                        o._iePopUp.style.left = o._div.offsetLeft + "px";
                        o._iePopUp.style.width = o._div.clientWidth + "px";
                        o._iePopUp.style.height = (o._div.clientHeight - 2) + "px";
                        if(cleanup) o._iePopUp.style.display = "none";
                }
        }
        
        o.show = function() {
                if(o._elem.disabled || o._visible) return;
                
                o.setDateFromInput();
                o.updateTable();
                o.ieHack(false);
                addEvent(document, "mousedown", o.events.onmousedown, true);
                addEvent(document, "keydown", o.events.onkeydown, true);

                if(!o._visible) {
                        // Comment the next 2 lines should you NOT wish to use the prototype libraries
                        if(!window.opera) { new Effect2.Appear(o._div, { from:0, to:0.9, duration:0.8 }); }
                        else if(window.opera) o._div.style.display = "block";
                        // Uncomment the next line should you NOT wish to use the prototype libraries
                        // if(o._visible) { o._div.style.display = "block"; }
                        o._visible = true;
                }
        }

        o.hide = function()   {
                // Comment the next 2 lines should you NOT wish to use the prototype libraries
                if(o._visible && !window.opera) new Effect2.Fade(o._div, { from:0.9, to:0, duration:0.8 });
                else if(o._visible && window.opera) o._div.style.display = "none";
                // Uncomment the next line should you NOT wish to use the prototype libraries
                // if(o._visible) { o._div.style.display = "none"; }
                removeEvent(document, "mousedown", o.events.onmousedown, true);
                removeEvent(document, "keydown", o.events.onkeydown, false);
                if(o._iePopUp) {
                        o._iePopUp.style.display = "none";
                }
                o._visible = false;
        }

        o.create();
        o.init();
        addEvent(window, 'resize', o.resize, true);
}

datePickerController = {
        datePickers: [],

        hideAll: function(exemption) {
                for(var i=0, dp; dp = datePickerController.datePickers[i]; i++) {
                        if(i != exemption) dp.hide();
                }
        },
        resizeAll: function() {
                for(var i=0, dp; dp = datePickerController.datePickers[i]; i++) {
                        dp.resize();
                }
        },
        create: function() {
                var inputs = document.getElementsByTagName('input');
                var regExp1 = /date-picker/g;
                var regExp2 = /start-day-([0-6]){1}/g;
                var regExp3 = /highlight-days-([0-6]){1,7}/g;
                
                var europeanFormat, dividor, firstDayOfWeek, highlightDays;
                
                for(var i=0, inp; inp = inputs[i]; i++) {
                        if(inp.className && inp.className.search(regExp1) != -1 && inp.type == "text") {
                                var len =datePickerController.datePickers.length;

                                // American or European date format?
                                
                                // American: mm/dd/yyyy
                                // European: mm/dd/yyyy

                                if(inp.className.search(/date-american/) != -1) {
                                        europeanFormat = false;
                                } else {
                                        europeanFormat = true;
                                }
                                
                                // What dividor to use, a "/" or a "-"
                                if(inp.className.search(/dash-dividor/) != -1) {
                                        dividor = false;
                                } else {
                                        dividor = true;
                                }
                                
                                // The first day of the week (0 = Sunday)
                                if(inp.className.search(regExp2) != -1) {
                                        var tmp = inp.className.match(regExp2);
                                        firstDayOfWeek = parseInt(tmp[0].replace(/start-day-/, ''));
                                } else {
                                        firstDayOfWeek = 0;
                                }

                                // The days to highlight
                                if(inp.className.search(regExp3) != -1) {
                                        var tmp = inp.className.match(regExp3);
                                        tmp = tmp[0].replace(/highlight-days-/, '');
                                        highlightDays = new Array();
                                        for(var j = 0; j < tmp.length; j++) {
                                                highlightDays[highlightDays.length] = tmp.charAt(j);
                                        }
                                } else {
                                        highlightDays = new Array(5,6);
                                }
                                
                                datePickerController.datePickers[len] = new datePicker(inp, europeanFormat, dividor, firstDayOfWeek, highlightDays);
                                var but = document.createElement('button');
                                but.setAttribute("type", "button");
                                but.className = "date-picker-control";
                                
                                // Internet explorer requires the addition of the _value attribute
                                // All the others can use the standard 'value' attribute with no side effects
                                // which doesn't suprise me at all...

                                but._value = len;

                                but.appendChild(document.createTextNode('...'));
                                but.onclick = function() {
                                        datePickerController.hideAll(this._value);
                                        datePickerController.datePickers[this._value].show();
                                        this.blur();
                                        return false;
                                }
                                
                                if(inp.nextSibling) {
                                        inp.parentNode.insertBefore(but, inp.nextSibling);
                                } else {
                                        inp.parentNode.appendChild(but);
                                }
                        }
                }
                addEvent(window, 'resize', datePickerController.resizeAll, true);
                datePickerController.resizeAll();
        }

};

})();

addEvent(window, 'load', datePickerController.create, true);


