/*  Scal - prototype calendar/date picker
 *   - Jamie Grove
 *   - Ian Tyndall
 *
 *  Scal is freely distributable under the terms of an MIT-style license.
 *  For details, see the Scal web site: http://scal.fieldguidetoprogrammers.com
 *
 *--------------------------------------------------------------------------*/
var scal_lang;
document.observe("dom:loaded", function() {
	scal_lang = new CSLang('scal_js');
});



Object.extend(Date.prototype, {
    monthnames: ['January','February','March','April','May','June','July','August','September','October','November','December'],
    daynames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
    succ: function(){
        // Fix problem with daylight saving change in March in Kiev timezone:
        var sd = new Date(this.getTime());
        sd.setDate(sd.getDate() + 1);
        return sd;
    },
    firstofmonth: function(){
        return new Date(this.getFullYear(),this.getMonth(),1);
    },
    lastofmonth: function(){
        return new Date(this.getFullYear(),this.getMonth()+1,0);
    },
    formatPadding: true,
    format: function(f){
        if (!this.valueOf()) { return '&nbsp;'; }
        var d = this;
        var formats = {
            'yyyy' : d.getFullYear(),
            'mmmm': scal_lang.translate(this.monthnames[d.getMonth()]),
            'mmm':  scal_lang.translate(this.monthnames[d.getMonth()].substr(0, 3)),
            'mm':   this.formatPadding ? ((d.getMonth()).succ()).toPaddedString(2) : (d.getMonth()).succ(),
            'dddd': scal_lang.translate(this.daynames[d.getDay()]),
            'ddd':  scal_lang.translate(this.daynames[d.getDay()]).substr(0, 3),
            'dd':   d.getDate().toPaddedString(2),
			'hh':   this.formatPadding ? (d.getHours() % 12 ? d.getHours() % 12: 12).toPaddedString(2) : d.getHours() % 12 ? d.getHours() % 12: 12,
            'tt':   this.formatPadding ? (d.getHours()).toPaddedString(2) : d.getHours(),
			'nn':   this.formatPadding ? (d.getMinutes()).toPaddedString(2) : d.getMinutes(),
            'ss':   this.formatPadding ? (d.getSeconds()).toPaddedString(2) : d.getSeconds(),
            'a/p':  d.getHours() < 12 ? 'a' : 'p',
			'am/pm': d.getHours() < 12 ? 'am' : 'pm'
        };
        return f.gsub(/(yyyy|mmmm|mmm|mm|dddd|ddd|dd|hh|tt|nn|ss|a\/p|am\/pm)/i,
            function(match) { return formats[match[0].toLowerCase()]; });
    }
});

