// ===================================================================
// Author: Matt Kruse <matt@mattkruse.com>
// WWW: http://www.mattkruse.com/
//
// NOTICE: You may use this code for any purpose, commercial or
// private, without any further permission from the author. You may
// remove this notice from your final code if you wish, however it is
// appreciated by the author if at least my web site address is kept.
//
// You may *NOT* re-distribute this code in any way except through its
// use. That means, you can include it in your product, or your web
// site, or any other form where the code is actually being used. You
// may not put the plain javascript up on your site for download or
// include it in your javascript libraries for download.
// If you wish to share this code with others, please just point them
// to the URL instead.
// Please DO NOT link directly to my .js files from your site. Copy
// the files to your server and use them there. Thank you.
// ===================================================================

// HISTORY
// ------------------------------------------------------------------
// May 17, 2003: Fixed bug in parseDate() for dates <1970
// March 11, 2003: Added parseDate() function
// March 11, 2003: Added "NNN" formatting option. Doesn't match up
//                 perfectly with SimpleDateFormat formats, but
//                 backwards-compatability was required.

// ------------------------------------------------------------------
// These functions use the same 'format' strings as the
// java.text.SimpleDateFormat class, with minor exceptions.
// The format string consists of the following abbreviations:
//
// Field        | Full Form          | Short Form
// -------------+--------------------+-----------------------
// Year         | yyyy (4 digits)    | yy (2 digits), y (2 or 4 digits)
// Month        | MMM (name or abbr.)| MM (2 digits), M (1 or 2 digits)
//              | NNN (abbr.)        |
// Day of Month | dd (2 digits)      | d (1 or 2 digits)
// Day of Week  | EE (name)          | E (abbr)
// Hour (1-12)  | hh (2 digits)      | h (1 or 2 digits)
// Hour (0-23)  | HH (2 digits)      | H (1 or 2 digits)
// Hour (0-11)  | KK (2 digits)      | K (1 or 2 digits)
// Hour (1-24)  | kk (2 digits)      | k (1 or 2 digits)
// Minute       | mm (2 digits)      | m (1 or 2 digits)
// Second       | ss (2 digits)      | s (1 or 2 digits)
// AM/PM        | a                  |
//
// NOTE THE DIFFERENCE BETWEEN MM and mm! Month=MM, not mm!
// Examples:
//  "MMM d, y" matches: January 01, 2000
//                      Dec 1, 1900
//                      Nov 20, 00
//  "M/d/yy"   matches: 01/20/00
//                      9/2/00
//  "MMM dd, yyyy hh:mm:ssa" matches: "January 01, 2000 12:30:45AM"
// ------------------------------------------------------------------

