/*
Script: datepicker.js
	Contains <MooDateSelect>

Author:
	Alan Roemen, <aroemen@aop.com>

Class: MooDateSelect
  A DatePicker for Mootools v1.11

Options:
  stylesheet:     Path to Cascading Style Sheet. (defaults to false for no stylesheet)
  months:         The number of months to dispaly. (defaults to {cols: 1, rows: 1}) (Not fully supported)
	offsets:        The distance of your datepicker from the input. (defaults to {'x': 0, 'y': 0})
  className:      Name for your DatePicker classNames. (defaults to MooDateSelect)
  weekFirstDay:   Integer, first day of the week. 0 (for Sunday) through 6 (for Saturday). (defaults to 0)
  dateFormat:     The return format of selected date like php function date. (defaults to d/m/Y)
		d -> Day of the month, 2 digits with leading zeros
		D -> A textual representation of a day, three letters
		j -> Day of the month without leading zero
		l -> (lowercase 'L') A full textual representation of the day of the week
		N -> ISO-8601 numeric representation of the day of the week. 1 (for Monday) through 7 (for Sunday)
		w -> Numeric representation of the day of the week
		m -> Numeric representation of a month, with leading zeros
		M -> A short textual representation of a month, three letters
		F -> A full textual representation of a month, such as January or March
		n -> Numeric representation of a month, without leading zeros
		Y -> A full numeric representation of a year, 4 digits
		y -> A two digit representation of a year

		Example :
			2/15/2007 -> dateFormat : 'd/m/Y', -> 15/02/2007
			2/15/2007 -> dateFormat : 'Y-m-d', -> 2007-15-02
			2/15/2007 -> dateFormat : 'M j Y', -> Feb 15 2007
		
	defaultDate:    Default date in the calendar. (defaults to new Date())
  selectMinDate:  Minimum date you can select. (defaults to false or no minimum date. use JS date object to specify date)
  selectMaxDate:  Maximum date you can select. (defaults to false or no maximum date. use JS date object to specify date)
  monthNames:     Array for month names. (defaults to ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'])
  monthAbbrev:    Array for month abbreviations. (defaults to ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'])
  dayNames:       Array for day names. (defaults to ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'])
  dayAbbrev:      Array for day abbreviations. (defaults to ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'])
  todayName:      Text to show in the TodayPicker. (defaults to Today)
  closeName:      Text to show in the ClosePicker. (defaults to Close)
  readOnly:       Sets the input to have readonly status. (defaults to true. false would ignore this rule.)
  closeOnSelect:  Close the date picker when a date is selected. (defaults to true)
  timeout:        Timeout setting for date picker in milliseconds (10000 is 10 seconds). (defaults to false or no timeout)

Events:
	onShow - optionally you can alter the default onShow behaviour with this option (like displaying a fade in effect);
	onHide - optionally you can alter the default onHide behaviour with this option (like displaying a fade out effect);
*/
var MooDateSelect = new Class({

	script_name: 'datepicker.js',

	options: {
		onShow: function(picker){
			//picker.setStyle('visibility', 'visible');
			picker.effects({duration: 500, transition: Fx.Transitions.quadInOut}).custom({'opacity': [0, 0.9]});
		},
		onHide: function(picker){
			//picker.setStyle('visibility', 'hidden');
			picker.effects({duration: 500, transition: Fx.Transitions.quadInOut}).custom({'opacity': [0.9, 0]});
		},
    stylesheet: false,
		months: {cols: 1, rows: 1},
		offsets: {'x': 0, 'y': 0},
		className: 'MooDateSelect',
		weekFirstDay : 0,
		dateFormat : 'd/m/Y',
		defaultDate: 'input',
		selectMinDate: false,
    selectMaxDate: false,
		monthNames : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
		monthAbbrev : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'],
		dayNames : ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
		dayAbbrev : ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
		todayName : 'Today',
		closeName : 'Close',
		readOnly: true,
    closeOnSelect: false,
    timeout: false
	},

	initialize: function(input, options){
		this.input = $(input);
		this.setOptions(options);

    // Include the Stylesheet
    if (this.options.stylesheet !== false) new Asset.css(this.options.stylesheet);
				
		// Check for valid dates
    if (this.options.defaultDate == 'input' && this.input.getProperty('value') != '')
      this.options.defaultDate = new Date( Date.parse(this.input.getProperty('value')) );
    if (this.options.defaultDate == false || $type(this.options.defaultDate) != 'object' || isNaN(this.options.defaultDate.getFullYear())) {
			this.options.defaultYear  = new Date().getFullYear();
			this.options.defaultMonth = new Date().getMonth();
			this.options.defaultDay   = new Date().getDate();
		} else {
			this.options.defaultYear  = this.options.defaultDate.getFullYear();
			this.options.defaultMonth = this.options.defaultDate.getMonth();
			this.options.defaultDay   = this.options.defaultDate.getDate();
    }
    
		if(this.options.selectMinDate && this.options.selectMaxDate && this.options.selectMinDate.getTime() > this.options.selectMaxDate.getTime())
			this.options.selectMaxDate = false;
		
		// Cache of the date selected
		this.OnSelected   = false;
		this.selectYear   = this.options.defaultYear;
		this.selectMonth  = this.options.defaultMonth;
		this.selectDay    = this.options.defaultDay;
		this.firstYear    = this.options.defaultYear;
		this.firstMonth   = this.options.defaultMonth;

    // Build calendar body
		this.wrapper = new Element('div', {
			'class': this.options.className,
			'styles': {
				'position': 'absolute',
				'top': '0',
				'left': '0',
				'visibility': 'hidden'
			}
		});
		this.titlebar = new Element('div', {'class': 'titlebar'}).injectInside(this.wrapper);
		this.title    = new Element('span').injectInside(this.titlebar); this.set_title();
		this.smallbar = new Element('small').injectInside(this.titlebar);
    this.today    = new Element('span', {'events': {'click': this.today.bindWithEvent(this)}}).setHTML(this.options.todayName);
    this.done     = new Element('span', {'events': {'click': this.end.bindWithEvent(this)}}).setHTML(this.options.closeName);
		this.next     = new Element('div', {'class': 'next-month', 'events': {'click': this.next.bindWithEvent(this)}}).injectTop(this.titlebar);
		this.prev     = new Element('div', {'class': 'prev-month', 'events': {'click': this.prev.bindWithEvent(this)}}).injectTop(this.titlebar);
		this.months   = new Element('div', {'class': 'months'}).injectInside(this.wrapper);

    // Setup Smallbar
    if (this.options.closeOnSelect) {
      if (this.options.todayName !== false) this.today.injectInside(this.smallbar);
    } else {
      if (this.options.todayName !== false) {
        this.smallbar.setHTML('&nbsp;:&nbsp;');
        this.today.injectTop(this.smallbar);
      }
      this.done.injectInside(this.smallbar);
    }

		this.wrapper.inject($$('body')[0]);
		this.update_date(true);
    this.set_position();
    if (this.options.readOnly) this.input.setProperty('readonly', 'readonly');
    this.input.addClass(this.options.className);
    this.input.addEvent('click', this.show.bindWithEvent(this));
    this.activeDatePicker = false;
	},

  show: function(){
    if (this.activeDatePicker) {
      this.end();
      return;
    }

		this.activeDatePicker = true;
		if (this.options.timeout) this.timer = this.end.delay(this.options.timeout, this);
		this.fireEvent('onShow', [this.wrapper]);
  },

  end: function(){
		$clear(this.timer);
		this.activeDatePicker = false;
		this.fireEvent('onHide', [this.wrapper]);
  },

	set_position: function(){
    var win     = window.getWidth().toInt();
		var input   = this.input.getCoordinates();
    var picker  = this.wrapper.getCoordinates().width.toInt();
    var adjust  = (input.left.toInt()+picker) - win;
    if (adjust < 0) adjust = 0;
		this.wrapper.setStyles({
			'left': input.left + this.options.offsets.x - adjust,
			'top': input.bottom + this.options.offsets.y
		});
	},

  next: function(){
    this.firstMonth++;
    if (this.firstMonth >= 12) {
      this.firstMonth = 0;
      this.firstYear++;
    }
    if ((this.options.months.cols * this.options.months.rows) == 1) this.selectMonth = this.firstMonth;
    this.update_date(true);
  },

  prev: function(){
    this.firstMonth--;
    if (this.firstMonth < 0) {
      this.firstMonth = 11;
      this.firstYear--;
    }
    if ((this.options.months.cols * this.options.months.rows) == 1) this.selectMonth = this.firstMonth;
    this.update_date(true);
  },

  today: function(){
    var today = new Date();
    this.selectMonth  = today.getMonth();
    this.selectYear   = today.getFullYear();
    this.selectDay    = today.getDate();
    this.firstMonth   = this.selectMonth;
    this.firstYear    = this.selectYear;
    this.update_date(true);
  },

	build: function(){
    this.months.empty();
		this.month = [];
    for (var row=0; row<this.options.months.rows; row++) {
      for (var col=0; col<this.options.months.cols; col++) {
        var c, j, d, m, wrap, cday;
        var el = new Element('div', {'class': 'month'})
        var selectYear = this.firstYear;
        var selectMonth = this.firstMonth.toInt() + this.month.length;
        if (selectMonth >= 12) {
          selectYear += Math.floor(selectMonth / 12);
          selectMonth = selectMonth % 12;
        }

        m = new Element('div', {'class': 'title'}).setHTML(this.select_date(this.options.monthNames[selectMonth] + ' 1, ' + selectYear, 'F Y')).injectInside(el);
        if ((this.options.months.cols * this.options.months.rows) == 1) m.setStyle('display', 'none');
        wrap = new Element('div', {'class': 'dom'}).injectInside(el);

        // Days of the Week
        for (j=0; j<7; j++) {
          new Element('div', {'class': 'dow'}).setHTML(this.options.dayAbbrev[(this.options.weekFirstDay + j)%7]).injectInside(wrap);
        }

        // Get first and last day of the month
        var fd = new Date(selectYear, selectMonth, 1);
        var ld = new Date(selectYear, selectMonth+1, 0);

        // Fill empty fields before first day of the month
        for (j=0; j<((7 + fd.getDay() - this.options.weekFirstDay)%7); j++) {
          new Element('div', {'class': 'day blank'}).setHTML('&nbsp;').injectInside(wrap);
        }

        // Fill days of the month
        for (j=1; j<=ld.getDate(); j++) {
          cday = new Date(selectYear, selectMonth, j);
          c = (cday.toString()==this.selectedDate.toString()?' active':'') + (cday.getDay()==0||cday.getDay()==6?' weekend':'');
          d = new Element('div', {'class': 'day' + c}).setHTML(j);
          if ((this.options.selectMinDate !== false && cday < this.options.selectMinDate) || (this.options.selectMaxDate !== false && cday > this.options.selectMaxDate)) {
            d.addClass('not-available');
          } else {
            d.addEvent('click', function(e) {
              e = new Event(e).stop();
              this.select_day(e);
            }.bind(this));
          }
          d.injectInside(wrap);
        }

        // Fill empty fields after last day of the month
        for (j=ld.getDay(); j<6; j++) {
          new Element('div', {'class': 'day blank'}).setHTML('&nbsp;').injectInside(wrap);
        }

        el.injectInside(this.months);
        this.month.push(el);
      }
      new Element('div', {'class': 'clear'}).injectInside(this.months);
    }
	},

  set_title: function(){
    this.title.empty();
    var selectedDate = new Date(this.selectYear, this.selectMonth, this.selectDay);
    new Element('em', {
      'class': 'title-month',
      'events': {
        'click': function(e){
          e = new Event(e).stop();
          this.choose_month();
        }.bind(this)
      }
    }).setHTML(this.select_date(selectedDate, 'F')).injectInside(this.title);

    //new Element('em', {'class': 'title-date'}).setHTML(this.select_date(selectedDate, ' j, ')).injectInside(this.title);

    new Element('em', {
      'class': 'title-year',
      'events': {
        'click': function(e){
          e = new Event(e).stop();
          this.choose_year();
        }.bind(this)
      }
    }).setHTML(this.select_date(selectedDate, 'Y')).injectInside(this.title);
  },

  update_date: function(build){
    build = build || false;
    this.selectedDate = new Date(this.selectYear, this.selectMonth, this.selectDay);
    this.set_title();
    this.input.setProperty('value', this.select_date());

		$clear(this.timer);
		if (this.options.timeout) this.timer = this.end.delay(this.options.timeout, this);
    if (build) this.build();
  },

  select_day: function(e){
    var el = e.target;
    this.selectDay = el.getText().toInt();
    this.selectMonth = this.options.monthNames.indexOf(el.getParent().getParent().getElement('.title').getText().split(' ', 2)[0]);
    this.selectYear = el.getParent().getParent().getElement('.title').getText().split(' ', 2)[1];
    this.update_date();
    this.months.getElements('.active').removeClass('active');
    el.addClass('active');
    if (this.options.closeOnSelect === true) this.end();
  },

  select_month: function(e){
    var el = e.target;
    this.selectMonth = this.options.monthNames.indexOf(el.getText());
    this.firstMonth = this.selectMonth;
    this.update_date(true);
  },

  select_year: function(e){
    var el = e.target;
    this.selectYear = el.getText();
    this.firstYear = el.getText();
    this.update_date(true);
  },

  choose_month: function(){
    if ($('dropdown-year')) {
      $('dropdown-year').remove();
    }
    if ($('dropdown-month')) {
      $('dropdown-month').remove();
      return;
    }
    var pos = {'left': this.title.offsetLeft, 'bottom': this.title.offsetTop + this.title.offsetHeight};

    this.month_dropdown = new Element('div', {
      'id': 'dropdown-month',
      'class': 'dropdown',
      'styles': {
        'position': 'absolute',
        'left': pos.left.toInt() - 4,
        'top': pos.bottom.toInt() + 4
      }
    });
    this.month_wrapper = new Element('div', {'class': 'wrap'}).injectInside(this.month_dropdown);
    this.month_elements = new Element('div', {'styles': {'margin-top':0}}).injectInside(this.month_wrapper);
    
    new Element('div', {
      'class': 'up',
      'events': {
        'click': function(){
          var m = this.month_elements.getStyle('margin-top').toInt() - (-1 * this.month_wrapper.getCoordinates().height.toInt());
          if (m > 0) m = 0;
          this.month_elements.setStyle('margin-top', m);
        }.bind(this)
      }
    }).injectTop(this.month_dropdown);

    this.month_wrapper.addEvent('mousewheel', function(e){
      e = new Event(e).stop();
      var m, max, wheel = e.wheel;  // direction of wheel: -1 is down & 1 is up

      if (wheel == 1) {
        if (this.month_elements.getStyle('margin-top').toInt() == 0) return;
        m= this.month_elements.getStyle('margin-top').toInt() - (-1 * this.month_wrapper.getCoordinates().height.toInt());
        if (m > 0) m = 0;
      } else {
        if (this.month_elements.getStyle('margin-top').toInt() == (-1*this.month_elements.getCoordinates().height.toInt())) return;
        m = this.month_elements.getStyle('margin-top').toInt() + (-1 * this.month_wrapper.getCoordinates().height.toInt());
        max = -1 * (this.month_elements.getCoordinates().height.toInt() - this.month_wrapper.getCoordinates().height.toInt());
        if (m < max) m = max;
      }
      this.month_elements.setStyle('margin-top', m);
    }.bind(this));

    new Element('div', {
      'class': 'down',
      'events': {
        'click': function(){
          var m = this.month_elements.getStyle('margin-top').toInt() + (-1 * this.month_wrapper.getCoordinates().height.toInt());
          var max = -1 * (this.month_elements.getCoordinates().height.toInt() - this.month_wrapper.getCoordinates().height.toInt());
          if (m < max) m = max;
          this.month_elements.setStyle('margin-top', m);
        }.bind(this)
      }
    }).injectInside(this.month_dropdown);


    this.options.monthNames.each(function(month){
      new Element('div', {
        'id': 'month-' + month,
        'class': month.toLowerCase(),
        'events': {
          'click': function(e){
            e = new Event(e).stop();
            this.select_month(e);
            this.month_dropdown.remove();
          }.bind(this),
          'mouseover': function(){
            this.addClass('active');
          },
          'mouseout': function(){
            this.removeClass('active');
          }
        }
      }).setHTML(month).injectInside(this.month_elements)
    }.bind(this));

    this.month_dropdown.inject(this.wrapper);
    var h = $('month-' + this.options.monthNames[this.selectMonth]).getCoordinates().height.toInt() * (this.selectMonth) * -1;
    var max = -1 * (this.month_elements.getCoordinates().height.toInt() - this.month_wrapper.getCoordinates().height.toInt());
    if (h < max) h = max;
    this.month_elements.setStyle('margin-top', h);
  },

  choose_year: function(){
    if ($('dropdown-month')) {
      $('dropdown-month').remove();
    }
    if ($('dropdown-year')) {
      $('dropdown-year').remove();
      return;
    }
    this.maxYear = (this.options.selectMaxDate?this.options.selectMaxDate.getFullYear():new Date().getFullYear());
    this.minYear = (this.options.selectMinDate?this.options.selectMinDate.getFullYear():new Date().getFullYear() - 100);

    var years = []
    for (var i=this.maxYear; i>=this.minYear; i--) years.push(i);
    if (years.length <= 1) return;

    var pos = {'right': this.title.offsetLeft + this.title.offsetWidth, 'bottom': this.title.offsetTop + this.title.offsetHeight};
    this.year_dropdown = new Element('div', {
      'id': 'dropdown-year',
      'class': 'dropdown',
      'styles': {
        'visibility': 'hidden',
        'position': 'absolute',
        'left': pos.right.toInt() - 30 - this.title.getElement('.title-year').getStyle('padding-right').toInt(),
        'top': pos.bottom.toInt() + 4
      }
    }).injectInside(this.wrapper);
    this.year_wrapper = new Element('div', {'class': 'wrap'}).injectInside(this.year_dropdown);
    this.year_elements = new Element('div', {'styles': {'margin-top':0}}).injectInside(this.year_wrapper);

    this.year_wrapper.addEvent('mousewheel', function(e){
      e = new Event(e).stop();
      var m, max, wheel = e.wheel;  // direction of wheel: -1 is down & 1 is up

      if (wheel == 1) {
        if (this.year_elements.getStyle('margin-top').toInt() == 0) return;
        m= this.year_elements.getStyle('margin-top').toInt() - (-1 * this.year_wrapper.getCoordinates().height.toInt());
        if (m > 0) m = 0;
      } else {
        if (this.year_elements.getStyle('margin-top').toInt() == (-1*this.year_elements.getCoordinates().height.toInt())) return;
        m = this.year_elements.getStyle('margin-top').toInt() + (-1 * this.year_wrapper.getCoordinates().height.toInt());
        max = -1 * (this.year_elements.getCoordinates().height.toInt() - this.year_wrapper.getCoordinates().height.toInt());
        if (m < max) m = max;
      }
      this.year_elements.setStyle('margin-top', m);
    }.bind(this));

    years.each(function(year){
      new Element('div', {
        'id': 'year-' + year,
        'events': {
          'click': function(e){
            e = new Event(e).stop();
            this.select_year(e);
            this.year_dropdown.remove();
          }.bind(this),
          'mouseover': function(){
            this.addClass('active');
          },
          'mouseout': function(){
            this.removeClass('active');
          }
        }
      }).setHTML(year).injectInside(this.year_elements)
    }.bind(this));

    if (($('year-' + years[0]).getCoordinates().height.toInt() * years.length) > this.year_wrapper.getStyle('max-height').toInt()) {
      new Element('div', {
        'class': 'up',
        'events': {
          'click': function(){
            var m = this.year_elements.getStyle('margin-top').toInt() - (-1 * this.year_wrapper.getCoordinates().height.toInt());
            this.year_elements.setStyle('margin-top', (m>0?0:m));
          }.bind(this)
        }
      }).injectTop(this.year_dropdown);

      new Element('div', {
        'class': 'down',
        'events': {
          'click': function(){
            var m = this.year_elements.getStyle('margin-top').toInt() + (-1 * this.year_wrapper.getCoordinates().height.toInt());
            var max = -1 * (this.year_elements.getCoordinates().height.toInt() - this.year_wrapper.getCoordinates().height.toInt());
            this.year_elements.setStyle('margin-top', (m<max?max:m));
          }.bind(this)
        }
      }).injectInside(this.year_dropdown);
    }

    this.year_dropdown.setStyle('visibility', 'visible');
    var h = $('year-' + this.selectYear).getCoordinates().height.toInt() * (this.maxYear-this.selectYear) * -1;
    var max = -1 * (this.year_elements.getCoordinates().height.toInt() - this.year_wrapper.getCoordinates().height.toInt());
    if (h < max) h = max;
    this.year_elements.setStyle('margin-top', h);
  },

	/*
	 * Helper function: Returns the date in a PHP formated style
	 * @var Date    parseDate    date object of the date to parse the format of
	 * @var String  dateFormat   PHP styled date format
	 * @see http://us3.php.net/manual/en/function.date.php
	 */
	select_date: function(parse_date, date_format){
		var selectedDate;
    if (!parse_date)
			selectedDate = new Date(this.selectYear, this.selectMonth, this.selectDay);
		else selectedDate = new Date(parse_date);

    var return_data;
		if (!date_format)
			return_date = this.options.dateFormat;
		else
			return_date = date_format;

		return_date = return_date.replace(/D/g, '[D]').replace(/l/g, '[l]').replace(/F/g, '[F]').replace(/M/g, '[M]');

		// [d] Day of the month, 2 digits with leading zeros
		if(selectedDate.getDate() < 10)
			return_date = return_date.replace(/d/g, '0' + selectedDate.getDate());
		else
			return_date = return_date.replace(/d/g, selectedDate.getDate());

		// [j] Day of the month without leading zero
		return_date = return_date.replace(/j/g, selectedDate.getDate());

		// [N] ISO-8601 numeric representation of the day of the week. 1 (for Monday) through 7 (for Sunday)
		if(selectedDate.getDay() == 0)
			return_date = return_date.replace(/N/g, 7);
		else
			return_date = return_date.replace(/N/g, selectedDate.getDay());

		// [w] Numeric representation of the day of the week
		return_date = return_date.replace(/w/g, selectedDate.getDay());

		// [m] Numeric representation of a month, with leading zeros
		if((selectedDate.getMonth() + 1) < 10)
			return_date = return_date.replace(/m/g, '0' + (selectedDate.getMonth() + 1));
		else
			return_date = return_date.replace(/m/g, (selectedDate.getMonth() + 1));

		// [n] Numeric representation of a month, without leading zeros
		return_date = return_date.replace(/n/g, (selectedDate.getMonth() + 1));

		// [Y] A full numeric representation of a year, 4 digits
		return_date = return_date.replace(/Y/g, selectedDate.getFullYear());

		// [y] A two digit representation of a year
		return_date = return_date.replace(/y/g, (selectedDate.getFullYear() + '').substr(2, 2) );

		// Textual replacement need to be at last

		// [D] A textual representation of a day, three letters
		return_date = return_date.replace(/\[D\]/g, this.options.dayNames[selectedDate.getDay()].substr(0, 3));

		// [l] (lowercase 'L') A full textual representation of the day of the week
		return_date = return_date.replace(/\[l\]/g, this.options.dayNames[selectedDate.getDay()]);

		// [F] A full textual representation of a month, such as January or March
		return_date = return_date.replace(/\[F\]/g, this.options.monthNames[selectedDate.getMonth()]);

		// [M] A short textual representation of a month, three letters
		return_date = return_date.replace(/\[M\]/g, this.options.monthNames[selectedDate.getMonth()].substr(0, 3));

		return return_date;
	}
});

MooDateSelect.implement(new Events, new Options);