var scal = {};
scal = Class.create();
scal.prototype = {
    initialize: function(element,update) {
        this.element = $(element);
        var type = Try.these(
            function(){ if(!Object.isUndefined(Effect)) { return 'Effect'; }},
            function(){ return 'Element'; }
        );
        this.options = Object.extend({
          oncalchange: Prototype.emptyFunction,
          daypadding: false,
          titleformat: 'mmmm yyyy',
          updateformat: common_localized_properties.getProperty('date_format', 'dd/mm/yyyy'),
          closebutton: 'X',
          prevbutton: '&laquo;',
          nextbutton: '&raquo;',
          yearnext: '&raquo;&raquo;',
          yearprev: '&laquo;&laquo;',
          openeffect: type == 'Effect' ? Effect.Appear : Element.show,
          closeeffect: type == 'Effect' ? Effect.Fade : Element.hide,
          exactweeks: false,
          dayheadlength: 2,
          weekdaystart: 0,
          planner: false,
          tabular: false,
		  year_links: false // year links to navigate through years
        }, arguments[2] || { });
        this.table = false;
        this.thead = false;
        this.startdate = this._setStartDate(arguments[2]);
        if(this.options.planner) { this._setupPlanner(this.options.planner); }
        if(this.options.tabular) {
            this.table = new Element('table',{'class': 'cal_table',border: 0,cellspacing: 0,cellpadding: 0});
            this.thead = new Element('thead');
            this.table.insert(this.thead);
            this.element.insert(this.table);
        }
        this.updateelement = update;
        this._setCurrentDate(this.startdate);
        this.initDate = new Date(this.currentdate);
        this.controls = this._buildControls();
        this.title.setAttribute('title', this.initDate.format(this.options.titleformat));
        this._updateTitles();
        this[this.table ? 'thead' : 'element'].insert(this.controls);
        this.cal_wrapper = this._buildHead();
        this.cells = [];
        this._buildCal();
    },
/*------------------------------- INTERNAL -------------------------------*/
    _setStartDate: function() {
	var args = arguments[0];
        var startday = new Date();
        this.options.month = args && args.month && Object.isNumber(args.month) ? args.month - 1 : startday.getMonth();
        this.options.year = args && args.year && Object.isNumber(args.year) ? args.year : startday.getFullYear();
        this.options.day = args && args.day && Object.isNumber(args.day) ? args.day : (this.options.month != startday.getMonth()) ? 1 : startday.getDate();
        startday.setHours(0,0,0,0);
        startday.setDate(this.options.day);
        startday.setMonth(this.options.month);
        startday.setFullYear(this.options.year);
        return startday;
    },
    _emptyCells: function() {
        if(this.cells.size() > 0) {
          this._stopCellObserving();
          this.cells[0].parentNode.innerHTML = "";
          this.cells = [];
        }
    },

    _stopCellObserving: function() {
      this.cells.each(function(cell) {
        cell.stopObserving('click', cell.bound_click);
        cell.bound_click = null;
      });
    },

    _buildCal: function() {
        this._emptyCells();
        if(!(Object.isUndefined(this.cal_weeks_wrapper) || this.table)) { this.cal_weeks_wrapper.remove(); }
        this.cal_weeks_wrapper = this._buildWrapper();
        if(this.table) {
            this.table.select('tbody tr.weekbox:not(.weekboxname)').invoke('remove');
            this.table.select('tbody.cal_wrapper').invoke('remove');
            this.cal_weeks_wrapper.each(function(row){
                this.cal_wrapper.insert(row);
            }.bind(this));
        } else {
            this.cal_wrapper.insert(this.cal_weeks_wrapper);
        }
        this[this.table ? 'table' : 'element'].insert(this.cal_wrapper);
    },
    _click: function(event,cellIndex) {
        this.element.select('.dayselected').invoke('removeClassName', 'dayselected');
        (event.target.hasClassName('daybox') ? event.target : event.target.up()).addClassName('dayselected');
        this._setCurrentDate(this.dateRange[cellIndex]);
        this._updateExternal();
    },
    _updateExternal: function(){
        if (Object.isFunction(this.updateelement)){
            this.updateelement(this.currentdate);
        } else {
            var updateElement = $(this.updateelement);
            updateElement[updateElement.tagName == 'INPUT' ? 'setValue' : 'update'](this.currentdate.format(this.options.updateformat));
        }
    },
    _buildHead: function() {
        var cal_wrapper = new Element(this.table ? 'tbody' : 'div');
        cal_wrapper.addClassName("cal_wrapper");

        var weekbox = new Element(this.table ? 'tr' : 'div');
        weekbox.addClassName("weekboxname");
        weekbox.addClassName("weekbox");
        Date.prototype.daynames.sortBy(function(s,i){
            i-=this.options.weekdaystart;
            if(i<0){i+=7;}
            return i;
        }.bind(this)).each(function(day,i) {
        var cell = new Element(this.table ? 'td' : 'div');
        cell.addClassName('cal_day_name_'+ i);
        cell.addClassName('daybox').addClassName('dayboxname');
        cell.update(day.substr(0,this.options.dayheadlength));
        if(i == 6) { cell.addClassName('endweek'); }
        weekbox.insert(cell);
        }.bind(this));
        return cal_wrapper.insert(weekbox);
    },
    _buildWrapper: function() {
        var firstdaycal = new Date(this.firstofmonth.getFullYear(),this.firstofmonth.getMonth(),this.firstofmonth.getDate());
        var lastdaycal = new Date(this.lastofmonth.getFullYear(),this.lastofmonth.getMonth(),this.lastofmonth.getDate());
		if(this.options.weekdaystart-firstdaycal.getDay() < firstdaycal.getDate()){
        firstdaycal.setDate(firstdaycal.getDate() - firstdaycal.getDay() + this.options.weekdaystart);
        } else {
		firstdaycal.setDate(firstdaycal.getDate() - (firstdaycal.getDay() + 7 - this.options.weekdaystart));
		}
        var dateRange = $A($R(firstdaycal,lastdaycal));
        var cal_weeks_wrapper = this.table ? [] : new Element('div',{'class': 'calweekswrapper'});
        var wk;
        var row;
        var lastday;
        this.dateRange = [];
        this.indicators = []; // holds values to determine if continued checking for custom classes is needed
        var buildWeek = function(day) {
            row.insert(this._buildDay(wk, day));
            lastday = day;
        }.bind(this);
        dateRange.eachSlice(7, function(slice,i) {
            wk = i;
            row = new Element(this.table ? 'tr' : 'div',{'class':'cal_week_' + wk}).addClassName('weekbox');
            while(slice.length < 7) {
                slice.push(slice.last().succ());
            }
            slice.map(buildWeek);
            cal_weeks_wrapper[this.table ? 'push' : 'insert'](row);
        }.bind(this));
        if(!this.options.exactweeks) {
            var toFinish = 42 - this.cells.size();
            var wkstoFinish = Math.ceil(toFinish / 7);
            if(wkstoFinish > 0) { toFinish = toFinish / wkstoFinish; }
            $R(1,wkstoFinish).each(function(w){
                wk += 1;
                row = new Element(this.table ? 'tr' : 'div',{'class':'cal_week_' + wk}).addClassName('weekbox');
                $R(1,toFinish).each(function(i) {
                    var d = lastday.succ();
                    row.insert(this._buildDay(wk, d));
                    cal_weeks_wrapper[this.table ? 'push' : 'insert'](row);
                    lastday = d;
                }.bind(this));
            }.bind(this));
        }
        return cal_weeks_wrapper;
    },
    _compareDates: function(date1,date2,type){
        return (this.indicators.indexOf(type) >= 0) ? false : Object.isUndefined(['getMonth','getDate','getFullYear'].find(function(n){ return date1[n]() != date2[n](); }));
    },
    _buildDay: function(week,day){
        this.dateRange.push(day);
        var cellid = 'cal_day_' + week + '_' + day.getDay();
        var cell = new Element(this.table ? 'td' : 'div',{'class':cellid});
        var celldate = new Element('div',{'class':cellid+'_date'}).addClassName('dayboxdate').update(this.options.daypadding ? ((day.getDate()).toPaddedString(2)) : day.getDate());
        var cellvalue = new Element('div',{'class':cellid+'_value'}).addClassName('dayboxvalue');
        if(this.options.planner) { this._updatePlanner(day,cellvalue); }
        cell.insert(celldate).insert(cellvalue).addClassName('daybox').addClassName('daybox'+ day.format('dddd').toLowerCase());
        // if we are on the currently selected date, set the class to dayselected (i.e. highlight it).
        if(this._compareDates(day,this.currentdate,'dayselected')) {
            cell.addClassName('dayselected');
            this.indicators.push('dayselected');
        }
        if(this._compareDates(day,new Date(),'today')) {
            cell.addClassName('today');
            this.indicators.push('today');
        }
        if(day.getDay() == 6) { cell.addClassName('endweek'); }
        // if we are outside the current month set the day style to 'deactivated'
        var cs = day.getMonth() != this.currentdate.getMonth() ? ['dayoutmonth','dayinmonth'] : ['dayinmonth','dayoutmonth'];
        cell.addClassName(cs[0]);
        if(cell.hasClassName(cs[1])) { cell.removeClassName(cs[1]); }
        this.cells.push(cell);
        cell.bound_click = this._click.bindAsEventListener(this, this.cells.size() - 1);
        return cell.observe('click', cell.bound_click);
    },
    _updateTitles: function() {
        var yr = this.currentdate.getFullYear();
        var mnth = this.currentdate.getMonth();
        var titles = {
            calprevmonth: Date.prototype.monthnames[(mnth - 1) == -1 ? 11 : mnth - 1],
            calprevyear: yr - 1,
            calnextyear: yr + 1,
            calnextmonth: Date.prototype.monthnames[(mnth + 1) == 12 ? 0 : mnth + 1]
        };
        this.controls.select('.calcontrol').each(function(ctrl) {
           var title = titles[ctrl.className.split(' ')[0]];
           if(!Object.isUndefined(title)) { ctrl.setAttribute('title',title); }
        });
    },
    _buildControls: function() {
        var hParts = [
            {p: 'calclose', u: this.options.closebutton, f:  this.toggleCalendar.bindAsEventListener(this)},
			{p: 'calprevyear', u: this.options.yearprev, f: this._switchCal.bindAsEventListener(this,'yeardown')},
            {p: 'calprevmonth', u: this.options.prevbutton, f: this._switchCal.bindAsEventListener(this,'monthdown')},
            {p: 'calnextyear', u: this.options.yearnext, f: this._switchCal.bindAsEventListener(this,'yearup')},
            {p: 'calnextmonth', u: this.options.nextbutton, f: this._switchCal.bindAsEventListener(this,'monthup')},
            {p: 'caltitle', u: this.currentdate.format(this.options.titleformat), f: this._switchCal.bindAsEventListener(this,'init')}
        ];
        if(this.table) { hParts = [hParts[1],hParts[2],hParts[5],hParts[3],hParts[4],hParts[0]]; }

		// remove year links if they are disable
		if( this.options.year_links === false )
		{
			if(this.table)
			{
				hParts = [hParts[1],hParts[2],hParts[4],hParts[5]];
			}
			else
			{
				hParts = [hParts[0],hParts[2],hParts[4],hParts[5]];
			}
		}

        var cal_header = new Element(this.table ? 'tr' : 'div');
        cal_header.addClassName('calheader');
        hParts.each(function(part) {
            var el = new Element(this.table ? 'td' : 'div');
            el.addClassName(part.p);

            if(part.p == 'caltitle') {
                this.title = el;
                if(this.table) { el.writeAttribute({colspan: 2}); }
                el.update(part.u);
            } else {
                el.addClassName('calcontrol');
                el[typeof(part.u) == 'object' ? 'insert' : 'update'](part.u);
            }
            el.bound_click = part.f;
            el.observe('click', el.bound_click);

            cal_header.insert(el);
        }.bind(this));
        return cal_header;
    },
    _switchCal: function(){
        if(arguments[1]) {
            var event = arguments[0];
            var direction = arguments[1];
            event.date = this.currentdate;
        } else {
            var direction = arguments[0];
        }
        var params = {f: 'setTime', p: this.initDate.getTime()};
        var sday = this.currentdate.getDate();
        if(direction != 'init') {
            var d = this.currentdate[direction.include('month') ? 'getMonth' : 'getFullYear']();
            params = {f: direction.include('month') ? 'setMonth' : 'setYear', p: direction.include('up') ? d + 1 : d - 1};
        }
        this.currentdate[params.f](params.p);
        if (this.currentdate.getDate() != sday){
            this.currentdate.setDate(0);
        }
        if(arguments[1]) { this.options.oncalchange(event); }
        this._update();
    },
    _update: function() {
        this._setCurrentDate(arguments[0] ? arguments[0] : this.currentdate);
        this.title.update(this.currentdate.format(this.options.titleformat));
        this._buildCal();
        this._updateTitles();
    },
    _setCurrentDate: function(date){
        this.currentdate = date ; //new Date(date.getFullYear(),date.getMonth(),date.getDate());
        this.firstofmonth = this.currentdate.firstofmonth();
        this.lastofmonth = this.currentdate.lastofmonth();
    },
    _getCellIndexByDate: function(d) {
        var findDate = d.getTime();
        var cellIndex = 0;
        this.dateRange.each(function(dt,i) {
            if(dt.getTime() == findDate) {
                cellIndex = i;
                throw $break;
            }
        });
        return cellIndex;
    },
/*------------------------------- PUBLIC -------------------------------*/
  destroy: function() {
    this._emptyCells();
    if (this.table) {
      this.table.remove();
    } else {
      this.cal_weeks_wrapper.remove();
    }

    this._stopObservingControls();

    [this.cal_wrapper,this.controls].invoke('remove');
  },

  _stopObservingControls: function() {
    this.controls.descendants().each(function(el) {
      if (el.bound_click) {
        el.stopObserving('click', el.bound_click);
        el.bound_click = null;
      }
    });
  },

  setCurrentDate: function(direction){
        this[(direction instanceof Date) ? '_update' : '_switchCal'](direction);
        if(!arguments[1]) { this._updateExternal(); }
        return this.currentdate;
    },
    toggleCalendar: function(){
        this.options[this.element.visible() ? 'closeeffect' : 'openeffect'](this.element, {duration: 0.5});
    },
    getElementByDate: function(d) {
        return this.cells[this._getCellIndexByDate(d)];
    },
    getElementsByWeek: function(week) {
        return this.element.select('.weekbox:nth-of-type(' + (week + 1) + ') .daybox:not(.dayboxname)');
    },
    getSelectedElement: function() {
        return this.element.select('.dayselected')[0];
    },
    getTodaysElement: function() {
        return this.element.select('.today')[0];
    },
    getDateByElement: function(element) {
        return this.dateRange[this.cells.indexOf(element)];
    },
/*------------------------------- PLUG-IN PLACEHOLDERS -------------------------------*/
    _setupPlanner: Prototype.emptyFunction,
    _updatePlanner: Prototype.emptyFunction,
/*------------------------------- DEPRECATED -------------------------------*/
    openCalendar: function(){
        if(!this.isOpen()){ this.toggleCalendar(); }
    },
    closeCalendar: function(){
        if(this.isOpen()){ this.toggleCalendar(); }
    },
    isOpen: function(){
        return this.element.visible();
    },

	getSelectedDate: function( updateelement ){
		var start_day_index = this.options.updateformat.indexOf('dd');
		var start_month_index = this.options.updateformat.indexOf('mm');
		var start_year_index = this.options.updateformat.indexOf('yyyy');
		var year = updateelement.substr(start_year_index, 4);
		var month = updateelement.substr(start_month_index, 2) - 1; // the month is 0 based offset. 0=jan, 11=dec
		var day = updateelement.substr(start_day_index, 2);

		if( updateelement.length > 11 ){

			var start_hour_index = this.options.updateformat.indexOf('hh');
			if( start_hour_index < 0){
				start_hour_index = this.options.updateformat.indexOf('tt');
			}
			var hour = updateelement.substr(start_hour_index, 2);
			var start_minute_index = this.options.updateformat.indexOf('nn');
			var minute = updateelement.substr(start_minute_index, 2);
			var start_am_pm_index = this.options.updateformat.indexOf('am');
			if( start_am_pm_index >= 0 ){
				var am_pm = updateelement.substr(start_am_pm_index, 2);
				if ( am_pm == 'pm' ){
					var int_hour = parseInt(hour,10);
					int_hour += 12;
					hour = int_hour.toString();
				}
				if( hour == '12' && am_pm == 'am' ){
					hour = '00';
				}
				if( hour == '24' && am_pm == 'pm' ){
					hour = '12';
				}
			}


		} else {
			var hour = '12';
			var minute = '00';
		}

		var date = new Date(year, month, day, hour, minute);

		if(updateelement.strip().empty()
			|| isNaN(date)){
			current_date = new Date();
			date = new Date( current_date.getFullYear(), current_date.getMonth(), current_date.getDate(), '12', '00');
		}
		return date;
	}
};



