/* Date slider element, Ajaxorized.com,  2008 
 * 
 * modified by Sunil K Chopra, June 2010
 */

/* Extend the date element a bit */
Date.prototype.getDiffDays = function(p_oDate) {
	p_iOneDay = 1000*60*60*24;
	return Math.ceil((p_oDate.getTime()-this.getTime())/(p_iOneDay));
}

/* The dateslider */
DateSlider = Class.create({
	initialize : function(options) {
	
		/* The options */
	    this.options = {
	      dayWidth:     1,
	      dragHandles:  true,
	      dragBar:      true,
	      dateFormat:   'yyyy-MM-dd',
		  zoom : 		false,
		  handleFinder: false,
		  appearOnFocus:false,
		  onEnd : 		null,
		  onStart : 	null,
		  duration: 	0.5,
		  lowerLimitYear: 	parseInt(new Date.today().addYears(-1).toString("yyyy"),10),
		  upperLimitYear: 	parseInt(new Date.today().addYears(1).toString("yyyy"),10),
		  startDate: 		new Date.today().addMonths(-1).toString("yyyy-MM-dd"),
		  endDate: 			new Date.today().addMonths(1).toString("yyyy-MM-dd"),
		  inputStartField: null,
		  inputEndField:   null,
		  insertLocation:  'after',
		  insertElement:   null
	    };
		
		Object.extend(this.options, options || { });
		if(this.options.inputStartField!==null && this.options.inputEndField!==null && this.options.insertElement!==null){
			/* Initialize the dateslider object:
			 * - Set the start/end days
			 * - Set options
			 * - Create the seperate elements of the dateslider
			 */
			this.barStartDate = new Date().set({year:this.options.lowerLimitYear, month:0, day:1});
			this.barEndDate = new Date().set({year:this.options.upperLimitYear, month:11, day:31});
			
			/* Panel dates */
			this.oStartDate = Date.parse(this.options.startDate);
			this.oEndDate = Date.parse(this.options.endDate);		
			
			/* The input fields (set later) */
			this.oStartField = null;
			this.oEndField = null;
			
			this.sliderBarMargin = 2;
			
			this.barReference = new Element("div",{className:"sliderBar"});
			var sliderContainer = new Element("div",{className:"sliderContainer"}).update(this.barReference);
			this.wrapperReference = new Element("div",{className:"sliderWrapper"}).update(sliderContainer);
			
			if(this.options.appearOnFocus){
				var offsetFromParent = this.options.insertElement.positionedOffset();
				this.wrapperReference.setStyle({
					display:"none",
					position:"absolute",
					left: offsetFromParent.left+"px"
				});
			}
			
			var insertObject = {};
			insertObject[this.options.insertLocation] = this.wrapperReference;
			
			$(this.options.insertElement).insert(insertObject);
			
			this.numberOfDays = null;
			if (this.options.dragHandles == false){this.numberOfDays = this.oStartDate.getDiffDays(this.oEndDate)};
			this.centerDate = Date.today();
			if (this.options.centerDate != null){this.centerDate = Date.parse(options['centerDate'])};
			
			this.iLeftOffsetLH = this.barStartDate.getDiffDays(this.oStartDate)*this.options.dayWidth;
			this.iLeftOffsetRH = this.barStartDate.getDiffDays(this.oEndDate)*this.options.dayWidth;
			
			this.createSliderBar();
			this.createHandles();
			this.createShiftPanel();
			
			if(this.options.zoom){this.setZoom();}
			if(this.options.handleFinder){this.setHandleFinder();}
			if(this.options.appearOnFocus){this.addCloseButton();}
			this.attachFields();
			
		}else{
			// no start and end input fields provided
			return false;
		}
		
		
		
	},
	createSliderBar : function() {
		/* Create the backgound (dategrid) :
		 * - Set the width of day
		 * - Loop through years/months to build the grid
		 * - Make it (optionally) draggable
		 */
		var sliderDayDivWidth = this.options.dayWidth;

		l_iYear = this.options.lowerLimitYear;
		while(l_iYear <= this.options.upperLimitYear) {		
			l_oData = Date.parse('01-01-'+l_iYear);
			if(Date.isLeapYear(l_oData)) iDays = 366; else iDays = 365;  
			
			divWidth = sliderDayDivWidth*iDays;
			l_oDiv = new Element('div', {className : 'slideYear', style : 'width:'+(divWidth-1)+'px'}).update(l_iYear);
			iTotalDays = 0;
			(12).times(function(e) {
				monthDivWidth = Date.getDaysInMonth(l_oData.getFullYear(),l_oData.getMonth())*this.options.dayWidth;
				l_oMonthDiv = new Element('div', {className : 'slideMonth',style : 'width:'+(monthDivWidth)+'px; left:'+iTotalDays+'px'});
				if(e==0) { 
					$(l_oMonthDiv).addClassName('firstMonth');
				} else {
					$(l_oMonthDiv).update(l_oData.toString("MMM"));
				}
				l_oDiv.appendChild(l_oMonthDiv);
				iTotalDays += monthDivWidth;
				l_oData.addMonths(1);
			}.bind(this));
			$(this.barReference).appendChild(l_oDiv);
			l_iYear++;
		}
		
		/* Set the the right position and length */
		l_iCorrection = $(this.barReference).up().getWidth()/2;
		//l_shiftLeft = 0+this.barStartDate.getDiffDays(this.centerDate)*sliderDayDivWidth  +l_iCorrection;
		l_oFinishDate = Date.parse((this.options.upperLimitYear+1)+'-01-01');
		iBarWidth =this.barStartDate.getDiffDays(l_oFinishDate);
	    $(this.barReference).setStyle({left : -1*this.iLeftOffsetLH+'px', width : iBarWidth*sliderDayDivWidth+'px'}); 
		
		/* Make the background grid draggable */
	    if (this.options.dragBar) {
			  new Draggable($(this.barReference), {snap: this.sliderLimitPos,
			  							  constraint:'horizontal',
										  starteffect : '',
										  endeffect:'',
										  zindex:'0'});
		}
	},
	_bgStopDrag : function() {
		/* Move? */
		l_iDiff = this.l_oRightHandle.offsetLeft + (this.barReference.offsetLeft-600);
		
		if(l_iDiff > -2) {
		/* Move the bgbar */
		var l_iLeft = '-'+(this.l_oRightHandle.offsetLeft-590)+'px';
		new Effect.Morph(this.barReference, { style: {left: l_iLeft}, duration:this.options.duration});
		}	
		
		/* Call the callback function */
		if(this.options.onEnd){this.options.onEnd();}	    
  	},
	createHandles : function() {
		/* Create the left and the right handle */
		this.l_oLeftHandle = new Element('span', {className: 'leftHandle', style:'left:'+this.iLeftOffsetLH+'px'}).update('&nbsp;');		
		this.l_oRightHandle = new Element('span', {className: 'rightHandle', style:'left:'+this.iLeftOffsetRH+'px'}).update('&nbsp;');
		
		$(this.barReference).appendChild(this.l_oLeftHandle);
		$(this.barReference).appendChild(this.l_oRightHandle);

		  if(this.options.dragHandles) {
			  /* Make the left handler draggable */
			  new Draggable(this.l_oLeftHandle,  {
				 snap: function(x,y,drag){
					 inbox = drag.element.getDimensions();
					 outbox = Element.getDimensions(drag.element.parentNode);
					 maxPos = drag.element.hasClassName('leftHandle') ? parseInt(this.l_oRightHandle.style.left)-inbox.width : outbox.width - inbox.width;
					 minPos = drag.element.hasClassName('rightHandle') ? parseInt(this.l_oLeftHandle.style.left)+inbox.width : 0;
					 return [ x > maxPos ? maxPos : (x < minPos ? minPos : x), y];
			  		}.bind(this),
				 containment: this.barReference,
				 constraint:'horizontal',
				 onDrag :  function(e,ev){
			  			if(ev.type == "mouseup" && this.options.onEnd != null){this.options.onEnd();}
			  			l_panelLength = this.l_oRightHandle.offsetLeft - this.l_oLeftHandle.offsetLeft - 4;
			  			this.l_oShiftPanel.setStyle({left: (this.l_oLeftHandle.offsetLeft+4)+'px', width : l_panelLength+'px'});
			  			this._setDates();
			  		}.bind(this),
				 onEnd :  function(e,ev){
		  			if(ev.type == "mouseup" && this.options.onEnd != null){this.options.onEnd();}
		  			l_panelLength = this.l_oRightHandle.offsetLeft - this.l_oLeftHandle.offsetLeft - 4;
		  			this.l_oShiftPanel.setStyle({left: (this.l_oLeftHandle.offsetLeft+4)+'px', width : l_panelLength+'px'});
		  			this._setDates();
		  		}.bind(this)
				 
				 });
			 
			  /* Make the right handler draggable */								 
			  new Draggable(this.l_oRightHandle,  {
					 snap: function(x,y,drag){
						 inbox = drag.element.getDimensions();
						 outbox = Element.getDimensions(drag.element.parentNode);
						 maxPos = drag.element.hasClassName('leftHandle') ? parseInt(this.l_oRightHandle.style.left)-inbox.width : outbox.width - inbox.width;
						 minPos = drag.element.hasClassName('rightHandle') ? parseInt(this.l_oLeftHandle.style.left)+inbox.width : 0;
						 return [ x > maxPos ? maxPos : (x < minPos ? minPos : x), y];
				  		}.bind(this),
					containment: this.barReference,
					constraint:'horizontal',
					onDrag : function(e,ev){
			  			if(ev.type == "mouseup" && this.options.onEnd != null){this.options.onEnd();}
			  			l_panelLength = this.l_oRightHandle.offsetLeft - this.l_oLeftHandle.offsetLeft - 5;
			  			this.l_oShiftPanel.setStyle({width : (l_panelLength+2*this.sliderBarMargin)+'px'});
			  			this._setDates();
			  		}.bind(this),
					onEnd : function(e,ev){
			  			if(ev.type == "mouseup" && this.options.onEnd != null){this.options.onEnd();}
			  			l_panelLength = this.l_oRightHandle.offsetLeft - this.l_oLeftHandle.offsetLeft - 5;
			  			this.l_oShiftPanel.setStyle({width : (l_panelLength+2*this.sliderBarMargin)+'px'});
			  			this._setDates();
			  		}.bind(this)
					});		  	
		  } else {
		  	this.l_oLeftHandle.setStyle({opacity:.01,cursor:'pointer'});
			this.l_oRightHandle.setStyle({opacity:.01,cursor:'pointer'});
		  }		 
	},
	dragShiftPanel : function() { 
  		/* Set the handlers while dragging the shiftpanel */
  		this.l_oLeftHandle.setStyle({left: (this.l_oShiftPanel.offsetLeft-this.sliderBarMargin)+'px'});
  		this.l_oRightHandle.setStyle({left: (this.l_oShiftPanel.offsetLeft + this.l_oShiftPanel.offsetWidth-this.sliderBarMargin)+'px'});						
  		this._setDates();
	},
	createShiftPanel : function() {
		/* Calculate width */
		l_iBarWidth = (this.iLeftOffsetRH-this.iLeftOffsetLH)+(2*this.sliderBarMargin);

		this.l_oShiftPanel = new Element('div', {className:"shiftPanel", style:'left:'+(this.iLeftOffsetLH)+'px; width:'+l_iBarWidth+'px'});
		$(this.barReference).appendChild(this.l_oShiftPanel.setStyle({ opacity: 0.5 }));
		new Draggable(this.l_oShiftPanel, 
			{
				 snap: function(x,y,drag){
					 inbox = drag.element.getDimensions();
					 outbox = Element.getDimensions(drag.element.parentNode);
					 maxPos = drag.element.hasClassName('leftHandle') ?
						parseInt(this.l_oRightHandle.style.left)-inbox.width : outbox.width - inbox.width;
					 
					 minPos = drag.element.hasClassName('rightHandle') ?
					 parseInt(this.l_oLeftHandle.style.left)+inbox.width : 0;
					 return [ x > maxPos ? maxPos : (x < minPos ? minPos : x), y];
		  		}.bind(this),
				  constraint:'horizontal',
				  starteffect : '',
				  endeffect:'',
				  zindex:'0',
				  onEnd : this._bgStopDrag.bindAsEventListener(this),
				  onDrag:function() { 
						/* Set the handlers while dragging the shiftpanel */
						this.l_oLeftHandle.setStyle({left: (this.l_oShiftPanel.offsetLeft-this.sliderBarMargin)+'px'});
						this.l_oRightHandle.setStyle({left: (this.l_oShiftPanel.offsetLeft + this.l_oShiftPanel.offsetWidth-this.sliderBarMargin)+'px'});						
						this._setDates();
					}.bind(this),
				  onStart : function() {
				  		if(this.options.onStart) this.options.onStart();
				  }.bind(this)
			}
		); 
	},
	sliderLimitPos: function(x, y, drag)
		{
		 inbox=drag.element.getDimensions();
		 outbox=Element.getDimensions(drag.element.parentNode);
		 return [x > 0 ? 0 : (x > outbox.width - inbox.width ? x : outbox.width - inbox.width), y];
		},
	_setDates : function() {
		/* Get the position of the handles */
		l_iLeftPos = this.l_oLeftHandle.offsetLeft/this.options.dayWidth;
		l_iRightPos = this.l_oRightHandle.offsetLeft/this.options.dayWidth;
		
		l_oDate = this.barStartDate.clone().addDays(l_iLeftPos);
		if (this.numberOfDays == null) {
		  l_oDate2 = this.barStartDate.clone().addDays(l_iRightPos);
		} else {
		  l_oDate2 = l_oDate.clone().addDays(this.numberOfDays);
		} 
	
		if(this.oStartField && this.oEndField) {
			this.oStartField.setValue(l_oDate.toString(this.options.dateFormat));		
			this.oEndField.setValue(l_oDate2.toString(this.options.dateFormat));		
		}	
	},
	morphTo : function (p_oDateStart, p_oDateEnd) {
		l_offsetLeftLH = this.barStartDate.getDiffDays(p_oDateStart)*this.options.dayWidth;
		l_offsetLeftRH = this.barStartDate.getDiffDays(p_oDateEnd)*this.options.dayWidth;
		l_panelLength = l_offsetLeftRH - l_offsetLeftLH  - 4;
		this.l_oLeftHandle.morph('left:'+l_offsetLeftLH+'px',{duration:this.options.duration});
		this.l_oRightHandle.morph('left:'+l_offsetLeftRH+'px',{duration:this.options.duration});
		this.l_oShiftPanel.morph('width : '+(l_panelLength+2*this.sliderBarMargin)+'px; left : '+(l_offsetLeftLH+2)+'px',{duration:this.options.duration});
	},
	attachFields : function () {
		this.oStartField = this.options.inputStartField;
		this.oEndField = this.options.inputEndField;
		
		this.oStartField.setValue(this.oStartDate.toString(this.options.dateFormat));
		this.oEndField.setValue(this.oEndDate.toString(this.options.dateFormat));
		
		[this.oStartField, this.oEndField].each(function(e) {
			e.observe('blur', function() {
				l_oStartDate = Date.parse(this.oStartField.getValue());
				l_oEndDate = Date.parse(this.oEndField.getValue());
				if(l_oStartDate!==null && l_oEndDate!==null){
					this.morphTo(l_oStartDate, l_oEndDate);
				}else{
					// can't parse Dates, replace input values with proper values
					this._setDates();
				}
			}.bind(this)); // end observe
		}.bind(this)); // end each
		
		if(this.options.appearOnFocus){
			[this.oStartField, this.oEndField].each(function(e) {
				e.observe('focus', function() {
					if(!this.wrapperReference.visible()){
						this.wrapperReference.appear({duration:this.options.duration});
					}
				}.bind(this)); // end observe
			}.bind(this)); // end each
		}
		

		
	},
	_removeSliderBar : function() {
		$(this.barReference).update('');
	},
	findStart: function(){
		this._setSliderPanel(this.l_oLeftHandle);
	},
	findEnd: function(){
		this._setSliderPanel(this.l_oRightHandle);
	},
	_setSliderPanel: function(handleObject){
		var leftValue = parseInt(handleObject.getStyle('left'),10);
		if(leftValue+parseInt(handleObject.getWidth())===this.barReference.getWidth()){
			leftValue = this.barReference.getWidth() - this.barReference.up().getWidth();
		}
		new Effect.Morph(this.barReference,{style:"left:"+-1*leftValue+"px;", duration:this.options.duration});
	},
	zoomIn : function() {
		this._zoom(1);
	},
	zoomOut : function() {
		this._zoom(-1);	
	},
	_zoom : function(p_iFactor) {
		if((this.options.dayWidth+p_iFactor) < 1) return;
		/* Get the current dates */
		l_iLeftPos = this.l_oLeftHandle.offsetLeft/this.options.dayWidth;
		l_iRightPos = this.l_oRightHandle.offsetLeft/this.options.dayWidth;
		
		l_oDateStart = this.barStartDate.clone().addDays(l_iLeftPos);
		l_oDateEnd	 = this.barStartDate.clone().addDays(l_iRightPos);
				
		this._removeSliderBar();
		this.options.dayWidth = this.options.dayWidth+p_iFactor;
		
		this.iLeftOffsetLH = this.barStartDate.getDiffDays(l_oDateStart)*this.options.dayWidth;
		this.iLeftOffsetRH = this.barStartDate.getDiffDays(l_oDateEnd)*this.options.dayWidth;	
				
		this.createSliderBar(this.barReference);
		this.createHandles(this.barReference, l_oDateStart, l_oDateEnd);
		
		this.createShiftPanel(this.barReference, l_oDateStart, l_oDateEnd);
		this.centerBar();
	},
	addCloseButton: function(){
		var closeButton = new Element("a",{href:"#",title:"Close slider",className:"close"}).update("close")
			.observe("click",function(e){
				e.stop();
				this.wrapperReference.fade({duration:this.options.duration});
			}.bind(this));
		$(this.barReference).up().insert({after:closeButton});
	},
	setHandleFinder: function(){
		var l_oFindStart = new Element("li",{className : 'findStart'}).update(new Element('a', {href:'#',title:"Find start handle"})
		   .update('find start')
		   .observe('click', function(ev) {
		   		this.findStart();
		   		ev.stop();
		   }.bind(this)));

		var l_oFindEnd = new Element("li",{className : 'findEnd'}).update(new Element('a', {href:'#',title:"Find end handle"})
		   .update('find end')
		   .observe('click', function(ev) {
		   		this.findEnd();
		   		ev.stop();
		   }.bind(this)));
		var l_oHandleFinderPanel = new Element("div",{className:'handleFinderPanel'}).update(new Element("ul").insert({bottom:l_oFindStart}).insert({bottom:l_oFindEnd}));
		$(this.barReference).up().insert({after:l_oHandleFinderPanel});
	},
	setZoom : function() {
		var l_oZoomIn = new Element("li",{className : 'zoomIn'}).update(new Element('a', {href:'#',title:"Zoom in"})
							   .update('zoom in')
							   .observe('click', function(ev) {
							   		this.zoomIn();
							   		ev.stop();
							   }.bind(this)));
		
		var l_oZoomOut = new Element("li",{className : 'zoomOut'}).update(new Element('a', {href:'#',title:"Zoom out"})
							   .update('zoom out')
							   .observe('click', function(ev) {
							   		this.zoomOut();
							   		ev.stop();
							   }.bind(this)));
		
		
		var l_oZoomPanel = new Element('div', {className : 'zoomPanel'}).update(new Element("ul").insert({bottom:l_oZoomIn}).insert({bottom:l_oZoomOut}));
		
		$(this.barReference).up().insert({after:l_oZoomPanel});
	},
	centerBar : function() {
	
		var l_iPanelWidth = this.iLeftOffsetRH-this.iLeftOffsetLH;
		var l_iShiftContainerWidth = this.barReference.up().getWidth();
		
		this.barReference.setStyle({left:(this.iLeftOffsetLH-(2*this.iLeftOffsetLH)+(l_iShiftContainerWidth/2)-(l_iPanelWidth/2))+'px'});
	}
});
