   /*********************************************************/
  /*   Calendar version 1.00 for prototype/script.aculo.us */
 /*   by Mumualex - http://www.mumualex.fr  2008/10/08    */
/*********************************************************/

/*

	@require prototype.js > 1.6.0
	@require scriptaculous.js > 1.8.1
		with 	builder.js
				dragdrop.js
				effects.js

    Calendar Class Declaration
    - initialize()
    - initializeCalendarFunctions()
    - u()
    - slideLeft()
	- slideRight()
	- fade()
	- switchLoaders()
	- resetArrows()
	- hideLeftArrow()
	- hideRightArrow()

    CalendarPicker Class Declaration
    - initialize()
    - position()
    - show()
    - hide()
	- toggle()
	- pick()
	- getInputDate()
	- linkedUpdate()
	- outsideClick()
	- isNumber()
*/

//check prototype & scriptaculous
var convertVersionString = function(versionString){
  var r = versionString.split('.');
  return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
};

if((typeof Prototype=='undefined') ||
   (typeof Effect == 'undefined') ||
   (typeof Element == 'undefined') ||
   (typeof Element.Methods=='undefined') ||
   (convertVersionString(Prototype.Version) <
    convertVersionString('1.6.0')) ||
   (convertVersionString(Scriptaculous.Version) <
    convertVersionString('1.8.1')))
   throw("calendar.js requires the Prototype JavaScript framework >= 1.6.0 and script.aculo.us 1.8.1 extended framework (with builder, dragdrop, effects)");

Object.extend(Event, {
	wheel:function (event){
		var delta = 0;
		if (!event) event = window.event;
		if (event.wheelDelta) {
			delta = event.wheelDelta/120;
			if (window.opera) delta = -delta;
		} else if (event.detail) { delta = -event.detail/3;     }
		return Math.round(delta); //Safari Round
	}
});

