/* ---------------------------------------------------------------------------------------------------- */
/* =MXM.Date */
/* ---------------------------------------------------------------------------------------------------- */

if (!$defined(MXM)) var MXM = {};


/* ---------------------------------------------------------------------------------------------------- */
/* =mootools.extend */
/* ---------------------------------------------------------------------------------------------------- */

/*
Script: Element.Form.js
	Contains Element prototypes to deal with Forms and their elements.

License:
	MIT-style license.
*/

/*
Class: Element
	Custom class to allow all of its methods to be used with any DOM element via the dollar function <$>.
*/

Element.extend({
	
	resetValue: function(){
		switch(this.getTag()){
			case 'select':
				$each(this.options, function(option){ option.selected = option.defaultSelected });
				break;
			case 'input':
				if (['checkbox','radio'].contains(this.type) || ['hidden','submit','button'].contains(this.type)) {
					if (['checkbox','radio'].contains(this.type)) this.checked = this.defaultChecked;
					break;
				}
			case 'textarea': this.value = this.defaultValue;
		}
	},
	
	resetFormElements: function(){
		this.getFormElements().each(function(el){ el.resetValue() });
	}
	
});


/* ---------------------------------------------------------------------------------------------------- */
/* =MXM.Date */
/* ---------------------------------------------------------------------------------------------------- */

Number.implement({
	lz: function (digits) {
		var length = this.toString().length;
		var zeros = '';
		if (digits > length) {
			for (var i = 0; i < (digits - length); i++) {
				zeros += '0';
			}
			return zeros + this.toString();
		}
		return this;
	}
});


Date.implement({
		
	getPreviousMonth: function () {
		var tmp = new Date(this);
		tmp.setPreviousMonth();
		return tmp;
	},
	
	getNextMonth: function () {
		var tmp = new Date(this);
		tmp.setNextMonth();
		return tmp;
	},
	
	setPreviousMonth: function () {
		this.setDate(1);
		this.setMonth(this.getMonth() - 1);
		return this;
	},
	
	setNextMonth: function () {
		this.setDate(1);
		this.setMonth(this.getMonth() + 1);
		return this;
	},
	
	getFirstDate: function () {
		var tmp = new Date(this);
		tmp.setDate(1);
		return tmp.getDate();
	},
	
	getLastDate: function () {
		var tmp = new Date(this);
		tmp.setNextMonth().setDate(0);
		return tmp.getDate();
	},
	
	getCalendarWeek: function () {
		var tmp = new Date(this);
		var weekday = tmp.getDay();
		tmp.setDate(tmp.getDate() - (weekday + 6) % 7 + 3); // Nearest Thu
		var ms = tmp.valueOf(); // GMT
		tmp.setMonth(0);
		tmp.setDate(4); // Thu in Week 1
		return Math.round((ms - tmp.valueOf()) / (7 * 864e5)) + 1;
	},
		
	format: function (specs) {
		var tmp = this;
		return specs.replace(/(d{2,4}|M{2,4}|y{2,4}|hh|mm|ss)/g, function ($1) {
			switch ($1) {
			case 'ddd': return MXM.Date.WeekDays[tmp.getDay()];
			case 'dd': return tmp.getDate().lz(2);
			case 'MMMM': return MXM.Date.Months[tmp.getMonth()];
			case 'MM': return (tmp.getMonth() + 1).lz(2);
			case 'yy': return tmp.getFullYear().toString().substring(2);
			case 'yyyy': return tmp.getFullYear();
			case 'hh': return tmp.getHours().lz(2);
			case 'mm': return tmp.getMinutes().lz(2);
			case 'ss': return tmp.getSeconds().lz(2);
			}
		})
	}
	
});

