String.prototype.leftPad = function (length, pad) { return new Array(length - this.length + 1).join(pad || '0') + this; };

var Calendar = function (p, t, lf, lp, w) {
	var self = this;
	
	this.parent = typeof p == 'string' ? document.getElementById(p) : p;
	this.target = typeof t == 'string' ? document.getElementById(t) : t;
	
	this.lock_future = lf;
	this.lock_past = lp;
	
	this.activate = function (e) {
		if (!self.prevent_activate) {
			Event.stop(e);
		
			self.target.blur();
			addClass(self.parent, 'active');
			self.target.style.display = 'none';
			
			return false;
		}
	};
	
	this.build = function () {
		var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], c = this.compare(this.current());
		
		this.caption = document.createElement('caption');
		this.table = document.createElement('table');
		this.table.className = 'calendar';
		this.table.appendChild(this.caption);
		
		function move (by) {
			var m = document.createElement('a'), forward = by > 0;
			
			m.className = 'move ' + (forward ? 'forward' : 'back');
			m.setAttribute('href', '#');
			m.setAttribute('by', by);
			m.title = 'Go ' + (forward ? 'forward' : 'back') + ' one month';
			m.innerHTML = forward ? '&gt;' : '&lt;';
			
			Event.add(m, 'click', self.scroll);
									
			return m;
		};
		
		this.caption.appendChild(move(1));
		this.caption.appendChild(move(-1));
		
		var header = document.createElement('tr');
		
		for (var i = 0, day; day = days[i]; i++) {
			header.appendChild(document.createElement('th'));
			header.lastChild.innerHTML = day;
		}
		
		this.table.appendChild(document.createElement('thead'));
		this.table.lastChild.appendChild(header);
		
		this.body = document.createElement('tbody');
		this.table.appendChild(this.body);
		
		Event.add(document, 'keyup', function (e) {
			if (e.keyCode == 27) self.deactivate();
		});
		
		return this.load();
	};
	
	this.compare = function (x, y) {
		if (!x) x = new Date();
		if (!y) y = this.date;
		
		if (x.getFullYear() == y.getFullYear() && x.getMonth() == y.getMonth()) return x.getDate();
	};

	this.current = function () {
		if (!this.target.value) return new Date();
		
		var d = this.target.value.split('-');
		
		return new Date(d[0], d[1] - 1, d[2]);
	};

	this.days = function () {
		return 32 - new Date(this.date.getFullYear(), this.date.getMonth(), 32).getDate();
	};

	this.deactivate = function () {
		removeClass(self.parent, 'active');
		self.target.style.display = '';
		
		return false;
	};
	
	this.load = function () {
		this.set_return();
		this.set_month();
		
		return this.set_days();
	};
	
	this.restore = function (e) {
		Event.stop(e);
				
		if (self.compare(self.current()) || Event.target(e) !== self.caption) return false;
		self.date = self.current();
		
		return self.load();
	};
	
	this.scroll = function (e) {
		Event.stop(e);
		
		self.date.setMonth(self.date.getMonth() + parseInt(this.getAttribute('by')), 1);
		
		return self.load();
	};
	
	this.allow_clicks = function () {
		var now = new Date();
		return this.date.getFullYear() > now.getFullYear() || this.date.getMonth() > now.getMonth() ? !this.lock_future : !this.lock_past;
	};
	
	this.set_days = function () {
		var row = document.createElement('tr'), s = this.start(), m = this.days() + s, n = this.compare(), c = this.compare(this.current());
		
		removeChildren(this.body);
		
		var clicks = this.allow_clicks();
		
		for (var i = 1; i <= m + 6; i++) {
			var cell = document.createElement('td');
			row.appendChild(cell);
			
			if (i > s && i <= m) {
				var day = document.createElement('a'), d = i - s;
				cell.appendChild(day);
				
				cell.className = 'day';
				
				if (clicks) day.setAttribute('href', '#');
				else addClass(cell, 'locked');
				
				day.innerHTML = d;
				
				if (d == c) this.set_selected(day);
				if (d == n) addClass(day, 'today');
				if (clicks) Event.add(day, 'click', this.update);				
				if (d == n - 1) clicks = !this.lock_future;
			}
			
			if (i % 7 == 0) {
				this.body.appendChild(row);
				row = document.createElement('tr');
			}
		}
		
		removeChildren(this.parent);
		this.parent.appendChild(this.table);
		
		return false;
	};
	
	this.set_month = function () {
		var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

		if (this.caption.lastChild.nodeType == 3) this.caption.removeChild(this.caption.lastChild);

		this.caption.appendChild(
			document.createTextNode([months[this.date.getMonth()], this.date.getFullYear()].join(' '))
		);
	};
	
	this.set_return = function () {
		Event.add(this.caption, 'dblclick', this.restore);
		this.caption.title = this.compare(this.current()) ? '' : 'Double-click to return to current date';
	};
	
	this.set_selected = function (day) {
		if (this.target.value) {
			removeClass(this.selected, 'current');
			addClass(day, 'current');
			this.selected = day;
		}
	};
	
	this.start = function () {
		return new Date(this.date.getFullYear(), this.date.getMonth(), 1).getDay();
	};
	
	this.update = function (e) {
		Event.stop(e);
		
		self.date.setDate(this.innerHTML);
		
		self.target.value = [
			self.date.getFullYear(),
			String(self.date.getMonth() + 1).leftPad(2),
			String(self.date.getDate()).leftPad(2)
		].join('-');
		
		self.set_selected(this);
		self.set_return();
		
		self.deactivate();
		
		self.on_update();
		
		return false;
	};
	
	this.on_update = function () { };
	
	Event.add(document, 'keydown', function (e) {
		if (e.keyCode == 16) self.prevent_activate = true;
	});
	
	Event.add(document, 'keyup', function (e) {
		if (e.keyCode == 16) self.prevent_activate = false;
	});
	
	Event.add(this.target, 'focus', this.activate);
	this.date = this.current();
	
	if (!w) this.build();
};
