// MooCal.js v1.1
// Author Robert Ross <robert@thirdoctave.com>

var MooCal = new Class({
    Implements: [Options, Events],
    
    options: {
        format: 'mm/dd/yyyy',
        yearrange: [new Date().getFullYear() - 3, new Date().getFullYear()],
        width: null,
        readonly: true
    },
    
    months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
    days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
    curdate: {},
    elem: {},
    container: '',
    tbody: {},
    visible: false,
    
    initialize: function(elem, options){
        this.setOptions(options);
        
        if(typeof elem == 'string'){
            elem = $(elem);
        }
        
        this.elem = elem;
        this.elem.set('autocomplete', false);
        this.elem.set('readonly', this.options.readonly);
        
        var datestring = elem.get('value');
        
        var date = new Date();
        
        if(datestring) {
            var parsestring = this.options.format;
            parsestring = parsestring.replace('mm', '%m');
            parsestring = parsestring.replace('dd', '%d');
            parsestring = parsestring.replace('yyyy', '%Y');
            
            Date.defineParser(parsestring);
            date.parse(datestring);
        }
        
        this.curdate = {
            'day': date.getDate(),
            'month': date.getMonth(),
            'year': date.getFullYear(),
            'dateobj': date
        };
        
        this.showDate = {
            'month': date.getMonth(),
            'year': date.getFullYear(),
            'dateobj': date
        };
        
        this.formatDate();
        
        elem.addEvents({
            'click': (function(evt){
                this.visible = true;
                this.showCal();
                
                this.fireEvent('show', [this.curdate.day, this.curdate.month + 1, this.curdate.year]);
                evt.stopPropagation();
            }).bind(this),
            'keyup': (function (evt) {
                if(evt.key === 'esc') {
                    this.hideCal();
                }
            }).bind(this)
        });
        
        $(document.body).addEvent('click', this.bodyClose.bind(this));
    },
    
    bodyClose: function (evt) {
        var elem = false;
        if(typeof evt.target != 'undefined') {
            elem = $(evt.target);
        }
        
        if(elem !== false && evt.target.hasChild(this.elem)){
            this.hideCal();
        } else {
            return true;
        }
    },
    
    collision: function (ele) {
        var coords1 = this.container.getCoordinates();
        var coords2 = ele.getCoordinates();
        
        // Collision Dectection
        var left1 = coords1.left;
        var left2 = coords2.left;
        var right1 = coords1.right;
        var right2 = coords2.right;
        var top1 = coords1.top;
        var top2 = coords2.top;
        var bottom1 = coords1.bottom;
        var bottom2 = coords2.bottom;
        var hit = true;
        if (bottom1 < top2) hit = false;
        if (top1 > bottom2) hit = false;
        
        if (right1 < left2) hit = false;
        if (left1 > right2) hit = false;
        // Collision Display
        if(hit) {
            return true;
        }
        return false;
    },
    
    showCal: function(){
        // Build the calendar...
        if(typeof this.container == 'object'){
            this.container.destroy();
        }
        var elempos = this.elem.getCoordinates();
        
        var container = new Element('div', {
            'class': 'cal-container',
            'styles': {
                'position': 'absolute',
                'left': elempos.left,
                'top': elempos.bottom,
                'width': (this.options.width) ? this.options.width : elempos.width
            }
        });
        
        this.container = container;
        this.visible = true;
        
        var controls = new Element('div', {
            'class': 'cal-controls'
        }).inject(container);
        
        var monthselect = new Element('select', {'class': 'cal-select'}).inject(controls);
        this.months.each((function(month, idx){
            var option = new Element('option', {
                'html': month.substr(0, 3),
                'value': idx
            }).inject(monthselect);
            
            if(this.curdate.month == idx){
                option.set('selected', true);
            }
        }).bind(this));
        
        // Month select change event.
        monthselect.addEvent('change', (function(evt) {
            var eleme = evt.target;
            
            this.changeShow(eleme.get('value').toInt(), this.showDate.year);
        }).bind(this));
        
        var yearselect = new Element('select', {'class': 'cal-select'}).inject(controls);
        for(var y = this.options.yearrange[0]; y <= this.options.yearrange[1]; y++){
            var option = new Element('option', {
                'html': y,
                'value': y
            }).inject(yearselect);
            
            if(this.curdate.year == y){
                option.set('selected', true);
            }
        }
        
        // Year select change event.
        yearselect.addEvent('change', (function(evt){
            var eleme = evt.target;
            
            this.changeShow(this.showDate.month, eleme.get('value').toInt());
        }).bind(this));
        
        // Close the window...
        var close = new Element('a', {
            'href': '',
            'html': 'X',
            'styles': {
                'float': 'right'
            },
            'class': 'cal-closelink'
        }).inject(controls);
        close.addEvent('click', (function(){
            this.hideCal();
            return false;
        }).bind(this));
        
        new Element('br', {
            'clear': 'right'
        }).inject(controls);
        
        var table   = new Element('table', {
            'width': '100%'
        }).inject(container);
        
        var tbody   = new Element('tbody').inject(table);
        this.tbody = tbody;
        
        var daysRow = new Element('tr').inject(tbody);
        
        this.days.each(function(day, idx) {
            var td = new Element('td', {
                'class': 'cal-dayrow',
                'html': day.substring(0, 1),
                'width': '14%'
            });
            td.inject(daysRow);
        });
        
        this.buildCal(tbody);
        
        container.inject($(document.body));
        
        var parent = this;
        $$('select[class!="cal-select"]').each(function (ele, i) {
            if(parent.collision(ele)) {
                ele.setStyle('visibility', 'hidden');
            }
        });
        
        return true;     
    },
    
    hideCal: function(){
        if(this.container) {
            this.container.destroy();
        }
        
        $$('select[class!="cal-select"]').setStyle('visibility', 'visible');
        this.visible = false;
    },
    
    buildCal: function(tbody){
        tbody.getElements('.cal-weekrow').each(function(elem, idx){
            elem.destroy();
        });
        
        var lastday = this.showDate.dateobj.get('lastdayofmonth');
        var firstday = this.showDate.dateobj.setDate(1);
        firstday = new Date(firstday).getDay();
        
        var totaldays = lastday + firstday;
        var weeks = Math.ceil(totaldays / 7);
        var active = false;
        var realday = 1;
        
        for(var i = 0; i < weeks; i++){
            var weekrow = new Element('tr', {
                'class': 'cal-weekrow'
            }).inject(tbody);            
            
            for(var d = 0; d < 7; d++){
                if(i === 0 && d == firstday){
                    active = true;
                }
                
                var td = new Element('td', {
                    'class': (active) ? 'cal-active' : 'cal-inactive',
                    'align': 'center',
                    'html': (active) ? realday : ''
                }).inject(weekrow);
                
                if(active){
                    if(realday == this.curdate.day && this.showDate.month == this.curdate.month && this.showDate.year == this.curdate.year){
                        td.addClass('cal-selected');
                    }
                    
                    realday = realday + 1;
                    td.addEvents({
                        'click': (function(evt){
                            var eleme = evt.target;
                            
                            this.changeDate(eleme.get('text').toInt(), this.showDate.month, this.showDate.year);
                            
                            this.fireEvent('change', [this.curdate.day, this.curdate.month + 1, this.curdate.year, this]);
                            this.formatDate();
                        }).bind(this),
                        
                        'mouseenter': function(){
                            this.addClass('cal-activehover');
                        },
                        
                        'mouseleave': function(){
                            this.removeClass('cal-activehover');
                        }
                    });
                }
                if(realday > lastday){
                    active = false;
                }
            }
        }
    },
    
    changeDate: function(day, month, year){
        var date = this.curdate.dateobj;
        this.curdate.day   = day.toInt();
        this.curdate.month = month.toInt();
        this.curdate.year  = year.toInt();
        
        date.setMonth(month.toInt());
        date.setFullYear(year.toInt());
        date.setDate(day.toInt());
        
        this.curdate.dateobj = date;
        
        this.buildCal(this.tbody);
    },
    
    changeShow: function(month, year){
        var date = this.showDate.dateobj;
        this.showDate.month = month;
        this.showDate.year  = year;
        
        date.setMonth(month.toInt());
        date.setFullYear(year.toInt());
        
        this.showDate.dateobj = date;
        
        this.buildCal(this.tbody);
    },
    
    formatDate: function(){
        var day = this.curdate.day;
        var month = this.curdate.month;
        var year = this.curdate.year;
        
        var format = this.options.format;
        
        format = format.replace('dd', day);
        format = format.replace('mm', month + 1);
        format = format.replace('yyyy', year);
        
        this.elem.set('value', format);
    },
    
    setCalDate: function(day, month, year){
        this.changeDate(day, monoth, year);
        this.formatDate();
    },
    
    onChange: function(day, month, year, calobj){
        // Change event
    },
    
    onShow: function(day, month, year){
        // Show event
    }
});