if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.date = JLG.date || {};
if( !JLG.date.Parser ) {
	
	JLG.date.Parser = function( p_config ) {

		/**
		 * Member variables
		 */
	     this.m_monthNames = [

	         'January',
	         'February',
	         'March',
	         'April',
	         'May',
	         'June',
	         'July',
	         'August',
	         'September',
	         'October',
	         'November',
	         'December',
	         'Jan',
	         'Feb',
	         'Mar',
	         'Apr',
	         'May',
	         'Jun',
	         'Jul',
	         'Aug',
	         'Sep',
	         'Oct',
	         'Nov',
	         'Dec'
	     ];

	     this.m_dayNames = [

	         'Sunday',
	         'Monday',
	         'Tuesday',
	         'Wednesday',
	         'Thursday',
	         'Friday',
	         'Saturday',
	         'Sun',
	         'Mon',
	         'Tue',
	         'Wed',
	         'Thu',
	         'Fri',
	         'Sat'
	     ];
	};
	gQuery.extend( JLG.date.Parser.prototype, {

	    isDate: function (val,format) {

	    	var date=this.getDateFromFormat(val,format);
	    	if (date===0) { return false; }
	    	return true;
		},

	    compareDates: function (date1,dateformat1,date2,dateformat2) {
	    	var d1=this.getDateFromFormat(date1,dateformat1);
	    	var d2=this.getDateFromFormat(date2,dateformat2);
	    	if (d1===0 || d2===0) {
	    		return -1;
	    		}
	    	else if (d1 > d2) {
	    		return 1;
	    		}
	    	return 0;
	    },

	    formatDate: function (date,format) {

	    	format=format+"";
	    	var result="";
	    	var i_format=0;
	    	var c="";
	    	var token="";
	    	var y=date.getYear()+"";
	    	var M=date.getMonth()+1;
	    	var d=date.getDate();
	    	var E=date.getDay();
	    	var H=date.getHours();
	    	var m=date.getMinutes();
	    	var s=date.getSeconds();
	    	var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,KK,K,kk,k;
	    	// Convert real date parts into formatted versions
	    	var value={};
	    	if (y.length < 4) {y=""+(y-0+1900);}
	    	value.y=""+y;
	    	value.yyyy=y;
	    	value.yy=y.substring(2,4);
	    	value.M=M;
	    	value.MM=this._lz(M);
	    	value.MMM=this.m_monthNames[M-1];
	    	value.NNN=this.m_monthNames[M+11];
	    	value.d=d;
	    	value.dd=this._lz(d);
	    	value.E=this.m_dayNames[E+7];
	    	value.EE=this.m_dayNames[E];
	    	value.H=H;
	    	value.HH=this._lz(H);
	    	if (H===0){value.h=12;}
	    	else if (H>12){value.h=H-12;}
	    	else {value.h=H;}
	    	value.hh=this._lz(value.h);
	    	if (H>11){value.K=H-12;} else {value.K=H;}
	    	value.k=H+1;
	    	value.KK=this._lz(value.K);
	    	value.kk=this._lz(value.k);
	    	if (H > 11) { value.a="PM"; }
	    	else { value.a="AM"; }
	    	value.m=m;
	    	value.mm=this._lz(m);
	    	value.s=s;
	    	value.ss=this._lz(s);
	    	while (i_format < format.length) {
	    		c=format.charAt(i_format);
	    		token="";
	    		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
	    			token += format.charAt(i_format++);
	    			}
	    		if (value[token] !== null) { result=result + value[token]; }
	    		else { result=result + token; }
	    		}
	    	return result;
	    },

	    getDateFromFormat: function (val, format) {

	    	val=val+"";
	    	format=format+"";
	    	var fmtLen;
	    	var i_val=0;
	    	var i_format=0;
	    	var c="";
	    	var token="";
	    	var token2="";
	    	var x,y;
	    	var now=new Date();
	    	var year=now.getYear();
	    	var month=now.getMonth()+1;
	    	var date=1;
	    	var hh=0;
	    	var mm=0;
	    	var ss=0;
	    	var ampm="";
	    	var mnLen;
	    	var dnLen;
			var i;
	    	var dayNames = this.m_dayNames;
	    	var monthNames = this.m_monthNames;

	        fmtLen = format.length;
	    	while (i_format < fmtLen) {
	    		// Get next token from format string
	    		c=format.charAt(i_format);
	    		token="";
	    		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
	    			token += format.charAt(i_format++);
	    			}
	    		// Extract contents of value based on format token
	    		if (token=="yyyy" || token=="yy" || token=="y") {
	    			if (token=="yyyy") { x=4;y=4; }
	    			if (token=="yy")   { x=2;y=2; }
	    			if (token=="y")    { x=2;y=4; }
	    			year=this._getInt(val,i_val,x,y);
	    			if (year===null) { return 0; }
	    			i_val += year.length;
	    			if (year.length==2) {
	    				if (year > 70) { year=1900+(year-0); }
	    				else { year=2000+(year-0); }
	    				}
	    			}
	    		else if (token=="MMM"||token=="NNN"){
	    			month=0;
	    			for (i=0, mnLen = monthNames.length; i < mnLen; i++) {
	    				var month_name=monthNames[i];
	    				if (val.substring(i_val,i_val+month_name.length).toLowerCase()==month_name.toLowerCase()) {
	    					if (token=="MMM"||(token=="NNN"&&i>11)) {
	    						month=i+1;
	    						if (month>12) { month -= 12; }
	    						i_val += month_name.length;
	    						break;
	    						}
	    					}
	    				}
	    			if ((month < 1)||(month>12)){return 0;}
	    			}
	    		else if (token=="EE"||token=="E"){
	    			for (i=0, dnLen = dayNames.length; i<dnLen; i++) {
	    				var day_name=dayNames[i];
	    				if (val.substring(i_val,i_val+day_name.length).toLowerCase()==day_name.toLowerCase()) {
	    					i_val += day_name.length;
	    					break;
	    					}
	    				}
	    			}
	    		else if (token=="MM"||token=="M") {
	    			month=this._getInt(val,i_val,token.length,2);
	    			if(month===null||(month<1)||(month>12)){return 0;}
	    			i_val+=month.length;}
	    		else if (token=="dd"||token=="d") {
	    			date=this._getInt(val,i_val,token.length,2);
	    			if(date===null||(date<1)||(date>31)){return 0;}
	    			i_val+=date.length;}
	    		else if (token=="hh"||token=="h") {
	    			hh=this._getInt(val,i_val,token.length,2);
	    			if(hh===null||(hh<1)||(hh>12)){return 0;}
	    			i_val+=hh.length;}
	    		else if (token=="HH"||token=="H") {
	    			hh=this._getInt(val,i_val,token.length,2);
	    			if(hh===null||(hh<0)||(hh>23)){return 0;}
	    			i_val+=hh.length;}
	    		else if (token=="KK"||token=="K") {
	    			hh=this._getInt(val,i_val,token.length,2);
	    			if(hh===null||(hh<0)||(hh>11)){return 0;}
	    			i_val+=hh.length;}
	    		else if (token=="kk"||token=="k") {
	    			hh=this._getInt(val,i_val,token.length,2);
	    			if(hh===null||(hh<1)||(hh>24)){return 0;}
	    			i_val+=hh.length;hh--;}
	    		else if (token=="mm"||token=="m") {
	    			mm=this._getInt(val,i_val,token.length,2);
	    			if(mm===null||(mm<0)||(mm>59)){return 0;}
	    			i_val+=mm.length;}
	    		else if (token=="ss"||token=="s") {
	    			ss=this._getInt(val,i_val,token.length,2);
	    			if(ss===null||(ss<0)||(ss>59)){return 0;}
	    			i_val+=ss.length;}
	    		else if (token=="a") {
	    			if (val.substring(i_val,i_val+2).toLowerCase()=="am") {ampm="AM";}
	    			else if (val.substring(i_val,i_val+2).toLowerCase()=="pm") {ampm="PM";}
	    			else {return 0;}
	    			i_val+=2;}
	    		else {
	    			if (val.substring(i_val,i_val+token.length)!=token) {return 0;}
	    			else {i_val+=token.length;}
	    			}
	    		}
	    	// If there are any trailing characters left in the value, it doesn't match
	    	if (i_val != val.length) { return 0; }
	    	// Is date valid for month?
	    	if (month==2) {
	    		// Check for leap year
	    		if ( ( (year%4===0)&&(year%100 !== 0) ) || (year%400===0) ) { // leap year
	    			if (date > 29){ return 0; }
	    			}
	    		else { if (date > 28) { return 0; } }
	    		}
	    	if ((month==4)||(month==6)||(month==9)||(month==11)) {
	    		if (date > 30) { return 0; }
	    		}
	    	// Correct hours value
	    	if (hh<12 && ampm=="PM") { hh=hh-0+12; }
	    	else if (hh>11 && ampm=="AM") { hh-=12; }
	    	var newdate=new Date(year,month-1,date,hh,mm,ss,0);
	    	return newdate.getTime();
		},

	    parse: function (val) {
	    	var preferEuro=(arguments.length==2)?arguments[1]:false;
	    	window.generalFormats=['y-M-d','MMM d, y','MMM d,y','y-MMM-d','d-MMM-y','MMM d'];
	    	window.monthFirst=['M/d/y','M-d-y','M.d.y','MMM-d','M/d','M-d'];
	    	window.dateFirst =['d/M/y','d-M-y','d.M.y','d-MMM','d/M','d-M'];
	    	var checkList=['generalFormats',preferEuro?'dateFirst':'monthFirst',preferEuro?'monthFirst':'dateFirst'];
	    	var d=null;
	    	var len;
	    	var jLen;
			var j;
			var i;
	    	for (i=0, len = checkList.length; i<len; i++) {
	    		var l=window[checkList[i]];
	    		for (j=0, jLen = l.length; j<jLen; j++) {
	    			d=this.getDateFromFormat(val,l[j]);
	    			if (d!==0) { return new Date(d); }
	    			}
	    		}
	    	return null;
		},

		/**
		 * Private methods
		 */
	    _isInteger: function (val) {
	    	var digits="1234567890";
	    	for (var i=0; i < val.length; i++) {
	    		if (digits.indexOf(val.charAt(i))==-1) { return false; }
	    		}
	    	return true;
	    },

	    _getInt: function (str,i,minlength,maxlength) {
	    	for (var x=maxlength; x>=minlength; x--) {
	    		var token=str.substring(i,i+x);
	    		if (token.length < minlength) { return null; }
	    		if (this._isInteger(token)) { return token; }
	    		}
	    	return null;
	    },

	     _lz: function (x) {

	         return(x<0||x>9?"":"0")+x;
	     }
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.date = JLG.date || {};
JLG.date.buildDateRangePickerCell = function (p_module) {

	var result = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( result.prototype, p_module.Cell.prototype, {

		create: function( p_day, p_parent, p_class ) {

		    // See if this is an available or unavailable cell
			p_module.Cell.prototype.create.call( this, p_day, p_parent, p_class );
			var config = this.getMonth().getCalendar().getConfig();
			this.onValidDatesChanged( config.minDate, config.maxDate );
		},

		onValidDatesChanged: function( p_min, p_max ) {

			this.m_disabled = ((p_min && this.getDate() < p_min) || (p_max && this.getDate() > p_max));
			if (this.m_disabled) {

				this.getCellEl().addClass( "jlg-cal-unavailable" );
				this.getDateEl().addClass( "jlg-cal-unavailable" );

			} else {

				this.getCellEl().removeClass( "jlg-call-unavailable" );
				this.getDateEl().removeClass( "jlg-call-unavailable" );
			}
		},

		onMouseDown: function( p_src ) {

			if (!this.m_disabled) {

				this.getMonth().getCalendar().getConfig().viewer.getConfig().window.unhighlight();
				return p_module.Cell.prototype.onMouseDown.call( this, p_src );
			}
			return true;
		},

		_on_mouse_enter: function( p_src ) {

			if (!this.m_disabled && !this.getMonth().getCalendar().isMouseDown) { this.getMonth().getCalendar().getConfig().viewer.getConfig().window.highlight( this.index ); }
			return true;
		}
	} );
	return result;
};


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.date = JLG.date || {};
JLG.date.buildDateRangePickerMonth = function( p_module ) {

	var result = function( p_calendar, p_year, p_month, p_div, p_dim, p_options ) { this.initialize( p_calendar, p_year, p_month, p_div, p_dim, p_options ); };
	gQuery.extend( result.prototype, p_module.Month.prototype, {

		getCellModel: function () {

			if (!this.m_cellModel) {this.m_cellModel = JLG.date.buildDateRangePickerCell (p_module);}
			return this.m_cellModel;
		},

		_alloc_cell: function () {

			return new (this.getCellModel ()) (this);
		}
	} );
	return result;
};


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.date = JLG.date || {};
JLG.date.buildDateRangePickerCalendar = function( p_module ) {

 	var result = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( result.prototype, p_module.Calendar.prototype, {

		getMonthModel: function () {

			if (!this.m_monthModel) {this.m_monthModel = JLG.date.buildDateRangePickerMonth (p_module);}
			return this.m_monthModel;
		},

		onValidDatesChanged: function (p_min, p_max) {

			for (var dates = this.getDates (), index = 0, len = dates.length; index < len; ++index) {dates [index].onValidDatesChanged (p_min, p_max);}
		},

		setMaxDate: function (p_value) {

			this.getConfig ().maxDate = p_value;
			this.onValidDatesChanged (this.getConfig ().minDate, p_value);
		},

		setMinDate: function (p_value) {

			this.getConfig ().minDate = p_value;
			this.onValidDatesChanged (p_value, this.getConfig ().maxDate);
		},

		_alloc_month: function (p_year, p_month, p_div, p_dim, p_config) {

			return new (this.getMonthModel ()) (this, p_year, p_month, p_div, p_dim, p_config);
		},

		_on_mouse_up: function( p_event ) {

			var chose = this.isMouseDown;

		    // Call the base class
		    p_module.Calendar.prototype._on_mouse_up.call( this, p_event );

		    // Process the selection
		    if (chose) { this.getConfig().viewer.getConfig().window._select(); }
		}
	} );
	return result;
};


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.date = JLG.date || {};
JLG.date.buildDateRangePickerViewer  = function (p_module) {

	var result = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( result.prototype, p_module.Viewer.prototype, {

		initialize: function( p_config ) {

			var config = p_config || {};
			var calConfig = gQuery.extend( { cssClass: "jlg-pac-panel-all" }, config.calendar || {} );

			// Clear modified objects
			delete config.calendar;

			// Call the base class
			p_module.Viewer.prototype.initialize.call( this, gQuery.extend( { calendar: calConfig, cssClass: "jlg-pac-viewer" }, config ) );
		},

		getCalendarModel: function () {

			if (!this.m_calendarModel) {this.m_calendarModel = JLG.date.buildDateRangePickerCalendar (p_module);}
			return this.m_calendarModel;
		},

		setMaxDate: function (p_value) {

			this.getConfig ().calendar.maxDate = p_value;
			if (this.getCalendar ()) { this.getCalendar ().setMaxDate (p_value); }
		},

		setMinDate: function (p_value) {

			this.getConfig ().calendar.minDate = p_value;
			if (this.getCalendar ()) { this.getCalendar ().setMinDate (p_value); }
		},

		/******************************************************
		 * PRIVATE METHODS
		 ******************************************************/
		_alloc_calendar: function( p_config ) {

			return new( this.getCalendarModel() )( gQuery.extend( { viewer: this }, p_config || {} ) );
		},

		/******************************************************
		 * MEMBER VARIABLES
		 ******************************************************/
		m_calendarModel: null
	});
	return result;
};


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.date = JLG.date || {};
JLG.date.buildDateRangePickerWindow = function( p_module ) {

	var result = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( result.prototype, {

		initialize: function( p_config ) {

			// Update the configuration
			p_config = p_config || {};
			this.m_idxHighlight = -1;
			this.m_config = this._mergeConfig( p_config );
			this.m_config.viewer.width = Math.ceil( (1.1 * this.m_config.viewer.height) * this.m_config.monthsDisplayed );
			this.setMaxDate( this.m_config.maxDate );
			this.setMinDate( this.m_config.minDate );

			// Determine proper date format
			this.m_preferEuro = (jgLocale || {}).ukDateFormat ? true : false;
		},

		blur: function( p_element ) {

			var binder = this.getBinder();
			var calendar = binder.getCalendar();

			// Are we allowed to hide?
			if (this.m_holder == p_element && (!calendar || !calendar.isMouseDown)) {

				// Do we have the focus?
				if (this.hasFocus()) {

					// Reset focus
					this.m_hasFocus = false;

				} else {

					// Hide the calendar
					this.hide();
				}
			}
		},

		focus: function() { if (this.m_holder) { this.m_holder.focus(); } },

		getBinder: function() { return this.m_viewer; },

		getConfig: function() { return this.m_config; },

		getElement: function() { return this.m_viewerEl; },

		getPreferEuro: function() { return this.m_preferEuro; },

		getQueuedDate: function() { return this.m_queuedDate; },

		getScrollTimer: function() { return this.m_scrollTimer; },

		getViewer: function() { return this.m_viewer; },

		getViewerModel: function() {

			if (!this.m_viewerModel) { this.m_viewerModel = JLG.date.buildDateRangePickerViewer( p_module ); }
			return this.m_viewerModel;
		},

		hasFocus: function() { return this.m_hasFocus; },

		hide: function( p_element ) {

			var calendar = this.getBinder().getCalendar();
			this.m_hasFocus = false;
			this.unhighlight();
			if (calendar) { calendar._unselect_all(); }
			this.m_popupEl.hide();
			this.m_holder = null;

			// Show elements that might interfere with the popup
			var ieVersion = gQuery.ieVersion();
	        if (ieVersion > 0 && ieVersion < 7) { this.hideElements( "select", "visible" ); }
			this.hideElements( "embed", "visible" );
			this.hideElements( "object", "visible" );
		},

	    hideElements: function( p_elem, p_visibility ){

	        var nodes = gQuery( p_elem ).css( "visibility", p_visibility );
	    },

		highlight: function( p_index ) {

			var calendar = this.getBinder().getCalendar();
			if (calendar) {

				// Highlight the selected date
				this.unhighlight();
				this.m_idxHighlight = p_index;
				calendar.getDates()[ this.m_idxHighlight ].getDateEl().addClass( this.getConfig().highlightDateCssClass );
			}
		},

		_mergeConfig: function (p_config) {

			var config = p_config || {};
			var viewerConfig = config.viewer || {};
			var calConfig = viewerConfig.calendar || {};
			var monthConfig = calConfig.month || {};

			// Clear objects
			delete calConfig.month;
			delete viewerConfig.calendar;
			delete config.viewer;

			// Extend the default config
			monthConfig = gQuery.extend( {}, monthConfig );
			calConfig = gQuery.extend({

				cssClass: "jlg-pac-panel",
				monthBodyCssClass: "jlg-pac-month-body",
				monthDivCssClass: "jlg-pac-month-div",
				monthHeaderCssClass: "jlg-pac-month-header",
				monthNameCssClass: "jlg-pac-month-name",
				monthDayNameCssClass: "jlg-pac-month-day-name",
				monthTableCssClass: "jlg-pac-month-table",
				monthPaddingLeftCssClass: "jlg_paw_padding_left",
				monthPaddingRightCssClass: "jlg_paw_padding_right",
				month: monthConfig

			}, calConfig );

			// Update the viewer options
			viewerConfig = gQuery.extend({ calendar: calConfig, height: 165 }, viewerConfig );

			// Return the total object extension
			config = gQuery.extend({ highlightDateCssClass: 'jlg-pac-date-highlight', monthsDisplayed: 2, viewer: viewerConfig }, config );

			// Return the config
			return config;
		},

		_nextId: gQuery.counter(),

		onClickedNextMonth: function() {

			// Clear the last click
			clearTimeout( this.getScrollTimer() );

			// Initialize
			var oldBinder = this.getBinder();
			var date = this.getQueuedDate() || oldBinder.getDate();
			var newDate = new Date();

			// Determine the new date
			newDate.setDate( 1 );
			if (date.getMonth() == 11) {

				newDate.setMonth( 1 );
				newDate.setFullYear( date.getFullYear() + 1 );

			} else {

				newDate.setMonth( date.getMonth () + 1 );
				newDate.setFullYear( date.getFullYear() );
			}
			this.m_queuedDate = newDate;

			// Delay the update for multiple clicks
			this.m_scrollTimer = setTimeout( gQuery.bindFn( function() {

				// Initialize
				var config = this.getConfig();
				var oldElement = this.getElement();

				// Create a new binder
				this.m_viewer = new( this.getViewerModel() )( gQuery.extend( { window: this }, config.viewer ) );
				this.m_viewerEl = gQuery( document.createElement( 'div' ) );
				var newBinder = this.getBinder();
				var newElement = this.getElement();
				oldElement.after( newElement );
				newBinder.render( gQuery.extend( gQuery.extend( {}, this.m_configRender ), { el: newElement } ) );
				newBinder.setDate( this.m_queuedDate, { count: config.monthsDisplayed } );
				try { oldElement.remove(); } catch (e) {}
				this.m_queuedDate = null;

				// Refresh the navbar
				this.onRefresh();

				// Reset the focus
				this.m_holder.focus();

			}, this ), 0);
		},

		onMouseDown: function() { this.m_hasFocus = true; },

		onClickedPrevMonth: function() {

			// Clear the last click
			clearTimeout( this.getScrollTimer() );

			// Initialize
			var oldBinder = this.getBinder();
			var date = this.getQueuedDate() || oldBinder.getDate();
			var newDate = new Date();

			// Determine the new date
			newDate.setDate( 1 );
			if (date.getMonth() === 0) {

				newDate.setMonth( 11 );
				newDate.setFullYear( date.getFullYear() - 1 );

			} else {

				newDate.setMonth( date.getMonth () - 1 );
				newDate.setFullYear( date.getFullYear() );
			}
			this.m_queuedDate = newDate;

			// Delay the update for multiple clicks
			this.m_scrollTimer = setTimeout( gQuery.bindFn( function() {

				// Initialize
				var config = this.getConfig();
				var oldElement = this.getElement();

				// Create a new viewer
				this.m_viewer = new (this.getViewerModel())( gQuery.extend( { window: this }, config.viewer ) );
				this.m_viewerEl = gQuery( document.createElement( 'div' ) );
				var newBinder = this.getBinder();
				var newElement = this.getElement();
				oldElement.after( newElement );
				newBinder.render( gQuery.extend( gQuery.extend( {}, this.m_configRender ), { el: newElement } ) );
				newBinder.setDate( this.m_queuedDate, { count: config.monthsDisplayed } );
				try { oldElement.remove(); } catch (e) {}
				this.m_queuedDate = null;

				// Refresh the navbar
				this.onRefresh ();

				// Reset the focus
				this.m_holder.focus();

			}, this ), 0);
		},

		onClose: function() {

			this.m_holder.drpwIgnoreFocus = true;
			this.m_holder.focus();
			this.hide();
		},

		onRefresh: function() {

			var binder = this.getBinder();
			var config = this.getConfig();
			var date = binder.getDate();

			// Set the date ranges
			if (config.maxDate) {

				if (date >= config.maxDate) {

					this.m_navLinkFwd.hide();

				} else {

					this.m_navLinkFwd.show();
				}
			} else {

				this.m_navLinkFwd.show();
			}
			if (config.minDate) {

				if (date <= config.minDate) {

					this.m_navLinkBack.hide();

				} else {

					this.m_navLinkBack.show();
				}
			} else {

				this.m_navLinkBack.show();
			}
		},

		render: function( p_config ) {

			var config = this.getConfig();

			// Initialize
			p_config = p_config || {};
			this.m_popupEl = gQuery( document.createElement( "div" ) );
			this.m_viewer = new (this.getViewerModel())( gQuery.extend( { window: this }, config.viewer ) );
			this.m_viewerEl = gQuery( document.createElement( "div" ) );

			// Create the container element
			this.m_popupEl.addClass( config.popupClass || "jlg-pac-popup" );

			// Render the viewer
			this.m_configRender = gQuery.extend( gQuery.extend( {}, config.viewer ), p_config || {} );
			this.m_viewer.render( gQuery.extend( gQuery.extend( {}, this.m_configRender ), { el: this.m_viewerEl } ) );
			this.m_popupEl.append( this.m_viewerEl );

			// Nav container
			var nav = gQuery( document.createElement( "div" ) ).addClass( config.navClass || "jlg-pac-nav" );

			// Backwards
			var navDiv = gQuery( document.createElement( "div" ) ).addClass( "jlg-pac-back" ).css( "float", "left" );
			this.m_navLinkBack = gQuery( document.createElement( "a" ) ).attr( "href", "javascript:void(0);" ).html( "&lt;&lt;" ).appendTo( navDiv );
			navDiv.appendTo( nav );

			// Forward
			navDiv = gQuery( document.createElement( "div" ) ).addClass( "jlg-pac-fwd" ).css( "float", "right" );
			this.m_navLinkFwd = gQuery( document.createElement( "a" ) ).attr( "href", "javascript:void(0);" ).html( "&gt;&gt;" ).appendTo( navDiv );
			navDiv.appendTo( nav );
			nav.appendTo( this.m_popupEl );

			// Close
			var closeDiv = gQuery( document.createElement( "div" ) ).addClass( "jlg-pac-close" );
			var closeLink = gQuery( document.createElement( "a" ) ).attr( "href", "javascript:void(0);" ).html( "Close" ).appendTo( closeDiv );
			closeDiv.appendTo( this.m_popupEl );

			// Append to the root
			var jwindow = gQuery( window );
			var dimBrowser = { w: jwindow.width(), h: jwindow.height() };
			var el = p_config.el ? gQuery( p_config.el ) : gQuery( "body" );
			this.m_popupEl.appendTo( el ).css({ left: dimBrowser.w, top: dimBrowser.h });

			// Connect event handlers
			closeLink.bind( "click", gQuery.bindFn( this.onClose, this ) );
			this.m_navLinkFwd.bind( "click", gQuery.bindFn( this.onClickedNextMonth, this ) );
			this.m_navLinkBack.bind( "click", gQuery.bindFn( this.onClickedPrevMonth, this ) );
			this.m_popupEl.bind( 'mousedown', gQuery.bindFn( this.onMouseDown, this ) );

			// Hide the popup
			this.m_popupEl.hide();

			// Rendered
			this.m_rendered = true;
		},

		setMaxDate: function( p_value ) {

			var binder = this.getBinder();
			var config = this.getConfig();

			// Determine the month ranges
			if (p_value) {

				var months = config.monthsDisplayed;
				var years = Math.floor( months / 12 );
				months = months % 12;
				if (months == 1 && years === 0) {

					months = 0;

				} else if (months > 0) {

					--months;
				}
				if ((p_value.getMonth () + 1) < months) { years += 1; }
				var newDate = new Date( p_value.getFullYear() - years, p_value.getMonth() - months, 1, 0, 0, 0, 0 );
				config.maxDate = newDate;
				p_value = new Date( p_value.getFullYear(), p_value.getMonth(), p_value.getDate (), 0, 0, 0, 0 );
			}
			config.viewer.calendar.maxDate = p_value;
			if (binder) { binder.setMaxDate( p_value ); }
		},

		setMinDate: function( p_value ) {

			var binder = this.getBinder();
			var config = this.getConfig();

			if (p_value) { p_value = new Date( p_value.getFullYear(), p_value.getMonth(), p_value.getDate(), 0, 0, 0, 0); }
			config.minDate = p_value;
			config.viewer.calendar.minDate = p_value;
			if (binder) { binder.setMinDate( p_value ); }
		},

		_select: function() {

		    var selDate = this.m_viewer.getCalendar().getDates()[ this.m_viewer.getCalendar().selFirst ].getDate();
			var month;
			var date;

		    // What dates are selected?
			month = selDate.getMonth() + 1;
			date = selDate.getDate();
			if (this.getPreferEuro()) {

				selDate = ((date < 10) ? '0' : '') + date + "/" + ((month < 10) ? '0' : '') + month + "/" + selDate.getFullYear();

			} else {

				selDate = ((month < 10) ? '0' : '') + month + "/" + ((date < 10) ? '0' : '') + date + "/" + selDate.getFullYear();
			}

			// Populate the holder
			this.m_holder.drpwIgnoreFocus = true;
			this.m_holder.value = selDate;
			this.m_holder.focus();
			this.m_holder.select();
			gQuery( this.m_holder ).fireEvent( "change" );
			this.hide();
		},

		show: function( p_element ) {

			var config = this.getConfig();

			// Render the popup
			if (!this.m_rendered) { this.render (); }

	        // Don't do anything if we are the holder
	        if (p_element == this.m_holder) {

	            return;
	            // NOTREACHED
	        }

			// Hide elements that might interfere with the popup
			p_element = gQuery( p_element );
			var ieVersion = gQuery.ieVersion();
	        if (ieVersion > 0 && ieVersion < 7) { this.hideElements( "select", "hidden" ); }
			this.hideElements( "embed", "hidden" );
			this.hideElements( "object", "hidden" );

			// Show the viewer
			if (!this.m_shown) { this.m_viewer.show( config.monthsDisplayed ); }

			var index;
			var jwindow = gQuery( window );
			var pos = p_element.offset();
			var dim = { w: p_element.outerWidth(), h: p_element.outerHeight() };
	        var elDate = JLG.util.date.parse( p_element.val(), this.getPreferEuro() );
	        if (!pos) {

	            return;
	            // NOTREACHED
	        }

			// Clear variables
			this.m_hasFocus = false;

			// Determine where to place the popup
			pos.top += dim.h;
			if (config.left) { pos.left += config.left; }
			if (config.right) { pos.left = pos.left + dim.w + config.right; }
			this.m_popupEl.absolutize();
			this.m_popupEl.css({ top: String( pos.top ) + "px", left: String( pos.left ) + "px" }).show();
			this.m_holder = p_element[ 0 ];

	        // Highlight the new select
	        if (elDate) {

				// Update the date if necessary
				var date = this.m_viewer.getDate ();
	            var elMonth = new Date( elDate.getFullYear(), elDate.getMonth(), 1 );
				if (config.monthsDisplayed == 1) {

					this.m_viewer.setDate (elMonth);

				} else {

					var offset = (elMonth - date) / (1000 * 60 * 60 * 24);
					var prev = (offset > 0) ? false : true;
					if (Math.abs(offset) > 31) {

						if (!prev) {

							if (elMonth === 0) {

								elMonth.setMonth (11);
								elMonth.setFullYear (elMonth.getFullYear () - 1);

							} else {

								elMonth.setMonth (elMonth.getMonth () - 1);
							}
						}
						this.m_viewer.setDate (elMonth);
					}
				}

	            // Select the new date
				var calendar = this.m_viewer.getCalendar ();
				calendar._unselect_all ();
	            calendar.select (elDate, elDate);

	        } else {

				this.m_viewer.getCalendar ()._unselect_all ();
				if ((config.minDate && this.m_viewer.getDate () < config.minDate) || (config.maxDate && this.m_viewer.getDate () > config.maxDate)) {

					this.m_viewer.setDate (config.minDate ? config.minDate : config.maxDate);
				}
			}

			// Refresh the navbar
			this.onRefresh ();
		},

		unhighlight: function() {

			var calendar = this.getBinder().getCalendar();
			if (calendar && this.m_idxHighlight >= 0) {

				// Unhighlight the last date
				var highlight = calendar.getDates()[ this.m_idxHighlight ];
				highlight.getDateEl().removeClass( this.getConfig().highlightDateCssClass );
				this.m_idxHighlight = -1;
			}
		}
	} );
	return result;
};


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.date = JLG.date || {};
JLG.date.buildDateRangePicker = function( p_module ) {

	var result = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( result.prototype, {

		initialize: function( p_config ) {

			// Initialize
			this.m_config = gQuery.extend( { min_span: 0 }, p_config || {} );
			this.m_config.min_span *= 1000 * 60 * 60 * 24;
			this.setMaxDate( this.m_config.maxDate );
			this.setMinDate( this.m_config.minDate );

			// Determine the date format
			this.m_preferEuro = (jgLocale || {}).ukDateFormat ? true : false;

			// Create the popup window
			this.m_popup = new( this.getWindowModel() )( this.m_config );

			// Connect signal handlers
			this._connect();
		},

		getPreferEuro: function () {

			return this.m_preferEuro;
		},

		getEndDate: function () {

	        var date = JLG.util.date.parse (this.m_endEl.val(), this.getPreferEuro ());
			return (date && (!this.m_config.minDate || date >= this.m_config.minDate) && (!this.m_config.maxDate || date <= this.m_config.maxDate)) ? date : null;
		},

		getStartDate: function() {

	        var date = JLG.util.date.parse( this.m_startEl.val(), this.getPreferEuro() );
			return (date && (!this.m_config.minDate || date >= this.m_config.minDate) && (!this.m_config.maxDate || date <= this.m_config.maxDate)) ? date : null;
		},

		getWindow: function() { return this.m_popup; },

		getWindowModel: function() {

			if (!this.m_windowModel) {this.m_windowModel = JLG.date.buildDateRangePickerWindow (p_module);}
			return this.m_windowModel;
		},

		onBlur: function( p_event ) { this.m_popup.blur( p_event.target ); },

		onChangeCheckin: function() {

			var start = JLG.util.date.parse( this.m_startEl.val(), this.getPreferEuro() );
			var end = JLG.util.date.parse( this.m_endEl.val(), this.getPreferEuro() );
			var month;
			var date;

			// Do we need to replace the end value?
			if (start && (!end || end.getTime() < (start.getTime() + this.m_config.min_span))) {

				// Update the end date
				end = new Date();
				end.setTime( start.getTime() + this.m_config.min_span );
				month = end.getMonth() + 1;
				date = end.getDate();
				if (this.getPreferEuro()) {

					this.m_endEl.val( ((date < 10) ? '0' : '') + date + "/" + ((month < 10) ? '0' : '') + month + "/" + end.getFullYear() );

				} else {

					this.m_endEl.val( ((month < 10) ? '0' : '') + month + "/" + ((date < 10) ? '0' : '') + date + "/" + end.getFullYear() );
				}
			}
		},

		onFocus: function( p_event ) { 

			var el = p_event.target;
			if( !el.drpwIgnoreFocus ) { this.m_popup.show( el ); }
			el.drpwIgnoreFocus = false;
		},

		reset: function () {

			this._disconnect();
			this._connect();
		},

		setMaxDate: function (p_value) {

			this.m_config.maxDate = p_value ? new Date (p_value.getFullYear (), p_value.getMonth (), p_value.getDate (), 0, 0, 0, 0) : null;
			if (this.m_popup) { this.m_popup.setMaxDate (p_value); }
		},

		setMinDate: function( p_value ) {

			this.m_config.minDate = p_value ? new Date( p_value.getFullYear(), p_value.getMonth(), p_value.getDate(), 0, 0, 0, 0 ) : null;
			if (this.m_popup) { this.m_popup.setMinDate( p_value ); }
		},

		/******************************************************
		 * PRIVATE METHODS
		 ******************************************************/
		_connect: function() {

			// Find the elements
			this.m_startEl = gQuery( this.m_config.startEl );
			this.m_endEl = gQuery( this.m_config.endEl );
			this.m_startEl.bind( "blur", gQuery.bindFn( this.onBlur, this ) ).bind( "change", gQuery.bindFn( this.onChangeCheckin, this ) ).bind( "click focus", gQuery.bindFn( this.onFocus,  this ) );
			this.m_endEl.bind( "blur", gQuery.bindFn( this.onBlur, this ) ).bind( "click focus", gQuery.bindFn( this.onFocus, this ) );
		},

		_disconnect: function() {

			// Clear signal handlers
			this.m_startEl.unbind();
			this.m_endEl.unbind();
		}
	} );
	return result;
};


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.calendar = JLG.calendar || {};
JLG.calendar.extra = JLG.calendar.extra || {};
if( !JLG.calendar.extra.Bubble ) {

	JLG.calendar.extra.Bubble = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( JLG.calendar.extra.Bubble.prototype, {

		initialize: function( p_config ) {

		    // Member variables
			this._config = p_config || {};
		    this.locker = null;
		    this.idSignals = [];
		    this.bubble = {};
		    this.bubble.element = gQuery( this.getConfig ().el );
		    this.connector = {};
		    this.connector.element = gQuery( this.getConfig ().el + "-connector" );
		    this.content = gQuery( this.getConfig ().el + "-content" );

		    // Connect our event handler(s)
		    this.connect( "mousemove", this, this._on_mouse_move );
		},

		getConfig: function () {

			return this._config;
		},

		connect: function( p_event, p_object, p_method ) {

		    // Connect the bubble to events
			var pfn = gQuery.bindFn( p_method, p_object );
			this.idSignals.push( pfn );
			this.bubble.element.bind( p_event, pfn );
			this.connector.element.bind( p_event, pfn );
		},

		_destroy: function() {

		    // Remove the signals
		    while( this.idSignals.length ) { 

				var pfn = this.idSignals.pop();
				this.bubble.element.unbind( pfn );
				this.connector.element.unbind( pfn );
			}
		},

		hide: function( p_force ) {

		    // Don't hide if locked
		    if (!p_force && this.locker) {

				return false;
				// NOTREACHED
			}

		    // Don't hide if we happen to appear right under the mouse (firefox)
		    if (p_force || !this.timeShow || (new Date().getTime() - this.timeShow) > 150) {

				var overlay = gQuery( "#rex-lb-overlay" );

		        // Clear the show time
		        this.timeShow = null;

		        // Is the fader visible?
				if( overlay && overlay.css( "display" ) != "none" ) {

					// Hide the overlay
					this._initOverlayStyle( false );
					overlay.css({ opacity: this._overlayStyle.opacity, "-moz-opacity": this._overlayStyle[ "-moz-opacity" ], "-ktml-opacity": this._overlayStyle[ "-khtml-opacity "], filter: this._overlayStyle.filter });
					if (this.m_window) {

						JLG.popup.window().hide( this.m_window, { caller: this } );
						this.m_window = null;
					}
				}

		        // Hide the bubble
				this.bubble.element.hide();
				this.connector.element.hide();
		    }
		},

		lock: function (p_locker) {

		    // Lock the bubble if not already locked
		    if (!this.locker) { this.locker = p_locker; }
		},

		unlock: function (p_locker) {

		    if (this.locker && this.locker == p_locker) { this.locker = null; }
		},

		_on_mouse_move: function (p_event) {

			this.hide();
		},

		show: function( p_pos, p_dim, p_fade ) {

		    var bubDimReal;
			var bubDimWant;
		    var adjust = { h: 0, w: 0 };
		    var bubPos = { left: 0, top: 0 };
		    var temp;
		    var conDisplay = 'block';
			var jwindow = gQuery( window );
		    var screen = { w: jwindow.width(), h: jwindow.height() };
		    var scrollPos = { left: jwindow.scrollLeft(), top: jwindow.scrollTop() };
		    var reset = (p_pos && p_dim);

		    // Are we showing (after being hidden) or repositioning?
			this.bubble.element.absolutize();
			this.connector.element.absolutize();
		    if (reset) {

		        // Don't show if locked
		        if (this.locker) {

					return false;
					// NOTREACHED
				}

		        // Set element positions for initial measurement
				this.bubble.element.css({ top: "0px", left: "0px" });
				this.connector.element.css({ top: "0px", left: "0px" });

		        // How big is the connector, and where should it ideally go?
				this.connector.element.show();
				this.connector.dim = { w: this.connector.element.outerWidth(), h: this.connector.element.outerHeight() };
				this.connector.element.hide();
		        this.connector.pos = { left: Math.round( p_pos.left + (p_dim.w / 2) ), top: Math.round( p_pos.top + (p_dim.h / 2) - this.connector.dim.h ) };
				this.connector.element.css({ top: String( this.connector.pos.top ) + "px", left: String( this.connector.pos.left ) + "px" });
		    }

		    // How big is the bubble?
	        this.bubble.element.show();
		    bubDimWant = { w: this.bubble.element.outerWidth(), h: this.bubble.element.outerHeight() };
	        this.bubble.element.hide();

		    // Calculate the bubble position
		    bubPos.top = 25 + this.connector.pos.top - bubDimWant.h;
		    if ((this.connector.pos.left + this.connector.dim.w + 25 < screen.w) && bubPos.top > 0) {

		        // Bubble with connector
		        bubPos.left = ((bubDimWant.w - this.connector.dim.w) / 2);
		        if (this.connector.pos.left > bubPos.left) {

					bubPos.left = this.connector.pos.left - bubPos.left;

				} else {

					bubPos.left = 0;
				}
		    } else {

		        // Position the bubble without a connector
		        conDisplay = 'none';
		        bubPos.left = Math.round( this.connector.pos.left - bubDimWant.w / 2 );
		        bubPos.top = Math.round( this.connector.pos.top + this.connector.dim.h - bubDimWant.h );
		    }

		    // Make sure we are in scroll position
		    if (bubPos.top < scrollPos.top) {

		        // Get inside the viewable area
		        bubPos.top = scrollPos.top;
		        conDisplay = 'none';
		    }
		    if (bubPos.left < scrollPos.left) {

		        // Get inside the viewable area
		        bubPos.left = scrollPos.left;
		        conDisplay = 'none';
		    }

		    // Are we setting the bubble off the screen?
		    temp = screen.w - bubPos.left - bubDimWant.w;
		    if (temp <= 0) { bubPos.left += temp - 1; }
		    if (bubPos.left <= 0) { bubPos.left = 1; }
		    if (bubPos.top <= 0) { bubPos.top = 1; }

		    // Show the bubble
			this.bubble.element.css({ top: String( bubPos.top ) + "px", left: String( bubPos.left ) + "px" }).show();
			bubDimReal = { w: this.bubble.element.outerWidth(), h: this.bubble.element.outerHeight() };

			// How big is the bubble now?
			if (bubDimReal.w != bubDimWant.w || bubDimReal.h != bubDimWant.h) {

				// Calculate the adjustments to be made
				adjust.w = bubDimWant.w - bubDimReal.w;
				adjust.h = bubDimWant.h - bubDimReal.h;

				if (adjust.w > 0) {

					// Move the bubble over, but not off screen
					bubPos.left -= adjust.w;
					if (bubPos.left <= 0) { bubPos.left = 1; }
				}
				if (adjust.h > 0) { bubPos.top += adjust.h; }

				// Should the connector be displayed?
				if ((bubPos.left + bubDimWant.w) < (this.connector.pos.left + this.connector.dim.w + 25) && conDisplay != "none") {

					// Not showing the connector
					bubPos.top += this.connector.dim.h - 25;
					conDisplay = "none";
				}

				// Check the size again
				this.bubble.element.css({ left: String( bubPos.left ) + "px", top: String( bubPos.top ) + "px" });
			    bubDimReal = { w: this.bubble.element.outerWidth(), h: this.bubble.element.outerHeight() };
			}

			// Show the connector
			this.connector.element.css({ display: conDisplay });

		    // Fade the background
		    if (p_fade && reset) {

				// Don't block as much with the overlay
				this._initOverlayStyle (true);
				gQuery( "#rex-lb-overlay" ).css({ "-moz-opacity": 0.1, opacity: 0.1, filter: "alpha(opacity=10);" });
				this.m_window = JLG.popup.window().show({

					caller: this,
					innerHTML: " ",
					layout: false,
					name: "jlg-calendar-bubble",
					noScroll: true
				});
			}

		    // Make sure the bubble isn't hidden immediately by mouseover
		    this.timeShow = new Date().getTime();
		},

		/**
		 * Private methods
		 */
		_initOverlayStyle: function (p_reset) {

			if (p_reset || !this._overlayStyle) {

				var overlay = gQuery( "#rex-lb-overlay" );
				this._overlayStyle = { opacity: overlay.css( "opacity" ), "-moz-opacity": overlay.css( "-moz-opacity" ), "-khtml-opacity": overlay.css( "-khtml-opacity" ), filter: overlay.css( "filter" ) };
			}
		}
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.core = JLG.calendar.core || {};
if( !JLG.calendar.core.Cell ) {

	JLG.calendar.core.Cell = function( p_month ) { this.initialize( p_month ); };
	gQuery.extend( JLG.calendar.core.Cell.prototype, {

		initialize: function( p_month ) {

			this.m_month = p_month;
			this.index = -1;
			this.selected = false;
		},

		create: function( p_day, p_parent, p_class ) {

		    var results;
		    var month = this.getMonth();
		    var calendar = month.getCalendar();

			// Member variables
			this.index = calendar.getDates().length;
			this.m_date = new Date( month.year, month.month - 1, p_day, 0, 0, 0, 0 );

			// Add us to the list of dates
			calendar.addDate (this);

			// Create the cell nodes
		    results = month.buildDay( p_parent, p_day, p_class );
		    this.m_cellEl = results.cell;
		    this.m_dateEl = results.date;
			this.bgColor = this.getDateEl().css( "background-color" );
			this.getCellEl()[ 0 ].jlgCalendarCell = this;
		},

		getDate: function() {

			return this.m_date;
		},

		getDateEl: function() {

			return this.m_dateEl;
		},

		getMonth: function() {

			return this.m_month;
		},

		getCellEl: function() {

			return this.m_cellEl;
		},

		_destroy: function() {

		    // Clear the DOM
		    if (this.getDateEl()) { this.getDateEl().remove(); }
		    if (this.getCellEl () && this.getCellEl() != this.getDateEl()) { this.getCellEl().remove(); }

		    // Clear pointers
		    this.m_cellEl = null;
		    this.m_dateEl = null;
		    this.m_month = null;
		},

		onMouseDown: function( p_src ) {

	        var calendar = this.getMonth().getCalendar();

		    // Reset the selections
		    calendar._unselect_all ();
			calendar.selFirst = this.index;
			calendar.selLast = this.index;
			calendar.isMouseDown = true;
			this._select ();
			return true;
		},

		_on_mouse_enter: function (p_src) {

			var index;
			var month = this.getMonth ();
			var calendar = month.getCalendar ();
			var dates = calendar.getDates ();
			var len;

			do {

				if (calendar.selFirst < 0 || !calendar.isMouseDown) {

				    break;
				}
				if (this.index > calendar.selFirst) {

					for (index = calendar.selLast, len = calendar.selFirst; index < len; index++) {

					    dates [index]._unselect ();
					}
					for (index = this.index + 1, len = calendar.selLast; index <= len; index++) {

					    dates [index]._unselect ();
					}
					for (index = calendar.selLast + 1, len = this.index; index <= len; index++) {

					    dates [index]._select ();
					}
				} else {

					for (index = calendar.selFirst + 1, len = calendar.selLast; index <= len; index++) {

					    dates [index]._unselect ();
					}
					for (index = calendar.selLast, len = this.index; index < len; index++) {

					    dates [index]._unselect ();
					}
					for (index = this.index, len = calendar.selFirst; index < len; index++) {

					    dates [index]._select ();
					}
				}
				calendar.selLast = this.index;
			}
			while (0);
			return true;
		},

		_select: function() {

			if (!this.selected) {

			    this.selected = true;
				this.getDateEl().addClass( this.getMonth().getCalendar().getConfig().cellSelectionClass );
			}
		},

		_unselect: function() {

			if (this.selected) {

			    this.selected = false;
				this.getDateEl().removeClass( this.getMonth().getCalendar().getConfig().cellSelectionClass );
			}
		}
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.core = JLG.calendar.core || {};
if( !JLG.calendar.core.Month ) {

	JLG.calendar.core.Month = function( p_calendar, p_year, p_month, p_div, p_dim, p_options ) { this.initialize( p_calendar, p_year, p_month, p_div, p_dim, p_options ); };
	gQuery.extend( JLG.calendar.core.Month.prototype, {

		initialize: function (p_calendar, p_year, p_month, p_div, p_dim, p_options) {

	    	// Initialize member variables
    		this.m_cellClass = JLG.calendar.core.Cell;
	    	this.m_calendar = p_calendar;
			this.div = p_div;
			this.year = p_year;
			this.month = p_month;
			this.days = [];
			this.id = "month" + p_year + p_month;
			this.options = p_options || {};

	        // Add extra methods
	        this.getNameByIndex = JLG.calendar.core.Month.getNameByIndex;

			// Create the table
			this._create_table (p_year, p_month, p_dim);
		},

		getCalendar: function () {return this.m_calendar;},

		/**
		 * Private methods
		 */
		_alloc_cell: function () {

		    return new this.m_cellClass (this);
		},

		_compute_header_height: function() {

			var display;
			var dim;

			// How big is the header?
			var table = gQuery( document.createElement( "table" ) ).attr({ border: 0, cellPadding: 0, cellSpacing: 0, height: 0, width: 0 }).css({ width: "auto", height: "auto" });
			this._create_table_header( table, 1900, 1 );
			table.appendTo( this.getCalendar ().div );
			display = this.getCalendar().css( 'display' );
			this.getCalendar().div.show();
			dim = { w: table.outerWidth(), h: table.outerHeight() };
			this.getCalendar().div.css( 'display', display );
			table.remove();

			// Return the height
			return dim.h;
		},

		buildDay: function( p_parent, p_day, p_class ) {

		    var row;
		    var cell = null;
		    var date = null;
			var table;

		    // Determine the date alignment
		    if (this.getCalendar().centerDate) {

		        // Create cells with centered information
				cell = p_parent;
				date = p_parent;
		    	p_parent.html( p_day );
				cell.css( 'vertical-align', "middle" );
				if (p_class) { p_parent.addClass( p_class ); }

		    } else {

		    	// Create the nodes
				cell = p_parent;
		        date = gQuery( document.createElement( "div" ) );

				// Update the node contents
		        date.html( p_day + "&nbsp;" );
				date.appendTo( cell );
				cell.css( 'vertical-align', "top" );
		        if (p_class) {

		            // Set the appropriate class
					cell.addClass( p_class );
					date.addClass( p_class );
		        }
		    }

			// Return the elements
			return {cell: cell, date: date};
		},

		_create_table: function( p_year, p_month, p_dim ) {

			// Initialize method variables
			var ieVersion = gQuery.ieVersion();

		    // Create the table
			var table = gQuery( document.createElement( "table" ) ).addClass( this.getCalendar ().getConfig ().monthTableCssClass ).attr({ cellPadding: 0, cellSpacing: 0, id: this.id }).css({ height: p_dim.h + "px", width: p_dim.w + "px" });

			// Create the table body
			var tBody = this._create_table_body( table, p_year, p_month );

			// Create the table header
			var tHeader = this._create_table_header( table, p_year, p_month );

			// Update the tbody class
			if (ieVersion < 0) { gQuery( table[ 0 ].tBodies[ 0 ] ).addClass( this.getCalendar ().getConfig ().monthBodyCssClass );}

			// Need to calculate the header and subtract from the total to get the IE height
			if (ieVersion > 0) {

				// Compute the header size
				table.appendTo( "body" );
				var dimHeader = { w: tHeader.outerWidth(), h: tHeader.outerHeight() };
				tBody.css( "height", p_dim.h - dimHeader.h );
				table.remove();

			} else {

				tBody.css( "height", p_dim.h );
			}

			// Append the table to the div
			table.appendTo( this.div );
		},

		_create_table_body: function( p_table, p_year, p_month ) {

			var cell;
			var data;
			var	day = 1;
			var index;
			var	jDate = new Date( p_year, p_month - 1, 1 );
			var row;
			var skip;
			var tbody;
			var maxDays = this._count_days_in_month( p_year, p_month );
			var maxDaysLast;
			var calendar = this.getCalendar();
			var calCfg = calendar.getConfig();
			var paddingLeftClass = calCfg.monthPaddingLeftCssClass;
			var paddingRightClass = calCfg.monthPaddingRightCssClass;
			var hideOther = this.options.hideOtherDays;
			var days = this.days;

		    // Calculate the number of days in the last month
		    if (p_month == 1) {

		        maxDaysLast = this._count_days_in_month( p_year - 1, 12 );

		    } else {

		        maxDaysLast = this._count_days_in_month( p_year, p_month - 1 );
		    }

		    // Create the table body
			if (gQuery.ieVersion() >= 6) {

				// Use two table for IE
				tbody = gQuery( document.createElement( "table" ) ).addClass( calCfg.monthBodyCssClass ).attr({ id: this.id, cellPadding: 0, cellSpacing: 0, width: "100%" }).css( "width", "100%" );
				cell = gQuery( p_table[ 0 ].insertRow( -1 ).insertCell( -1 ) ).append( tbody );

			} else {

				// Just use one table for everyone else
				tbody = p_table;
			}

		    // What is the first day of the week?
		    skip = jDate.getDay() - calendar.dowFirst;
		    if (skip < 0) { skip += 7; }

			// Cells from the previous month
			row = tbody[ 0 ].insertRow( -1 );
			gQuery( row.insertCell( -1 ) ).addClass( paddingLeftClass );
			for (index = skip; index > 0; index--) {

		    	// Create a blank cell
				cell = gQuery( row.insertCell( -1 ) );
				day = hideOther ? "&nbsp;" : (1 + maxDaysLast - index);
		        this.buildDay( cell, day, "jlg-cal-empty" );
		    }

		    // Current month cells
			day = 1;
	 	    do {

				// Beginning of another week?
		    	if (day > 1) {

			    	// Start a new week
					gQuery( row.insertCell( -1 ) ).addClass( paddingRightClass );
					row = tbody[ 0 ].insertRow( -1 );
					gQuery( row.insertCell( -1 ) ).addClass( paddingLeftClass );
				}

		    	// Create the days of the week
		    	for (index = skip + 1; index <= 7 && day <= maxDays; index++, day++) {

		            // Create the next day
					data = gQuery( row.insertCell( -1 ) );
					cell = this._alloc_cell();
					cell.create( day, data );
					days.push( cell );
				}
				skip = 0;
			}
			while (day <= maxDays);

			// Cells from the next month
			for (skip = 1; index <= 7; index++, skip++) {

				// Create a blank cell
				cell = gQuery( row.insertCell( -1 ) );
				day = hideOther ? "&nbsp;" : skip;
		        this.buildDay( cell, day, "jlg-cal-empty" );
			}
			gQuery( row.insertCell( -1 ) ).addClass( paddingRightClass );

			// Return the body
			return tbody;
		},

		_create_table_header: function( p_table, p_year, p_month ) {

			var cell;
		    var dow = this.getCalendar()._days_of_week();
			var index;
			var len;
			var pos = this.getCalendar().dowFirst;
			var row;
			var thead;
			var calendar = this.getCalendar();
			var calCfg = calendar.getConfig();
			var paddingLeftClass = calCfg.monthPaddingLeftCssClass;
			var paddingRightClass = calCfg.monthPaddingRightCssClass;

		    // Create the table header
			if (gQuery.ieVersion() >= 6) {

				// Use two table for IE
				thead = gQuery( document.createElement( "table" ) ).addClass( calCfg.monthHeaderCssClass ).attr({ cellPadding: 0, cellSpacing: 0, width: "100%" }).css( "width", "100%" );
				cell = gQuery( p_table[ 0 ].createTHead().insertRow( -1 ).insertCell( -1 ) ).append( thead );

			} else {

				// Just use one table for everyone else
				thead = gQuery( p_table[ 0 ].createTHead() ).addClass( calCfg.monthHeaderCssClass );
			}

		    // Add the Month header
		    row = thead[ 0 ].insertRow( -1 );
			gQuery( row.insertCell( -1 ) ).addClass( paddingLeftClass );
		    cell = gQuery( row.insertCell( -1 ) ).attr( "colSpan", 7 ).addClass( calCfg.monthNameCssClass ).html( this.getNameByIndex( p_month - 1 ) + ' ' + p_year );
			gQuery( row.insertCell( -1 ) ).addClass( paddingRightClass );

		    // Create the headers
		    row = thead[ 0 ].insertRow( -1 );
			gQuery( row.insertCell( -1 ) ).addClass( paddingLeftClass );
		    for (index = 0, len = dow.length; index < len; index++) {

				// Insert the next weekday name
		        cell = gQuery( row.insertCell( -1 ) ).addClass( calCfg.monthDayNameCssClass ).html( dow[ pos++ ] );
				if (pos > 6) { pos = 0; }
		    }
			gQuery( row.insertCell( -1 ) ).addClass( paddingRightClass );

			// Return the header
			return thead;
		},

		_count_days_in_month: function (p_year, p_month) {

			var m = [31,28,31,30,31,30,31,31,30,31,30,31];
			if (p_month != 2) {return m[p_month - 1];}
			if (p_year%4 !== 0) {return m[1];}
			if (p_year%100 === 0 && p_year%400 !== 0) {return m[1];}
			return m[1] + 1;
		},

		_destroy: function() {

		    // Clear the cells
			if (gQuery.ieVersion() < 0) {

				var index;
				var len;
				var days = this.days;
				var pfnPopDay = days.pop;
				for (index = 0, len = days.length; index < len; index++) { pfnPopDay.call (days)._destroy (); }
			}

		    // Clear the DOM
		    if (this.div) { this.div.remove(); }

		    // Clear pointers
		    this.m_calendar = null;
		    this.div = null;
		}
	} );
}

/**
 * Add class methods
 */
JLG.calendar.core.Month.getNameByIndex = function (p_index) {

	return JLG.calendar.core.Month._monthNames [p_index];
};

JLG.calendar.core.Month._monthNames = [

	"January",
	"February",
	"March",
	"April",
	"May",
	"June",
	"July",
	"August",
	"September",
	"October",
	"November",
	"December"
];


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.core = JLG.calendar.core || {};
if( !JLG.calendar.core.Calendar ) {

	JLG.calendar.core.Calendar = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( JLG.calendar.core.Calendar.prototype, {

		initialize: function( p_config ) {

		   	// Member variables
			this.m_daysOfWeekLong = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
			this.m_daysOfWeekShort = ["S", "M", "T", "W", "T", "F", "S"];
		    this.brTerm = null;
		    this.m_dates = null;
		    this.daysOfWeekArray = null;
		    this.div = null;
		    this.firstDate = null;
		    this.id = null;
		    this.m_months = null;
		    this.selFirst = -1;
		    this.selLast = -1;
		    this.sizeMonth = 0;
			this.m_config = gQuery.extend({

				cellSelectionClass: "jlg-cell-selected",
				cssClass: "jlg-cal-panel",
				monthBodyCssClass: "jlg-cal-month-body",
				monthDivCssClass: "jlg-cal-month-div",
				monthHeaderCssClass: "jlg-cal-month-header",
				monthNameCssClass: "jlg-cal-month-name",
				monthDayNameCssClass: "jlg-cal-month-day-name",
				monthTableCssClass: "jlg-cal-month-table",
				monthPaddingLeftCssClass: "jlg-calendar-padding-left",
				monthPaddingRightCssClass: "jlg-calendar-padding-right"

			}, p_config || {});
		},

	    addDate: function (p_date) {

	        this.m_dates.push (p_date);
	    },

		create: function( p_date, p_dow, p_parent ) {

		    // Member variables
		    this.m_dates = [];
		    this.dowFirst = p_dow;
		    this.firstDate = new Date( p_date.getFullYear(), p_date.getMonth(), 1, 0, 0, 0, 0 );
		    this.id = "calendar" + this._nextId();
		    this.m_months = [];

		    // Create the div
			var divHtml = "<div class='" + this.m_config.cssClass + "' style='display: none;'  id='" + this.id + "'></div>";
		    if( p_parent ) {

				p_parent.append( divHtml );

		    } else {

		        document.write( divHtml );
		    }
			this.div = p_parent.find( "#" + this.id );

		    // Disable text selection inside the div
			this.div[ 0 ].onselectstart = function() { return false; };
			this.div[ 0 ].unselectable = "on";
			this.div.css( '-moz-user-select', "none" );
 
		    // Connect event handlers
			this.div.bind( "mousedown", gQuery.bindFn( this.onMouseDown, this ) ).bind( "mouseover", gQuery.bindFn( this._on_mouse_enter, this ) ).bind( "mouseup", gQuery.bindFn( this._on_mouse_up, this ) );
		},

		empty: function () {

	        var index;
	        var len;
	        var dates = this.m_dates || {};
	        var months = this.m_months || {};
	        var pfnPopDate = dates.pop;
	        var pfnPopMonth = months.pop;

		    // Clear the dates array
		    for (index = 0, len = (dates.length || 0); index < len; index++) { pfnPopDate.call (dates); }

		    // Destroy the month objects
		    for (index = 0, len = (months.length || 0); index < len; index++) { pfnPopMonth.call (months)._destroy (); }

		    // clear the DOM
		    if (this.brTerm) { this.brTerm.remove(); }

		    // Clear members
		    this.brTerm = null;
		    this.daysOfWeekArray = null;
		    this.selFirst = -1;
		    this.selLast = -1;
		},

		getConfig: function () {

			return this.m_config;
		},

	    getDates: function () {

	        return this.m_dates;
	    },

		getMonths: function () {

			return this.m_months;
		},

	    select: function (p_start, p_end) {

	        if ((p_start || p_end) && this.firstDate) {

	            var offStart = Math.max (Math.floor (((p_start || p_end) - this.firstDate) / 86400000), 0);
	            var offEnd = Math.min (Math.floor (((p_end || p_start) - this.firstDate) / 86400000), this.getDates ().length - 1);
	            var dates = this.getDates ();
	            var index;
	            var len = dates.length;

	            // Make sure the dates are visible
	            if (offEnd >= 0 && offStart < len) {

	                // Select all of the dates
	                for (index = offStart; index <= offEnd; index++) {

	                    dates [index]._select ();
	                }

	                // Store the selection
	                this.selFirst = offStart;
	                this.selLast = offEnd;
	            }
	        }
	    },

		show: function (p_count, p_dim, p_max) {

		    // All-in-one show
		    this._before_show (p_count, p_dim, p_max);
		    this._do_show ();
		},

	    /**
	     * Private methods
	     */
		_alloc_month: function (p_year, p_month, p_div, p_dim, p_config) {

			return new this.m_monthClass (this, p_year, p_month, p_div, p_dim, p_config);
		},

		_before_show: function (p_want, p_dim, p_max) {

			var results = this._compute_size (p_want, p_dim, null, p_max);

		    // How many months did we end up with?
		    this.centerDate = results.center || (this.m_config.centerDate ? true : false);
		    this.countMonths = results.months;
		    this.sizeMonth = results.month_size;
		    this.maxDim.w = results.total_width;
			this.maxDim.h = results.total_height;
	    },

		_compute_size: function (p_want, p_dim, p_min, p_max) {

		    var cols;
			var jwindow = gQuery( window );
		    var dimView = { w: jwindow.width(), h: jwindow.height() };
		    var height;
		    var margins = this._month_margins ();
		    var rows;
		    var size;
			var orig_want = p_want;
			var twant = Math.abs (p_want);
			var width;
			var minSize = p_min || 165;
			p_want = twant;

			// Adjust margins
			if ( margins.w < 0 ) { margins.w = 0; }
			if ( margins.h < 0 ) { margins.h = 0; }

		    // What is our max width?
		    this.maxDim = gQuery.extend( {}, p_dim );
		    if ( p_dim.h <= 0 ) { p_dim.h = dimView.h; }
		    if ( p_dim.w <= 0 ) { p_dim.w = dimView.w; }

		    do {

		        // Determine the largest allowable calendar size
		        if (p_want == 1) {

		            // Easy for only one calendar
		            cols = 1;
					size = ( p_dim.w < p_dim.h ) ? ( p_dim.w - margins.w ) : ( p_dim.h - margins.h );

		        } else {

		            // How many calendars will fit?
		            for (cols = 1; cols <= p_want; cols++) {

						// How many rows?
		                rows = Math.ceil (p_want / cols);

		                // Calculate the height with cols number of items
		                width = Math.min (Math.floor ((p_dim.w - (cols * margins.w)) / cols), p_dim.h - margins.h);
		                height = (width + margins.h) * rows;
		                if (height < p_dim.h) {

							// Calendar fits without adjustment
							size = width;
							break;
							// NOTREACHED
						}

						// How big should it be for the rows to fit?
		                height = Math.min (Math.floor ((p_dim.h - (rows * margins.h)) / rows), p_dim.w - margins.w);
		                width = (height + margins.w)  * cols;
						if ((width + height + margins.w) > p_dim.w || cols == p_want) {

							// Found our best bet
							size = height;
							break;
							// NOTREACHED
						}
		            }
		        }

		        // The calendars should never be smaller than the min size
		        if (size < minSize) {

		            // Adjust the number of columns as well
		            size = minSize;
		            cols = Math.floor (p_dim.w / (size + margins.w));
	                rows = Math.ceil (p_want / cols);
		        }

		        // Limit rows if necessary
		        if (this.maxDim.h > 0) {

		            // Enough calendars to fill the space
					twant = Math.floor (this.maxDim.h / (size + margins.h)) * cols;

		        } else {

		            // Can we keep adding more?
		            if (p_max) {

		                // Can we fit more calendars now that we know the max size?
		                for (; p_max < 0 || twant < p_max; twant++) {

		                    // Calculate the height with index number of items
		                    rows = Math.ceil (twant / cols);
		                    height = (size * rows) + ((rows - 1) * margins.h);
		                    if (height > p_dim.h) {

		                        break;
		                    }
		                }

		                // Go back by 1
		                --twant;
		            }

		            // Even out based on the number of columns
		            twant = Math.ceil (twant / cols) * cols;
		        }

				// What number to use?
				if (twant < p_want && orig_want > 0) {

					// Going to have to scroll
					size = minSize;
		            cols = Math.floor (p_dim.w / (size + margins.w));
	                rows = Math.ceil (p_want / cols);
					p_want = cols * rows;

				} else {

					// Reset in case we added some
					p_want = twant;
		            cols = Math.floor (p_dim.w / (size + margins.w));
					if (cols > p_want) {

						cols = p_want;
					}
	                rows = Math.ceil (p_want / cols);
				}
		    }
		    while (0);

	        var div;
	        var month;
	        var dim = { w: size - margins.w, h: size - margins.h - (gQuery.ieVersion() > 0 ? 15 : 0) };

			// Move to the next array
			div = gQuery( document.createElement( "div" ) ).addClass( this.m_config.monthDivCssClass ).appendTo( "body" );
			month = this._alloc_month (2008, 11, div, dim, this.m_config.month || {});
			dim = { w: div.outerWidth(), h: div.outerHeight() };
			div.remove();
			if ((dim.w > size || dim.h > size) && (!p_min || gQuery.ieVersion() < 0)) {

				return this._compute_size (p_want, p_dim, Math.max(dim.w, dim.h), p_max);
			}

			// Return the results
			return {

				center: (size < 250),
				months: p_want,
				month_size: size,
				total_width: (size + margins.w) * cols,
				total_height: (size + margins.h) * rows
			};
		},

		_days_of_week: function () {

		    // Return the appropriate list
		    if (!this.daysOfWeekArray) {

		        var div;
		        var month;
		        var dim = { w: this.sizeMonth, h: this.sizeMonth };
			    var dimPos = { x: 0, y: 0 };

		        // Iterate through the arrays
		        this.daysOfWeekArray = this.m_daysOfWeekLong;
		        while (this.daysOfWeekArray != this.m_daysOfWeekShort) {

		            // Move to the next array
		            div = gQuery( document.createElement( "div" ) ).addClass( this.m_config.monthDivCssClass );
		            month = this._alloc_month (1900, 1, div, dim, this.m_config.month || {});
					div.appendTo( "body" ).css({ top: dimPos.y, left: dimPos.x });
		            dim = { w: div.outerWidth(), h: div.outerHeight() };
		            div.remove();

		            // Done?
		            if (dim.w <= this.sizeMonth) {

		                break;
		            }
		            else if (this.daysOfWeekArray == this.m_daysOfWeekLong) {

		                this.daysOfWeekArray = this.m_daysOfWeekShort;
		            }
		        }
		    }

		    // Return the array of days
		    return this.daysOfWeekArray;
		},

		_destroy: function () {

	        var index;
	        var len;
	        var dates = this.m_dates || {};
	        var months = this.m_months || {};
	        var pfnPopDate = dates.pop;
	        var pfnPopMonth = months.pop;

		    // Clear the dates array
		    for (index = 0, len = (dates.length || 0); index < len; index++) { pfnPopDate.call (dates); }

		    // Destroy the month objects
		    for (index = 0, len = (months.length || 0); index < len; index++) { pfnPopMonth.call (months)._destroy (); }

		    // Disconnect signals
		    if (this.div) { this.div.unbind(); }

		    // Clear the DOM
		    if (this.div) { this.div.remove(); }
		    if (this.brTerm) { this.brTerm.remove(); }

		    // Clear pointer
		    this.brTerm = null;
		    this.daysOfWeekArray = null;
		    this.div = null;
		},

		_do_show: function () {

			var year;
			var index;
			var obj;
			var node;
			var month;
			var monthCount = this.countMonths;
			var months = this.m_months;
			var dim = { w: this.sizeMonth, h: this.sizeMonth };
			var divClass = this.m_config.monthDivCssClass;
			var pfnPushMonth = months.push;

		    // Determine the days of week to be used
		    this.daysOfWeekArray = null;
		    this._days_of_week();
		    obj = this.daysOfWeekArray;
		    this.empty();
		    this.daysOfWeekArray = obj;

		    // Create the months
		    for (index = 0, year = this.firstDate.getFullYear(), month = this.firstDate.getMonth() + 1; index < monthCount; index++, month++) {

				node = gQuery( document.createElement( "div" ) ).addClass( divClass ).attr({ height: dim.h, width: dim.w }).css({ width: String( dim.w ) + "px", height: "" + String( dim.h ) + "px" });
		        obj = this._alloc_month( year, month, node, dim, this.m_config.month || {} );
		        pfnPushMonth.call( months, obj );
				node.appendTo( this.div );
		        if (month == 12) {

		            // Round to next year
		            month = 0;
		            year++;
		        }
		    }

		    // Set the container width & height
		    this.brTerm = gQuery( document.createElement( "br" ) );
			this.div.css({ width: String( this.maxDim.w ) + "px", height: String( this.maxDim.h ) + "px" });
			this.brTerm.appendTo( this.div );

			// Show the div
			this.div.show();
		},

		_month_margins: function () {

		    var month;
		    var margins;

		    // Calculate the margins of a month object
		    month = gQuery( document.createElement( "div" ) ).addClass( this.m_config.monthDivCssClass ).attr({ height: "1px", width: "1px" }).css({ height: "1px", width: "1px" }).appendTo( "body" );
		    margins = { w: month.outerWidth(), h: month.outerHeight() };
			month.remove();
		    margins.w--;
		    margins.h--;

		    // Return the margins
		    return margins;
		},

		_on_mouse_up: function (p_event) {

			this.isMouseDown = false;
		},

		_on_mouse_enter: function( p_event ) {

			var jlgCell = p_event.target.jlgCalendarCell;
			if( jlgCell ) { jlgCell._on_mouse_enter( p_event ); }
		},

		onMouseDown: function( p_event ) {

			var jlgCell = p_event.target.jlgCalendarCell;
			if( jlgCell ) { jlgCell.onMouseDown( p_event ); }
		},

		_unselect_all: function () {

			var	index;
			var dates = this.getDates ();
			var len;

			// Unselect existing selections
			if (this.selFirst >= 0 && this.selLast >= 0) {

				for (index = this.selFirst, len = this.selLast; index <= len; index++) { dates [index]._unselect (); }
				for (index = this.selLast, len = this.selFirst; index <= len; index++) { dates [index]._unselect (); }
			}

		    // Nothing selected
			this.selFirst = -1;
			this.selLast = -1;
		},

		/**
		 * Private methods
		 */
		_nextId: gQuery.counter()
	} );
};


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.core = JLG.calendar.core || {};
if( !JLG.calendar.core.Viewer ) {

	JLG.calendar.core.Viewer = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( JLG.calendar.core.Viewer.prototype, {

		initialize: function( p_config ) {

			this.m_asyncRequestCount = 0;
			this.m_asyncShowCalendar = false;
			var config = p_config || {};
			var calConfig = gQuery.extend( { cssClass: "jlg-calendar-all" }, config.calendar || {} );

			// Delete modified objects
			delete config.calendar;

	        // Initialize members
	        this.m_config = gQuery.extend( { calendar: calConfig, cssClass: "jlg-calendar-viewer", weekStartsOn: 0 }, config );

	        // Grab the div elements
	        this.m_date = new Date();
			if (this.getConfig().href_base) {

				this.urlAsync = this.getConfig().href_base + this.getConfig().asyncUrl;

			} else {

				this.urlAsync = this.getConfig().asyncUrl;
			}
		},

		getCalendar: function () {

			return this.m_calendar;
		},

		getConfig: function () {

			return this.m_config;
		},

		getDate: function () {

			return this.m_date;
		},

		getProperty: function () {

			return this.m_config.propertyId;
		},

		hasOutstandingRequest: function () {

			return (this.m_asyncRequestCount > 0);
		},

		render: function( p_config ) {

			// Make sure we have a config
			p_config = p_config || {};

	        // Container div
			var cssViewer = this.getConfig().cssClass;
			this.container = gQuery( document.createElement( "div" ) );
			if( cssViewer ) { this.container.addClass( cssViewer ); }

			// All inclusive calendar
			var cssCalendar = (this.getConfig().calendar || {}).cssClass;
	        this.div = gQuery( document.createElement( "div" ) );
			if( cssCalendar ) { this.div.addClass( cssCalendar ); }
			this.div.appendTo( this.container );

			// Break floats
			var br = gQuery( document.createElement( "br" ) ).css( "clear", "both" ).appendTo( this.container );

			// Append to the body
			this.container.appendTo( gQuery( p_config.el || this.getConfig().el || "body" ) );
		},

		show: function (p_count) {

		    var dimDiv;
		    var dimView;
			var index;
			var height;
			var width;
			var jwindow = gQuery( window );

		    // Store the count
		    this.count = p_count || this.getConfig ().monthCount || 1;

		    // Destroy the calendars
		    if (this.getCalendar ()) {

				this.getCalendar ()._destroy ();
				this.m_calendar = null;
			}

		    // Reset calendar variables
	        this.div.show();

		    // Calculate dimensions
		    dimDiv = { w: this.div.innerWidth(), h: this.div.innerHeight() };
		    dimView = { w: jwindow.width(), h: jwindow.height() };

			// Calculate the height
			height = this.getConfig().height;
		    if (height) {

				// Did we get passed a string?
				if (typeof(height) == "string") {

					// Is this a percentage or just a straight number?
					index = height.indexOf ('%');
					if (index >= 0)
						{height = parseInt (dimView.h * (parseInt (height.slice (0, index), 10) / 100), 10);}
					else
						{height = parseInt (height, 10);}
				}

				// Calculate the height
				dimDiv.h = (height < 0) ? dimView.h + height : height;

			} else {

				// Height is negotiable
				dimDiv.h = -1;
			}

			// Calculate the width
			width = this.getConfig ().width;
		    if (width) {

				// Did we get passed a string?
				if (typeof(width) == "string") {

					// Is this a percentage or just a straight number?
					index = width.indexOf ('%');
					if (index >= 0) {
					
						width = parseInt (dimView.w * (parseInt (width.slice (0, index), 10) / 100), 10);

					} else {

						width = parseInt (width, 10);
					}
				}
				dimDiv.w = (width < 0) ? dimView.w + width : width;

		    } else {

				// Width is negotiable
				// dimDiv.w = -1;
			}

		    // Create the calendar
		    this.m_calendar = this._alloc_calendar (this.getConfig ().calendar || {});
		    this.getCalendar ().create (this.m_date, this.getConfig ().weekStartsOn, this.div);
		    this.getCalendar ()._before_show (this.count, dimDiv, (this.count == 1) ? 1 : -1);
		    if (this._beforeShowCalendar ()) {

		        // Show now
		        this.m_asyncShowCalendar = false;
		        this.getCalendar ()._do_show ();
		        this._afterShowCalendar ();

		    } else {

		        // Show when the async call completes
		        this.m_asyncShowCalendar = true;
		    }
		},

		setMonthCount: function (p_count) {

		    // Change properties and refresh calendars
		    this.count = p_count;
		    this.show (this.count);
		},

		setDate: function (p_date, p_options) {

		    // Change properties and refresh calendars
			p_options = p_options || {};
		    this.m_date = p_date;
		    this.show (p_options.count || this.count);
		},

		setProperty: function (p_property, p_config) {

		    this.m_config.propertyId = p_property;
		    this.show ((p_config || {}).monthCount || this.count);
		},

		/**
		 * Private methods
		 */
		_adjust_date: function (p_date, p_offset)
		{
		    // Adjust the calendar date offset
		    if (p_offset > 0 && p_date.getMonth () == 11)
		    {
		        // Rollover to the next year
		        p_date.setMonth (0);
		        p_date.setYear (p_date.getFullYear () + 1);
		    }
		    else if (p_offset < 0 && p_date.getMonth () === 0)
		    {
		        // Rollover to the previous year
		        p_date.setMonth (11);
		        p_date.setYear (p_date.getFullYear () - 1);
		    }
		    else
		    {
		        // Roll to the next/prev month
		        p_date.setMonth (p_date.getMonth () + ((p_offset > 0) ? 1 : -1));
		    }

		    // Return the date
		    return p_date;
		},

		_afterShowCalendar: function () {
		},

		_alloc_calendar: function( p_config ) {

			return new JLG.calendar.core.Calendar( gQuery.extend( { viewer: this }, p_config || {} ) );
		},

		_asyncShow: function() {

		    if (this.m_asyncShowCalendar) {

		        // Clear the flags and show
		        this.m_asyncShowCalendar = false;
		        this.getCalendar()._do_show ();
		        this._afterShowCalendar();
		    }
		},

		_afterAsyncUpdate: function() {

			// Unlock and show the calendars
			--this.m_asyncRequestCount;
			this.unlock();
			this._asyncShow();
		},

		_asyncUpdate: function( p_startsOn, p_count, p_method ) {

			if( this.getConfig().propertyId ) {

			    var startsOn = p_startsOn.getFullYear() + "-" + (p_startsOn.getMonth() + 1) + "-" + p_startsOn.getDate();
			    var query = ".js?starts_on=" + escape( startsOn ) + "&count_months=" + escape( p_count ) + "&json_callback=" + escape( p_method );

				// Log the fact that there is an outstanding update
				++this.m_asyncRequestCount;

			    // Asynchronously get the calendar information
				gQuery.getScript( 'http://owner.rentexpert.com/property/' + this.getConfig().propertyId + this.urlAsync + query, gQuery.bindFn( this._afterAsyncUpdate, this ), true );
			}
		},

		_beforeShowCalendar: function() { return true; },

		unlock: function () {},

		/**
		 * Private methods
		 */
		_nextId: gQuery.counter()
	} );
}

// Create a default picker
JLG.date.DateRangePicker = JLG.date.buildDateRangePicker( JLG.calendar.core );


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.availability = JLG.calendar.availability || {};
JLG.calendar.availability.Range = JLG.calendar.availability.Range || function (p_startsOn, p_endsOn) {

    // Member variables
    this.starts_on = p_startsOn;
    this.ends_on = p_endsOn;
    this.start_time = p_startsOn.getTime ();
    this.end_time = p_endsOn.getTime ();
};


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.availability = JLG.calendar.availability || {};
if( !JLG.calendar.availability.Cell ) {

	JLG.calendar.availability.Cell = function( p_month ) { this.initialize( p_month ); };
	gQuery.extend( JLG.calendar.availability.Cell.prototype, JLG.calendar.core.Cell.prototype, {

		initialize: function( p_month ) {

    		this.m_availAvailable = true;
			JLG.calendar.core.Cell.prototype.initialize.call( this, p_month );
		},

		create: function (p_day, p_parent) {

		    // See if this is an available or unavailable cell
		    this.m_availAvailable = this.getMonth ().getCalendar ().isAvailable (new Date (this.getMonth ().year, this.getMonth ().month - 1, p_day));
			JLG.calendar.core.Cell.prototype.create.call (this, p_day, p_parent, this.m_availAvailable ? null : "jlg-cal-unavailable");
		},

	    /**
	     * Private Methods
	     */
	 	onMouseDown: function (p_src) {

	        return this.m_availAvailable ? JLG.calendar.core.Cell.prototype.onMouseDown.call (this, p_src) : true;
	 	},

	 	_on_mouse_enter: function (p_src) {

	        return this.m_availAvailable ? JLG.calendar.core.Cell.prototype._on_mouse_enter.call (this, p_src) : true;
	 	},

	 	_select: function () {

	        if (this.m_availAvailable) { JLG.calendar.core.Cell.prototype._select.call (this); }
	 	},

	 	_unselect: function () {

	        if (this.m_availAvailable) { JLG.calendar.core.Cell.prototype._unselect.call (this); }
	 	}
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.availability = JLG.calendar.availability || {};
if( !JLG.calendar.availability.Month ) {

	JLG.calendar.availability.Month = function( p_calendar, p_year, p_month, p_div, p_dim, p_options ) { this.initialize( p_calendar, p_year, p_month, p_div, p_dim, p_options ); };
	gQuery.extend( JLG.calendar.availability.Month.prototype, JLG.calendar.core.Month.prototype, {

		_alloc_cell: function () {

			return new JLG.calendar.availability.Cell (this);
		}
	} );
}


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.calendar = JLG.calendar || {};
JLG.calendar.availability = JLG.calendar.availability || {};
if( !JLG.calendar.availability.Calendar ) {

	JLG.calendar.availability.Calendar = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( JLG.calendar.availability.Calendar.prototype, JLG.calendar.core.Calendar.prototype, {

		initialize: function( p_config ) {

			this._availabilities = [];
			JLG.calendar.core.Calendar.prototype.initialize.call( this, p_config );
		},

		setAvailability: function( p_availability ) {

			this._availabilities = p_availability || [];
		},

		_alloc_month: function (p_year, p_month, p_div, p_dim, p_options) {

			return new JLG.calendar.availability.Month (this, p_year, p_month, p_div, p_dim, p_options);
		},

		findReservation: function() {

			var query;
		    var starts_on = this.getDates()[ this.selFirst ].getDate();
		    var ends_on = this.getDates()[ this.selLast ].getDate();
			var temp;

		    // What dates are selected?
		    if (starts_on.getTime() > ends_on.getTime()) {

		        // Swap dates
		        temp = starts_on;
		        starts_on = ends_on;
		        ends_on = temp;
		    }
			if (starts_on.getTime() == ends_on.getTime()) { ends_on.setTime( ends_on.getTime() + 86400000 ); }

			// Make sure the dates are property formatted
			starts_on = starts_on.getFullYear() + "-" + (starts_on.getMonth() + 1) + "-" + starts_on.getDate();
			ends_on = ends_on.getFullYear() + "-" + (ends_on.getMonth() + 1) + "-" + ends_on.getDate();

		    // Asynchronously get the calendar information
		    query = "?checkin=" + escape( starts_on ) + "&checkout=" + escape( ends_on );
			gQuery.loadScript( 'http://owner.rentexpert.com/property/' + this.getConfig ().viewer.getConfig().propertyId + '/booking/popup_find' + query, null, true );
		},

		isAvailable: function( p_date ) {

			var result = false;
		    var testTime = p_date.getTime();

		    // Scoot past all rnages (in case there are extra)
			for (var avail = this._availabilities, index = 0, len = avail.length; index < len; ++index) {

				var range = avail[ index ];

		        // Haven't yet reached the next availability
		        if (range.start_time > testTime) {

					break;
					// NOTREACHED
				}

		        // Haven't reached the end of this availability range
		        if (testTime <= range.end_time) {

					result = true;
					break;
					// NOTREACHED
				}
		    }

		    // Not available
		    return result;
		},

		_on_mouse_up: function( p_event ) {

		    // Process the selection
		    if (!this.getConfig().disableBooking && this.isMouseDown) { this.findReservation(); }

		    // Call the base class
		    JLG.calendar.core.Calendar.prototype._on_mouse_up.call( this, p_event );
		}
	} );
};


if (typeof window.JLG === "undefined" || !window.JLG) { window.JLG = {}; }
JLG.calendar = JLG.calendar || {};
JLG.calendar.availability = JLG.calendar.availability || {};
if( !JLG.calendar.availability.Viewer ) {

	JLG.calendar.availability.Viewer = function( p_config ) { this.initialize( p_config ); };
	gQuery.extend( JLG.calendar.availability.Viewer.prototype, JLG.calendar.core.Viewer.prototype, {

		initialize: function( p_config ) {

			// Call the base class
			JLG.calendar.core.Viewer.prototype.initialize.call( this, gQuery.extend( { asyncUrl: '/calendar/availability' }, p_config || {} ) );

			// Initialize custom options
			this.m_availabilityCallback = this.getConfig().availabilityCallback || "JLG.calendar.availability.viewer.setCentralAvailabilityInPlace";

			// Store ourself as the instance
			JLG.calendar.availability.viewer = this;
		},

		setCentralAvailabilityInPlace: function( p_json ) {

			// Should we store to a window variable?
			var availVar = this.getConfig().availabilityVar;
			if( availVar ) { window[ availVar ] = p_json; }

			// Determine the availability
			var availability = [];
			for( var index = 0, len = p_json.length; index < len; index++ ) { availability.push( new JLG.calendar.availability.Range( p_json[ index ].starts_on, p_json[ index ].ends_on ) ); }
			this.getCalendar().setAvailability( availability );
		},

		_alloc_calendar: function( p_config ) {

			return new JLG.calendar.availability.Calendar( gQuery.extend( { viewer: this }, p_config || {} ) );
		},

		_beforeShowCalendar: function() {

			// Do we have a property defined
			if( this.getProperty() ) {

				// See if we have availability information already defined
				var availability = this.getConfig().availabilityVar;
				if( availability ) {

					var value = eval( "window." + availability );
					if( value ) {

						eval( this.m_availabilityCallback +"(window. " + availability + ")" );
						return true;
						// NOTREACHED
					}
				}

			    // Don't show until we have the data
			    this._asyncUpdate( this.getCalendar().firstDate, this.getCalendar().countMonths, this.m_availabilityCallback );
		    	return false;
				// NOTREACHED

			} else {

				return true;
				// NOTREACHED
			}
		}
	} );
}


if( typeof window.JLG === "undefined" || !window.JLG ) { window.JLG = {}; }
JLG.calendar = JLG.calendar || {};
JLG.calendar.availability = JLG.calendar.availability || {};
JLG.calendar.availability.DateRangePicker = JLG.date.buildDateRangePicker( JLG.calendar.availability );

/******************************************************
 * ADD AVAILABILITY INFO TO PICKER
 ******************************************************/
var proto = JLG.calendar.availability.DateRangePicker.prototype;
proto.initialize_without_availability = proto.initialize;
proto.initialize = function( p_config ) {

	// Call the base class
	p_config = p_config || {};
	p_config.viewer = p_config.viewer || {};	
	p_config.viewer.calendar = p_config.viewer.calendar || {};
	p_config.viewer.calendar.disableBooking = true;
	if( p_config.propertyId ) { p_config.viewer.propertyId = p_config.propertyId; }
	proto.initialize_without_availability.call( this, p_config );
};

proto.setAvailability = function( p_range ) { this.getWindow().getViewer().setCentralAvailabilityInPlace( p_range ); };

proto.setProperty = function( p_property ) { this.getWindow().getViewer().setProperty( p_property, { monthCount: this.getWindow().getConfig().monthsAvailabl } ); };


if (typeof window.JLG === "undefined" || !window.JLG) {window.JLG = {};}
JLG.util = JLG.util || {};
JLG.util.date = JLG.util.date || new JLG.date.Parser ();