var Calendar = Class.create({

	initialize: function(_container, _options) {

		// options loading
		this.options = Object.extend({
			slideDuration: 		.5,
			fadeDuration: 		.5,
			startMonday: 		true,
			filePath: 			'inc/calendar/',
			defaultView: 		'month',
			style: 				'',	//example: adobe_cs3, apple_widget
			wheel:				true,
			opacity:			.9
		}, _options || {});

		this.loading = false;
		this.container = _container = $(_container);
		var _class = this;

		//Insert the base into the container and initialize elements
		var  pars = 'defaultView='+ this.options.defaultView;
		if(this.picker) {
			if((this.options.prefillDate) && this.getInputDate(this.options.prefillDate)) pars += '&pickedDate='+ this.getInputDate(this.options.prefillDate);
			if(this.options.linkWithInput) pars += '&gotoPickedDate=1';
		}
		this.u('base', pars, function() {
			_class.mainLoader 	=  _container.getElementsBySelector('div[class=loaderA]')[0];
			_class.tempLoader 	=  _container.getElementsBySelector('div[class=loaderB]')[0];
			_class.label 		=  _container.getElementsBySelector('span[class=label]')[0];
			_class.arrowLeft 	=  _container.getElementsBySelector('div[class=arrowLeft]')[0];
			_class.arrowRight 	=  _container.getElementsBySelector('div[class=arrowRight]')[0];
			_class.initializeCalendarFunctions();

			//Prefill/load picker date elements
			if(_class.picker) {
				if(_class.options.prefillDate && _class.getInputDate(_class.options.prefillDate)) _class.pick(_class.options.prefillDate);
				//else if(_class.options.prefillDate == false) _class.pick((_class.label.readAttribute('date')).evalJSON());
			}
		}, _container);
	},

	initializeCalendarFunctions: function() {
		this.resetArrows();

		//Retrieve data (label, timestamp etc) which are stored as a Json string in the table attribute summary
		var vars =  (this.mainLoader.getElementsBySelector('table')[0].readAttribute('summary')).evalJSON();

		var _class = this;

		//Change the label
		this.label.removeClassName('noHover');
		this.label.update(vars.label);

		this.label.onclick = function(e){
			if(vars.parent) _class.u(vars.parent, 'ts=' + vars.ts + '&parent=' + vars.current, function() { _class.fade() });
		};

		//Hide arrows if necessary and add arrow click events
		if(vars.hide_left_arrow) _class.hideLeftArrow();
		else if(vars.hide_right_arrow) _class.hideRightArrow();

		this.arrowLeft.onclick =  function(){_class.u(vars.current, 'ts=' + vars.pr_ts, function() { _class.slideLeft() }) };
		this.arrowRight.onclick = function(){_class.u(vars.current, 'ts=' + vars.nx_ts, function() { _class.slideRight() }) };

		if(this.options.wheel){
			var fctWheelCalendar = function(event){
				var vars =  (_class.mainLoader.getElementsBySelector('table')[0].readAttribute('summary')).evalJSON();
				if(Event.wheel(event) < 0){
					_class.u(vars.current, 'ts=' + vars.pr_ts, function() { _class.slideLeft() });
				} else {
					_class.u(vars.current, 'ts=' + vars.nx_ts, function() { _class.slideRight() })
				}
				event.stop();
				return false;
			};
			Event.observe(this.container, "mousewheel", fctWheelCalendar, false);
			Event.observe(this.container, "DOMMouseScroll", fctWheelCalendar, false); // Firefox
		}

		//Add cell click events
		var clickables = this.mainLoader.getElementsBySelector('td');
		switch(vars.current) {
			case 'month':
				if(this.picker) {
					clickables.each(function(_clickable) {
						_clickable.onclick = function(){
							_class.pick((_clickable.readAttribute('date')).evalJSON());
							_class.mainLoader.getElementsBySelector('td').each(function(_clickable) { _clickable.removeClassName('selected') });
							this.addClassName('selected');
						};
					});
				}
				break;
			case 'year':
				clickables.each(function(_clickable) {
					_clickable.onclick = function(){
						_class.u('month', 'ts=' + _clickable.readAttribute('ts'), function() { _class.fade() })
					};
				});
				break;
			case 'decade':
				this.label.addClassName('noHover');
				clickables.each(function(_clickable) {
					_clickable.onclick = function(){
						_class.u('year', 'ts=' + _clickable.readAttribute('ts') + '&m_ts=' + _clickable.readAttribute('m_ts'), function() { _class.fade() })
					};
				});
				break;
		}
	},

	//Ajax updater function which handles all requests
	u: function(_url, _pars, _onComplete, _id) {
		if(!this.loading && !this.transitioning) {
			var _class = this;
			this.loading = true;
			var element = $(_id ? _id : this.tempLoader);
			_pars += '&picker=' + (this.picker ? 1 : 0) + '&startMonday=' + (this.options.startMonday ? 1 : 0) + '&style=' +  this.options.style;
			if(this.picker && this.getInputDate()) _pars += '&pickedDate='+ this.getInputDate();

			new Ajax.Request(this.options.filePath + _url + '.php', 	{
				method:'post',
				asynchronous:true,
				parameters:_pars,
				onSuccess: function(t) {
					if(t.responseText != ""){
						element.update(t.responseText);
					} else {
						// alert("Unable to load calendar");
					}
					_onComplete();
					_class.loading = false;
				},
				evalScripts:true
			});
		}
	},
	slideLeft: function() {
		if(this.arrowLeft.getStyle('visibility') != "hidden"){
			var _class = this;
			_class.transitioning = true;

			this.tempLoader.setStyle({position:'absolute', left:-164+"px", opacity : 1});

			_class.tempLoader.setStyle({display:'block'});
			_class.mainLoader.setStyle({display:'block'});

			new Effect.Move(this.tempLoader, { x: 0, y: 0, mode: 'absolute' });

			this.mainLoader.setStyle({position:'absolute', left:0+"px", opacity : 1});
			new Effect.Move(this.mainLoader, { x: 164, y: 0, mode: 'absolute', afterFinish:function(){
				_class.transitioning = false;
				_class.tempLoader.setStyle({display:'none', left: 0+"px"});
				_class.mainLoader.setStyle({left: 0+"px"});
			} });

			this.switchLoaders();
		}
	},

	slideRight: function() {

		if(this.arrowRight.getStyle('visibility') != "hidden"){
			var _class = this;
			_class.transitioning = true;

			this.mainLoader.setStyle({position:'absolute', left:0+"px", opacity : 1});

			_class.tempLoader.setStyle({display:'block'});
			_class.mainLoader.setStyle({display:'block'});
			new Effect.Move(this.mainLoader, { x: -164, y: 0, mode: 'absolute' });

			this.tempLoader.setStyle({position:'absolute', left:164+"px", opacity : 1});
			new Effect.Move(this.tempLoader, { x: 0, y: 0, mode: 'absolute', afterFinish:function(){
				_class.transitioning = false;
				_class.tempLoader.setStyle({display:'none', left: 0+"px"});
				_class.mainLoader.setStyle({left: 0+"px"});
			} });

			this.switchLoaders();
		}
	},
	fade: function(overRuleTrans) {
		var _class = this;
		this.transitioning = overRuleTrans ? false : true;

		new Effect.Fade(this.mainLoader, {
			from:this.options.opacity,
			duration:this.options.fadeDuration,
			afterFinish:function(){}
		});

		new Effect.Appear(this.tempLoader, {
			to:this.options.opacity,
			duration:this.options.fadeDuration,
			afterFinish:function(){
				_class.transitioning = false;
			}
		});

		this.switchLoaders();
	},

	switchLoaders: function() {
		this.mainLoader = this.mainLoader.className == 'loaderA' ? this.container.getElementsBySelector('div[class=loaderB]')[0] : this.container.getElementsBySelector('div[class=loaderA]')[0];
		this.tempLoader = this.tempLoader.className == 'loaderA' ? this.container.getElementsBySelector('div[class=loaderB]')[0] : this.container.getElementsBySelector('div[class=loaderA]')[0];
		this.initializeCalendarFunctions();
	},

	resetArrows: function() {
		this.arrowLeft.setStyle({visibility : 'visible'});
		this.arrowRight.setStyle({visibility : 'visible'});
	},

	hideLeftArrow: function() {
		this.arrowLeft.setStyle({visibility : 'hidden'});
	},

	hideRightArrow: function() {
		this.arrowRight.setStyle({visibility : 'hidden'});
	}
});

