/////////////////////////////////////////////////////////////////////////////////////
//
// Datetime Toolbocks - Intuitive Date Input Selection
// http://datetime.toolbocks.com
//
// Created by:
//      Nathaniel Brown - http://nshb.net
//      Email: nshb(at)inimit.com
//
// Inspired by:
//      Simon Willison - http://simon.incutio.com
//
// Contributors:
//      Darrell Taylor <darrellt(at)gmail.com>
//
// Adjustements for TYPO3:
//      Stefan Galinski <stefan.galinski(at)gmail.com>
//
// License:
//      Modified GNU Lesser General Public License version 2.1
//
// Dependencies:
//      Prototype 1.5.0_rc1 and 1.6.0
//
// Bugs:
//      Please submit bug reports to http://dev.toolbocks.com
//
// Comments:
//      Any feedback or suggestions, please email nshb(at)inimit.com
//
// Donations:
//      If Datebocks has saved your life, or close to it, please send a donation
//      to donate(at)toolbocks.com
//
/////////////////////////////////////////////////////////////////////////////////////

Object.extend(Hash, {
  toString: function(hash) {
	var values = [];

	hash.each(function(pair) {
	  var string = pair.key + ':';

	  if (typeof(pair.value) == 'object') {
		  string += pair.value.toSource();
	  } else if (typeof(pair.value) == 'string') {
		  string += '"' + pair.value + '"';
	  } else if (typeof(pair.value) == 'number') {
		  string += pair.value;
	  } else if (typeof(pair.value) == 'boolean') {
		  string += pair.value;
	  } else if (typeof(pair.value) == 'function') {
		  string += '""';
	  } else {
		  string += '""';
	  }

	  values.push(string);
	});

	return '{' + values.join(', ') + '}';
  }
});

////////////////////////////////////////////////////////////////////////////////
///// Datetime Toolbocks

var datetimeToolbocksHelpPage = '';
var datetimeToolbocksOnChange = new Array();
var datetimeToolbocksStaticOnChangeIndex = 0;

var DatetimeToolbocks = Class.create();

DatetimeToolbocks.VERSION = '3.0.3';

DatetimeToolbocks.CalendarOptions = $H({
  align                   : 'Br'
});

DatetimeToolbocks.DefaultOptions = {
  /* Configuration Options ---------------------------------------------- */
  //  - iso
  //  - de
  //  - us
  //  - dd/mm/yyyy
  //  - dd-mm-yyyy
  //  - mm/dd/yyyy
  //  - mm.dd.yyyy
  //  - mm-dd-yyyy
  //  - yyyy-mm-dd

  inputValue              : false,
  inputName               : 'DatetimeToolbocks',
  elementId               : 'DatetimeToolbocks',
  elementIdSuffixInput    : '_hr',
  elementIdSuffixCheckbox : '_cb',
  elementIdSuffixMessage  : '_msg',
  elementIdSuffixMessageCnt  : '_msgCnt',
  elementIdSuffixHelp     : '_help',
  elementIdSuffixButton   : '_trigger',
  elementIdSuffixFlat     : '_flat',
  classNameError          : 'error',
  classNameSuccess        : 'success',
  calendarOptions         : $H(),
  autoRollOver            : true,
  format                  : 'iso'
};

DatetimeToolbocks.Collection = {
  _collection: [],

  add: function(object) {
	DatetimeToolbocks.Collection._collection.push(object);
  },

  find: function(element_id) {
	var found = false;

	this._collection.each(function(datetime_toolbocks) {
	  datetime_toolbocks_element_id = datetime_toolbocks.options.elementId + datetime_toolbocks.options.elementIdSuffixInput;
	  if (datetime_toolbocks_element_id == element_id) {
		found = datetime_toolbocks;
		throw $break;
	  }
	});

	return found;
  }
};

