/*
Shower Calendar
Version 0.1

Author: Nate Weiner
http://ideashower.com
*/


function shower_calendar( opts ) {
    var y,m,d
    
    //Determine Default date values - checks for an input field to read from or uses current date
    if (opts.dateInput) {
        var parts = opts.dateInput.value.split('/');
        if (parts.length>1) {
            y = parts[2] * 1;
            m = parts[0] * 1;
            d = parts[1] * 1;
            
            if (y < 10) {
                y = y < 50 ? '200'+y : '199'+y;
            }
             if (y < 100) {
                y = y < 50 ? '20'+y : '19'+y;
            }
            y *= 1;
        }
    }
    if (!y) {
        var curDate = new Date();
        y = curDate.getFullYear();
        m = curDate.getMonth() + 1;
        d = curDate.getUTCDate();
    }
    
    //Set Default Options - can be overriden with opts argument
    this.o = {
        //commonly changed variables
        autofill                    : true, //create calendar immediately 
		disable_input				: true, //don't allow user to change input field (if input exists)       
       
        y                           : y,
        m                           : m,
        d                           : d,
        
        //display options
        show_weekdays               : true,
        show_non_month_dates        : false,
        button_move_year            : true,
        button_move_month           : true,
        advance_button_text         : {
                                            prev  :   '&nbsp;',
                                            next  :   '&nbsp;'
                                        },
		anchor_offset_x				: 0, //if calendar opens from clicking anchor, this defines where to position it relative to the anchor
		anchor_offset_y				: 25, //if calendar opens from clicking anchor, this defines where to position it relative to the anchor
                                        
        //Language Options
        labels_months               : new Array('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sept','Oct','Nov','Dec'),
        labels_weekdays             : new Array('S','M','T','W','T','F','S'),
        
        //variables normally unchanged
        namespace                   : 'tr_cal_',
        rows                        : 6,
        cols                        : 7
        
        //Non-defaulted values, but can be sent        
        //openclose_toggle : object when clicked opens the calendar
        //updateField : field to update when clicked
        //clickEvent : default function to call when date is clicked
		//eventArgs : object to send to event function with custom variables
        //filters : object of functions to run on each date when being created, allows you to do whatever you want to each date, change attributes, events, etc
        
    };
    
    //applying custom options
    for(var i in opts) {
        this.o[i] = opts[i];       
    }
    
    //general definitions
    this.e = {};
	this.sel = {};
    
    //start
    this._init();
    
    //auto fill
    if (this.o.autofill) {
        this._fill();
    }
}