/* ------------------------------- CUSTOMIZED --------------------------------*/
//Extend the scal library to add draggable calendar support.
//This script block can be added to the scal.js file.
Object.extend(scal.prototype,
{
	toggleCalendar: function()
	{
		var element = $(this.options.wrapper) || this.element;
		var wasOriginallyOpen = element.visible();
		this.options[wasOriginallyOpen ? 'closeeffect' : 'openeffect'](element, {duration: 0.5});
		this.options[wasOriginallyOpen ? 'onclose' : 'onopen'](this);
	},

	isOpen: function()
	{
		return ( $(this.options.wrapper) || this.element).visible();
	}
});

/* stime Time picker, built to fit right in with the scal date picker. */
var stime = {};
stime = Class.create();
stime.prototype = {
	initialize: function (element, update) {
		this.element = $(element);
		this.updateelement = update;
		this.options = Object.extend({
			twentyfour_hour: false,
			updateformat: 'hh:nn am/pm'
		}, arguments[2] || {});

		this.options.updateformat = this.options.twentyfour_hour ? 'tt:nn' : 'hh:nn am/pm';

		this.currenttime = this._setStartTime(arguments[2]);
		this._generateControls();
	},
	
	/*---------------------- PRIVATE METHODS ----------------------*/
	_setStartTime: function() {
		var args = arguments[0];
		var starttime = new Date();
		if (this.options.twentyfour_hour){
			this.options.hour = args && args.hour && Object.isNumber(args.hour) ? args.hour : starttime.getHours();
		} else {
			var am_hrs = starttime.getHours() % 12;
			this.options.hour = args && typeof( args['hour'] ) && Object.isNumber(args.hour) ? args.hour : (am_hrs ? am_hrs : 12);
			this.options.am_pm = args && args.am_pm && (args.am_pm.toLowerCase() == 'am' || args.am_pm.toLowerCase() == 'pm') ? args.am_pm : (starttime.getHours() < 12 ? 'am' : 'pm');
		}
		this.options.minute = args && typeof( args['minute'] ) && Object.isNumber(args.minute) ? args.minute : starttime.getMinutes();
        
        starttime.setDate(0);
        starttime.setMonth(0);
        starttime.setFullYear(0);

		if (this.options.twentyfour_hour){
			starttime.setHours(this.options.hour, this.options.minute)
		} else {
			starttime.setHours((this.options.am_pm == 'pm' && this.options.hour != 12) ? this.options.hour + 12 : this.options.hour, this.options.minute)
		}
        return starttime;
	},
	
	_generateControls: function () {
		// Generate the hours select box:
		var max_hours = (this.options.twentyfour_hour) ? 23 : 12;
		var start_hour = ( this.options.twentyfour_hour ) ? 1 : 0;

		var select_hour = (this.options.twentyfour_hour) ? this.options.hour : ( this.options.hour % 12 ? this.options.hour % 12 : 12 );
		this.hour_select = new Element('select', {'class': 'hour time_select', 'name': 'hour_select'});
		
		for (var h = start_hour; h <= max_hours; h++) {
			if (select_hour == h){
				var option = new Element('option', {'value': h, 'selected': 'selected'});
			} else {
				var option = new Element('option', {'value': h});
			}
			option.insert(h.toPaddedString(2));
			this.hour_select.insert(option);
		}
		this.element.insert(this.hour_select);
		this.hour_select.observe('change', this._change.bindAsEventListener(this, 'hour'));
		
		// Generate the minutes select box:
		this.minute_select = new Element('select', {'class': 'minute time_select', 'name': 'minute_select'});
		for (var m = 0; m < 60; m++) {
			if (this.options.minute == m) {
				var option = new Element('option', {'value': m, 'selected': 'selected'});
			} else {
				var option = new Element('option', {'value': m});
			}
			option.insert(m.toPaddedString(2));
			this.minute_select.insert(option);
		}
		this.element.insert(this.minute_select);
		this.minute_select.observe('change', this._change.bindAsEventListener(this, 'minute'));
		
		// Generate the AM / PM box, if not using 24 hours.
		if (!this.options.twentyfour_hour) {
			this.am_pm_select = new Element('select', {'class': 'am time_select', 'name': 'am_pm_select'});
			if (this.options.am_pm == 'am') {
				var am_option = new Element('option', {'value': 'am', 'selected': 'selected'});
				var pm_option = new Element('option', {'value': 'pm'});
			} else {
				var am_option = new Element('option', {'value': 'am'});
				var pm_option = new Element('option', {'value': 'pm', 'selected': 'selected'});
			}
			am_option.insert('AM');
			pm_option.insert('PM');
			this.am_pm_select.insert(am_option);
			this.am_pm_select.insert(pm_option);
			this.element.insert(this.am_pm_select);
			this.am_pm_select.observe('change', this._change.bindAsEventListener(this, 'am_pm'));
		}
		
	},
	
	_buildTime: function() {
		this.element.update(); // Clear element.
  		this._generateControls();
    },

	_change: function(event, control) {
		if (control == 'hour') {
			var val = event.target.value;
			if ( !this.options.twentyfour_hour && this.options.am_pm == 'pm') {
				var int_val = parseInt(val);
				int_val += 12;
				val = int_val.toString();
			} else if (this.options.am_pm == 'am' && val == 12) {
				val = 0;
			}
			this.currenttime.setHours(val);
			this._setCurrentTime(this.currenttime);
			this._updateExternal();
		} else if (control == 'minute') {
			this.currenttime.setMinutes(event.target.value);
			this._setCurrentTime(this.currenttime);
			this._updateExternal();
		} else if (control == 'am_pm') {
			var val = event.target.value;
			if (val == 'am' && this.options.hour >= 12) {
				this.currenttime.setHours(this.options.hour - 12);
			} else if (val == 'pm' && this.options.hour <= 12) {
				this.currenttime.setHours(this.options.hour + 12)
			}
			this._setCurrentTime(this.currenttime);
			this._updateExternal();
		}
	},
	
	_update: function() {
        this._setCurrentTime(arguments[0] ? arguments[0] : this.currenttime);
        this._buildTime();
    },

	_setCurrentTime: function(time){
        this.currenttime = new Date();
		this.currenttime.setHours(time.getHours(), time.getMinutes());
		
		this.options.hour = this.currenttime.getHours();
		this.options.minute = this.currenttime.getMinutes();
		this.options.am_pm = (!this.options.twentyfour_hour) ? (this.currenttime.getHours() < 12 ? 'am' : 'pm') : '';
    },

	_updateExternal: function(){	
        if (Object.isFunction(this.updateelement)){
            this.updateelement(this.currenttime);
        } else {	
            var updateElement = $(this.updateelement);
            updateElement[updateElement.tagName == 'INPUT' ? 'setValue' : 'update'](this.currenttime.format(this.options.updateformat));
        }            
    },
	
	/*---------------------- PUBLIC METHODS -----------------------*/
	
	
	setCurrentTime: function(direction){
        this[(direction instanceof Date) ? '_update' : '_switchCal'](direction);
        //if(!arguments[1]) { this._updateExternal(); }
        return this.currentdate; 
    }
}