DatetimeToolbocks.prototype = {
  _formatString: 'yyyy-mm-dd',

  initialize: function(options) {
	options.calendarOptions = Object.clone(Object.extend(Object.clone(DatetimeToolbocks.CalendarOptions), options.calendarOptions || {}));

	if (parseFloat(Prototype.Version) >= parseFloat('1.6.0')) {
		temp = $H(options)._object;
	}
	else {
		temp = $H(options);
	}

	this.options  = Object.clone(Object.extend(Object.clone(DatetimeToolbocks.DefaultOptions), temp || {}));

	this.setDateType();

	this._configureOptions();
	this._createHtml();

	DatetimeToolbocks.Collection.add(this);

	this._attachEvents();
	this.setDefaultFormatMessage();

  },

  _configureOptions: function() {
	this.options.calendarOptions.inputField = this.options.elementId + this.options.elementIdSuffixInput;
	this.options.messageField = this.options.elementId + this.options.elementIdSuffixMessage;

	if (this.options.calendarOptions.flat) {
	  // remove all default options that assume it's not flat

	  // remove align
	  this.options.calendarOptions.remove('align');

	  // set show button to false
	  this.options.showButton = false;

	  // if the option is set to true, then calculate the element id for the flat calendar
	  this.options.calendarOptions.flat = (this.options.calendarOptions.flat == true) ? (this.options.elementId + this.options.elementIdSuffixFlat) : this.options.calendarOptions.flat;
	}

	this.options.showHelp = (this.options.showHelp == true || this.options.showHelp == false) ? this.options.showHelp : true;
	this.options.showButton = (this.options.showButton == true || this.options.showButton == false) ? this.options.showButton : true;
	this.options.buildFlat = (this.options.buildFlat == true || this.options.buildFlat == false) ? this.options.buildFlat : false;

	if (this.options.showButton == true && !this.options.calendarOptions.flat) {
	  this.options.calendarOptions.button = this.options.elementId + this.options.elementIdSuffixButton;
	}

	if (this.options.showHelp == true) {
	  this.options.helpField = this.options.elementId + this.options.elementIdSuffixHelp;
	}
  },

  _createHtml: function() {
	// set the default options
	var html = '';

	if (this.options.buildFlat)
	  html += '<div class="DatetimeToolbocksFlat" id="' + this.options.calendarOptions.flat + '"></div>\n';

	html += '<div id="' + this.options.elementId + this.options.elementIdSuffixMessage + '">&nbsp;</div>';
	var container_id = this.options.elementId + this.options.elementIdSuffixMessageCnt;
	document.write('<span id="' + container_id + '"></span>');
	new Insertion.Bottom(container_id, html);

	if (this.options.calendarOptions.flat || this.options.showButton) {
	  Calendar.setup(this.options.calendarOptions);
	}
  },

  _attachEvents: function() {
	 // attach the events to the Datetime Toolbocks element
	 var input_field = $(this.options.calendarOptions.inputField);

	// TYPO3 onchange hack
	if (input_field.onchange) {
		datetimeToolbocksOnChange[datetimeToolbocksStaticOnChangeIndex] = new Object();
		datetimeToolbocksOnChange[datetimeToolbocksStaticOnChangeIndex]['id'] = this.options.elementId + this.options.elementIdSuffixInput;
		datetimeToolbocksOnChange[datetimeToolbocksStaticOnChangeIndex][this.options.elementId + this.options.elementIdSuffixInput] = input_field.onchange;
		++datetimeToolbocksStaticOnChangeIndex;
	}

	 input_field.onchange = function(event) {
		// TYPO3 onchange function
		var val = this.value;
		for (var i = 0; i < datetimeToolbocksOnChange.length; ++i) {
			if(datetimeToolbocksOnChange[i]['id'] != this.id)
				continue;

			var temp = datetimeToolbocksOnChange[i][this.id].toString().split('{')[1].split('}')[0];
			eval(temp);
			break;
		}
		this.value = val;

		// datetime_toolbocks stuff
		if (!event) { var event = window.event; }

		var element_id = (this && this.id) ? this.id : Event.element(event).id;
		var datetime_toolbocks = DatetimeToolbocks.Collection.find(element_id);
		if (!datetime_toolbocks) { return; }

		return datetime_toolbocks.magicDate();
	 }

	 input_field.onclick = function() {
		this.select();
	 }

	 input_field.onkeypress = function(event) {
		if (!event) { var event = window.event; }

		var element_id = (this && this.id) ? this.id : Event.element(event).id;

		var datetime_toolbocks = DatetimeToolbocks.Collection.find(element_id);
		if (!datetime_toolbocks) { return; }

		datetime_toolbocks.keyObserver(event, 'parse');

		var keyCode = (event.keyCode) ? event.keyCode : ((event.which) ? event.which : event.charCode);
		if ((keyCode == 13 || keyCode == 10) && this.value != '') {
			var val = this.value;
			// TYPO3 onchange function
			for (var i = 0; i < datetimeToolbocksOnChange.length; ++i) {
				if(datetimeToolbocksOnChange[i]['id'] != this.id)
					continue;

				var temp = datetimeToolbocksOnChange[i][this.id].toString().split('{')[1].split('}')[0];
				eval(temp);
				break;
			}
			this.value = val;
		}

		return datetime_toolbocks.keyObserver(event, 'return');
	 }

	 // add the event for when a user clicks on the help icon
	 datetimeToolbocksHelpPage = this.options.calendarOptions.helpPage;
	 if (this.options.showHelp) {
	   $(this.options.helpField).onclick = function() {
		DatetimeToolbocks.windowOpenCenter(datetimeToolbocksHelpPage, 'DatetimeToolbocksHelp', 'width=500,height=430,autocenter=true');
	   }
	 }
  },

  getOptions: function(datetime_toolbocks_id) {
	if (DateBocks.Collection.find(datetime_toolbocks_id)) {
	  return DateBocks.Collection.find(datetime_toolbocks_id).options;
	} else {
	  return $H();
	}

  },

  /* Properties --------------------------------------------------------- */

  _monthNames: [
	'January',
	'February',
	'March',
	'April',
	'May',
	'June',
	'July',
	'August',
	'September',
	'October',
	'November',
	'December'
  ],

  _weekdayNames: [
	'Sunday',
	'Monday',
	'Tuesday',
	'Wednesday',
	'Thursday',
	'Friday',
	'Saturday'
  ],

  _dateParsePatterns: [
	// Today
	{
	  re: /^tod|^now/i,
	  handler: function(db, bits) {
		return new Date();
	  }
	},
	// Tomorrow
	{
	  re: /^tom/i,
	  handler: function(db, bits) {
		var d = new Date();
		d.setDate(d.getDate() + 1);
		return d;
	  }
	},
	// Yesterday
	{
	  re: /^yes/i,
	  handler: function(db, bits) {
		var d = new Date();
		d.setDate(d.getDate() - 1);
		return d;
	  }
	},
	// 4th
	{
	  re: /^(\d{1,2})(st|nd|rd|th)?$/i,
	  handler: function(db, bits) {
		var d = new Date();
		var yyyy = d.getFullYear();
		var dd = parseInt(bits[1], 10);
		var mm = d.getMonth();

		if ( db.dateInRange( yyyy, mm, dd ) ) {
		   return db.getDateObj(yyyy, mm, dd);
		}
	  }
	},
	// 4th Jan
	{
	  re: /^(\d{1,2})(?:st|nd|rd|th)? (?:of\s)?(\w+)$/i,
	  handler: function(db, bits) {
		var d = new Date();
		var yyyy = d.getFullYear();
		var dd = parseInt(bits[1], 10);
		var mm = db.parseMonth(bits[2]);

		if ( db.dateInRange( yyyy, mm, dd ) ) {
		   return db.getDateObj(yyyy, mm, dd);
		}
	  }
	},
	// 4th Jan 2003
	{
	  re: /^(\d{1,2})(?:st|nd|rd|th)? (?:of )?(\w+),? (\d{4})$/i,
	  handler: function(db, bits) {
		var d = new Date();
		d.setDate(parseInt(bits[1], 10));
		d.setMonth(db.parseMonth(bits[2]));
		d.setYear(bits[3]);
		return d;
	  }
	},
	// Jan 4th
	{
	  re: /^(\w+) (\d{1,2})(?:st|nd|rd|th)?$/i,
	  handler: function(db, bits) {
		var d = new Date();
		var yyyy = d.getFullYear();
		var dd = parseInt(bits[2], 10);
		var mm = db.parseMonth(bits[1]);
		if ( db.dateInRange( yyyy, mm, dd ) ) {
		  return db.getDateObj(yyyy, mm, dd);
		}
	  }
	},
	// Jan 4th 2003
	{
	  re: /^(\w+) (\d{1,2})(?:st|nd|rd|th)?,? (\d{4})$/i,
	  handler: function(db, bits) {
		var yyyy = parseInt(bits[3], 10);
		var dd = parseInt(bits[2], 10);
		var mm = db.parseMonth(bits[1]);

		if ( db.dateInRange( yyyy, mm, dd ) )
		   return db.getDateObj(yyyy, mm, dd);

	  }
	},
	// Next Week, Last Week, Next Month, Last Month, Next Year, Last Year
	{
	  re: /^((next|last)\s(week|month|year))$/i,
	  handler: function(db, bits) {
		var objDate = new Date();

		var dd = objDate.getDate();
		var mm = objDate.getMonth();
		var yyyy = objDate.getFullYear();

		switch (bits[3]) {
		  case 'week':
			var newDay = (bits[2] == 'next') ? (dd + 7) : (dd - 7);
			objDate.setDate(newDay);
			break;
		  case 'month':
			var newMonth = (bits[2] == 'next') ? (mm + 1) : (mm - 1);
			objDate.setMonth(newMonth);
			break;
		  case 'year':
			var newYear = (bits[2] == 'next') ? (yyyy + 1) : (yyyy - 1);
			objDate.setYear(newYear);
			break;
		}

		return objDate;
	  }
	},
	// 2 years from now , 3 days ago
	{
	  re: /^(\d{1,2}|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty) (day|week|month|year)s? (from|ago)(\s(today|now))?$/i,
	  handler: function(db, bits) {
		var objDate = new Date();
		var dd = objDate.getDate();
		var mm = objDate.getMonth();
		var yyyy = objDate.getFullYear();

		if(isNaN(bits[1])) {
		  switch (bits[1]) {
			case 'one': bits[1] = 1; break;
			case 'two': bits[1] = 2; break;
			case 'three': bits[1] = 3; break;
			case 'four': bits[1] = 4; break;
			case 'five': bits[1] = 5; break;
			case 'six': bits[1] = 6; break;
			case 'seven': bits[1] = 7; break;
			case 'eight': bits[1] = 8; break;
			case 'nine': bits[1] = 9; break;
			case 'ten': bits[1] = 10; break;
			case 'eleven': bits[1] = 11; break;
			case 'twelve': bits[1] = 12; break;
			case 'thirteen': bits[1] = 13; break;
			case 'fourteen': bits[1] = 14; break;
			case 'fifteen': bits[1] = 15; break;
			case 'sixteen': bits[1] = 16; break;
			case 'seventeen': bits[1] = 17; break;
			case 'eighteen': bits[1] = 18; break;
			case 'nineteen': bits[1] = 19; break;
			case 'twenty': bits[1] = 20; break;
		  }
		}

		var number = parseInt(bits[1], 10);
		var period = bits[2];
		var direction = bits[3];

		switch (period) {
		  case 'day':
			var newDay = (direction == 'ago') ? (dd - number) : (dd + number);
			objDate.setDate(newDay);
			break;
		  case 'week':
			var newDay = (direction == 'ago') ? (dd - (number*7)) : (dd + (number*7));
			objDate.setDate(newDay);
			break;
		  case 'month':
			var newMonth = (direction == 'ago') ? (mm - number) : (mm + number);
			objDate.setMonth(newMonth);
			break;
		  case 'year':
			var newYear = (direction == 'ago') ? (yyyy - number) : (yyyy + number);
			objDate.setYear(newYear);
			break;
		}

		return objDate;
	  }
	},
	// next tuesday
	// this mon, tue, wed, thu, fri, sat, sun
	// mon, tue, wed, thu, fri, sat, sun
	{
	  re: /^(next|this)?\s?(\w+)$/i,
	  handler: function(db, bits) {
		var d = new Date();
		var day = d.getDay();
		var newDay = db.parseWeekday(bits[2]);
		var addDays = newDay - day;
		if (newDay <= day) {
			addDays += 7;
		}
		d.setDate(d.getDate() + addDays);
		return d;
	  }
	},
	// last Tuesday
	{
	  re: /^last (\w+)$/i,
	  handler: function(db, bits) {
		var d = new Date();
		var wd = d.getDay();
		var nwd = db.parseWeekday(bits[1]);

		// determine the number of days to subtract to get last weekday
		var addDays = (-1 * (wd + 7 - nwd)) % 7;

		// above calculate 0 if weekdays are the same so we have to change this to 7
		if (0 == addDays) {
		  addDays = -7;
		}

		// adjust date and return
		d.setDate(d.getDate() + addDays);
		return d;

	  }
	},
	// mm/dd/yyyy (American style)
	{
	  re: /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/,
	  handler: function(db, bits) {

		// if config date type is set to another format, use that instead
		test = db.getFormat();
	  	if (db.options.calendarOptions.showsTime) {
	  		test = db.getFormat().split(' ')[1];
		}
	  	if (test == '%m/%d/%Y') {
		  var mm = parseInt(bits[1], 10);
		  var dd = parseInt(bits[2], 10);
		} else {
		  var dd = parseInt(bits[1], 10);
		  var mm = parseInt(bits[2], 10);
		}

		if ((mm - 1) > 12) {
			real_day = mm;
			real_month = dd;

			mm = real_month;
			dd = real_day;
		}

		mm -= 1;

		var yyyy = parseInt(bits[3], 10);

		if ( db.dateInRange( yyyy, mm, dd ) ) {
		  return db.getDateObj(yyyy, mm, dd);
		}

	  }
	},
	// mm/dd/yy (American style) short year
	{
	  re: /^(\d{1,2})\/(\d{1,2})\/(\d{1,2})$/,
	  handler: function(db, bits) {

		var d = new Date();
		var yyyy = d.getFullYear() - (d.getFullYear() % 100) + parseInt(bits[3], 10);
		var dd = parseInt(bits[2], 10);
		var mm = parseInt(bits[1], 10) - 1;

		if ( db.dateInRange(yyyy, mm, dd) ) {
		  return db.getDateObj(yyyy, mm, dd);
		}
	  }
	},
	// mm/dd (American style) omitted year
	{
	  re: /^(\d{1,2})\/(\d{1,2})$/,
	  handler: function(db, bits) {
		var d = new Date();
		var yyyy = d.getFullYear();
		var dd = parseInt(bits[2], 10);
		var mm = parseInt(bits[1], 10) - 1;

		if ( db.dateInRange(yyyy, mm, dd) ) {
		  return db.getDateObj(yyyy, mm, dd);
		}
	  }
	},
	// mm-dd-yyyy
	{
	  re: /^(\d{1,2})-(\d{1,2})-(\d{4})$/,
	  handler: function(db, bits) {
	  	test = db.getFormat();
	  	if (db.options.calendarOptions.showsTime) {
	  		test = db.getFormat().split(' ')[1];
	  	}
	  	if (test == '%d-%m-%Y') {
		  // if the config is set to use a different schema, then use that instead
		  var yyyy = parseInt(bits[3], 10);
		  var dd = parseInt(bits[1], 10);
		  var mm = parseInt(bits[2], 10) - 1;
		} else {
		  var yyyy = parseInt(bits[3], 10);
		  var dd = parseInt(bits[2], 10);
		  var mm = parseInt(bits[1], 10) - 1;
		}

		if ( db.dateInRange( yyyy, mm, dd ) ) {
		   return db.getDateObj(yyyy, mm, dd);
		}

	  }
	},
	// mm.dd.yyyy || dd.mm.yyyy
	{
	  re: /^(\d{1,2})\.(\d{1,2})\.(\d{4})$/,
	  handler: function(db, bits) {
		test = db.getFormat();
	  	if (db.options.calendarOptions.showsTime) {
	  		test = db.getFormat().split(' ')[1];
		}
	  	if (test == '%d.%m.%Y') {
			var dd = parseInt(bits[1], 10);
			var mm = parseInt(bits[2], 10);
		} else {
			var mm = parseInt(bits[1], 10);
			var dd = parseInt(bits[2], 10);
		}

		mm -= 1;
		var yyyy = parseInt(bits[3], 10);

		if ( db.dateInRange( yyyy, mm, dd ) ) {
		  return db.getDateObj(yyyy, mm, dd);
		}
	  }
	},
	// yyyy-mm-dd (ISO style) + yyyy.mm.dd
	{
	  re: /^(\d{4})[-|\.](\d{1,2})[-|\.](\d{1,2})$/,
	  handler: function(db, bits) {
		var yyyy = parseInt(bits[1], 10);
		var dd = parseInt(bits[3], 10);
		var mm = parseInt(bits[2], 10) - 1;

		if ( db.dateInRange( yyyy, mm, dd ) ) {
		  return db.getDateObj(yyyy, mm, dd);
		}
	  }
	},
	// yy-mm-dd (ISO style) short year + yy.mm.dd
	{
	  re: /^(\d{1,2})[-|\.](\d{1,2})[-|\.](\d{1,2})$/,
	  handler: function(db, bits) {
		var d = new Date();
		test = db.getFormat();
	  	if (db.options.calendarOptions.showsTime) {
	  		test = db.getFormat().split(' ')[1];
		}
	  	if (test == '%d-%m-%y' || test == '%d.%m.%y') {
	  		var yyyy = d.getFullYear() - (d.getFullYear() % 100) + parseInt(bits[3], 10);
	  		var dd = parseInt(bits[1], 10);
	  		var mm = parseInt(bits[2], 10) - 1;
	  	} else if (test == '%m-%d-%y' || test == '%m.%d.%y') {
	  		var yyyy = d.getFullYear() - (d.getFullYear() % 100) + parseInt(bits[3], 10);
	  		var dd = parseInt(bits[2], 10);
	  		var mm = parseInt(bits[1], 10) - 1;
	  	} else {
	  		var yyyy = d.getFullYear() - (d.getFullYear() % 100) + parseInt(bits[1], 10);
	  		var dd = parseInt(bits[3], 10);
	  		var mm = parseInt(bits[2], 10) - 1;
	  	}

		if ( db.dateInRange( yyyy, mm, dd ) ) {
		  return db.getDateObj(yyyy, mm, dd);
		}
	  }
	},
	// mm-dd (ISO style) omitted year
	{
	  re: /^(\d{1,2})-(\d{1,2})$/,
	  handler: function(db, bits) {
		var d = new Date();
		var yyyy = d.getFullYear();
		var dd = parseInt(bits[2], 10);
		var mm = parseInt(bits[1], 10) - 1;

		if ( db.dateInRange( yyyy, mm, dd ) ) {
		   return db.getDateObj(yyyy, mm, dd);
		}
	  }
	}
  ],

  /* Methods ------------------------------------------------------------ */

  setDateType: function() {
/*	switch (this.options.format) {
		case 'typo3':
			if(this.options.calendarOptions.showsTime) {
				this.options.calendarOptions.ifFormat = '%H:%M %d-%m-%Y';
				this._formatString = 'HH:MM dd-mm-yyyy';
				this.options.format = 'dd-mm-yyyy';
			}
			else {
				this.options.calendarOptions.ifFormat = '%d-%m-%Y';
				this._formatString = 'dd-mm-yyyy';
				this.options.format = 'dd-mm-yyyy';
			}
			break;

		case 'typo3US':
			if(this.options.calendarOptions.showsTime) {
				this.options.calendarOptions.ifFormat = '%H:%M %m-%d-%Y';
				this._formatString = 'HH:MM mm-dd-yyyy';
				this.options.format = 'mm-dd-yyyy';
			}
			else {
				this.options.calendarOptions.ifFormat = '%m-%d-%Y';
				this._formatString = 'mm-dd-yyyy';
				this.options.format = 'mm-dd-yyyy';
			}
			break;

	  case 'mm/dd/yyyy':
	  case 'us':
		this.options.calendarOptions.ifFormat = '%m/%d/%Y';
		this._formatString = 'mm/dd/yyyy';
		break;
	  case 'mm.dd.yyyy':
	  case 'de':
		this.options.calendarOptions.ifFormat = '%m.%d.%Y';
		this._formatString = 'mm.dd.yyyy';
		break;
	  case 'dd/mm/yyyy':
		this.options.calendarOptions.ifFormat = '%d/%m/%Y';
		this._formatString = 'dd/mm/yyyy';
		break;
	  case 'dd-mm-yyyy':
		this.options.calendarOptions.ifFormat = '%d-%m-%Y';
		this._formatString = 'dd-mm-yyyy';
		break;
	  case 'yyyy-mm-dd':
	  case 'iso':
	  case 'default':*/

	  	this.options.calendarOptions.ifFormat = this.options.format;
		this._formatString = this.options.format.replace(/%Y/, 'yyyy').replace(/%d/,
			'dd').replace(/%m/, 'mm').replace(/%H/, 'HH').replace(/%M/, 'MM').replace(/%y/, 'yy');
  },

  /* Takes a string, returns the index of the month matching that string,
	 throws an error if 0 or more than 1 matches
  */
  parseMonth: function(month) {
  	var matches = this._monthNames.findAll(function(item) {
	  return new RegExp("^" + month, "i").test(item);
	});
	if (matches.length == 0) {
	  throw new Error("Falsche Monatsangabe. Format: " + this.getFormat());
	}
	if (matches.length < 1) {
	  throw new Error("Uneindeutige Monatsangabe. Format: " + this.getFormat());
	}
	return this.monthNames.indexOf(matches[0]);
  },

  /* Same as parseMonth but for days of the week */
  parseWeekday: function(weekday) {
	var matches = this._weekdayNames.findAll(function(item) {
	  return new RegExp("^" + weekday, "i").test(item);
	});
	if (matches.length == 0) {
	  throw new Error("Falsche Tagesangabe. Format: " + this.getFormat());
	}
	if (matches.length < 1) {
	  throw new Error("Uneindeutige Tagesangabe. Format: " + this.getFormat());
	}
	return this._weekdayNames.indexOf(matches[0]);
  },

  getFormat: function() {
	switch (this.options.format) {
	  case 'de':
	   format = 'mm.dd.yyyy'; break;
	  case 'us':
	   format = 'mm/dd/yyyy'; break;
	  case 'iso':
	   format = 'yyyy-mm-dd'; break;
	  default:
	   format = this.options.format; break;
	}

	return format;
  },

  /* Given a year, month, and day, perform sanity checks to make
	 sure the date is sane.
  */
  dateInRange: function(yyyy, mm, dd) {
  	// if month out of range
	if ( mm < 0 || mm > 11 )
	  throw new Error('Falsche Monatsangabe.  Erlaubte Werte zwischen 1 und 12. Format: ' + this.getFormat());

	if (!this.options.autoRollOver) {
	  // get last day in month
	  var d = (11 == mm)
		? new Date(yyyy + 1, 0, 0)
		: new Date(yyyy, mm + 1, 0);

	  // if date out of range
	  if ( dd < 1 || dd > d.getDate() ) {
		throw new Error('Nicht erlaubtes Datum.  Erlaubte Daten '
		  + this._monthNames[mm]
		  + ' are 1 to '
		  + d.getDate().toString()
		  + '. Format: ' + this.getFormat()
		);
	  }
	}

	return true;
  },

  /* Get Date Object */

  getDateObj: function(yyyy, mm, dd) {
	var obj = new Date();

	obj.setDate(1);
	obj.setYear(yyyy);
	obj.setMonth(mm);
	obj.setDate(dd);

	return obj;
  },

  /* Take a string and run it through the dateParsePatterns.
	 The first one that succeeds will return a Date object. */
  parseDateString: function(s) {
	var dateParsePatterns = this._dateParsePatterns;
	for (var i = 0; i < dateParsePatterns.length; i++) {
	  var re      = dateParsePatterns[i].re;
	  var handler = dateParsePatterns[i].handler;
	  var bits    = re.exec(s);
	  if (bits) {
		return handler(this, bits);
	  }
	}
	throw new Error("Falsches Datumsformat. Format: " + this.getFormat());
  },

  /* Put an extra 0 in front of single digit integers. */
  zeroPad: function(integer) {
	if (integer < 10) {
	  return '0' + integer;
	} else {
	  return integer;
	}
  },

  /* Try to make sense of the date in id.value . */
  magicDate: function() {
	var input = $(this.options.calendarOptions.inputField);
	var messageSpan = $(this.options.messageField);

	try {
		// user date
		var val = '';
		var time = '';
		var split = input.value.split(' ');
		if (split[1]) {
			time = split[0];
			val = split[1];
		} else
			val = split[0];

	  var d = this.parseDateString(val);
	  var t = time.split(':');

	  var day = this.zeroPad(d.getDate());
	  var month = this.zeroPad(d.getMonth());
	  var year = d.getFullYear();
		var hours = this.zeroPad((t[0] ? t[0] : d.getHours()));
		var minutes = this.zeroPad((t[1] ? t[1] : d.getMinutes()));

		// build date
		d.setFullYear(year);
		d.setMonth(month);
		d.setDate(day);
		d.setHours(hours);
		d.setMinutes(minutes);
		input.value = d.print(this.options.calendarOptions.ifFormat);

	 /* switch (this.options.format) {
		case 'dd/mm/yyyy':
		  input.value = day + '/' + month + '/' + year;
		  break;
		case 'dd-mm-yyyy':
		  input.value = day + '-' + month + '-' + year;
		  break;
		case 'mm-dd-yyyy':
		  input.value = month + '-' + day + '-' + year;
		  break;
		case 'mm/dd/yyyy':
		case 'us':
		  input.value = month + '/' + day + '/' + year;
		  break;
		case 'mm.dd.yyyy':
		case 'de':
		  input.value = month + '.' + day + '.' + year;
		  break;
		case 'default':
		case 'iso':
		case 'yyyy-mm-dd':
		default:
		  input.value = year + '-' + month + '-' + day;
		  break;
	  }*/

		// add time
/*		if (this.options.calendarOptions.showsTime) {
			if (!time)
				input.value = hours + ':' + minutes + ' ' + input.value;
			else
				input.value = time + ' ' + input.value;
		}*/

	  // Human readable date
	  if (messageSpan) {
		messageSpan.innerHTML = d.toLocaleDateString();
		messageSpan.className = this.options.classNameSuccess;
	  }
	} catch (e) {
	  input.className = this.options.classNameError;

	  if (messageSpan) {
		var message = e.message;
		// Fix for IE6 bug
		if (message.indexOf('is null or not an object') > -1) {
			message = 'Falsches Datumsformat.';
		}
		messageSpan.innerHTML = message;
		messageSpan.className = this.options.classNameError;
	  }
	}
  },

  /* Key listener to catch the enter and return event */
  keyObserver: function(event, action) {
	if (!event) { var event = window.event; }
	var keyCode = (event.keyCode) ? event.keyCode : ((event.which) ? event.which : event.charCode);

  	if (keyCode == 13 || keyCode == 10) {
  		switch(action) {
			case 'parse':
			  this.magicDate();
			  break;
			case 'return':
			case 'false':
			default:
			  return false;
			  break;
  		}
	}
  },

  setDefaultFormatMessage: function() {
	$(this.options.messageField).innerHTML = this._formatString;
  }
};