shower_calendar.prototype = {    
    
    _init : function() {
		this.mergeSelected();
		
        if (!this.o.output) {
            this.o.output = document.createElement('div');
            this.o.output.className = this.n('wrapper');
            document.body.appendChild( this.o.output );
        }
        if (this.o.openclose_toggle) {
            var self = this;
            
            this.o.openclose_toggle.onclick = function(e) {
                stopBubble(e);
                
                self._fill();
                self.o.output.onclick = function(e) {
                    stopBubble(e);
                }
                
                var self2 = self;
                window.document.onclick = function() {
                    self2.o.output.style.display = 'none';
                    window.document.onclick = null;
                }
                
				var pos = findPos(self.o.openclose_toggle);
				self.o.output.style.left = pos[0] + self.o.anchor_offset_x + 'px';
				self.o.output.style.top = pos[1] + self.o.anchor_offset_y + 'px';
                self.o.output.style.display = 'block';
            }
            this.o.output.style.display = 'none';
        } 
		if (this.o.dateInput && this.o.disable_input) {
			var self = this;
			this.o.dateInput.onkeyup = function() { 
				self.setDateInput( self.o.y, self.o.m, self.o.d );
			}
		}      
    },
    
    _fill : function() {
        //destory previous calendar
        if (this.e.cal && this.e.cal.parentNode == this.o.output) {
            this.o.output.removeChild( this.e.cal );
        }
        
        this.e = this.generate(this.o.y, this.o.m, this.o.d);      
        
        this.o.output.insertBefore( this.e.cal, this.o.output.firstChild );       
    },    
    
    generate : function(y,m,d_selected) {
        
        var e = { buttons:{}, dates:{} };
		this.e = e;
        var self = this;
        
        //Generate Calendar Wrapper
        e.cal       =  this.createNode('div', {'class':this.n('cal')} );
        
        //Generate Calendar Header
        e.header    =  this.createNode('div', {'class':this.n('header')} );
        e.buttons.prev_month    =  this.button('prev', 'month', -1); 
        e.buttons.prev_year     =  this.button('prev', 'year', -1);
        e.buttons.next_year     =  this.button('next', 'year', 1);
        e.buttons.next_month    =  this.button('next', 'month', 1);        
        e.month_name = this.createNode('span', {'class':this.n('month_name'), innerHTML:this.o.labels_months[m-1] + ' ' + this.o.y} )
        
        if ( this.o.button_move_month ) { e.header.appendChild( e.buttons.prev_month ); }
        if ( this.o.button_move_year ) { e.header.appendChild( e.buttons.prev_year ); } 
        e.header.appendChild( e.month_name );
        if ( this.o.button_move_year ) { e.header.appendChild( e.buttons.next_year ); }
        if ( this.o.button_move_month ) { e.header.appendChild( e.buttons.next_month ); }

        e.cal.appendChild( e.header );
        
        //Generate Weekday Heading
        e.weekdays = this.newRow();
        e.weekdays.className += ' ' + this.n('weekdays');
        for(var i=0; i<7; i++) {
            e.weekdays.appendChild( this.createNode('a', {innerHTML:this.o.labels_weekdays[i]}));
        }
        e.cal.appendChild( e.weekdays );
        
        //generate dates
        var da = new Date(y, m-1, 1);
        da.setDate( da.getDate() + (-1 - da.getUTCDay()) ) ;       
        
        row = this.newRow();
        for( var i=0; i<(this.o.cols*this.o.rows); i++) {
            
            da.setDate( da.getDate() + 1 );
            day = da.getUTCDate();
            month = da.getMonth() + 1;
            year = da.getFullYear();
            dow = da.getUTCDay();
             
            
            a = this.createNode('a', {
                    innerHTML:      (month == m || this.o.show_non_month_dates) ? day : '',
                    day:            day,
                    month:          month,
                    year:           year,
                    dow:            dow
            } );
            a.onclick = function(e) { self.clicked(e, this, self); };
             
            a = this.applyFilters( a );
             
            row.appendChild( a );
            
			if (!e.dates[year]) { e.dates[year] = {}; }
			if (!e.dates[year][month]) { e.dates[year][month] = {}; }
			e.dates[year][month][day] = a;
			
            if ((i+1)%this.o.cols==0) {
                e.cal.appendChild( row );
                row = this.newRow();
            }
        }		
			
		//selected date
		var selDate = this.dateExists(this.sel.y,this.sel.m,this.sel.d);
		if (selDate && selDate.innerHTML.length>0) {
			selDate.className += ' '+this.n('selected');
			e.selected = a;
		}
		
		//current date
		var curDate = new Date();
		var curA = this.dateExists(curDate.getFullYear(),curDate.getMonth()+1,curDate.getUTCDate());
		if (curA && curA.innerHTML.length>0) {
			curA.className += ' '+this.n('current_date');
		}
        
        return e;
        
    },
    
    applyFilters : function( a ) {
        for(var i in this.o.filters) {
            a = this.o.filters[i](a, this.o.eventArgs);            
        }
        return a;
    },
    
    newRow : function() {
        return this.createNode('div', {'class':'row'});        
    },
    
    button : function(direction, inc_type, inc) {
        var self = this; 
        var node = this.createNode('a', {
            adv_direction : direction,
            inc : inc,
            inc_type : inc_type,
            'class' : this.n(direction + '_' + inc_type),
            innerHTML : this.o.advance_button_text[direction]
        } );
        node.onclick = function(e){ self.advance(this); }
        this.disableSelection(node);
        return node;
    },
    
    // -- //
    
    advance : function(clicked) {
        var da = new Date( this.o.y, this.o.m - 1, 1 );
        switch( clicked.getAttribute('inc_type') ) {
            case('month'):
                da.setMonth( da.getMonth() + (clicked.getAttribute('inc') * 1) );
                break;
            case('year'):
                da.setFullYear( da.getFullYear() + (clicked.getAttribute('inc') * 1) );
                break;
        }

        this.o.m = da.getMonth() + 1;
	this.o.y = da.getFullYear();
	this.o.d = null;
        
        this._fill();
    },
    
    clicked : function(e, a, obj) {     
       	obj.selectDate( a.getAttribute('year') , a.getAttribute('month') , a.getAttribute('day') );  
    },
	
	selectDate : function(y,m,d) {
		// -- Work in progress -- //
		var a = this.dateExists(y,m,d);

		
		//Fire custom click event
		if (this.o.clickEvent) {
            if ( this.o.clickEvent(y, m, d, a, this, this.o.eventArgs) ) {
                return false; //allows a click event to stop or override default events by returning true             
            }
        }
		
		
		this.o.y = y;
        this.o.m = m;
        this.o.d = d;
		this.mergeSelected();
		
		if (a) {
			this._fill();
		}
		
		//Select date element
		if (this.e.selected) {
			this.e.selected.className = this.e.selected.className.replace(this.n('selected'));
		}
		if (!this.e.dates[y][m][d].className.match(this.n('selected'))) {
			this.e.dates[y][m][d].className += ' ' + this.n('selected');
		}
		this.e.selected = this.e.dates[y][m][d];
        
        //update input field if set
        if (this.o.dateInput) {
            this.setDateInput(y,m,d);
			
            //Close calendar
            this.o.output.style.display = 'none';
            window.document.onclick = null;
        }
	},
	
	setDateInput : function(y,m,d) {
		var da = new Date(y, m-1, d);
		var Y = da.getFullYear().toString();
		Y = Y.substr(2,2);
		this.o.dateInput.value = this.str_padL((da.getMonth() + 1),'0',2) + '/' + this.str_padL(da.getDate(),'0',2) + '/' + Y;            
	},
	
	mergeSelected : function() {
		this.sel.d = this.o.d;
		this.sel.m = this.o.m;
		this.sel.y = this.o.y;
	},
	
    // -- //
    
    n : function(n) {
        return this.o.namespace + n;  
    },
    
    createNode : function(ty, attributes) {
	node = document.createElement(ty);
	for(ik in attributes) {
		switch(ik) {
			case('class'):
				node.className = attributes[ik];
				break;
			case('style'):
				if (typeof attributes[ik] == 'string') {
					its = attributes[ik].split(';');
					for(i=0; i<its.length; i++) {
						parts = its[i].split(':');
						node.style[parts[0]] = parts[1];
					}
				} else {
					for(i in attributes[ik]) {
						node.style[ i ] = attributes[ik][i];
					}
				}
				break;
			case('innerHTML'):
				node.innerHTML = attributes[ik];
				break;
			default:
				node.setAttribute(ik, attributes[ik]);
		}				
	}
	return node;
    } ,
	
	dateExists : function(y,m,d) {
		if ( this.e.dates && this.e.dates[y] && this.e.dates[y][m] && this.e.dates[y][m][d] ) {
			return this.e.dates[y][m][d];
		}
	},
	
	findPos : function(obj) {
		var curleft = curtop = 0;
		if (obj.offsetParent) {
			curleft = obj.offsetLeft
			curtop = obj.offsetTop
			while (obj = obj.offsetParent) {
				curleft += obj.offsetLeft
				curtop += obj.offsetTop
			}
		}
		return [curleft,curtop];
	},
        
    disableSelection : function(element) { 
        if (element) {
            element.onselectstart = function() {
                    return false;
            };
            element.unselectable = "on";
            element.style.MozUserSelect = "none";
        }
    },
    
    str_padL : function(str, padwith, howlong) {
	str = str.toString();
	if (str.length < howlong && str.length > 0) {
		while(str.length < howlong) {
			str = padwith+str;	
		}
	}
	return str;
    }
    
    
}

function stopBubble(e) {
    if (!e) var e = window.event;
        e.cancelBubble = true;
        if (e.stopPropagation) e.stopPropagation();
}



// -- TR Additions -- //




function parseTimes(str) {
	var newtimes = [];
	var times = JSON.parse(str);
	for(y in times) {
		newtimes[y] = [];
		for(m in times[y]) {
			newtimes[y][m] = [];
			for(i in times[y][m]) {
				newtimes[y][m][ times[y][m][i] ] = true;
			}
		}
	}
	return newtimes;
}

function dateFilter(a, A) {
	var Y = a.getAttribute('year');
	var m = a.getAttribute('month');
	var d = a.getAttribute('day');

	if (A.dateObj.times[Y] && A.dateObj.times[Y][m] && A.dateObj.times[Y][m][d]) {
		a.className = 'tr_active';
	}

	return a;
}