var CalendarUtil = {
	calendar: null,
	scal_calendar_container: null,
	showCalendar: function(targetInputId, event, renderTo, year_links) {
		var source = event.element();
		
		// Remove the previous scal_calendar_container. If there is a date time picker (DateTimeUtil) and a time picker (CalendarUtil) both on the same page
		// and we DON'T remove this then we wind up with one of each after both icons are clicked, not good.
		if ($('scal_calendar_container')) {
			$('scal_calendar_container').remove();
		}
		
		if(!$('scal_calendar_container')){ // the calendar html isnt in the DOM yet.
			this.scal_calendar_container = Builder.node("div", {id: "scal_calendar_container", style:"width:221px; position:absolute; display:none; z-index:10001; right: -200px"}, [
				Builder.node("b", {id: "scal_top_bar", className: "rtop"}, [
					Builder.node("b", {className: "r1"}),
					Builder.node("b", {className: "r2"}),
					Builder.node("b", {className: "r3"}),
					Builder.node("b", {className: "r4"})
				]),
				Builder.node("div", {id: "scal_dates", className: "floating"})
			]);

			if(renderTo && $(renderTo)){ // renders to a specified element id.
				$(renderTo).insert({top: this.scal_calendar_container});
			} else{ // renders to the parent html tag
				var body_tst = Element.extend(document.body);
				body_tst.insert({top: this.scal_calendar_container});
			}

			this.calendar = null; // resets the calendar instance
		}
		//		container = $(container);
		var element = $('scal_dates');
		var container = $('scal_calendar_container');
		if(!this.calendar){
			//the Draggable handle is hard coded to "rtop" to avoid other parameter.
			new Draggable(container, {handle: "rtop", starteffect: Prototype.emptyFunction, endeffect: Prototype.emptyFunction});

			//The singleton calendar is created.

			var options = {
				openeffect: Element.show,
				closeeffect: Element.hide,
				updateformat: common_localized_properties.getProperty('date_format', 'dd/mm/yyyy'),
				closebutton: '&nbsp;',
				wrapper: container,
				onclose: function(theCalendar){
					Event.stopObserving(document, 'click', cached_window_click_handler);
				},
				onopen: function(){
					Event.observe(document, 'click', cached_window_click_handler);
				}
			};

			if ((typeof year_links != 'undefined'))
			{
				Object.extend(options, {year_links: year_links});
			}

			this.calendar = new scal(element, $(targetInputId),options);

			var cached_window_click_handler = function(e){ // this allow us to stopObserving it later on.
				var targ;
				targ = Event.element(e);
				
				if (!targ.hasClassName('scal_trigger_class')) {
					if (targ.hasClassName('dayboxdate')) {
						// all this simulate, blur, focus is to make formfactory happy and clear-out any error message that's been shown
						this.updateelement.simulate('change');
						this.updateelement.focus();
						this.updateelement.blur();
						this.closeCalendar();
					} else {
						var close_flag = true;
						var ancestors = targ.ancestors();
						ancestors.each(function(element) {
							if (element.identify() == 'scal_calendar_container') {
								close_flag = false;
							}
						});

						if (close_flag) {
							this.closeCalendar();
						}
					}
				}
			}.bind(this.calendar);
		} else {
			this.calendar.updateelement = $(targetInputId);
		}

		// sets the calendar widget to the date in the input field
		var start_day_index = this.calendar.options.updateformat.indexOf('dd');
		var start_month_index = this.calendar.options.updateformat.indexOf('mm');
		var start_year_index = this.calendar.options.updateformat.indexOf('yyyy');
		var year = $F(targetInputId).substr(start_year_index, 4);
		var month = $F(targetInputId).substr(start_month_index, 2) - 1; // the month is 0 based offset. 0=jan, 11=dec
		var day = $F(targetInputId).substr(start_day_index, 2);

		var date = new Date(year, month, day);
		if($F(targetInputId).strip().empty() || isNaN(date)){
			date = new Date();
		}
		this.calendar.setCurrentDate(date);
		//Locates the calendar over the calling control  (in this example the "img").
		if (source = $(source))
		{
			var x = source.cumulativeOffset().left + container.getWidth();
			var offsetLeft = source.getWidth() + 2 // positions 2px off the little calendar image (to the right side).
			if(x > document.viewport.getWidth()){
				offsetLeft = container.getWidth() * (-1); // flips to the left side of the little calendar image. with enough space to display the calendar completely.
			}
			Position.clone($(source), container, {setWidth: false, setHeight: false, offsetLeft: offsetLeft});
		}

		//finally show the calendar =)
		this.calendar.openCalendar();
	}
};

