﻿/**
 * @author tfuhlroth
 * @version 0.1
 * @dependencies Date.js
 */

var Calendar = new Class({
	
	Implements: [Options, Events],
	
	options: {
		className: 'calendar',
		embed: null,
		isDraggable: true,
		doFade: true,
		showCW: false,
		oneClick: true,
		multiSelect: false,
		
		Months: ['Januar', 'Februar', 'Marz', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
		WeekDays: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
		Today: 'Heute',
		DisplayRange: ['01.01.1900', '31.12.2100'],
		SelectRange: ['01.01.1900', '31.12.2100'],
		SelectedDates: []
	},
	
	initialize: function (options) {
		this.setOptions(options);
		
		this.date = new Date();
		this.selectedDate = null;
		this.selectedElement = null;
		this.selectedDates = [];
		
		this.options.SelectedDates.forEach(function (date) {
			this.selectedDates.push(Date.parse(date));
		}, this);
		
		this.options.DisplayRange.forEach(function (date, i) {
			this.options.DisplayRange[i] = Date.parse(date);
		}, this);
		
		this.options.SelectRange.forEach(function (date, i) {
			this.options.SelectRange[i] = Date.parse(date);
		}, this);		
		
		this.fxs = {};
		this.bound = {
			hide: this.hide.bind(this),
			checkFocus: this.checkFocus.bind(this)
		};
		
		this.build();
		this.attachEvents();
		this.setToday();
		
		// embed calendar for "in page" use
		if ($(this.options.embed)) {
			this.options.doFade = false;
			this.options.oneClick = false;
			this.container.inject(this.options.embed);
		} else {
			this.container.setStyle('display', 'none');
			this.container.inject(document.body);
			if (this.options.isDraggable) this.makeDraggable();
		}
		
		if (this.options.doFade) {
			this.fxs.fade = new Fx.Tween(this.container, {property: 'opacity', link: 'cancel', transition: 'sine:out'}).set(0);
		}
		
	},
	
	build: function () {
		// container
		this.container = new Element('div', {'class': this.options.className + '-container'});
		
		// header
		this.header = new Element('div', {'class': this.options.className + '-header'}).inject(this.container);
		this.closeButton = new Element('a', {
			'class': this.options.className + '-closebutton',
			'text': 'x'
		}).inject(this.header);
		this.headerLabel = new Element('span').inject(this.header);
		
		// controls
		this.controls = new Element('div', {'class': this.options.className + '-controls clearfix'}).inject(this.container);
		[['setPrevYear', '&laquo;'], ['setPrevMonth', '&lt;'], ['setNextYear', '&raquo;'], ['setNextMonth', '&gt;'], ['setToday', this.options.Today]].forEach(function (control) {
			new Element('a', {
				'class': this.options.className + '-' + control[0],
				'events': {
					'click': this[control[0]].bind(this)
				}
			}).set('html', control[1]).inject(this.controls);
		}, this);
		
		// body
		this.table = new Element('table', {
			'class': this.options.className + '-body',
			'cellspacing': 0
		}).inject(this.container);
		this.thead = new Element('thead').inject(this.table);
		this.tbody = new Element('tbody').inject(this.table);
		
		var row = new Element('tr');
		// add calendarweek-column
		if (this.options.showCW) {
			new Element('th', {
				'class': this.options.className + '-cw',
				'text': 'KW' // TODO: move label to dict
			}).inject(row);
		}
		[1, 2, 3, 4, 5, 6, 0].forEach(function (weekday, index) {
			var cell = new Element('th', {'text': this.options.WeekDays[weekday]}).inject(row);
			if (index > 4) cell.addClass(this.options.className + '-weekend');
		}, this);
		row.inject(this.thead);
		
		// footer
		this.footer = new Element('div', {
			'class': this.options.className + '-footer',
			'html': '&nbsp;'
		}).inject(this.container);
		
	},
	
	getElement: function () {
		return this.container;
	},
	
	attachEvents: function () {
		this.closeButton.addEvent('click', this.bound.hide);
	},
	
	detachEvents: function () {
		this.closeButton.removeEvent('click', this.bound.hide);
	},
	
	makeDraggable: function () {
		this.container.makeDraggable({
			handle: this.header,
			onStart: function (element) { this.fireEvent('dragstart', this) }.bind(this),
			onDrag: function (element) { this.fireEvent('drag', this) }.bind(this),
			onComplete: function (element) { this.fireEvent('dragend', this) }.bind(this)
		});
		this.header.setStyle('cursor', 'move');
	},
	
	show: function (event) {
		event.stop();
		this.container.setStyles({
			'display': 'block',
			'left': event.page.x,
			'top': event.page.y
		});
		if (this.fxs.fade) this.fxs.fade.start(1);
		document.addEvent('click', this.bound.checkFocus);
	},
	
	hide: function () {
		if (this.fxs.fade) {
			this.fxs.fade.start(0).chain(function () {
				this.container.setStyle('display', 'none');
			}.bind(this));
		} else {
			this.container.setStyle('display', 'none');
		}
		document.removeEvent('click', this.bound.checkFocus);
	},
	
	update: function () {
		
		this.headerLabel.set('text', this.options.Months[this.date.get('month')] + ', ' + this.date.get('year'));
		this.tbody.empty();
				
		var tmpDate = this.date.clone().set('date', 1);
		var startDay = (tmpDate.get('day') == 0) ? 7 : tmpDate.get('day');
		var cells = Math.ceil((((startDay - 1) + tmpDate.getLastDate()) / 7)) * 7;
		var rows = cells / 7;
		var i = 0;
		var j = 1;
		var k = 1;
		
		for (i; i < rows; i++) {
			var row = new Element('tr');
			if (this.options.showCW) new Element('td').addClass(this.options.className + '-cw').set('text', tmpDate.getCalendarWeek()).inject(row);
			
			for (j; j <= cells; j++) {
				var cell = new Element('td');
				
				if (j < startDay || k > this.date.getLastDate()) {
					cell.set('html', '&nbsp;').addClass(this.options.className + '-other');
				} else {
					
					if (tmpDate.compare(this.options.SelectRange[0], { accuracy: 'date', operator: '>=' }) && tmpDate.compare(this.options.SelectRange[1], { accuracy: 'date', operator: '<=' })) {
						var anchor = new Element('a', {'text': k});
						anchor.addEvent('click', this.selectDate.pass([anchor, k], this));
						anchor.inject(cell);
						var target = anchor;
					} else {
						cell.set('text', k).addClass(this.options.className + '-disabled');
						var target = cell;
					}
					if (tmpDate.compare(new Date(), { accuracy: 'date' })) target.addClass(this.options.className + '-today');
					if (tmpDate.get('day') == 6 || tmpDate.get('day') == 0) target.addClass(this.options.className + '-weekend');
					
					this.selectedDates.forEach(function (date) {
						if (tmpDate.compare(date, { accuracy: 'date' })) {
							target.addClass(this.options.className + '-selected');
							this.selectedElement = target;
						}
					}, this);
					/*if (this.selectedDate && tmpDate.compare(this.selectedDate, { accuracy: 'date' })) {
						target.addClass(this.options.className + '-selected');
						this.selectedElement = target;
					}*/
					
					k++;
					tmpDate.set('date', k);
				}
				
				cell.inject(row);
				if (j % 7 == 0) {
					j++;
					break;
				}
			}
			
			row.inject(this.tbody);
		}
	},
	
	load: function (event, options) {
		if ($defined(options.date)) {
			if (Date.parse(options.date) && !isNaN(Date.parse(options.date).get('date'))) {
				this.selectedDate = Date.parse(options.date);
				this.date = Date.parse(options.date);
				this.selectedDates[0] = this.selectedDate; // review
			}
		}
		this.update();
		this.show(event);
	},
		
	checkFocus: function (event) {
		if (event.target !== this.container && !this.container.hasChild(event.target)) {
			this.hide();
		}
	},
	
	setPrevYear: function () {
		var tmp = this.date.clone().decrement('year');
		if (tmp.compare(this.options.DisplayRange[0], { accuracy: 'month', operator: '>=' })) {
			this.date.decrement('year');
		} else {
			this.date.set('year', this.options.DisplayRange[0].get('year'));
			this.date.set('month', this.options.DisplayRange[0].get('month'));
		}
		this.update();
	},
	
	setPrevMonth: function () {
		var tmp = this.date.clone().decrement('month');
		if (tmp.compare(this.options.DisplayRange[0], { accuracy: 'month', operator: '>=' })) {
			this.date.decrement('month');
			this.update();
		}
	},
	
	setNextYear: function () {
		var tmp = this.date.clone().increment('year');
		if (tmp.compare(this.options.DisplayRange[1], { accuracy: 'month', operator: '<=' })) {
			this.date.increment('year');
		} else {
			this.date.set('year', this.options.DisplayRange[1].get('year'));
			this.date.set('month', this.options.DisplayRange[1].get('month'));
		}
		this.update();
	},
	
	setNextMonth: function () {
		var tmp = this.date.clone().increment('month');
		if (tmp.compare(this.options.DisplayRange[1], { accuracy: 'month', operator: '<=' })) {
			this.date.increment('month');
			this.update();
		}
	},
	
	setToday: function () {
		var tmp = new Date();
		if (tmp.compare(this.options.DisplayRange[0], { accuracy: 'month', operator: '>=' }) && tmp.compare(this.options.DisplayRange[1], { accuracy: 'month', operator: '<=' })) {
			this.date = new Date();
		} else {
			this.date.set('year', this.options.DisplayRange[0].get('year'));
			this.date.set('month', this.options.DisplayRange[0].get('month'));
		}
		this.update();
	},
	
	selectDate: function (anchor, date) {
		this.selectedDate = this.date.clone().set('date', date);
		this.date.set('date', date);
		
		if (this.options.multiSelect) {
			var contains = -1;
			this.selectedDates.forEach(function (date, index) {
				if (date.compare(this.selectedDate, {accuracy: 'date'})) contains = index;
			}, this);
			if (contains > -1) {
				this.selectedDates.splice(contains, 1);
				anchor.removeClass(this.options.className + '-selected');
			} else {
				this.selectedDates.include(this.selectedDate);
				anchor.addClass(this.options.className + '-selected');
			}
		} else {
			if (this.selectedElement) this.selectedElement.removeClass(this.options.className + '-selected');
			anchor.addClass(this.options.className + '-selected');
			this.selectedElement = anchor;
			
			this.selectedDates[0] = this.selectedDate;
		}
		
		/*if (this.selectedElement) this.selectedElement.removeClass(this.options.className + '-selected');
			anchor.addClass(this.options.className + '-selected');
			this.selectedElement = anchor;*/	
		this.fireEvent('onSelect', this.date);
		if (this.options.oneClick) this.hide();
	}
	
});