DatetimeToolbocks.windowProperties = function(params) {
	var oRegex = new RegExp('');
	oRegex.compile( "(?:^|,)([^=]+)=(\\d+|yes|no|auto)", 'gim' );

	var oProperties = new Object();
	var oPropertiesMatch;

	while( ( oPropertiesMatch = oRegex.exec( params ) ) != null )  {
		var sValue = oPropertiesMatch[2];

		if ( sValue == ( 'yes' || '1' ) ) {
			oProperties[ oPropertiesMatch[1] ] = true;
		} else if ( (! isNaN( sValue ) && sValue != 0) || ('auto' == sValue ) ) {
			oProperties[ oPropertiesMatch[1] ] = sValue;
		}
	}

	return oProperties;
};

DatetimeToolbocks.windowOpenCenter = function(url, name, properties){
	  try {
		var oProperties = DatetimeToolbocks.windowProperties(properties);

		//if( !oProperties['autocenter'] || oProperties['fullscreen'] ) {
		//  return window.open(window_url, window_name, window_properties);
		//}

		w = parseInt(oProperties['width']);
		h = parseInt(oProperties['height']);
		w = w > 0 ? w : 640;
		h = h > 0 ? h : 480;

		if (screen) {
		  t = (screen.height - h) / 2;
		  l = (screen.width - w) / 2;
		} else {
		  t = 250;
		  l = 250;
		}

		properties = (w>0?",width="+w:"") + (h>0?",height="+h:"") + (t>0?",top="+t:"") + (l>0?",left="+l:"") + "," + properties.replace(/,(width=\s*\d+\s*|height=\s*\d+\s*|top=\s*\d+\s*||left=\s*\d+\s*)/gi, "");
		return window.open(url, name, properties);
	  } catch( e ) {};
};
