// The global array of objects that have been instanciated
if (!Bs_Objects) {var Bs_Objects = [];};

/*
* unfortunately, these need to be in the global scope.
*/
function bs_dp_inputFieldBlur() {
	event.srcElement.bsObj.updateByInputFieldBlur();
}
function bs_dp_inputFieldChange() {
	event.srcElement.bsObj.updateByInputFieldChange();
}
function bs_dp_inputFieldWheel() {
	return event.srcElement.bsObj.updateByMouseWheel();
}



/**
* JavaScript DatePicker Component.
* 
* Lets the user type in a date or select it from a popin calendar visually. 
* Works like the windows date utility where you set the system date.
* 
* <b>Features:</b> 
* - IE6 and NS(Mozilla7) compliant.
* - Simulates a normal HTML-form text input field; so your form-hanling will not change.
* - Reads american (mm/dd/yyyy), european (dd.mm.yyyy) and iso (yyyy-mm-dd) dates. 
* - Validates the dates, also detects things like 2003-02-29 as wrong.
* - Can work with dates from before 1970.
* - Customized GUI (Colors can be customized.)
* - Language support (currently en/de/fr, create your own, check the lang folder.)
* 
* <b>Includes (+Dependences):</b>
* <code>
*   <script type="text/javascript" src="/_bsJavascript/core/lang/Bs_Misc.lib.js"></script>
*   <script type="text/javascript" src="/_bsJavascript/core/date/Bs_DateUtil.lib.js"></script>
*   <script type="text/javascript" src="/_bsJavascript/core/form/Bs_FormFieldSelect.class.js" ></script>
*   <script type="text/javascript" src="/_bsJavascript/core/components/Bs_DatePicker.class.js"></script>
*   <script type="text/javascript" src="/_bsJavascript/components/toolbar/Bs_Button.class.js"></script>
* </code>
* 
* <b>How to use:</b>
* 1. Have a look at the examples (see example link below)
* 2. Create a function in the HTML-header called init(). Place the javascript code 
*    that instanciates and inits this component into init().
* 3. Place an 'onLoad' in your body-tag: e.g. <body onLoad="init();">
* 4. In the HTML body: Place a div- or span-tag with a unique ID where you want the component to be.
*    Alternatively you can convert an existing text input field into a datepicker, check the 
*    examples.
* 
* <b>How it works:</b>
* - [After instanciating and initializing the object]. Call the drawInto([tag id]) method.
*   This will search the HTML code for a tag with the given id and insert HTML- and JS- code
*   dynamically (using DOM) to display the component and handle it.
* - This class generates a normal HTML text input field and saves the value in there.
*   When used in a form, it will be submitted like any other HTML-form-field.
* 
* <b>What is returned to the server:</b>
*<pre>
*  +------------------------+------------------------------------------------------+
*  | fieldName              | string, date in format you set. e.g. "2003-12-03"    |
*  +------------------------+------------------------------------------------------+
*  | fieldName +"_month"    | int, (month 1=jan 12=dez)  e.g. "12"                 |
*  +------------------------+------------------------------------------------------+
*  | fieldName +"_year"     | int, (4 digit year)        e.g. "2003"               |
*  +------------------------+------------------------------------------------------+
*  | fieldName +"_display"  | only in some cases. the value in the display format. |
*  +------------------------+------------------------------------------------------+
*</pre>
* 
* 
* common javascript pitfalls (about date handling):
*   - parseInt('09') won't return what you'd think. numbers with a 0 in front are treated as 
*     octal in javascript. use parseInt('09', 10) to specify the base 10.
*   - mozilla (at least 1.2beta) returns 2 digits for pre2000 dates in Date.getYear(). 
*     example: 98 for 1998. for post2000 years it returns 3 strange digits. for 2003 it returns 
*     103 and for 2004 it returns 104. rick says that it returns the 'years since 1900', which 
*     makes sense. i think that's bullshit.
*     todo: check getFullYear() if that helps, but it is js1.3 :/
*   - the javascript Date object has the methods 
*       getYear() to fetch the year
*       getMonth() to fetch the month
*       and not getDay() but getDate() to fetch the day. how stupid.
*   - while the year 2003 means 2003 and the day 3 means the 3rd of the month, the month 2 
*     does not mean february in javascript. months start at 0 (january) and end at 11 (december). 
*     very confusing.
*   - in js the weekday 0 is sunday, not monday. monday is 1, tuesday is 2, ...
* 
* 
* @example components/datepicker/examples/example1.php Simple example
* @example components/datepicker/examples/example2.php Extended example
* @example components/datepicker/examples/example3.php Extended example
* @example components/datepicker/examples/example4.php Extended example
* @example components/datepicker/examples/example5.php Extended example
* @example components/datepicker/examples/example6.php Extended example
* @example components/datepicker/examples/example7.php Extended example
* @example components/datepicker/examples/example8.php Extended example
* @example components/datepicker/examples/example9.php Extended example
* 
* @author     andrej arn <andrej-at-blueshoes-dot-org>
* @param      string the fieldname to use (also used as prefix see header above).
* @package    javascript_components
* @subpackage datepicker
* @copyright  blueshoes.org
*/
function Bs_DatePicker(fieldName) {
	
	/**
  * Unique Object/Tag ID is initialized in the constuctor.
  * Based on this._id. Can be used in genarated JS-code as ID. Is set together 
  * from the classname + this._id (see _constructor() code).
  *
  * @access private
  * @var  string 
  */
  this._objectId;
	
  /**
  * When submitting the form, you'll receive the date value under this name.
	* In other words you'll receive the data back to the server as if you had placed <br>
  * <code><input type=text name="[the fieldName]" id="[the fieldName]"  value="[the value]"></code><br>
	* into your HTML-form. 
	* @access public
	* @var    string
	*/
	this.fieldName = fieldName;
	
	/**
	* the name of the visible field name.
	* the value is this.fieldName + '_display'.
	* @var string visibleFieldName
	*/
	this.visibleFieldName;
	
	/**
	* if the internal and display date format are different then we need a hidden field 
	* for the real value. this var tells if this is the case.
	* then there will be another field submitted to the server: this.fieldName + '_display'.
	* @access private
	* @var    bool false
	*/
	this._hasHiddenField = false;
	
	/**
	* the base dir to the blueshoes javascript directory.
	* if you don't use the framework you should change it, see the examples.
	* @access public
	* @var    string jsBaseDir
	*/
	this.jsBaseDir = '';
	
  /**
  * Directory where the images are. 
  * Feel free to create your own images, put them into another directory (by using the same file names, etc.)
  * @access public
  * @var    string 
	* @since  bs-4.6
  */
  this.imgDir = 'img/';
	
	/**
	* If the datePicker should be open by default.
  * @access public
	* @var    bool openByInit (default is false.)
	* @see    autoClose
	*/
	this.openByInit = false;
	
	/**
	* If the datePicker should be closed automatically when a date is selected.
  * @access public
	* @var    bool autoClose (default is true.)
	* @see    openByInit
	*/
	this.autoClose  = true;
	
	/**
	* if mouse wheeling over the input field should change the value.
	* the mouse wheel feature is disabled by default because it can get quite annoying.
	* @access public
	* @var    bool enableMouseWheel
	* @since  bs-4.6
	*/
	this.enableMouseWheel = true;
	
	/**
	* @access public
	* @since  bs-4.6
	*/
	this.txtHideCalendar = 'Fermer le calendrier';
	
	/**
	* @access public
	* @since  bs-4.6
	*/
	this.txtShowCalendar = 'Choisir votre date';
	
	/**
	* overwrites the validation error msg from the language file, if set.
	* @access public
	* @var    string validateErrorMsg
	*/
	this.validateErrorMsg = "Not a valid date: '__VALUE__'. Try again or use the date picker. Valid formats are:\nAmerican mm/dd/yyyy (eg 12/31/2003)\nEuropean dd.mm.yyyy (eg 31.12.2003)\nISO yyyy-mm-dd (eg 2003-12-31)";
	
	/**
	* DEPRECATED
	* Date validation error message in English.
	* The special string '__VALUE__' can be used and will be replaced with the user-supplied date.
  * @access public
	* @var    string
	* @deprecated see this.validateErrorMsg
	*/
	this.validateErrorMsgEn;
	
	/**
	* overwrites the validation error msg from the language file, if set.
	* @access public
	* @var    string rangeErrorMsg
	*/
	this.rangeErrorMsg = "Not a valid date: '__VALUE__'. It is not in the allowed range.";
	
	/**
	* language iso code. note that such a language file needs to exist in the lang folder.
	* @access private
	* @var    string _language
	* @see    this.setLanguage()
	* @since  bs-4.6
	*/
	this._language = 'en';
	
	/**
	* overwrites the months from the language file, if set.
	* @access public
	* @var    array monthLong
	*/
	this.monthLong = new Array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');;
	
	/**
	* DEPRECATED
	* abbreviated forms are made by just using the first 1, 2 or 3 characters.
  * @access public
	* @var    array monthLongEn (vector)
	* @deprecated see this.monthLong
	*/
	this.monthLongEn;
	
	/**
	* The number of chars to display for the abbreviated month names (0=all).
	* A value of 0(=all), 2 or 3 makes sense. 1 is not recommended.
  * @access public
	* @var    int 
	* @see    monthLong
	*/
	this.monthNumChars = 0;
	
	/**
	* overwrites the days from the language file, if set.
	* @access public
	* @var    array days
	*/
	this.days = new Array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday');;
	
	/**
	* DEPRECATED
	* Abbreviated forms are made by just using the first 1, 2 or 3 characters.
  * @access public
	* @var    array
	* @deprecated see this.days
	*/
	this.daysEn;
	
	/**
	* The number of chars to display for the abbreviated day names.
	* A value of 1, 2 or 3 makes sense. 0 means all but is not recommended.
  * @access public
	* @var    int 
	* @see    days
	*/
	this.daysNumChars = 2;
	
	
	/**
	* how to use the month field. options: 
	*   0 = not at all.
	*   1 = show text only.
	*   2 = use select field.
	*   3 = use select field as number field (spinedit).
	* @access public
	* @var    int useYearField
	* @since  bs-4.6
	*/
	this.useYearField = 3;
	
	/**
	* how to use the month field. options: 
	*   0 = not at all.
	*   1 = show text only.
	*   2 = use select field. default.
	* @access public
	* @var    int useMonthField
	* @since  bs-4.6
	*/
	this.useMonthField = 2;
	
	/**
	* deprecated use this.useYearField instead
	* 
	* if the year input field should be converted into a Bs_NumberField.
	* Default is true *IF* Bs_NumberField is present (included) otherwise false. 
	* it is great fun. see the example 1 at:
	* http://www.blueshoes.org/_bsJavascript/components/datepicker/examples/example1.html
	* 
	* @access public
	* @var    bool useSpinEditForYear
	* @since  bs4.4
	* @deprecated use this.useYearField instead
	*/
	this.useSpinEditForYear	= (typeof(Bs_NumberField) != 'undefined');
	
	/**
	* use arrows to scroll the month to the left/right? default is false.
	* @access public
	* @var    bool useArrows
	* @since  bs-4.6
	*/
	this.useArrows = false;
	
	/**
	* use the 'today' button to go to the current date? default is false.
	* @access public
	* @var    bool useButtonToday
	* @since  bs-4.6
	*/
	this.useButtonToday = false;
	
	
  /**#@+
	* Customize look. By setting a color code (e.g. '#D4D0C8') or color string (e.g. 'white') 
	* you can customize the look.
  * @access public
	* @var    string
	*/
	this.dayHeaderFontColor = '#D4D0C8';
	this.dayHeaderBgColor   = '#808080';
	this.dayFontColor       = 'black';
	this.dayBgColor         = 'white';
	this.dayFontColorActive = 'white';
	this.dayBgColorActive   = '#0A246A';
	this.dayTableBgColor    = 'white';
	this.dayBgColorOver     = '#FFFFE1';
	/**#@-*/
	
	
	/**
	* The table attributes of the day table.
	* By changing this you can for example add horizontal and vertical lines between 
	* the days.
	* 
	* Note:
	* - 'bgcolor' and 'class' cannot be used here, they will be added manually, 
  *
	* @access public
	* @var   string 
	* @see   dayTableBgColor
	*/
	this.dayTableAttributeString = 'width="100%" border="0" cellspacing="0" cellpadding="3"';
	
	/**
	* The width of the whole date picker box in pixel, 150 is a good value.
	* 
	* note that the text input field currently uses the same width (size), even if 
	* a dateInputClassName (css) is defined.
	* 
  * @access public
	* @var    int
	*/
	this.width = 150;
	
  /**
	* How the date should be submitted to the server.
	* 
	* Possible values (default is 'iso'):
  * <pre>
	*   'eu'  => dd.mm.yyyy (eg 31.12.2003)
	*   'us'  => mm/dd/yyyy (eg 12/31/2003)
	*   'iso' => yyyy-mm-dd (eg 2003-12-31)
	* </pre>
  * 
  * @access public
  * @var    string internalDateFormat
  * @see    this.displayDateFormat
  * @since  bs4.5
  * @todo   implement this
	* @see    displayDateFormat, getInternalDateFormat()
  */
	this.internalDateFormat = 'iso';
  
  /**
	* How the date should be shown to the user.
	* 
	* Possible values (default is 'iso'):
  * <pre>
	*   'eu'  => dd.mm.yyyy (eg 31.12.2003)
	*   'us'  => mm/dd/yyyy (eg 12/31/2003)
	*   'iso' => yyyy-mm-dd (eg 2003-12-31)
	* </pre>
  * 
  * @access public
  * @var    string displayDateFormat
  * @see    this.internalDateFormat
  * @since  bs4.5
  * @todo   implement this
	* @see    internalDateFormat, getDisplayDateFormat()
  */
  this.displayDateFormat = 'iso';
  
	/**
	* How the date should be shown to the user and submitted to the server.
  * 
  * !!! DEPRECATED !!! use internalDateFormat and displayDateFormat instaed.
	* 
	* Possible values (default is 'iso'):
  * <pre>
	*   'eu'  => dd.mm.yyyy (eg 31.12.2003)
	*   'us'  => mm/dd/yyyy (eg 12/31/2003)
	*   'iso' => yyyy-mm-dd (eg 2003-12-31)
	* </pre>
	* If you change this value at runtime (after rendering) you have to call 
	* {@link updateInputField()} to make the change active immediatly.
	* 
  * @deprecated use internalDateFormat and displayDateFormat instead.
	* @access public
	* @var    string
	* @see    dateToIsoDate()
	*/
	this.dateFormat = 'iso';
  
	/**
	* is made up based on internalDateFormat and dateFormat.
	* @access private
	*/
	this._internalDateFormat;
	
	/**
	* is made up based on displayDateFormat and dateFormat.
	* @access private
	*/
	this._displayDateFormat;
	
	
	/**
	* if the time should be allowed too, on the right side. 
	* makes it 19 chars long instead of the usual 10 chars. 
	* example: 2004-12-31 23:59:59
	* @access public
	* @var    bool withTime
	* @see    timeFormat
	* @since  bs-4.6
	*/
	this.withTime = false;
	
	/**
	* the format of the time, if used. 
	* the letters are used like in PHP:
	*    H = hours in 2 digits from 00 to 23
	*    i = minutes in 2 digits from 00 to 59
	*    s = seconds in digits from 00 to 59
	* 
	* currently supported are: 'H:i:s', 'H:i'
	* 
	* @access public
	* @var    string timeFormat
	* @see    getCurrentTimeFormatted(), var withTime
	* @since  bs-4.6
	*/
	this.timeFormat = 'H:i:s';
	
	/**
	* if the picker should be rendered absolute. try it.
	* @todo add an example.
	* @access public
	* @var    bool positionAbsolute
	* @since  bs-4.6
	*/
	this.positionAbsolute = false;
	
	/**
	* //If set then overwrites this.dayClassNameByWeekday and this.dayClassName.
	* @var    array (hash with numeric keys 1-31)
  * @access private
	* @todo   implement this
	*/
	this.dayClassNameByDay = new Array();
	
	/**
	* iso date yyyy-mm-dd, default is '1800-01-01'.
	* the lowest date that is still valid (including).
  * @access public
	* @var    string dateRangeLower
	* @since  bs-4.6
	* @see    dateRangeUpper
	*/
	this.dateRangeLower = '1800-01-01';
	
	/**
	* iso date yyyy-mm-dd, default is '2100-12-31'.
	* the highest date that is still valid (including).
  * @access public
	* @var    string dateRangeUpper
	* @since  bs-4.6
	* @see    dateRangeLower
	*/
	this.dateRangeUpper = '2100-12-31';
	
	/**
	* tells which week days are allowed. 
	* array from 0-6 with boolean values where 0=monday and 6=sunday.
	* example to disallow wednesday:
	* myDatePickerObject.allowedWeekdays[2] = false;
	* @access public
	* @var    array allowedWeekdays
	* @since bs-4.6
	*/
	this.allowedWeekdays = new Array(true, true, true, true, true, true, true);
	
	/**
	* Current internal date in iso format. example: '2003-02-15'
	* @access private
	* @see    setDateByChunks(), setDateByIso(), setDateByObject()
	*/
	this._currentDate  = '';
	
	/**
	* current internal time. example: '23:59:59'
	* @access private
	* @see    setTime(), getCurrentTimeFormatted(), var timeFormat
	*/
	this._currentTime  = '00:00:00';
	
  /**#@+
	* The year, month or day of the current internal date.
	* 
	* day:   1-31
	* month: 1-12 (not 0-11 !!!)
	* 
  * @access private
	* @var    string
	* @see    _currentDate, _currentYear, _currentMonth, _currentDate
	*/
	this._currentYear  = 0;
	this._currentMonth = 0;
	this._currentDay   = 0;
	/**#@-*/
	
	
	/**
	* instance of Bs_Button. used to open/close the calendar layer.
	* @access public
	* @var    object toggleButton
	* @since  bs4.4
	*/
	this.toggleButton;
	
	/**
	* the url of the popup window in case the picker should be rendered this way.
	* example: http://www.blueshoes.org/_bsJavascript/components/datepicker/examples/example8.html
	* @access public
	* @var    string popupUrl
	* @see    var asPopup
	*/
	this.popupUrl = 'popup.html';
	
	/**
	* if the picker should be rendered as popup. default is false.
	* example: http://www.blueshoes.org/_bsJavascript/components/datepicker/examples/example8.html
	* @access public
	* @var    bool asPopup
	* @see    var popupUrl
	*/
	this.asPopup;
	
	/**
	* @access private
	* @var    int _lastCursorPos
	* @see    this._rememberCursorPos(), this._resetCursorPos(), this.setCursorPos()
	*/
	this._lastCursorPos = 0;
	
	/**
	* the return of setTimeout() from javascript.
	* @access private
	* @var    (int?) _wheelTimeout
	* @see    this.updateByMouseWheel()
	*/
	this._wheelTimeout;
	
	
	/**
	* a js date object with todays date.
	* @access public
	* @var    object dateToday
	* @since  bs-4.6
	*/
	this.dateToday = new Date();
	
	
	/**
	* the holidays. default is:
	* 
	* new Array(
	* 	new Array(null, 1, 1), 
	* 	new Array(null, 12, 31) 
	* );
	* 
	* @access public
	* @var    array holidays
	* @since  bs-4.6
	*/
	this.holidays = new Array(
		new Array(null, 1, 1), 
		new Array(null, 12, 31) 
	);
	
	
	/**
	* the pseudo constructor.
	* @access private
	* @return void
	*/
	this._constructor = function() {
  	// Put this instance into the global object instance list
    this._id = Bs_Objects.length;
    Bs_Objects[this._id] = this; 
    this._objectId = "Bs_DatePicker_"+this._id;
		
		var btnName = this._objectId + '_tglBtn';
		this.toggleButton = new Bs_Button(btnName);
		eval(btnName + ' = this.toggleButton;');
		this.toggleButton.group           = 'toggleButton';
		this.toggleButton.imgName         = 'bs_calendar';
		this.toggleButton.cssClassDefault = 'bsBtnMouseOver';
		this.toggleButton.attachEvent('Bs_Objects['+this._id+'].toggleSelector();', 'on');
		this.toggleButton.attachEvent('Bs_Objects['+this._id+'].toggleSelector();', 'off');
	}
	
	
	/**
	* sets the language specified.
	* note that such a language file needs to exist in the lang folder, and the file needs to be included.
	* check the examples.
	* @access public
	* @param  string language (iso code.)
	* @return bool (true if the language is available, false if not.)
	* @see    this._language
	* @since  bs-4.6
	*/
	this.setLanguage = function(language) {
		//lang handling
		if (typeof(Bs_DatePicker_Lang) == 'undefined') return false;
		if (typeof(Bs_DatePicker_Lang[language]) == 'undefined') return false;
		this._language = language;
		this.monthLong        = Bs_DatePicker_Lang[language]['months'];
		this.days             = Bs_DatePicker_Lang[language]['days'];
		this.validateErrorMsg = Bs_DatePicker_Lang[language]['errorValidate'];
		this.rangeErrorMsg    = Bs_DatePicker_Lang[language]['errorRange'];
		this.txtHideCalendar  = Bs_DatePicker_Lang[language]['hideCalendar'];
		this.txtShowCalendar  = Bs_DatePicker_Lang[language]['showCalendar'];
		return true;
	}
	
	
	/**
	* disables or enables the datepicker.
	* call this after the datepicker is rendered.
	* @access public
	* @param  bool bool (true=disable, false=enable, nothing=toggle)
	* @return void
	* @since  bs-4.6
	*/
	this.setDisabled = function(bool) {
		try {
			if (typeof(bool) == 'undefined') {
				bool = !document.getElementById(this.fieldName).disabled;
			}
			if (bool) {
				this.toggleButton.setStatus(0);
				document.getElementById(this.fieldName).disabled = true;
			} else {
				this.toggleButton.setStatus(1);
				document.getElementById(this.fieldName).disabled = false;
			}
			//todo: maybe toggleSelector ?
		} catch (e) {
		}
	}
	
	
	/**
	* Sets the internal date with 0 and the input field to "". 
	* @access public
	* @return void
	*/
	this.resetDate = function() {
		this._currentYear  = 0;
		this._currentMonth = 0;
		this._currentDay   = 0;
		this.updateCurrentDate();
		this.updateInputField(); //this line (resetting the input field) was missing until 2005-03-17! --andrej
	}
	
	
	/**
	* Set the date using any date format. tries its best to guess it.
	* use this if you don't know yourself what you have in your hands.
	* @access public
	* @param  string date (eg '2005-02-23' or '2005-02-23 23:59:59' or '23.02.2005' ...)
	* @param  bool reRender (default is true which re-renders the date picker.)
	* @return bool TRUE if date is valid; FALSE otherwise
	* @see    setDateByChunks(), setDateByIso(), setDateByEu(), setDateByObject()
	*/
	this.setDate = function(date, reRender) {
		var isoDate = this.dateToIsoDate(date, true);
		return this.setDateByIso(date, reRender);
	}
	
	
	/**
	* Set the date. 
	* @access public
	* @param  int year  (4 digits if you can)
	* @param  int month (1-12 not 0-11!!!)
	* @param  int day   (1-31)
	* @param  bool reRender (default is true which re-renders the date picker.)
	* @return bool TRUE if date is valid; FALSE otherwise
	*/
	this.setDateByChunks = function(year, month, day, reRender) {
		if (typeof(reRender) == 'undefined') reRender = true;
		year = this.fixYear(year);
		
		var newDate = this.dateToIsoDate(year + '-' + month + '-' + day);
		if (newDate == false) return false; //invalid date
		if (!this.isDateSelectable(year, month, day)) return false;
		
		this._currentYear  = year;
		this._currentMonth = month;
		this._currentDay   = day;
		this.updateCurrentDate();
		
		//re-rendering:
		this.updateInputField();
		if (reRender) this._reRenderDatePicker();
		
		return true;
	}
	
	/**
	* Set the date.
	* @access public
	* @param  int year  (4 digits if you can)
	* @param  int month (1-12)
	* @param  int day   (1-31)
	* @return bool TRUE if date is valid; FALSE otherwise
	* @deprecated because of typo 'Junks' (instead of Chunks :) use setDateByChunks() it's the same.
	*/
	this.setDateByJunks = function(year, month, day) {
	  return this.setDateByChunks(year, month, day);
	}
	
	/**
	* Set the date using ISO format e.g. '2005-02-23'.
	* To reset the date you may also set an empty string - signaling that *no* date is set.
	* @access public
	* @param  string isoDate (eg '2005-02-23' or '2005-02-23 23:59:59'
	* @param  bool reRender (default is true which re-renders the date picker.)
	* @return bool TRUE if date is valid; FALSE otherwise
	*/
	this.setDateByIso = function(isoDate, reRender) {
		if ("" == isoDate) {
      this.resetDate();
		} else {
  		var newDate = this.dateToIsoDate(isoDate);
  		if (newDate == false) return false; //invalid date
			
  		var year  = parseInt(newDate.substr(0, 4), 10);
  		var month = parseInt(newDate.substr(5, 2), 10);
  		var day   = parseInt(newDate.substr(8, 2), 10);
			if (!this.isDateSelectable(year, month, day)) return false;
			
  		this._currentYear  = year;
  		this._currentMonth = month;
  		this._currentDay   = day;
  		this.updateCurrentDate();
			
			if (newDate.length > 14) { //14 is a guess.
				/*
				var hour   = parseInt(newDate.substr(11, 2), 10);
				var minute = parseInt(newDate.substr(14, 2), 10);
				var second = parseInt(newDate.substr(17, 2), 10);
				if (hour   < 10) hour   += '0';
				if (minute < 10) minute += '0';
				if (second < 10) second += '0';
				this._currentTime = hour + ':' + minute + ':' + second;
				*/
				this.setTime(newDate.substr(11));
			}
    }
		
		//re-rendering:
		this.updateInputField();
		if (reRender) this._reRenderDatePicker();
		
		return true;
	}
	
	/**
	* Set the date using EU format e.g. '23.02.2005'.
	* To reset the date you may also set an empty string - signaling that *no* date is set.
	* @access public
	* @param  string isoDate (eg '23.02.2005' or '23.02.2005 23:59:59'
	* @param  bool reRender (default is true which re-renders the date picker.)
	* @return bool TRUE if date is valid; FALSE otherwise
	*/
	this.setDateByEu = function(euDate, reRender) {
		var isoDate = this.dateToIsoDate(euDate, true);
		return this.setDateByIso(isoDate, reRender);
	}
	
	
	/**
	* Set the date using a js date object.
	* @access public
	* @param  object dateObject 
	* @param  bool reRender (default is true which re-renders the date picker.)
	* @return bool TRUE if date is valid; FALSE otherwise
	* @since  bs-4.6
	*/
	this.setDateByObject = function(dateObject, reRender) {
		return this.setDateByChunks(dateObject.getYear(), dateObject.getMonth()+1, dateObject.getDate(), reRender);
	}
	
	/**
	* returns the current date in the current format.
	* @access public
	* @return date
	* @throws bool false (no date set)
	* @since  bs-4.6
	* @see    getDateAsIso(), var displayDateFormat
	*/
	this.getDate = function() {
		if (this._currentYear == 0) return false;
		switch (this.getDisplayDateFormat()) {
			case 'eu':
				return this._currentDay   + '.' + this._currentMonth + '.' + this._currentYear;
			case 'us':
				return this._currentMonth + '/' + this._currentDay   + '/' + this._currentYear;
			case 'iso':
			default:
				return this._currentYear  + '-' + this._currentMonth + '-' + this._currentDay;
		}
	}
	
	/**
	* returns the current date in iso format.
	* @access public
	* @return date
	* @throws bool false (no date set)
	* @see    getDate()
	* @since  bs-4.6
	*/
	this.getDateAsIso = function() {
		if (this._currentYear == 0) return false;
		return this._currentYear + '-' + this._currentMonth + '-' + this._currentDay;
	}
	
	/**
	* sets the time.
	* @access public
	* @var    string time (example: '23:59:59')
	* @return bool
	*/
	this.setTime = function(time) {
		switch (time.length) {
			case 0:
				time = '00:00:00';
			case 2:
				time += ':00:00';
			case 5:
				time += ':00';
		}
		this._currentTime = time;
		
		//re-rendering:
		this.updateInputField();
		
		return true;
	}
	
	
	/**
	* Sets the internal date with todays date. this gets done if 
	* no internal date is set - so the datepicker can start somewhere. 
	* The text form field will still remain empty.
	* @access public
	* @return void
	*/
	this.seedInternalWithCurrentDate = function() {
		var dateNow = new Date();
		this._currentYear  = this.fixYear(dateNow.getYear());
		this._currentMonth = dateNow.getMonth() +1;
		this._currentDay   = dateNow.getDate();
		this.updateCurrentDate();
	}
	
  /**
  * Renders the datepicker component and places it into the page.
	* 
  * @access public
  * @param string tagId id of the tag. (Use <div> or <span> to hole the ID)
	* @return bool true on success, false on failure.
  */
	this.drawInto = function(tagId) {
		var tag = document.getElementById(tagId);
		if ((tag == null) || !tag) return false;
		if (this.asPopup) {
			this.toggleButton.detachEvents('on');
			this.toggleButton.detachEvents('off');
			this.toggleButton.attachEvent('Bs_Objects['+this._id+'].openPopup();', 'on');
			//this.toggleButton.attachEvent('Bs_Objects['+this._id+'].toggleSelector();', 'off');
		} else {
		}
		tag.innerHTML = this.render();
		this._convertYearToSpinEdit();
		return true;
	}
	
	/**
	* @access private
	* @return int
	*/
	this._calculateFieldMaxlength = function() {
		if (this.withTime) {
			switch (this.timeFormat) {
				case 'H:i':
					return 16;
					break;
				default: //also case 'H:i:s':
					return 19;
			}
		} else {
			return 10;
		}
	}
	
	/**
	* takes an existing html form field (of type text) and upgrades it into 
	* a Bs_DatePicker field.
	* 
	* @access public
	* @param  string fieldId
	* @return bool true on success, false on failure.
	*/
	this.convertField = function(fieldId) {
		var origFld = document.getElementById(fieldId);
		if ((origFld == null) || !origFld) return false;
		
		if (origFld.value != '') this.setDate(origFld.value, false);
		
		origFld.maxLength = this._calculateFieldMaxlength();
		
		if (bs_isEmpty(origFld['name']))  origFld['name'] = fieldId; //works in moz but not in ie.
		if (!bs_isEmpty(origFld['name'])) this.fieldName = origFld['name'];
		
		var formatInternal = this.getInternalDateFormat();
		var formatDisplay  = this.getDisplayDateFormat();
		if (formatInternal != formatDisplay) {
			//special case: that input field becomes the visible field. additionally we're going 
			//to add a hidden field which will be responsible for the value. thus we need to change 
			//the name, id and value of the existing field.
			//origFld.name = this.fieldName + '_display';
			//origFld.id   = this.fieldName + '_display';
			origFld.setAttribute('name', this.fieldName + '_display');
			origFld.setAttribute('id', this.fieldName + '_display');
			if (origFld.value != '') {
				var curDate = this.dateToIsoDate(origFld.value, true);
				if (curDate) {
					this.setDateByIso(curDate);
					origFld.value = this.getCurrentDateFormatted();
				}
			}
		} else {
			//
		}
		
		var visibleFieldCode = origFld.outerHTML;
		if ((formatInternal != formatDisplay) && (ie)) { //stupid shitfuck bug
			//alert(visibleFieldCode);
			var myRegExp = new RegExp("name=" + this.fieldName + " ", "i");
			var to   = 'name=' + this.fieldName + '_display ';
			visibleFieldCode = visibleFieldCode.replace(myRegExp, to);
			
			var myRegExp = new RegExp("name=" + this.fieldName + ">", "i");
			var to   = 'name=' + this.fieldName + '_display>';
			visibleFieldCode = visibleFieldCode.replace(myRegExp, to);
			
			var myRegExp = new RegExp("id=" + this.fieldName + " ", "i");
			var to   = 'id=' + this.fieldName + '_display ';
			visibleFieldCode = visibleFieldCode.replace(myRegExp, to);
			
			var myRegExp = new RegExp("id=" + this.fieldName + ">", "i");
			var to   = 'id=' + this.fieldName + '_display>';
			visibleFieldCode = visibleFieldCode.replace(myRegExp, to);
			
			//alert(origFld.name);
			//alert(visibleFieldCode);
		}
		
		
		
		//old: var htmlCode = this.render(true);
		var htmlCode = this.render(visibleFieldCode);
		try {
			// Does NS support this function? --sam  yes with Bs_Misc.lib.js --andrej
			//old: origFld.insertAdjacentHTML('afterEnd', htmlCode);
			origFld.outerHTML = htmlCode;
			origFld = document.getElementById(fieldId); //re-assign it cause we've newly inserted the field.
			
			this._convertYearToSpinEdit();
			origFld.bsObj = this;
			
			var visibleFieldElm = document.getElementById(this.visibleFieldName);
			visibleFieldElm.bsObj = this;
			visibleFieldElm.attachEvent('onblur',       bs_dp_inputFieldBlur);
			visibleFieldElm.attachEvent('onkeyup',      bs_dp_inputFieldChange);
			visibleFieldElm.attachEvent('onmousewheel', bs_dp_inputFieldWheel);
		} catch (e) {
			//alert(e);
		}
		
		if ((origFld.value == '0000-00-00') || (origFld.value == '0000-00-00 00:00:00') || (origFld.value == '0000-00-00 00:00')) {
			origFld.value = '';
		}
		
		return true;
	}
	
	
	/**
  * renders the component and returns the generated html code. 
  * 
  * We added this method to the public API for the advanced user. It's half-private :).
  * This is a call for the advanced user, who would like to fetch the html- / js-
  * that is rendered to make this component running.<br>
	* The average user will normally use {@link drawInto()} or {@link convertField()}, 
	* that will render and place the code into the site in one simple step.
	* 
	* param inputField: 
	*   <null>    => specify nothing if you want the input field to be added automatically.
	*   bool true => if you don't want the input field to be added.
	*   string    => html code of the input field to use
	* 
	* @access public
	* @param  mixed inputField (see above)
  * @return string
  * @see    drawInto(), convertField()
	*/
	this.render = function(inputField) {
		if (typeof(this.monthLongEn)        != 'undefined') this.monthLong        = this.monthLongEn;
		if (typeof(this.daysEn)             != 'undefined') this.days             = this.daysEn;
		if (typeof(this.validateErrorMsgEn) != 'undefined') this.validateErrorMsg = this.validateErrorMsgEn;
		
		var ret = new Array();
		
		//open outer container
		ret[ret.length] = '<table border="0" cellspacing="0" cellpadding="0"><tr><td valign="top">';
		
		//complete date field:
		var formatInternal = this.getInternalDateFormat();
		var formatDisplay  = this.getDisplayDateFormat();
		if (formatInternal != formatDisplay) {
			//need a hidden field because internal and display format are different.
			ret[ret.length] = '<input type="hidden" name="'+this.fieldName+'" id="'+this.fieldName+'" value="'+this.getCurrentDateFormatted(true)+'">';
			this._hasHiddenField = true;
			this.visibleFieldName = this.fieldName + '_display';
		} else {
			this.visibleFieldName = this.fieldName;
		}
		
		if (inputField == true) {
			//do nothing
		} else if (typeof(inputField) == 'string') {
			ret[ret.length] = inputField;
		} else { 'undefined'
			ret[ret.length] = '<input';
			ret[ret.length] = ' type="text"';
			ret[ret.length] = ' name="'    + this.visibleFieldName + '"';
			ret[ret.length] = ' id="'      + this.visibleFieldName + '"';
			ret[ret.length] = ' onblur="Bs_Objects['+this._id+'].updateByInputFieldBlur();change_date_blur('+this._id+');"';
			ret[ret.length] = ' onkeyup="Bs_Objects['+this._id+'].updateByInputFieldChange();"';
			ret[ret.length] = ' onmousewheel="Bs_Objects['+this._id+'].updateByMouseWheel(); return false;"';
			ret[ret.length] = ' size="10"';
			ret[ret.length] = ' maxlength="' + this._calculateFieldMaxlength() + '"';
			var cssWidth = this.width -22;
			if (this.withTime) {
				if (cssWidth < 140) cssWidth = 140;
			}
			ret[ret.length] = ' style="width:' + cssWidth + 'px;"'; //do that even if a dateInputClassName is defined.
			ret[ret.length] = ' class="BsDp_date"';
			ret[ret.length] = ' title="' + this.getCurrentDateReadable() + '"';
			ret[ret.length] = ' value="' + this.getCurrentDateFormatted() + '">';
		}
		
		ret[ret.length] = '</td><td valign="top">';
		
		/*
		//old +/- code:
		ret[ret.length] = ' <span style="font-weight:bold; text-align:middle; cursor:hand; cursor:pointer;" onclick="Bs_Objects['+this._id+'].toggleSelector();" id="' + this._objectId + '_toggleButton" unselectable="on">';
		ret[ret.length] = (this.openByInit) ? '-' : '+';
		ret[ret.length] = '</span>';
		*/
		//new button code:
		this.toggleButton.title = (this.openByInit) ? this.txtHideCalendar : this.txtShowCalendar;
		if (this.openByInit) this.toggleButton.setStatus(2);
		var btnHtml = this.toggleButton.render();
		ret[ret.length] = btnHtml;
		
		//close outer container
		ret[ret.length] = '</td></tr></table>';
		
		//hideable date picker
		if (!this.asPopup) {
			ret[ret.length] = '<div id="' + this._objectId + '_div"';
			//ret[ret.length] = ' style="width:' + this.width + 'px; border:1px solid black;';
			ret[ret.length] = ' style="width:' + this.width + 'px;';
			if (!this.openByInit) {
				ret[ret.length] = ' display:none;';
			}
			if (this.positionAbsolute) {
				ret[ret.length] = ' position:absolute;';
			}
			ret[ret.length] = '"';
			ret[ret.length] = ' class="BsDp_div"';
			
			ret[ret.length] = '>';
			ret[ret.length] = this.renderDatePicker();
			ret[ret.length] = '</div>';
		}
		//alert(ret.join(''));
		return ret.join('');
	}
	
	
	/**
	* renders the date picker; the hideable area.
	* @access private
	* @return string
	*/
	this.renderDatePicker = function() {
		var ret = new Array();
		
		//can only do this after the regular input field has been spitted out.
		if (this._currentDate.length == 0) {
			this.seedInternalWithCurrentDate();
		}
		
		ret[ret.length] = '<div class="BsDp_monthYearDiv">';
		ret[ret.length] = '<table border="0" width="100%" cellpadding="0" cellspacing="0" align="center"><tr>';
		
		//arrow > next month
		if (this.useArrows) {
			ret[ret.length] = '<td align="center" onclick="Bs_Objects['+this._id+'].dateCalc(0, -1);" style="cursor:hand;cursor:pointer;" class="BsDp_arrowLeft">';
			ret[ret.length] = '&nbsp;';
			ret[ret.length] = '<img src="' + this.imgDir + 'arrowLeft.gif' + '" align="middle" border="0"/>';
			ret[ret.length] = '&nbsp;';
			ret[ret.length] = '</span>';
			ret[ret.length] = '</td><td align="center">';
		} else {
			ret[ret.length] = '<td align="center">';
		}
		
		//month select field:
		if (this.useMonthField == 1) {
			ret[ret.length] = '<span class="BsDp_monthText">' + this.monthLong[this._currentMonth -1] + '</span>';
		} else if (this.useMonthField == 2) {
			ret[ret.length] = '<select name="' + this.fieldName + '_month"';
			ret[ret.length] = ' id="' + this._objectId + '_month" size="1"';
			ret[ret.length] = ' onChange="Bs_Objects['+this._id+'].updateByMonth();"';
			ret[ret.length] = ' class="BsDp_month"';
			ret[ret.length] = '>';
			var i = 1;
			for (var m=0; m<this.monthLong.length; m++) {
				ret[ret.length] = '<option value="' + i + '"';
				if (this._currentMonth == i) ret[ret.length] = ' selected';
				ret[ret.length] = '>';
				if (this.monthNumChars > 0) {
					ret[ret.length] = this.monthLong[m].substr(0, this.monthNumChars);
				} else {
					ret[ret.length] = this.monthLong[m];
				}
				ret[ret.length] = '</option>';
				i++;
			}
			ret[ret.length] = '</select>';
		}
		
		ret[ret.length] = '</td>';
		if (moz && this._useSpinEditForYear()) {
			//mozilla bug in 1.5: if set to center then the spinedit buttons will be centered in the middle of the page. 
			//even when they are positioned ABSOLUTE at x/y pixels. very bad. --andrej
			ret[ret.length] = '<td align="left">';
		} else {
			ret[ret.length] = '<td align="center">';
		}
		
		//year field:
		if (this.useYearField == 1) {
			ret[ret.length] = '<span class="BsDp_yearText">' + this._currentYear + '</span>';
		} else if (this.useYearField >= 2) {
			ret[ret.length] = '<input type="text" name="' + this.fieldName + '_year"';
			ret[ret.length] = ' id="' + this._objectId + '_year" value="' + this._currentYear + '"';
			ret[ret.length] = ' size="4" maxlength="4" onKeyUp="Bs_Objects['+this._id+'].updateByYearChange();"';
			ret[ret.length] = ' onBlur="Bs_Objects['+this._id+'].updateByYearBlur();"';
			ret[ret.length] = ' class="BsDp_year"';
			ret[ret.length] = '>';
			if (this._useSpinEditForYear()) {
				//spaceholder for the spinedit buttons
				ret[ret.length] = '&nbsp;&nbsp;&nbsp;';
			}
		}
		
		//arrow > next month
		if (this.useArrows) {
			ret[ret.length] = '</td><td align="center" onclick="Bs_Objects['+this._id+'].dateCalc(0, 1);" style="cursor:hand;cursor:pointer;" class="BsDp_arrowRight">';
			ret[ret.length] = '&nbsp;';
			ret[ret.length] = '<img src="' + this.imgDir + 'arrowRight.gif' + '" align="middle" border="0"/>';
			ret[ret.length] = '&nbsp;';
		}
		
		ret[ret.length] = '</td>';
		ret[ret.length] = '</tr></table>';
		
		//ret[ret.length] = '</nobr>';
		ret[ret.length] = '</div>'; //<br>
		
		//div for day table:
		ret[ret.length] = '<div id="' + this._objectId + '_dayDiv" class="BsDp_dayTableDiv">';
		ret[ret.length] = this.renderDayTable(this._currentYear, this._currentMonth, this._currentDay);
		ret[ret.length] = '</div>';
		
		if (this.useButtonToday) {
			ret[ret.length] = '<div class="BsDp_buttonsDiv">';
			ret[ret.length] = '<img src="' + this.imgDir + 'en_btnToday.gif' + '" align="middle" border="0"';
			ret[ret.length] = ' onclick="Bs_Objects['+this._id+'].setDateByObject(Bs_Objects['+this._id+'].dateToday);"';
			ret[ret.length] = ' style="cursor:hand; cursor:pointer;"';
			ret[ret.length] = '/>';
			ret[ret.length] = '</div>';
		}
		
		return ret.join('');
	}
	
	/**
	* re-renders the date picker; the hideable area.
	* @access private
	* @return void
	*/
	this._reRenderDatePicker = function() {
		var div = document.getElementById(this._objectId + '_div');
		if ((div == null) || !div) return;
		div.innerHTML = this.renderDatePicker();
		this._convertYearToSpinEdit();
	}
	
	
	/**
	* Renders the day table and returns it as string.
	* Outsourced here so we can only render the daytable without the rest.
	* @access private
	* @param  int currentYear
	* @param  int currentMonth
	* @param  int currentDay
	* @return string
	*/
	this.renderDayTable = function(currentYear, currentMonth, currentDay) {
		var day            = 1;
		var lastDayOfMonth = this.getNumberOfDays(currentYear, currentMonth);
		var ret            = new Array();
		
		//day table:
		ret[ret.length] = '<table ' + this.dayTableAttributeString;
		ret[ret.length] = ' bgcolor="' + this.dayTableBgColor + '"';
		ret[ret.length] = ' class="BsDp_dayTable"';
		ret[ret.length] = '>';
		
		if (currentYear >= 1970) {
			//day header line
			//ret[ret.length] = '<tr>';
			ret[ret.length] = '<tr bgcolor="' + this.dayHeaderBgColor + '">';
			//for (var d in this.days) {
			for (var d=0; d<this.days.length; d++) {
				ret[ret.length] = '<td width="14%" bgcolor="' + this.dayHeaderBgColor + '" align="right"';
				//ret[ret.length] = '<span';
				ret[ret.length] = ' title="' + this.days[d] + '"';
				ret[ret.length] = ' class="BsDp_dayHeader"';
				ret[ret.length] = ' style="color:' + this.dayHeaderFontColor + '; cursor:default;">';
				if (this.daysNumChars > 0) {
					ret[ret.length] = this.days[d].substr(0, this.daysNumChars);
				} else {
					ret[ret.length] = this.days[d];
				}
				//ret[ret.length] = '</span>';
				ret[ret.length] = '</td>';
			}
			ret[ret.length] = '</tr>';
			var dateObjFirst = new Date(currentYear, currentMonth-1, 1, 0, 0, 0);
			var weekDayFirst = dateObjFirst.getDay();
			if (weekDayFirst == 0) weekDayFirst = 7; //sunday
		} else {
			var weekDayFirst = 1; //dunno where the week starts.
		}
		//day lines
		for (var i=0; i<6; i++) {
			ret[ret.length] = '<tr>';
			for (var j=1; j<8; j++) {
				var isSelectableDate = this.isDateSelectable(this._currentYear, this._currentMonth, day);
				if ((day > lastDayOfMonth) || ((i == 0) && (j < weekDayFirst))) {
					ret[ret.length] = '<td>&nbsp;</td>';
				} else {
					ret[ret.length] = '<td';
					ret[ret.length] = ' id="' + this._objectId + '_td' + day + '"';
					ret[ret.length] = ' align="right"';
					if (isSelectableDate) {
						ret[ret.length] = ' onMouseOver="Bs_Objects['+this._id+'].dayMouseOver(' + day + ');"';
						ret[ret.length] = ' onMouseOut="Bs_Objects['+this._id+'].dayMouseOut(' + day + ');"';
						//ret[ret.length] = ' onClick="Bs_Objects['+this._id+'].updateByDay(' + day + ');change_date_blur('+this._id+');afficherElements();"';
						ret[ret.length] = ' onClick="Bs_Objects['+this._id+'].updateByDay(' + day + ');change_date_blur('+this._id+');"';
						ret[ret.length] = ' onMouseDown="afficherElements();"';

					}
					var classArr = new Array();
					classArr[classArr.length] = 'BsDp_day';
					classArr[classArr.length] = 'BsDp_weekday' +j;
					if (typeof(this.dayClassNameByDay[day])   != 'undefined') classArr[classArr.length] = this.dayClassNameByDay[day];
					
					if (this.isHoliday(this._currentYear, this._currentMonth, day)) {
						classArr[classArr.length] = 'BsDb_holiday';
					}
					
					//currentYear, currentMonth, 
					if ((this.dateToday.getDate() == day) && (this.dateToday.getMonth() == (this._currentMonth -1)) && (this.fixYear(this.dateToday.getYear()) == this._currentYear)) {
						classArr[classArr.length] = 'BsDp_dayToday';
					}
					//if (day == currentDay) classArr[classArr.length] = 'BsDp_dayCurrent'; //would need js to update that on each date change.
					if (!isSelectableDate) classArr[classArr.length] = 'BsDp_dayUnselectable';
					
					if (classArr.length > 0) {
						ret[ret.length] = ' class="' + classArr.join(' ') + '"';
						ret[ret.length] = ' style="';
					} else {
						ret[ret.length] = ' style="font-family:arial; font-size:11px;';
					}
					if (isSelectableDate) {
						ret[ret.length] = 'cursor:hand; cursor:pointer;';
					} else {
						ret[ret.length] = 'cursor:default;';
					}
					if (day == currentDay) {
						ret[ret.length] = ' color:' + this.dayFontColorActive + '; background-color:' + this.dayBgColorActive + ';';
					} else {
						//ret[ret.length] = ' color:' + this.dayFontColor + '; background-color:' + this.dayBgColor + ';';
					}
					ret[ret.length] = '">';
					ret[ret.length] = day;
					ret[ret.length] = '</td>';
					day++;
				}
			}
			ret[ret.length] = '</tr>';
			if (day >= (lastDayOfMonth +1)) break; //don't display last line if not needed.
		}
		ret[ret.length] = '</table>';
		return ret.join('');
	}
	
	/**
	* re-renders the square day table. needed after date changes when already rendered.
	* @access private
	* @return void
	*/
	this.updateDayTable = function() {
		document.getElementById(this._objectId + '_dayDiv').innerHTML = this.renderDayTable(this._currentYear, this._currentMonth, this._currentDay);
	}
	
	/**
	* Returns the current date [and time] in a human-readable format.
	* examples: 
	* - for post-1970 dates: "Monday, January 1st 2003"
	* - for pre-1970 dates:  "January 1st 1960"
	* - with time: "Monday, January 1st 2003 23:59:59"
	* 
	* @access public
	* @return string (empty string if no date was set so far.)
	*/
	this.getCurrentDateReadable = function() {
		if (this._currentDay   == 0) return '';
		if (this._currentMonth == 0) return '';
		if (this._currentYear  == 0) return '';
		
		var ret = '';
		
		//add weekday
		var dateObj = new Date(this._currentYear, this._currentMonth -1, this._currentDay);
		var weekDay = dateObj.getDay();
		if (weekDay == 0) weekDay = 7; //sunday
		ret += this.days[weekDay -1] + ', '
		
		ret += this.monthLong[this._currentMonth -1] + ' ';
		ret += this._currentDay;
		switch (this._currentDay) {
			case 1: case 21: case 31:
				ret += 'st'; break;
			case 2: case 22:
				ret += 'nd'; break;
			case 3: case 23:
				ret += 'rd'; break;
			default:
				ret += 'th';
		}
		ret += ' ' + this._currentYear;
		
		if (this.withTime) {
			ret += ' ' + this.getCurrentTimeFormatted(); //this._currentTime;
		}
		return ret;
	}
	
	
	/**
	* Returns the current internal date [and time] formatted 
	* based on {@link displayDateFormat} or {@link internalDateFormat} 
	* depending on param internal.
	* 
	* @access public
	* @param  bool internal (
	* @return string (empty string if no current date.)
	* @see displayDateFormat
	*/
	this.getCurrentDateFormatted = function(internal) {
		if (this._currentDay   == 0) return '';
		if (this._currentMonth == 0) return '';
		if (this._currentYear  == 0) return '';
		
		var format = ((typeof(internal) != 'undefined') && internal) ? this.getInternalDateFormat() : this.getDisplayDateFormat();
		switch (format) {
			case 'us':
				var ret = '';
				if (this._currentMonth < 10) ret += '0';
				ret += this._currentMonth + '/';
				if (this._currentDay < 10) ret += '0';
				ret += this._currentDay + '/';
				ret += this._currentYear;
				break;
			case 'eu':
				var ret = '';
				if (this._currentDay < 10) ret += '0';
				ret += this._currentDay + '.';
				if (this._currentMonth < 10) ret += '0';
				ret += this._currentMonth + '.';
				ret += this._currentYear;
				break;
			default: //also case 'iso'
				var ret = this._currentDate;
		}
		if (this.withTime) {
			ret += ' ' + this.getCurrentTimeFormatted(); //this._currentTime;
		}
		return ret;
	}
	
	/**
	* Returns the current internal time formatted based on {@link timeFormat}.
	* @access public
	* @return string (empty string if no current time.)
	* @see    var timeFormat, getCurrentDateFormatted()
	* @since  bs-4.6
	*/
	this.getCurrentTimeFormatted = function() {
		if (!this.withTime) return '';
		switch (this.timeFormat) {
			case 'H:i':
				return this._currentTime.substr(0, 5);
				break;
			default: //also case 'H:i:s':
				return this._currentTime;
		}
	}
	/*
	this.formatTime = function() {
	}*/

	
	/**
	* Updates the input field with the new/actual internal value.
	* @access private
	* @return void
	*/
	this.updateInputField = function() {
		var formatInternal = this.getInternalDateFormat();
		var formatDisplay  = this.getDisplayDateFormat();
		if (formatInternal != formatDisplay) {
			//update hidden field:
			var fld = document.getElementById(this.fieldName);
			if ((fld == null) || !fld) return;
	 		fld.value = this.getCurrentDateFormatted(true);
			//update text field:
			var fld = document.getElementById(this.visibleFieldName);
			if ((fld == null) || !fld) return;
	 		fld.value = this.getCurrentDateFormatted();
		  fld.title = this.getCurrentDateReadable();
		} else {
			var fld = document.getElementById(this.fieldName);
			if ((fld == null) || !fld) return;
	 		fld.value = this.getCurrentDateFormatted();
		  fld.title = this.getCurrentDateReadable();
		}
	}
	
	
	/**
	* scrolling allows to modify the date.
	* 
	* default: goes dates up and down in 1-day steps.
	* if the cursor is inside the field then it scrolls the part that is active. 
	* this can be the year, month or day.
	* 
	* @access private
	* @return bool false (so that the event does not bubble)
	*/
	this.updateByMouseWheel = function() {
		if (!this.enableMouseWheel) return true;
		
		if (typeof(this._wheelTimeout) != 'undefined') clearTimeout(this._wheelTimeout)
		
		var pos = this._rememberCursorPos();
		if (pos > 0) {
			var negative = (event.wheelDelta < 0);
			var calcObj = this._posToDateCalcHelper(pos, negative);
			this.dateCalc(calcObj.calcDay, calcObj.calcMonth, calcObj.calcYear, false);
			this._resetCursorPos();
		} else {
			if (event.wheelDelta > 0) {
				this.dateCalc(1, 0, 0, false);
			} else if (event.wheelDelta < 0) {
				this.dateCalc(-1, 0, 0, false);
			}
		}
		
		this._wheelTimeout = setTimeout('Bs_Objects['+this._id+']._reRenderDatePicker();', 300);
		
		return false;
	}
	
	/**
	* Fires on a change on the input field, but only if the input is currently a valid date.
	* @access private
	* @see updateByInputFieldChange()
	*/
	this.updateByInputFieldChange = function() {
		//alert(window.event.keyCode);
	  switch (window.event.keyCode) {
	    case 16: //shift (to select, like shift-left to select a char)
	    case 35: //end
	    case 36: //home
	    case 37: //cursor-left
	    case 39: //cursor-right
				//really need to ignore these keys.
				return;
	    case 38: //cursor-up
	    case 40: //cursor-down
	    //case 33: //page-up     //page-up-down don't work here cause we're in the key-up
	    //case 34: //page-down   //and not key-down event, it has already fired. up acts like home, down like end.
				var negative = (window.event.keyCode == 40) || ((window.event.keyCode == 34));
				if ((document.selection) && (document.selection.createRange)) {
					var pos = this._rememberCursorPos();
					var calcObj = this._posToDateCalcHelper(pos, negative);
					this.dateCalc(calcObj.calcDay, calcObj.calcMonth, calcObj.calcYear);
					this._resetCursorPos();
				} else {
					this.dateCalc((negative) ? -1 : 1);
				}
				return;
			case 107: //plus
				var fld = document.getElementById(this.fieldName);
				var pos = fld.value.indexOf('+');
				var calcObj = this._posToDateCalcHelper(pos);
				this.dateCalc(calcObj.calcDay, calcObj.calcMonth, calcObj.calcYear);
				this.setCursorPos(pos);
				return;
			default:
				//alert(window.event.keyCode);
		}
		
		var fld = document.getElementById(this.visibleFieldName);
		var userVal = fld.value;
		if ((userVal.length == 10) && (this.dateToIsoDate(userVal) != false)) {
			//if the user is still typing and the date is wrong we don't care. only 
			//fire the warning on blur.
			this.updateByInputFieldBlur();
		}
	}
	
	
	/**
	* Fires on an onblur event in the input field.
	* @access private
	* @see updateByInputFieldChange()
	*/
	this.updateByInputFieldBlur = function() {
		var fld = document.getElementById(this.visibleFieldName);
		var userVal = fld.value;
		
		//changed at all?
		if (userVal == this._currentDate) return;
		
		if (userVal.length > 0) {
			var isDateOk = false;
			do {
				//check that it is a valid date:
				if (this.withTime) {
					var newVal = this.dateToIsoDate(userVal.substr(0, 10));
					var time   = userVal.substr(11);
				} else {
					var newVal = this.dateToIsoDate(userVal);
				}
				if (newVal == false) {
					alert(this.validateErrorMsg.replace(/__VALUE__/, userVal));
					break;
				}
				
				var year  = parseInt(newVal.substr(0, 4), 10);
				var month = parseInt(newVal.substr(5, 2), 10);
				var day   = parseInt(newVal.substr(8, 2), 10);
				
				if (!this.isDateSelectable(year, month, day)) {
					fld.value = '';
					alert(this.rangeErrorMsg.replace(/__VALUE__/, userVal));
					break;
				}
				
				isDateOk = true;
			} while (false);
			
			if (!isDateOk) {
				/*
				this._currentYear  = '';
				this._currentMonth = '';
				this._currentDay   = '';
				this.updateCurrentDate();
				this.updateInputField();
				*/
				return;
			}
			
			this._currentYear  = year;
			this._currentMonth = month;
			this._currentDay   = day;
			this._currentTime  = time;
			this.updateCurrentDate();
			this.updateInputField();
		} else {
			//empty field; just use the current date for the date picker.
			this.seedInternalWithCurrentDate();
		}
		this._reRenderDatePicker();
	}
	
	
	/**
	* is called when the user clicks a day in the picker.
	* @access private
	* @param  int day
	* @return void
	*/
	this.updateByDay = function(day) {
		//unlight old day:
		try {
			var oldTd = document.getElementById(this._objectId + '_td' + this._currentDay);
			oldTd.style.backgroundColor = this.dayBgColor;
			oldTd.style.color           = this.dayFontColor;
		} catch (e) {
			//happens if the old day was no valid date. never mind.
		}
		
		//highlight new day:
		var oldTd = document.getElementById(this._objectId + '_td' + day);
		oldTd.style.backgroundColor = this.dayBgColorActive;
		oldTd.style.color           = this.dayFontColorActive;
		
		this._currentDay = parseInt(day, 10);
		this.updateCurrentDate();
		this.updateInputField();
		
		if (this.autoClose) this.toggleSelector();
	}
	
	/**
	* ???
	* @access private
	*/
	this.updateByMonth = function() {
		var tmp = new Bs_FormFieldSelect();
		var monthSelect = document.getElementById(this._objectId + '_month');
		tmp.init(monthSelect);
		this._currentMonth = parseInt(monthSelect.getValue(), 10);
		
		var isDateOk = false;
		do {
			if (!this.isValidDate(this._currentYear, this._currentMonth, this._currentDay)) {
				break;
			}
			if (!this.isDateSelectable(this._currentYear, this._currentMonth, this._currentDay)) {
				break;
			}
			isDateOk = true;
		} while (false);
		
		this.updateCurrentDate();
		if (isDateOk) {
			this.updateInputField();
		} else {
			var fld = document.getElementById(this.fieldName);
			fld.value = '';
			fld.title = '';
		}
		this.updateDayTable();
	}
	
	/**
	* Only does something if the year is 4 digits. the user may still be typing.
	* @access private
	* @see updateByYearBlur()
	*/
	this.updateByYearChange = function() {
		var tmpYear = parseInt(document.getElementById(this._objectId + '_year').value, 10);
		if ((tmpYear < 2100) && (tmpYear > 1800)) { //hacky
			this.updateByYearBlur();
		}
	}
	
	/**
	* Works on the year no matter if it is valid or not.
	* @access private
	* @see updateByYearChange()
	* @todo check year
	*/
	this.updateByYearBlur = function() {
		var tmpYear = parseInt(document.getElementById(this._objectId + '_year').value, 10);
		tmpYear = this.fixYear(tmpYear);
		this._currentYear = tmpYear;
		
		var isDateOk = false;
		do {
			if (!this.isValidDate(this._currentYear, this._currentMonth, this._currentDay)) {
				break;
			}
			if (!this.isDateSelectable(this._currentYear, this._currentMonth, this._currentDay)) {
				break;
			}
			isDateOk = true;
		} while (false);
		
		this.updateCurrentDate();
		if (isDateOk) {
			this.updateInputField();
		} else {
			var fld = document.getElementById(this.fieldName);
			fld.value = '';
			fld.title = '';
		}
		this.updateDayTable();
	}
	
	
	/**
	* updates the internal date values. 
	* no visual updates are made. use updateInputField() for that.
	* @access private
	* @see    this.updateInputField()
	* @return void
	*/
	this.updateCurrentDate = function() {
		if ((0 == this._currentYear) && 
		    (0 == this._currentMonth) && 
		    (0 == this._currentDay) ) {
		  this._currentDate = "";
		} else {
		  this._currentDate = this._currentYear + '-';
		  if (this._currentMonth < 10) this._currentDate += '0';
	  	this._currentDate += this._currentMonth + '-';
	  	if (this._currentDay < 10) this._currentDate += '0';
	  	this._currentDate += this._currentDay;
    }
	}
	
	
	/**
	* shows/hides the date selector box.
	* @access public
	* @return void
	*/
	this.toggleSelector = function() {
		var div = document.getElementById(this._objectId + '_div');
		//div.style.display = (div.style.display == 'none') ? 'block' : 'none';
		if (div.style.display == 'none') {
			div.style.display = 'block';
			
			//maybe we need to indent the calendar layer:
			var inputField = document.getElementById(this.fieldName);
			if (inputField.offsetLeft > div.offsetLeft) {
				div.style.marginLeft = inputField.offsetLeft + 'px';
			}
			
			var newChar  = '-';
			if (this._useSpinEditForYear()) {
				//re-render the numberfield:
				var objName = this._objectId + '_yObj';
				eval(objName + '.redraw();');
			}
			this.toggleButton.setStatus(2);
			this.toggleButton.setTitle(this.txtHideCalendar);
		} else {
			div.style.display = 'none';
			var newChar  = '+';
			this.toggleButton.setStatus(1);
			this.toggleButton.setTitle(this.txtShowCalendar);
		}
	}
	
	/**
	* Fires when the user moves his mouse over a day.
	* @access private
	* @param int
	* @see   dayMouseOut()
	*/
	this.dayMouseOver = function(day) {
		var td = document.getElementById(this._objectId + '_td' + day);
		if (td.style.backgroundColor.toLowerCase() == this.dayBgColor.toLowerCase()) {
			td.style.backgroundColor = this.dayBgColorOver;
		}
	}
	
	/**
	* Fires when the user moves his mouse out of a day.
	* @access private
	* @param int day
	* @see   dayMouseOver()
	*/
	this.dayMouseOut = function(day) {
		var td = document.getElementById(this._objectId + '_td' + day);
		/*
		//old code, ie only:
		if (td.style.backgroundColor.toLowerCase() == this.dayBgColorOver.toLowerCase()) {
			td.style.backgroundColor = this.dayBgColor;
		}
		*/
		//new code:
		if (day != this._currentDay) {
			td.style.backgroundColor = this.dayBgColor;
		}
	}
	
	
	/**
	* tells if the date specified is a holiday or not.
	* @access public
	* @param  int year
	* @param  int month
	* @param  int day
	* @return bool
	*/
	this.isHoliday = function(year, month, day) {
		year  = parseInt(year);
		month = parseInt(month);
		day   = parseInt(day);
		
		var holidays = this.holidays;
		for (var i=0; i<holidays.length; i++) {
			if ((holidays[i][0] != null) && (holidays[i][0] != year))  continue;
			if ((holidays[i][1] != null) && (holidays[i][1] != month)) continue;
			if ((holidays[i][2] != null) && (holidays[i][2] != day))   continue;
			return true;
		}
		
		return false;
	}
	
	
	/**
	* works in ie only. 
	* @access public
	* @param  int pos
	* @return mixed (int new cursor pos on success, false on failure.)
	* @see    _rememberCursorPos(), _resetCursorPos()
	* @since  bs-4.6
	*/
	this.setCursorPos = function(pos) {
		if ((document.selection) && (document.selection.createRange)) {
			var fld = document.getElementById(this.fieldName);
			var x = document.selection.createRange();
			x.move('character', -(fld.value.length - pos));
			x.select();
			return this._lastCursorPos;
		} else {
			return false;
		}
	}
	
	/**
	* works in ie only. 
	* @access public
	* @return int pos (cursor pos 0-n, failure returns 0.)
	* @see    setCursorPos()
	* @since  bs-4.6
	*/
	this.getCursorPos = function() {
		/*
   if (window.getSelection) alert(window.getSelection());
   else if (document.getSelection) alert(document.getSelection());
        else if (document.selection) alert(document.selection.createRange().text);
  }*/
		
		if ((document.selection) && (document.selection.createRange)) {
			var text = '!';
			var fld = document.getElementById(this.fieldName);
			if (document.activeElement == fld) {
				var caretPos = document.selection.createRange().duplicate();
				caretPos.collapse(false);
				caretPos.text = "!";
				
				var pos = fld.value.indexOf('!');
				
				fld.value = fld.value.substring(0, pos) + fld.value.substring(pos +1);
				caretPos.collapse(false);
				
				return pos;
			}
		}
		return 0;
	}
	
	
	/**
	* @status experimental
	*/
	this.loadSkin = function(skinName, pathpartner) {
		//load css:
		try {
			/*
			//did not work:
			var bodies = document.getElementsByTagName('head');
			bodies[0].insertAdjacentHTML('beforeEnd', '<link rel="stylesheet" href="css/' + skinName + '.css" type="text/css"></link>');
			*/
			
			//This will only work in DOM compatible browsers like Opera 7+, IE5+, and NN6+. 
			var oLink = document.createElement("link")
			oLink.href = pathpartner + '/css/' + skinName + '.css';
			// oLink.href = 'css/' + skinName + '.css';
			oLink.rel  = "stylesheet";
			oLink.type = "text/css";
			document.body.appendChild(oLink);
		} catch (e) {
			//alert(e);
		}
		
		switch (skinName) {
			case 'win2k':
				this.imgDir               =  'images/';
				this.toggleButton.imgPath = this.imgDir;
				this.toggleButton.imgName = 'toggleButton';
				this.daysNumChars   = 1;
				return true;
			case 'osx':
				this.imgDir               =  'images/';
				this.toggleButton.imgPath         = this.imgDir;
				this.toggleButton.imgName         = 'toggleButton';
				this.toggleButton.backgroundColor = 'transparent';
				this.toggleButton.cssClassDefault     = '';
				this.toggleButton.cssClassMouseOver   = '';
				this.toggleButton.cssClassMouseDown   = '';
				this.daysNumChars   = 1;
				this.useMonthField  = 1;
				this.useYearField   = 1;
				this.useArrows      = true;
				this.useButtonToday = false;
				this.dayTableAttributeString    = 'width="100%" border="0" cellspacing="0" cellpadding="2" bordercolor="white"';
				this.dayHeaderFontColor         = 'black';
				this.dayHeaderBgColor           = 'white';
				this.dayFontColor               = 'black';
				this.dayBgColor                 = 'white';
				this.dayFontColorActive         = 'white';
				this.dayBgColorActive           = 'red';
				this.dayTableBgColor            = 'white';
				this.dayBgColorOver             = '#B5B6BD';
				this.autoClose					= 'true';
				this.openByInit = false;
				return true;
				case 'h2ogtk2':
				this.imgDir               =  'img/';
				this.toggleButton.imgPath = this.imgDir;
				this.toggleButton.imgName = 'toggleButton';
				this.daysNumChars   = 1;
				return true;
		}
		return false;
	}
	
	
	/**
	* @access public
	* @since  bs-4.6
	* @status experimental
	*/
	this.openPopup = function() {
		//var url = '/_bsJavascript/components/datepicker/Bs_DatePicker.class.js';
		var url = this.popupUrl + '?objectId=' + this._id;
		var w=window.open(url, 'DatePicker', 'toolbar=no,scrollbars=no,resizable=yes,width=180,height=260'); 
		w.focus();
	}
	/*
this.clone = function() {
	var ret = new Object();
	for (i in this) {
		ret[i] = this[i];
	}
	return ret;
}*/
	
	/**
	* tells if the given date is selectable.
	* @access public
	* @param  int year (4 digits)
	* @param  int month (1-12 not 0-11!)
	* @param  int day (1-31)
	* @return bool
	*/
	this.isDateSelectable = function(year, month, day) {
		if (typeof(this._dateRangeLower_year) == 'undefined') {
			this._dateRangeLower_year  = this.dateRangeLower.substr(0, 4);
			this._dateRangeLower_month = this.dateRangeLower.substr(5, 2);
			this._dateRangeLower_day   = this.dateRangeLower.substr(8, 2);
			this._dateRangeUpper_year  = this.dateRangeUpper.substr(0, 4);
			this._dateRangeUpper_month = this.dateRangeUpper.substr(5, 2);
			this._dateRangeUpper_day   = this.dateRangeUpper.substr(8, 2);
		}
		var isSelectableDate = false;
		do {
			//check the date range:
			if (year < this._dateRangeLower_year) break;
			if ((year == this._dateRangeLower_year) && (month < this._dateRangeLower_month)) break;
			if ((year == this._dateRangeLower_year) && (month == this._dateRangeLower_month) && (day < this._dateRangeLower_day)) break;
			if (year > this._dateRangeUpper_year) break;
			if ((year == this._dateRangeUpper_year) && (month > this._dateRangeUpper_month)) break;
			if ((year == this._dateRangeUpper_year) && (month == this._dateRangeUpper_month) && (day > this._dateRangeUpper_day)) break;
			
			//check the weekday:
			var checkWeekday = false;
			for (var i=0; i<7; i++) {
				if (!this.allowedWeekdays[i]) {
					checkWeekday = true;
					break;
				}
			}
			if (checkWeekday) {
				var dateObj = new Date(year, month-1, day); //js month goes from 0-11 not 1-12.
				var weekday = dateObj.getDay(); 
				weekday--; if (weekday < 0) weekday = 6; // 0=sunday, 1=monday ... so we need to 'fix' that.
				if (!this.allowedWeekdays[weekday]) break;
			}
			
			isSelectableDate = true;
		} while (false);
		return isSelectableDate;
	}
	
	
	/**
	* deprecated, use bs_isValidDate() (from core/date/Bs_DateUtil.lib.js).
	*/
	this.isValidDate = function(year, month, day) {
		return bs_isValidDate(year, month, day);
	}
	
	/**
	* deprecated, use bs_fixYear() (from core/date/Bs_DateUtil.lib.js).
	*/
	this.fixYear = function(year) {
		return bs_fixYear(year);
	}
	
	
	/**
	* deprecated, use bs_getNumberOfDays() (from core/date/Bs_DateUtil.lib.js).
	*/
	this.getNumberOfDays = function(year, month) {
		return bs_getNumberOfDays(year, month);
	}
	
	
	/**
	* deprecated, use bs_dateToIsoDate() (from core/date/Bs_DateUtil.lib.js).
	*/
	this.dateToIsoDate = function(someDate, allowTime) {
		return bs_dateToIsoDate(someDate, allowTime);
	}
	
	/**
	* deprecated, use bs_isLeapYear() (from core/date/Bs_DateUtil.lib.js).
	*/
	this.isLeapYear = function(year) { 
		return bs_isLeapYear(year);
	}
	
	
	/**
	* does calculations with the current date. re-renders the field and calendar.
	* 
	* examples: 
	* to add 3 days to the current date:  yourDatePicker.dateCalc(3);
	* to add a year and subtract a month: yourDatePicker.dateCalc(0, -1, 1);
	* 
	* @access public
	* @param  int addDays
	* @param  int addMonths
	* @param  int addYears
	* @param  bool reRender (default is true which re-renders the date picker.)
	* @return bool (true on success, false on failure or if no current date set.)
	* @since bs-4.6
	*/
	this.dateCalc = function(addDays, addMonths, addYears, reRender) {
		if (typeof(reRender) == 'undefined') reRender = true;
		if (this._currentDay   == 0) return false;
		if (this._currentMonth == 0) return false;
		if (this._currentYear  == 0) return false;
		if (typeof(addDays)   == 'undefined') addDays   = 0;
		if (typeof(addMonths) == 'undefined') addMonths = 0;
		if (typeof(addYears)  == 'undefined') addYears  = 0;
		var tmpDate = new Date(this._currentYear+addYears, this._currentMonth-1+addMonths, this._currentDay+addDays);
		this.setDateByChunks(tmpDate.getYear(), tmpDate.getMonth()+1, tmpDate.getDate(), reRender);
		return true;
	}
	
	
	/**
	* @access private
	* @param  int pos
	* @param  bool negative (default is false, if true then the numbers are inverted.)
	* @return object (with the fields calcDay, calcMonth and calcYear)
	*/
	this._posToDateCalcHelper = function(pos, negative) {
		var ret = new Object();
		ret.calcDay   = 0;
		ret.calcMonth = 0;
		ret.calcYear  = 0;
		switch (this.getDisplayDateFormat()) {
			case 'eu':
				if (pos <= 2) {
					ret.calcDay   = 1;
				} else if (pos <= 5) {
					ret.calcMonth = 1;
				} else {
					ret.calcYear  = 1;
				}
				break;
			case 'us':
				if (pos <= 2) {
					ret.calcMonth = 1;
				} else if (pos <= 5) {
					ret.calcDay   = 1;
				} else {
					ret.calcYear  = 1;
				}
				break;
			default: //also case 'iso':
				if (pos <= 4) {
					ret.calcYear  = 1;
				} else if (pos <= 7) {
					ret.calcMonth = 1;
				} else {
					ret.calcDay   = 1;
				}
		}
		if (negative) {
			ret.calcDay   = -ret.calcDay;
			ret.calcMonth = -ret.calcMonth;
			ret.calcYear  = -ret.calcYear;
		}
		return ret;
	}
	
	
	/**
	* make the year field a number field for easier switching.
	* @access private
	* @return void
	*/
	this._convertYearToSpinEdit = function() {
		if (this._useSpinEditForYear()) {
			var objName = this._objectId + '_yObj';
			var myNf = new Bs_NumberField(this._objectId + '_year');
			eval(objName + ' = myNf;');
			myNf.buttonUp.imgPath   = this.jsBaseDir + 'components/numberfield/img/';
			myNf.buttonDown.imgPath = this.jsBaseDir + 'components/numberfield/img/';
			myNf.minValue = 1800;
			myNf.maxValue = 2300;
			myNf.attachEvent('onAfterChange', 'Bs_Objects['+this._id+'].updateByYearBlur();');
			myNf.draw();
		}
	}
	
	/**
	* @access private
	* @return bool
	*/
	this._useSpinEditForYear = function() {
		return (!this.asPopup &&
			 ( (this.useYearField == 3) && ((typeof(Bs_NumberField) != 'undefined')) ) || 
			 ((typeof(this.useYearField) == 'undefined') && this.useSpinEditForYear)
		);
	}
	
	/**
	* works in ie only. 
	* @access private
	* @return int (cursor pos, 0-n, failure returns 0.)
	* @see    _resetCursorPos(), setCursorPos()
	*/
	this._rememberCursorPos = function() {
		this._lastCursorPos = this.getCursorPos();
		return this._lastCursorPos;
	}
	
	/**
	* works in ie only. 
	* @access private
	* @return mixed (int new cursor pos on success, false on failure.)
	* @see    _rememberCursorPos(), setCursorPos()
	*/
	this._resetCursorPos = function() {
		return this.setCursorPos(this._lastCursorPos)
	}
	
	
	/**
	* @access public
	* @return string
	* @see    getDisplayDateFormat()
	*/
	this.getInternalDateFormat = function() {
		if (typeof(this._internalDateFormat) != 'undefined') return this._internalDateFormat;
		if (this.dateFormat != 'iso') {
			this._internalDateFormat = this.dateFormat;
		} else {
			this._internalDateFormat = this.internalDateFormat;
		}
		return this._internalDateFormat;
	}
	
	/**
	* @access public
	* @return string
	* @see    getInternalDateFormat()
	*/
	this.getDisplayDateFormat = function() {
		if (typeof(this._displayDateFormat) != 'undefined') return this._displayDateFormat;
		if (this.dateFormat != 'iso') {
			this._displayDateFormat = this.dateFormat;
		} else {
			this._displayDateFormat = this.displayDateFormat;
		}
		return this._displayDateFormat;
	}
	
	
	this._constructor(); //call the constructor. needs to be at the end.
	
	
}