var CalendarPicker = Class.create(Calendar, {

	initialize: function($super, _element, _options) {

		// options loading
		this.options = Object.extend({
			separateInput: 		false,
			prefillDate: 		false,
			linkWithInput: 		true,
			leadingZero: 		true,
			twoDigitYear: 		false,
			separator: 			'-',
			format: 			'd/m/y',
			openWith: 			null,
			alignX: 			'center',
			alignY: 			'top',
			offset: 			{ 'x': 0, 'y': 0 },
			style: 				'',
			ieTransitionColor : '#ffffff',
			toggleDuration: 	.8,
			draggable:			true,
			onload:				Prototype.emptyFunction,
			onunload:			Prototype.emptyFunction,
			opacity:			1
		}, _options || {});

		this.element = $(_element);
		if(!this.element) throw 'No (existing) element to create a calendar for specified: new CalendarPicker(ELEMENT, [options])';

		if(this.options.showTime) this.options.style+=" extend";

		//Check if the user wants multiple input
		if(this.options.separateInput != false) {
			this.element.day   = this.element.getElementsBySelector('input[name='+ this.options.separateInput.day +']')[0];
			this.element.month = this.element.getElementsBySelector('input[name='+ this.options.separateInput.month +']')[0];
			this.element.year  = this.element.getElementsBySelector('input[name='+ this.options.separateInput.year +']')[0];
		}

		//Create the picker and calendar and inject in in the body
		this.picker = Builder.node('div', { className:'vlaCalendarPicker' + (this.options.style != '' ? ' ' + this.options.style : ''), style:'display:none' });
		document.body.insertBefore(this.picker, document.body.firstChild);
		this.bodyContent = Builder.node('div', { className:'pickerBackground' });
		this.picker.appendChild(this.bodyContent);
		this.pickerContent = Builder.node('div', {className:'pickerBackgroundContent'});
		this.bodyContent.appendChild(this.pickerContent);

		//heritage du parent
		$super(this.pickerContent, this.options);

		//Add events for showing and hiding the picker
		var _class = this;
		var pointer = this.options.openWith ? $(this.options.openWith) : this.element;
		Event.observe(pointer, 'focus', function(){_class.show()});
		Event.observe(pointer, 'click', function(){_class.options.openWith ? _class.toggle() : _class.show()});
		Event.observe(pointer, 'change', function(){_class.show()});

		//If the datepicker is visible an outside click makes it hide
		Event.observe(document.body, 'mousedown', function(e){
			if(_class.outsideHide && _class.outsideClick(e, _class.picker)) _class.hide();
			//if(_class.outsideHide && _class.outsideClick(e, _class.picker)) _class.hide();
		});

		//linkWithInput
		if(this.options.linkWithInput) {
			if(this.options.separateInput != false) {
				Event.observe(this.element.day, 'keyup', function(){_class.linkedUpdate()});
				Event.observe(this.element.month, 'keyup', function(){_class.linkedUpdate()});
				Event.observe(this.element.year, 'keyup', function(){_class.linkedUpdate()});
			} else {
				Event.observe(this.element, 'keyup', function(){_class.linkedUpdate()});
			}
		}

		//set draggable
		if(this.options.draggable) new Draggable(this.picker,{revert:false});

		this.visible = false;
		this.outsideHide = false;

		this.notify("onload");
	},
	position: function() {
		var top, left;

        switch(this.options.alignX){
    		case "left": 		left = 0 - this.picker.getWidth();	break;
    		case "center": 		left = -this.picker.getWidth() / 2; break;
    		case "right": 		left = 0; break;
		    default :			left = 0; break;
		}
		element = this.element.day ? this.element.day : this.element;
		switch(this.options.alignY){
    		case "bottom": 		top = element.offsetHeight;	break;
    		case "top": 		top = element.offsetHeight - element.getHeight() - this.picker.getHeight() - 4; break;
		    default :			top = element.offsetHeight - element.getHeight() - this.picker.getHeight() - 4; break;
		}
		//top+=document.viewport.getScrollOffsets().top;
		Position.clone(this.element, this.picker, {
			setHeight: false,
			setWidth: false,
			offsetTop: top,
			offsetLeft: left
        });
	},
	show:function(){
		this.position();
		if(!this.visible){
			this.visible = true;
			var _class = this;
			if(Prototype.Browser.IE) this.picker.setStyle({backgroundColor: this.ieTransitionColor});
			new Effect.Appear(this.picker, {
				to:this.options.opacity,
				duration:this.options.toggleDuration,
				afterFinish:function(){
					if(Prototype.Browser.IE) _class.picker.setStyle({backgroundColor: 'transparent'});
					_class.outsideHide = true;
				}
			});
		}
	},
	hide: function(){
		if(this.visible){
			var _class = this;
			if(Prototype.Browser.IE) this.picker.setStyle({backgroundColor: this.ieTransitionColor});
			new Effect.Fade(this.picker, {
				from:this.options.opacity,
				duration:this.options.toggleDuration,
				afterFinish:function(){
					_class.picker.setStyle({display: 'none'});
					_class.outsideHide = false;
					_class.visible = false;
				}
			});
		}
	},
	toggle: function() {
		if(this.visible) this.hide();
		else this.show();
	},
	pick: function(_date) {
		if(this.options.leadingZero) {
			if(_date.day < 10)   _date.day = '0' + _date.day;
			if(_date.month < 10) _date.month = '0' + _date.month;
		}
		if(this.options.twoDigitYear) _date.year = _date.year.substring(2, 4);

		if(this.element.day) {
			if(this.element.day)   this.element.day.value = _date.day;
			if(this.element.month) this.element.month.value = _date.month;
			if(this.element.year)  this.element.year.value = _date.year;
			this.hide();
		} else {
			switch(this.options.format) {
				case "m/d/y": this.element.value = _date.month + this.options.separator + _date.day + this.options.separator + _date.year; break;
				case "y/m/d": this.element.value = _date.year + this.options.options.separator + _date.month + this.options.separator + _date.day; break;
				case "y/d/m": this.element.value = _date.year + this.options.separator +  _date.day + this.options.separator + _date.month; break;
				case "d/m/y": default: this.element.value = _date.day + this.options.separator + _date.month + this.options.separator + _date.year;
			}
			this.hide();
		}
		this.notify("onunload");
	},

	getInputDate: function(_date) {
		var day, month, year;

		if(_date) {
			day = _date.day;
			month = _date.month;
			year = _date.year;
		} else if(this.options.separateInput != false) {
			day = parseInt(this.element.day.value);
			month = parseInt(this.element.month.value);
			year = parseInt(this.element.year.value);
		} else {
			var date = this.element.value.split(this.options.separator);
			if(date.length != 3) return null;
			switch(this.options.format) {
				case "m/d/y": day = date[1]; month = date[0]; year = date[2]; break;
				case "y/m/d": day = date[2]; month = date[1]; year = date[0]; break;
				case "y/d/m": day = date[1]; month = date[2]; year = date[0]; break;
				case "d/m/y": default: day = date[0]; month = date[1]; year = date[2];
			}
		}

		if( !this.isNumber(day) || !this.isNumber(month) || !this.isNumber(year) ||	day == 0 || month == 0 || year == '0' ||
		    (this.options.twoDigitYear && year > 99) || (!this.options.twoDigitYear && year < 1979) || (!this.options.twoDigitYear && year > 2030) || month > 12 || day > 31 ) return null;

		if(this.options.twoDigitYear && this.isNumber(year) && year < 100) {
			year = parseInt(year);
			if(year < 10) year = '200'+  year;
			else if(year < 70) year = '20'+  year;
			else if(year > 69) year = '19'+  year;
			else year = new Date().getFullYear();
		}

		return day +'/'+ month +'/'+ year;
	},
	//This function is being called on keyup event if linkWithInput is set to true and when a date is picked
	//If the full date is inserted the picker will change itself to that specific date (month view)
	linkedUpdate: function() {
		var _class = this;
		var date = this.getInputDate();
		if(date && this.pickedDate != date) {
			this.u('month', 'gotoPickedDate=1', function() { _class.fade(true) });
			this.pickedDate = date;
		}
	},
	outsideClick: function(_event, _element) {
		var mousePos = new Object();
		mousePos.x = Event.pointerX(_event);
		mousePos.y = Event.pointerY(_event);
		var elementData1 = Element.cumulativeOffset(_element);
		var elementData2 = Element.getDimensions(_element);
		return (mousePos.x > elementData1.left && mousePos.x < (elementData1.left + elementData2.width)) &&
			   (mousePos.y > elementData1.top  && mousePos.y < (elementData1.top + elementData2.height)) ? false : true;
	},
	isNumber: function(_number) {
		if(_number == '') return false;
		return (_number >= 0) || (_number < 0) ? true : false;
	},
	notify: function(eventName) {
		if (this.options[eventName])
			this.options[eventName](this);
	}
});