MXM.Date = {
	Months: ['Januar', 'Februar', 'Marz', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
	
	WeekDays: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
	
	convertToFullYear: function (year) {
		if (year.length <= 2) {
			var yy = year.toInt(); yy.lz(2);
			return (yy < 38) ? yy + 2000 : yy + 1900;
		}
		return year;
	},
	
	extract: function (str) {
		var date = new Date();
		var tmp = str.match(/\d{1,4}([- /.])\d{1,4}[- /.]\d{1,4}/);
		if (tmp) {
			var arr = tmp[0].split(tmp[1]);
			(arr[0].length <= 2) ? date.setFullYear(MXM.Date.convertToFullYear(arr[2])) : date.setFullYear(MXM.Date.convertToFullYear(arr[0]));
			date.setMonth(--arr[1]);
			(arr[0].length <= 2) ? date.setDate(arr[0]) : date.setDate(arr[2]);
			return date;
		} else {
			return null;
		}
	},
	
	compare: function (date1, date2, options) {
		var options = $merge({ accuracy: 'ms', operator: '==' }, options);
		var accuracy = ['ms', 's', 'm', 'h', 'd', 'M', 'y'];
		var methods = ['setMilliseconds', 'setSeconds', 'setMinutes', 'setHours', 'setDate', 'setMonth', 'setFullYear'];
		
		var tmp1 = new Date(date1);
		var tmp2 = new Date(date2);
		for (var i = 0; i < accuracy.indexOf(options.accuracy); i++) {
			var value = (i == 4) ? 1 : 0;
			tmp1[methods[i]](value);
			tmp2[methods[i]](value);
		}
				
		switch (options.operator) {
			case '==': return (Date.parse(tmp1) == Date.parse(tmp2));
			case '!=': return (Date.parse(tmp1) != Date.parse(tmp2));
			case '>': return (Date.parse(tmp1) > Date.parse(tmp2));
			case '<': return (Date.parse(tmp1) < Date.parse(tmp2));
			case '>=': return (Date.parse(tmp1) >= Date.parse(tmp2));
			case '<=': return (Date.parse(tmp1) <= Date.parse(tmp2));
		}
		
		return false;
	}
	
};


/* -------------------------------------------------- */
/* =MXM.Date.Calendar */

MXM.Date.Calendar = new Class({
	
	Implements: [Events, Options],
	
	options: {
		Months: MXM.Date.Months,
		WeekDays: MXM.Date.WeekDays,
		Today: 'Heute',
		oneClick: true,
		showCW: false,
		DisplayRange: ['01.01.1900', '31.12.2100'],
		SelectRange: ['01.01.1900', '31.12.2100'],
		onLoad: $empty,
		onDrag: $empty,
		onAdjust: $empty,
		onClose: $empty,
		onSelect: $empty
	},
	
	initialize: function (options) {
		this.setOptions(options);
		this.date = new Date();
		this.selectedDate = null;
		this.selectedElement = null;
		
		this.options.DisplayRange.each(function (date, i) {
			this.options.DisplayRange[i] = MXM.Date.extract(date);
		}, this);
		
		this.options.SelectRange.each(function (date, i) {
			this.options.SelectRange[i] = MXM.Date.extract(date);
		}, this);
		
		this.bound = {
			checkFocus: this.checkFocus.bind(this),
			adjust: this.adjust.bind(this)
		};
		
		this.build();
		this.setToday();
	},
	
	build: function () {
		this.container = new Element('div').addClass('calendar-container');
		this.header = new Element('div').addClass('calendar-header').injectInside(this.container);
		new Element('a', {
			'href': '#',
			'class': 'calendar-close',
			'events': {
				'click': this.close.bind(this)
			}
		}).set('html', 'x').injectInside(this.header);
		this.headerLabel = new Element('span').injectInside(this.header);
		
		this.fx = {};
		
		this.fx.fade = new Fx.Tween(this.container, { property: 'opacity', link: 'cancel', transition: Fx.Transitions.Sine.easeOut, duration: 300 }).set(0); // hide()
		this.fx.pos = new Fx.Morph(this.container, {
			link: 'cancel', transition:
			Fx.Transitions.Back.easeOut,
			duration: 500,
			onComplete: this.onAdjust.bind(this)
		});
		this.container.makeDraggable({
			handle: this.header,
			onStart: this.fx.fade.start.pass(0.5, this.fx.fade),
			onDrag: this.drag.bind(this),
			onComplete: function () {
				this.fx.fade.start(1);
				this.adjust();
			}.bind(this)
		});
				
		this.controls = new Element('div').addClass('calendar-controls').injectInside(this.container);
		[['setPrevYear', '&laquo;'], ['setPrevMonth', '&lt;'], ['setNextYear', '&raquo;'], ['setNextMonth', '&gt;'], ['setToday', this.options.Today]].each(function (control) {
			new Element('a', {
				'href': '#',
				'class': 'calendar-' + control[0],
				'events': {
					'click': this[control[0]].bind(this)
				}
			}).set('html', control[1]).injectInside(this.controls);
		}, this);
		this.table = new Element('table').setProperty('cellspacing', 0).addClass('calendar-body').injectInside(this.container);
		this.thead = new Element('thead').injectInside(this.table);
		this.tbody = new Element('tbody').injectInside(this.table);
		this.footer = new Element('div').addClass('calendar-footer').set('html', '&nbsp;').injectInside(this.container);
		this.container.injectInside(document.body);
		
		var row = new Element('tr');
		if (this.options.showCW) new Element('th').addClass('calendar-cw').set('html', 'KW').injectInside(row);
		
		[1, 2, 3, 4, 5, 6, 0].each(function (weekday, i) {
			var cell = new Element('th').set('html', this.options.WeekDays[weekday]);
			if (i > 4) cell.addClass('calendar-weekend');
			cell.injectInside(row);
		}, this);
		row.injectInside(this.thead);
		
		// shim
		this.iframeShim = new IFrameShim(this.container, {zindex: 999});
		if (this.iframeShim.isActive) {
			this.addEvent('load', this.iframeShim.show.bind(this.iframeShim));
			this.addEvent('drag', this.iframeShim.adjust.bind(this.iframeShim));
			this.addEvent('adjust', this.iframeShim.adjust.bind(this.iframeShim));
			this.addEvent('close', this.iframeShim.hide.bind(this.iframeShim));
		}
		
	},
	
	drag: function () {
		this.fireEvent('drag');
	},
	
	adjust: function () {
		var posLeft = this.fx.pos.element.getLeft();
		var posTop = this.fx.pos.element.getTop();
		if (posLeft < 0 + window.getScrollLeft()) {
			posLeft = window.getScrollLeft() + 10;
		} else if (posLeft + this.fx.pos.element.getSize().x > window.getWidth() + window.getScrollLeft()) {
			posLeft = ((window.getWidth() + window.getScrollLeft()) - this.fx.pos.element.getSize().x) - 10;
		}
		if (posTop < window.getScrollTop()) {
			posTop = 10 + window.getScrollTop();
		} else if (posTop + this.fx.pos.element.getSize().y > window.getHeight() + window.getScrollTop()) {
			posTop = ((window.getHeight() + window.getScrollTop()) - this.fx.pos.element.getSize().y) - 10;
		}
		this.fx.pos.start({
			'left': posLeft,
			'top': posTop
		});
	},
	
	onAdjust: function () {
		this.fireEvent('adjust');
	},
	
	update: function () {
		
		this.headerLabel.set('html', this.options.Months[this.date.getMonth()] + ', ' + this.date.getFullYear());
		
		this.tbody.getChildren().destroy();
				
		var tmpDate = new Date(this.date);
		tmpDate.setDate(1);
		var startDay = (tmpDate.getDay() == 0) ? 7 : tmpDate.getDay();
		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('calendar-cw').set('html', tmpDate.getCalendarWeek()).injectInside(row);
			for (j; j <= cells; j++) {
				var cell = new Element('td');
				if (j < startDay || k > this.date.getLastDate()) {
					cell.set('html', '&nbsp;').addClass('calendar-other');
				} else {
					if (MXM.Date.compare(tmpDate, this.options.SelectRange[0], { accuracy: 'd', operator: '>=' }) && MXM.Date.compare(tmpDate, this.options.SelectRange[1], { accuracy: 'd', operator: '<=' })) {
						var anchor = new Element('a').setProperty('href', '#').set('html', k);
						anchor.addEvent('click', this.selectDate.create({ bind: this, event: true, arguments: [anchor, k] }));
						anchor.injectInside(cell);
						var target = anchor;
					} else {
						cell.set('html', k).addClass('calendar-disabled');
						var target = cell;
					}		
					if (MXM.Date.compare(tmpDate, new Date(), { accuracy: 'd' })) target.addClass('calendar-today');
					if (tmpDate.getDay() == 6 || tmpDate.getDay() == 0) target.addClass('calendar-weekend');
					if (MXM.Date.compare(tmpDate, this.selectedDate, { accuracy: 'd' })) {
						target.addClass('calendar-selected');
						this.selectedElement = target;
					}
					k++;
					tmpDate.setDate(k);
				}
				cell.injectInside(row);
				if (j % 7 == 0) {
					j++;
					break;
				}
			}
			row.injectInside(this.tbody);
		}
	},
	
	load: function (event, options) {
		event.stop();
		var options = options;
		
		if (this.container.getStyle('display') === 'block') return false;
		
		if ($defined(options.date)) {
			if (MXM.Date.extract(options.date)) {
				this.selectedDate = MXM.Date.extract(options.date);
				this.date = MXM.Date.extract(options.date);
			}
		}
		
		this.update();
		
		this.container.setStyles({
			'display': 'block',
			'left': event.page.x,
			'top': event.page.y
		});
		
		this.fx.fade.start(1).chain(function () {
			this.adjust();
		}.bind(this));
		document.addEvent('click', this.bound.checkFocus);
		window.addEvent('resize', this.bound.adjust); // experimental: remove before deployment
		window.addEvent('scroll', this.bound.adjust); // experimental: remove before deployment
		this.fireEvent('load');
	},
	
	close: function (event) {
		event.stop();
		this.fx.fade.start(0).chain(function () {
			this.container.setStyle('display', 'none');
		}.bind(this));
		document.removeEvent('click', this.bound.checkFocus);
		window.removeEvent('resize', this.bound.adjust); // experimental: remove before deployment
		window.removeEvent('scroll', this.bound.adjust); // experimental: remove before deployment
		this.fireEvent('close');
	},
	
	checkFocus: function (event) {
		event.stop();
		if (event.target !== this.container && !this.container.hasChild(event.target)) {
			this.close(event);
		}
	},
	
	setPrevYear: function (event) {
		event.stop();
		var tmp = new Date(this.date); tmp.setFullYear(tmp.getFullYear() - 1);
		if (MXM.Date.compare(tmp, this.options.DisplayRange[0], { accuracy: 'M', operator: '>=' })) {
			this.date.setFullYear(this.date.getFullYear() - 1);
		} else {
			this.date.setYear(this.options.DisplayRange[0].getFullYear());
			this.date.setMonth(this.options.DisplayRange[0].getMonth());
		}
		this.update();
	},
	
	setPrevMonth: function (event) {
		event.stop();
		var tmp = new Date(this.date); tmp.setPreviousMonth();
		if (MXM.Date.compare(tmp, this.options.DisplayRange[0], { accuracy: 'M', operator: '>=' })) {
			this.date.setPreviousMonth();
			this.update();
		}
	},
	
	setNextYear: function (event) {
		event.stop();
		var tmp = new Date(this.date); tmp.setFullYear(tmp.getFullYear() + 1);
		if (MXM.Date.compare(tmp, this.options.DisplayRange[1], { accuracy: 'M', operator: '<=' })) {
			this.date.setFullYear(this.date.getFullYear() + 1);
		} else {
			this.date.setYear(this.options.DisplayRange[1].getFullYear());
			this.date.setMonth(this.options.DisplayRange[1].getMonth());
		}
		this.update();
	},
	
	setNextMonth: function (event) {
		event.stop();
		var tmp = new Date(this.date); tmp.setNextMonth();
		if (MXM.Date.compare(tmp, this.options.DisplayRange[1], { accuracy: 'M', operator: '<=' })) {
			this.date.setNextMonth();
			this.update();
		}
	},
	
	setToday: function (event) {
		if (event) event.stop();
		var tmp = new Date();
		if (MXM.Date.compare(tmp, this.options.DisplayRange[0], { accuracy: 'M', operator: '>=' }) && MXM.Date.compare(tmp, this.options.DisplayRange[1], { accuracy: 'M', operator: '<=' })) {
			this.date = new Date();
		} else {
			this.date.setYear(this.options.DisplayRange[0].getFullYear());
			this.date.setMonth(this.options.DisplayRange[0].getMonth());
		}
		this.update();
	},
	
	selectDate: function (event, anchor, date) {
		event.stop();
		this.selectedDate = new Date(); this.selectedDate.setDate(date);
		this.date.setDate(date);
		if (this.selectedElement) this.selectedElement.removeClass('calendar-selected');
		anchor.addClass('calendar-selected');
		this.selectedElement = anchor;
		this.fireEvent('onSelect', this.date);
		if (this.options.oneClick) this.close(event);
	}
	
});