var DateTimeUtil = {
	calendar: null,
	timepicker: null,
	scal_calendar_container: null,
	target_input_id: null,
	showCalendar: function(targetInputId, event, renderTo, options) {
		if (!options)
		{
			options = [];
		}
		source = Event.element(event);
		this.target_input_id = targetInputId;
		
		// Remove the previous scal_calendar_container. If there is a date time picker (DateTimeUtil) and a time picker (CalendarUtil) both on the same page
		// and we DON'T remove this then we wind up with one of each after both icons are clicked, not good.
		if ($('scal_calendar_container')) {
			$('scal_calendar_container').remove();
		}
		
		if(!$('scal_calendar_container')){ // the calendar html isnt in the DOM yet.
			this.scal_calendar_container = Builder.node("div", {id: "scal_calendar_container", style:"width:221px; position:absolute; display:none; z-index:10001; right: -200px"}, [
				Builder.node("b", {id: "scal_top_bar", className: "rtop"}, [
					Builder.node("b", {className: "r1"}),
					Builder.node("b", {className: "r2"}),
					Builder.node("b", {className: "r3"}),
					Builder.node("b", {className: "r4"})
				]),
				Builder.node("div", {id: "scal_dates", className: "floating"}),
				Builder.node("div", {id: "stime", className: "floating_time"})
			]);
			
			if(renderTo && $(renderTo)){ // renders to a specified element id.
				$(renderTo).insert({top: this.scal_calendar_container});
			} else{ // renders to the parent html tag
				var body_tst = Element.extend(document.body);
				body_tst.insert({top: this.scal_calendar_container});
			}

			this.calendar = null; // resets the calendar instance
			this.timepicker = null; // resets the time picker instance
			
			//the Draggable handle is hard coded to "rtop" to avoid other parameter.
			new Draggable(this.scal_calendar_container, {handle: "rtop", starteffect: Prototype.emptyFunction, endeffect: Prototype.emptyFunction});
		}
		//		container = $(container);
		var container = $('scal_calendar_container');
		var cal_element = $('scal_dates');
		var time_element = $('stime');
		
		if(!this.calendar){
			//The singleton calendar is created.
			this.calendar = new scal(cal_element, this.date_update.bind(this),
			{
				openeffect: Element.show,
				closeeffect: Element.hide,
				updateformat: common_localized_properties.getProperty('date_format', 'dd/mm/yyyy'),
				closebutton: '&nbsp;',
				wrapper: container,
				onclose: function(theCalendar){
					Event.stopObserving(document, 'click', cached_window_click_handler);
				},
				onopen: function(){
					Event.observe(document, 'click', cached_window_click_handler);
				}
			});
		} else {
			this.calendar.updateelement = this.date_update.bind(this);
		}
		
		if (!this.timepicker) {
			// Create singleton timepicker
			this.timepicker = new stime(time_element, this.time_update.bind(this), { twentyfour_hour: options.twentyfour_hour, hour: 12, minute: 0 } );
		}

		this.calendar.options.updateformat += ' ' + this.timepicker.options.updateformat;
		
		// Handle document clicks when the date / time picker is open.
		var cached_window_click_handler = function(targetInput, e){ // this allow us to stopObserving it later on.			
			var targ;
			targ = Event.element(e);

			if (!targ.hasClassName('scal_trigger_class')) {
				if (targ.hasClassName('dayboxdate') || targ.hasClassName('timecontrol')) {
                    // all this simulate, blur, focus is to make formfactory happy and clear-out any error message that's been shown
					$(targetInput).simulate('change');
					$(targetInput).focus();
					$(targetInput).blur();
				} else {
					var close_flag = true;
					var ancestors = targ.ancestors();
					ancestors.each(function(element) {
						if (element.identify() == this.scal_calendar_container.id) {
							close_flag = false;
						}
					}, this);

					if (close_flag) {
						
						Event.stopObserving(document, 'click', cached_window_click_handler);
						this.scal_calendar_container.hide();
					}
				}
			}
		}.bind(DateTimeUtil, this.target_input_id);

		var date = this.calendar.getSelectedDate( $F(this.target_input_id) );
		this.timepicker.setCurrentTime(isNaN(date) ? new Date() : date);
		this.calendar.setCurrentDate( date );
		
		//Locates the calendar over the calling control  (in this example the "img").
		if (source = $(source))
		{
			var x = source.cumulativeOffset().left + container.getWidth();
			var offsetLeft = source.getWidth() + 2 // positions 2px off the little calendar image (to the right side).
			if(x > document.viewport.getWidth()){
				offsetLeft = container.getWidth() * (-1); // flips to the left side of the little calendar image. with enough space to display the calendar completely.
			}
			Position.clone($(source), container, {setWidth: false, setHeight: false, offsetLeft: offsetLeft});
		}

		//finally show the calendar =)
		this.calendar.openCalendar();
	},
	
	date_update: function (date) {
		update = new Date ( date.getFullYear(), date.getMonth(), date.getDate(), this.timepicker.currenttime.getHours(), this.timepicker.currenttime.getMinutes() );
		$(this.target_input_id)[$(this.target_input_id).tagName == 'INPUT' ? 'setValue' : 'update'](update.format(this.calendar.options.updateformat));
		$(this.target_input_id).simulate('change');
	},
	
	time_update: function (time) {
		var currentdate = this.calendar.getSelectedDate( $F(this.target_input_id) );
		if (!isNaN(currentdate)) {
			var update = new Date (currentdate.getFullYear(), currentdate.getMonth(), currentdate.getDate(), time.getHours(), time.getMinutes(), time.getSeconds());
		} else {
			var update = time;
		}
		$(this.target_input_id)[$(this.target_input_id).tagName == 'INPUT' ? 'setValue' : 'update'](update.format(this.calendar.options.updateformat));
		$(this.target_input_id).simulate('change');
	},
	
	form_lib_onclick: function (event, target, renderTo, options) {
		DateTimeUtil.showCalendar (target, event, renderTo, options);
	}
};

document.observe("dom:loaded", function() {
	if (common_localized_properties == undefined || !common_localized_properties)
	{
		common_localized_properties = new CSLang('common_localized_properties_js');
	}
	//DateTimeUtil.output_format = common_localized_properties.getProperty('date_format', 'dd/mm/yyyy') + ' hh:nn am/pm';
});
