1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc.
  5  *
  6  * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at: https://www.zimbra.com/license
  9  * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15
 10  * have been added to cover use of software over a computer network and provide for limited attribution
 11  * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B.
 12  *
 13  * Software distributed under the License is distributed on an "AS IS" basis,
 14  * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing rights and limitations under the License.
 16  * The Original Code is Zimbra Open Source Web Client.
 17  * The Initial Developer of the Original Code is Zimbra, Inc.  All rights to the Original Code were
 18  * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015.
 19  *
 20  * All portions of the code are Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * Creates a calendar widget
 26  * @constructor
 27  * @class
 28  * This class provides a calendar view.
 29  *
 30  * @author Ross Dargahi
 31  * @author Roland Schemers
 32  *
 33  * @param {hash}		params			a hash of parameters
 34  * @param {DwtComposite}      params.parent			the parent widget
 35  * @param {string}      params.className			the CSS class
 36  * @param {constant}      params.posStyle			the positioning style (see {@link Dwt})
 37  * @param {constant}     [params.firstDayOfWeek=DwtCalendar.SUN]		the first day of the week
 38  * @param {boolean}	[params.forceRollOver=true] 	if <code>true</code>, then clicking on (or setting) the widget to a 
 39  *												date that is not part of the current month (i.e. one of 
 40  *												the grey prev or next month days) will result in the 
 41  *												widget rolling 	the date to that month.
 42  * @param {array}      params.workingDays		a list of days that are work days. This array assumes that
 43  * 												index 0 is Sunday. Defaults to Mon-Fri being work days.
 44  * @param {boolean}      params.hidePrevNextMo 	a flag indicating whether widget should hide days of the 
 45  *												previous/next month
 46  * @param {boolean}      params.readOnly 		a flag indicating that this widget is read-only (should not 
 47  *												process events such as mouse clicks)
 48  * @param {boolean}      params.showWeekNumber	a flag indicating whether widget should show week number
 49  *        
 50  * @extends		DwtComposite
 51  */
 52 DwtCalendar = function(params) {
 53 	if (arguments.length == 0) { return; }
 54 	params = Dwt.getParams(arguments, DwtCalendar.PARAMS);
 55 	params.className = params.className || "DwtCalendar";
 56 	DwtComposite.call(this, params);
 57 
 58 	this._skipNotifyOnPage = false;
 59 	this._hidePrevNextMo = params.hidePrevNextMo;
 60 	this._readOnly = params.readOnly;
 61 	this._showWeekNumber = params.showWeekNumber;
 62 	this._uuid = Dwt.getNextId();
 63 	var cn = this._origDayClassName = params.className + "Day";
 64 	this._todayClassName = " " + params.className + "Day-today";
 65 	this._selectedDayClassName = " " + cn + "-" + DwtCssStyle.SELECTED;
 66 	this._hoveredDayClassName = " " + cn + "-" + DwtCssStyle.HOVER;
 67 	this._activeDayClassName = " " + cn + "-" + DwtCssStyle.ACTIVE;
 68 	this._hiliteClassName = " " + cn + "-hilited";
 69 	this._greyClassName = " " + cn + "-grey";
 70 	
 71 	if (!this._readOnly) {
 72 		this._installListeners();
 73 	}
 74 
 75 	this._selectionMode = DwtCalendar.DAY;
 76 	
 77 	this._init();
 78 
 79 	this._weekDays = new Array(7);
 80 	this._workingDays = params.workingDays || DwtCalendar._DEF_WORKING_DAYS;
 81     this._useISO8601WeekNo = params.useISO8601WeekNo;
 82 	this.setFirstDayOfWeek(params.firstDayOfWeek || DwtCalendar.SUN);
 83 	
 84 	this._forceRollOver = (params.forceRollOver !== false);
 85 };
 86 
 87 DwtCalendar.PARAMS = ["parent", "className", "posStyle", "firstDayOfWeek", "forceRollOver",
 88 					  "workingDaysArray", "hidePrevNextMo", "readOnly"];
 89 
 90 DwtCalendar.prototype = new DwtComposite;
 91 DwtCalendar.prototype.constructor = DwtCalendar;
 92 
 93 /**
 94  * Sunday.
 95  */
 96 DwtCalendar.SUN = 0;
 97 /**
 98  * Monday.
 99  */
100 DwtCalendar.MON = 1;
101 /**
102  * Tuesday.
103  */
104 DwtCalendar.TUE = 2;
105 /**
106  * Wednesday.
107  */
108 DwtCalendar.WED = 3;
109 /**
110  * Thursday.
111  */
112 DwtCalendar.THU = 4;
113 /**
114  * Friday.
115  */
116 DwtCalendar.FRI = 5;
117 /**
118  * Saturday.
119  */
120 DwtCalendar.SAT = 6;
121 
122 // Selection modes
123 /**
124  * Defines the "day" selection mode.
125  */
126 DwtCalendar.DAY = 1;
127 /**
128  * Defines the "week" selection mode.
129  */
130 DwtCalendar.WEEK = 2;
131 /**
132  * Defines the "work week" selection mode.
133  */
134 DwtCalendar.WORK_WEEK = 3;
135 /**
136  * Defines the "month" selection mode.
137  */
138 DwtCalendar.MONTH = 4;
139 
140 DwtCalendar.RANGE_CHANGE = "DwtCalendar.RANGE_CHANGE";
141 
142 DwtCalendar._FULL_WEEK = [1, 1, 1, 1, 1, 1, 1];
143 DwtCalendar._DEF_WORKING_DAYS = [0, 1, 1, 1, 1, 1, 0];
144 DwtCalendar._DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
145 
146 DwtCalendar._NO_MONTH = -2;
147 DwtCalendar._PREV_MONTH = -1;
148 DwtCalendar._THIS_MONTH = 0;
149 DwtCalendar._NEXT_MONTH = 1;
150 
151 DwtCalendar._NORMAL = 1;
152 DwtCalendar._HOVERED = 2;
153 DwtCalendar._ACTIVE = 3;
154 DwtCalendar._SELECTED = 4;
155 DwtCalendar._DESELECTED = 5;
156 
157 DwtCalendar.DATE_SELECTED 		= 1;
158 DwtCalendar.DATE_DESELECTED 	= 2;
159 DwtCalendar.DATE_DBL_CLICKED 	= 3;
160 
161 DwtCalendar._LAST_DAY_CELL_IDX = 41;
162 
163 DwtCalendar._BUTTON_CLASS = "DwtCalendarButton";
164 DwtCalendar._BUTTON_HOVERED_CLASS = DwtCalendar._BUTTON_CLASS + "-" + DwtCssStyle.HOVER;
165 DwtCalendar._BUTTON_ACTIVE_CLASS = DwtCalendar._BUTTON_CLASS + "-" + DwtCssStyle.ACTIVE;
166 
167 DwtCalendar._TITLE_CLASS = "DwtCalendarTitle";
168 DwtCalendar._TITLE_HOVERED_CLASS = DwtCalendar._TITLE_CLASS + "-" + DwtCssStyle.HOVER;
169 DwtCalendar._TITLE_ACTIVE_CLASS = DwtCalendar._TITLE_CLASS + "-" + DwtCssStyle.ACTIVE;
170 
171 /**
172  * Returns a string representation of the object.
173  * 
174  * @return		{string}		a string representation of the object
175  */
176 DwtCalendar.prototype.toString = 
177 function() {
178 	return "DwtCalendar";
179 };
180 
181 /**
182  * Adds a selection listener.
183  * 
184  * @param	{AjxListener}	listener		the listener
185  */
186 DwtCalendar.prototype.addSelectionListener = 
187 function(listener) {
188 	this.addListener(DwtEvent.SELECTION, listener);
189 };
190 
191 /**
192  * Removes a selection listener.
193  * 
194  * @param	{AjxListener}	listener		the listener
195  */
196 DwtCalendar.prototype.removeSelectionListener = 
197 function(listener) { 
198 	this.removeListener(DwtEvent.SELECTION, listener);
199 };
200 
201 /**
202  * Adds an action listener.
203  * 
204  * @param	{AjxListener}	listener		the listener
205  */
206 DwtCalendar.prototype.addActionListener = 
207 function(listener) {
208 	this.addListener(DwtEvent.ACTION, listener);
209 };
210 
211 /**
212  * Removes an action listener.
213  * 
214  * @param	{AjxListener}	listener		the listener
215  */
216 DwtCalendar.prototype.removeActionListener = 
217 function(listener) { 
218 	this.removeListener(DwtEvent.ACTION, listener);
219 };
220 
221 /**
222  * Adds a date range listener. Date range listeners are called whenever the date range of the calendar
223  * changes (i.e. when it rolls over due to a programatic action via {@link #setDate} or
224  * via user selection).
225  *
226  * @param 	{AjxListener}		listener		the listener
227  */
228 DwtCalendar.prototype.addDateRangeListener = 
229 function(listener) {
230 	this.addListener(DwtEvent.DATE_RANGE, listener);
231 };
232 
233 /**
234  * Removes a date range listener.
235  * 
236  * @param 	{AjxListener}		listener		the listener
237  */
238 DwtCalendar.prototype.removeDateRangeListener = 
239 function(listener) { 
240 	this.removeListener(DwtEvent.DATE_RANGE, listener);
241 };
242 
243 /**
244  * Sets the skip notify on page. This method notify (or not) selection when paging arrow buttons
245  * are clicked.
246  *
247  * @param	{boolean}	skip		if <code>true</code>, do not notify selection
248  */
249 DwtCalendar.prototype.setSkipNotifyOnPage = 
250 function(skip) {
251 	this._skipNotifyOnPage = skip;
252 };
253 
254 /**
255  * Gets the skip notify on page setting.
256  * 
257  * @return	{boolean}	<code>true</code>, do not notify selection
258  */
259 DwtCalendar.prototype.getSkipNotifyOnPage = 
260 function() {
261 	return this._skipNotifyOnPage;
262 };
263 
264 /**
265  * Sets the date.
266  * 
267  * @param	{Date}	date	the date
268  * @param	{boolean}	skipNotify		if <code>true</code>, do not notify selection
269  * @param {boolean}	forceRollOver 	if <code>true</code>, then clicking on (or setting) the widget to a 
270  *												date that is not part of the current month (i.e. one of 
271  *												the grey prev or next month days) will result in the 
272  *												widget rolling 	the date to that month.
273  * @param	{boolean}	dblClick		if <code>true</code>, require a double click
274  */
275 DwtCalendar.prototype.setDate =
276 function(date, skipNotify, forceRollOver, dblClick) {
277 
278 	forceRollOver = (forceRollOver == null) ? this._forceRollOver : forceRollOver;
279 	
280 	// Check if the date is available for selection. Basically it is unless we are in
281 	// work week selection mode and <date> is not a working day
282 	//if (this._selectionMode == DwtCalendar.WORK_WEEK && !this._currWorkingDays[date.getDay()])
283 	//	return false;
284 
285 	if(!date) {
286 		date = new Date();
287 	}
288 	var newDate = new Date(date.getTime());
289 	var oldDate = this._date;
290 
291 	var layout = false;
292 	var notify = false;
293 	var cellId;
294 
295 	if (this._date2CellId != null) {
296 		var idx = (newDate.getFullYear() * 10000) + (newDate.getMonth() * 100) + newDate.getDate();
297 		var cellId = this._date2CellId[idx];
298 		
299 		if (cellId) {
300 		 	if (cellId == this._selectedCellId)
301 		 		notify = true;
302 
303 			var cell = document.getElementById(cellId);
304 			if (cell._dayType == DwtCalendar._THIS_MONTH)
305 				notify = true;
306 			else if (forceRollOver)
307 				notify = layout = true;
308 			else
309 				notify = true;
310 		} else {
311 			 notify = layout = true;
312 		}
313 	} else {
314 		notify = layout = true;
315 	}
316 
317 	// update before layout, notify after layout
318 	if (notify) {
319 		if (this._date){
320 			// 5/13/2005 EMC -- I'm not sure why this was setting the hours to 0.
321 			// I think it should respect what the user passed in, and only change
322 			// the parts of the date that it is responsible for.
323 			//newDate.setHours(0,0,0,0);
324 			//handle daylight saving
325 			if(AjxDateUtil.isDayShifted(newDate)) {
326 				AjxDateUtil.rollToNextDay(newDate);
327 			}
328 			newDate.setHours(this._date.getHours(), this._date.getMinutes(), this._date.getSeconds(), 0);            
329 		}
330 
331 		this._date = newDate;
332 		if (!layout && !this._readOnly) {
333 			this._setSelectedDate();
334 			this._setToday();
335 		}
336 	}
337 
338 	if (layout) {
339 		this._layout();
340 	}
341 
342 	if (notify && !skipNotify) {
343 		var type = dblClick ? DwtCalendar.DATE_DBL_CLICKED : DwtCalendar.DATE_SELECTED;
344 		this._notifyListeners(DwtEvent.SELECTION, type, this._date);
345 	}
346 	
347 	return true;
348 };
349 
350 /**
351  * Checks if the cell is selected.
352  * 
353  * @param	{string}	cellId			the cell id	
354  * @return	{boolean}	<code>true</code> if the cell is the selected day
355  */
356 DwtCalendar.prototype.isSelected =
357 function(cellId) {
358 	// if cellId is the selected day, then return true, else if we are NOT in
359 	// day selection mode (i.e. week/work week) then compute the row and index
360 	// of cellId and look it up in the week array to see if it is a selectable day
361 	if (cellId == this._selectedDayElId) {
362 		return true;
363 	} else if (this._selectionMode != DwtCalendar.DAY) {
364 		// If the cell is in the same row as the currently selected cell and it
365 		// is a selectable day (i.e. a working day in the case of work week),
366 		// then say it is selected
367 		var cellIdx = this._getDayCellIndex(cellId);
368 		if (Math.floor(cellIdx / 7) == Math.floor(this._getDayCellIndex(this._selectedDayElId) / 7)
369 			&& this._currWorkingDays[cellIdx % 7])
370 			return true;
371 	}
372 	return false;
373 };
374 
375 /**
376  * Gets the force roll over setting. Force roll over is occurs when a date that
377  * is not part of the current month (i.e. one of the grey prev or next month
378  * days) will result in the widget rolling 	the date to that month.
379  * 
380  * @return	{boolean}	<code>true</code> if force roll over is set
381  */
382 DwtCalendar.prototype.getForceRollOver =
383 function() {
384 	return this._forceRollOver;
385 };
386 
387 /**
388  * Sets the force roll over setting. Force roll over is occurs when a date that
389  * is not part of the current month (i.e. one of the grey prev or next month
390  * days) will result in the widget rolling 	the date to that month.
391  * 
392  * @param	{boolean}	force		if <code>true</code>, force roll over
393  */
394 DwtCalendar.prototype.setForceRollOver =
395 function(force) {
396 	if (force == null) { return; }
397 	
398 	if (this._forceRollOver != force) {
399 		this._forceRollOver = force;
400 		this._layout();
401 	}
402 };
403 
404 /**
405  * Gets the selection mode.
406  * 
407  * @return	{constant}		the selection mode
408  */
409 DwtCalendar.prototype.getSelectionMode =
410 function() {
411 	return this._selectionMode;
412 };
413 
414 /**
415  * Sets the selection mode.
416  * 
417  * @return	{constant}		selectionMode		the selection mode
418  */
419 DwtCalendar.prototype.setSelectionMode =
420 function(selectionMode) {
421 	if (this._selectionMode == selectionMode) { return; }
422 
423 	this._selectionMode = selectionMode;
424 	if (selectionMode == DwtCalendar.WEEK) {
425 		this._currWorkingDays = DwtCalendar._FULL_WEEK;
426 	} else if (selectionMode == DwtCalendar.WORK_WEEK) {
427 		this._currWorkingDays = this._workingDays;
428 	}
429 
430 	this._layout();
431 };
432 
433 /**
434  * Sets the working week.
435  * 
436  * @param	{array}	workingDaysArray		an array of days
437  */
438 DwtCalendar.prototype.setWorkingWeek =
439 function(workingDaysArray) {
440 	// TODO Should really create a copy of workingDaysArray
441 	this._workingDays = this._currWorkingDays = workingDaysArray;
442 	
443 	if (this._selectionMode == DwtCalendar.WORK_WEEK) {
444 		DBG.println("FOO!!!");
445 		this._layout();
446 	}
447 };
448 
449 /**
450  * Enables/disables the highlight (i.e. "bolding") on the dates in <code><dates></code>.
451  *
452  * @param {object} dates associative array of {@link Date} objects for
453  * which to enable/disable highlighting
454  * @param {boolean}	enable 	if <code>true</code>, enable highlighting
455  * @param {boolean}	clear 	if <code>true</code>, clear current highlighting
456  */
457 DwtCalendar.prototype.setHilite =
458 function(dates, enable, clear) {
459 	if (this._date2CellId == null) { return; }
460 
461 	var cell;
462 	var aDate;
463 	if (clear) {
464 		for (aDate in this._date2CellId) {
465 			cell = document.getElementById(this._date2CellId[aDate]);
466 			if (cell._isHilited) {
467 				cell._isHilited = false;
468 				this._setClassName(cell, DwtCalendar._NORMAL);
469 			}	
470 		}
471 	}
472 
473 	var cellId;
474 	for (var i in dates) {
475         // NOTE: Protect from prototype extensions.
476         if (dates.hasOwnProperty(i)) {
477             aDate = dates[i];
478             cellId = this._date2CellId[aDate.getFullYear() * 10000 + aDate.getMonth() * 100 + aDate.getDate()];
479 
480             if (cellId) {
481                 cell = document.getElementById(cellId);
482                 if (cell._isHilited != enable) {
483                     cell._isHilited = enable;
484                     this._setClassName(cell, DwtCalendar._NORMAL);
485                 }
486             }
487         }
488 	}
489 };
490 
491 /**
492  * Gets the date.
493  * 
494  * @return	{Date}	the date
495  */
496 DwtCalendar.prototype.getDate =
497 function() {
498 	return this._date;
499 };
500 
501 /**
502  * Sets the first date of week.
503  * 
504  * @param	{constant}		firstDayOfWeek		the first day of week
505  */
506 DwtCalendar.prototype.setFirstDayOfWeek =
507 function(firstDayOfWeek) {
508 	for (var i = 0; i < 7; i++) {
509 		this._weekDays[i] = (i < firstDayOfWeek)
510 			? (6 - (firstDayOfWeek -i - 1))
511 			: (i - firstDayOfWeek);
512 
513 		var dowCell = document.getElementById(this._getDOWCellId(i));
514 		dowCell.innerHTML = AjxDateUtil.WEEKDAY_SHORT[(firstDayOfWeek + i) % 7];
515 	}
516     this._firstDayOfWeek = firstDayOfWeek
517 	this._layout();
518 };
519 
520 /**
521  * Gets the date range.
522  * 
523  * @return	{Object}		the range (<code>range.start</code> and <code>range.end</code>)
524  */
525 DwtCalendar.prototype.getDateRange =
526 function () {
527 	return this._range;
528 };
529 
530 DwtCalendar.prototype._getDayCellId =
531 function(cellId) {
532 	return ("c:" + cellId + ":" + this._uuid);
533 };
534 
535 DwtCalendar.prototype._getDayCellIndex =
536 function(cellId) {
537 	return cellId.substring(2, cellId.indexOf(":", 3));
538 };
539 
540 DwtCalendar.prototype._getDOWCellId =
541 function(cellId) {
542 	return ("w:" + cellId + ":" + this._uuid);
543 };
544 
545 DwtCalendar.prototype._getWeekNumberCellId =
546 function(cellId) {
547 	return ("k:" + cellId + ":" + this._uuid);
548 };
549 
550 DwtCalendar.prototype._getDaysInMonth =
551 function(mo, yr) {
552 	/* If we are not dealing with Feb, then simple lookup
553 	 * Leap year rules
554 	 *  1. Every year divisible by 4 is a leap year.
555 	 *  2. But every year divisible by 100 is NOT a leap year
556 	 *  3. Unless the year is also divisible by 400, then it is still a leap year.*/
557 	if (mo != 1) {
558 		return DwtCalendar._DAYS_IN_MONTH[mo];
559 	}
560 
561 	if (yr % 4 != 0 || (yr % 100 == 0 && yr % 400 != 0)) {
562 		return 28;
563 	}
564 
565 	return 29;
566 };
567 
568 DwtCalendar.prototype._installListeners =
569 function() {
570 	this._setMouseEventHdlrs();
571 	this.addListener(DwtEvent.ONMOUSEOVER, new AjxListener(this, this._mouseOverListener));
572 	this.addListener(DwtEvent.ONMOUSEOUT, new AjxListener(this, this._mouseOutListener));
573 	this.addListener(DwtEvent.ONMOUSEDOWN, new AjxListener(this, this._mouseDownListener));
574 	this.addListener(DwtEvent.ONMOUSEUP, new AjxListener(this, this._mouseUpListener));
575 	this.addListener(DwtEvent.ONDBLCLICK, new AjxListener(this, this._doubleClickListener));
576 };
577 
578 DwtCalendar.prototype._notifyListeners =
579 function(eventType, type, detail, ev) {
580 	if (!this.isListenerRegistered(eventType)) { return; }
581 
582 	var selEv = DwtShell.selectionEvent;
583 	if (ev) {
584 		DwtUiEvent.copy(selEv, ev);
585 	} else {
586 		selEv.reset();
587 	}
588 	selEv.item = this;
589 	selEv.detail = detail;
590 	selEv.type = type;
591 	this.notifyListeners(eventType, selEv);
592 };
593 
594 DwtCalendar.prototype._layout =
595 function() {
596 	if (this._date == null) { this._date = new Date(); }
597 
598 	if (!this._calWidgetInited) {
599 		this._init();
600 	}
601 
602 	var date = new Date(this._date.getTime());
603 	date.setDate(1);
604 	var year = date.getFullYear();
605 	var month  = date.getMonth();
606 	var firstDay = date.getDay();
607 	var daysInMonth = this._getDaysInMonth(month, year);
608 	var day = 1;
609 	var nextMoDay = 1;
610 
611 	this._date2CellId = new Object();
612 	this._selectedDayElId = null;
613 
614 	// Figure out how many days from the previous month we have to fill in
615 	// (see comment below)
616 	var lastMoDay, lastMoYear, lastMoMonth, nextMoMonth, nextMoYear;
617 	if (!this._hidePrevNextMo) {
618 		if (month != 0) {
619 			lastMoDay = this._getDaysInMonth(month - 1, year) - this._weekDays[firstDay] + 1;
620 			lastMoYear = year;
621 			lastMoMonth = month - 1;
622 			if (month != 11) {
623 				nextMoMonth = month + 1;
624 				nextMoYear = year;
625 			} else {
626 				nextMoMonth = 0;
627 				nextMoYear = year + 1;
628 			}
629 		} else {
630 			lastMoDay = this._getDaysInMonth(11, year - 1) - this._weekDays[firstDay] + 1;
631 			lastMoYear = year - 1;
632 			lastMoMonth = 11;
633 			nextMoMonth = 1;
634 			nextMoYear = year;
635 		}
636 	}
637 
638 	for (var i = 0; i < 6; i++) {
639 		for (var j = 0; j < 7; j++) {
640 			var dayCell = document.getElementById(this._getDayCellId(i * 7 + j));
641 
642 			if (dayCell._isHilited == null) {
643 				dayCell._isHilited = false;
644 			}
645 
646 			if (day <= daysInMonth) {
647 				/* The following if statement deals with the first day of this month not being
648 				 * the first day of the week. In this case we must fill the preceding days with
649 				 * the final days of the previous month */
650 				if (i != 0 || j >= this._weekDays[firstDay]) {
651 					this._date2CellId[(year * 10000) + (month * 100) + day] = dayCell.id;
652 					dayCell._day = day;
653 					dayCell._month = month;
654 					dayCell._year = year;
655 					dayCell.innerHTML = day++;
656 					dayCell._dayType = DwtCalendar._THIS_MONTH;
657 					if (this._readOnly) {
658 						dayCell.style.fontFamily = "Arial";
659 						dayCell.style.fontSize = "10px";
660 					}
661 				} else {
662 					if (this._hidePrevNextMo) {
663 						dayCell.innerHTML = "";
664 					} else {
665 						this._date2CellId[(lastMoYear * 10000) + (lastMoMonth * 100) + lastMoDay] = dayCell.id;
666 						dayCell._day = lastMoDay;
667 						dayCell._month = lastMoMonth;
668 						dayCell._year = lastMoYear;
669 						dayCell.innerHTML = lastMoDay++;
670 						dayCell._dayType = DwtCalendar._PREV_MONTH;
671 					}
672 				}
673 			} else if (!this._hidePrevNextMo) {
674 				// Fill any remaining slots with days from next month
675 				this._date2CellId[(nextMoYear * 10000) + (nextMoMonth * 100) + nextMoDay] = dayCell.id;
676 				dayCell._day = nextMoDay;
677 				dayCell._month = nextMoMonth;
678 				dayCell._year = nextMoYear;
679 				dayCell.innerHTML = nextMoDay++;
680 				dayCell._dayType = DwtCalendar._NEXT_MONTH;
681 			}
682 			this._setClassName(dayCell, DwtCalendar._NORMAL);
683 		}
684 
685 		if (this._showWeekNumber) {
686 			var kwCellId = this._getWeekNumberCellId('kw' + i * 7);
687 			var kwCell = document.getElementById(kwCellId);
688 			if (kwCell) {
689 				var firstDayCell = document.getElementById(this._getDayCellId(i * 7));
690 				kwCell.innerHTML = AjxDateUtil.getWeekNumber(new Date(firstDayCell._year, firstDayCell._month, firstDayCell._day), this._firstDayOfWeek, null, this._useISO8601WeekNo);
691 			}
692 		}
693 	}
694 
695 	this._setTitle(month, year);
696 
697 	// Compute the currently selected day
698 	if (!this._readOnly) {
699 		this._setSelectedDate();
700 		this._setToday();
701 	}
702 	
703 	this._setRange();
704 };
705 
706 DwtCalendar.prototype._setRange =
707 function() {
708 	var cell = document.getElementById(this._getDayCellId(0));
709 	var start = new Date(cell._year, cell._month, cell._day, 0, 0, 0, 0);
710 
711 	cell = document.getElementById(this._getDayCellId(DwtCalendar._LAST_DAY_CELL_IDX));
712 	
713 	var daysInMo = this._getDaysInMonth(cell._month, cell._year);
714 	var end;
715 	if (cell._day < daysInMo) {
716 		end = new Date(cell._year, cell._month, cell._day + 1, 0, 0, 0, 0);
717 	} else if (cell._month < 11) {
718 		end = new Date(cell._year, cell._month + 1, 1, 0, 0, 0, 0);
719 	} else {
720 		end = new Date(cell._year + 1, 0, 1, 0, 0, 0, 0);
721 	}
722 
723 	if (this._range == null) {
724 		this._range = {};
725 	} else if (this._range.start.getTime() == start.getTime() && this._range.end.getTime() == end.getTime()) {
726 		return false;
727 	}
728 
729 	this._range.start = start;
730 	this._range.end = end;
731 
732 	// Notify any listeners
733 	if (!this.isListenerRegistered(DwtEvent.DATE_RANGE)) { return; }
734 
735 	if (!this._dateRangeEvent) {
736 		this._dateRangeEvent = new DwtDateRangeEvent(true);
737 	}
738 
739 	this._dateRangeEvent.item = this;
740 	this._dateRangeEvent.start = start;
741 	this._dateRangeEvent.end = end;
742 	this.notifyListeners(DwtEvent.DATE_RANGE, this._dateRangeEvent);
743 };
744 
745 DwtCalendar.prototype._setToday =
746 function() {
747 	var cell;
748 	var today = new Date();
749 	var todayDay = today.getDate();
750 
751 	if (!this._todayDay || this._todayDay != todayDay) {
752 		if (this._todayCellId != null) {
753 			cell = document.getElementById(this._todayCellId);
754 			cell._isToday = false;
755 			this._setClassName(cell, DwtCalendar._NORMAL);
756 		}
757 
758 		this._todayCellId = this._date2CellId[(today.getFullYear() * 10000) + (today.getMonth() * 100) + todayDay];
759 		if (this._todayCellId != null) {
760 			cell = document.getElementById(this._todayCellId);
761 			cell._isToday = true;
762 			this._setClassName(cell, DwtCalendar._NORMAL);
763 		}
764 	}
765 };
766 
767 DwtCalendar.prototype._setSelectedDate =
768 function() {
769 	var day = this._date.getDate();
770 	var month = this._date.getMonth();
771 	var year = this._date.getFullYear();
772 	var cell;
773 
774 	if (this._selectedDayElId) {
775 		cell = document.getElementById(this._selectedDayElId);
776 		this._setClassName(cell, DwtCalendar._DESELECTED);
777 	}
778 
779 	var cellId = this._date2CellId[(year * 10000) + (month * 100) + day];
780 	cell = document.getElementById(cellId);
781 	this._selectedDayElId = cellId;
782 	this._setClassName(cell, DwtCalendar._SELECTED);
783 };
784 
785 DwtCalendar.prototype._setCellClassName = 
786 function(cell, className, mode) {
787 	if (cell._dayType != DwtCalendar._THIS_MONTH) {
788 		className += this._greyClassName;
789 	}
790 
791 	if (this._selectionMode == DwtCalendar.DAY &&
792 		cell.id == this._selectedDayElId &&
793 		mode != DwtCalendar._DESELECTED)
794 	{
795 		className += this._selectedDayClassName;
796 	}
797 	else if (this._selectionMode != DwtCalendar.DAY &&
798 			 mode != DwtCalendar._DESELECTED &&
799 			 this._selectedDayElId != null)
800 	{
801 		var idx = this._getDayCellIndex(cell.id);
802 		if (Math.floor(this._getDayCellIndex(this._selectedDayElId) / 7) == Math.floor(idx / 7) &&
803 			this._currWorkingDays[idx % 7])
804 		{
805 			className += this._selectedDayClassName;
806 		}
807 	}
808 
809 	if (cell._isHilited) {
810 		className += this._hiliteClassName;
811 	}
812 
813 	if (cell._isToday) {
814 		className += this._todayClassName;
815 	}
816 
817 	return className;
818 };
819 
820 DwtCalendar.prototype._setClassName = 
821 function(cell, mode) {
822 	var className = "";
823 	
824 	if (mode == DwtCalendar._NORMAL) {
825 		className = this._origDayClassName;
826 	} else if (mode == DwtCalendar._HOVERED) {
827 		className = this._hoveredDayClassName;
828 	} else if (mode == DwtCalendar._ACTIVE) {
829 		className = this._activeDayClassName;
830 	} else if (mode == DwtCalendar._DESELECTED && this._selectionMode == DwtCalendar.DAY) {
831 		className = this._origDayClassName;
832 	} else if (this._selectionMode != DwtCalendar.DAY &&
833 			(mode == DwtCalendar._SELECTED || mode == DwtCalendar._DESELECTED))
834 	{
835 		// If we are not in day mode, then we need to highlite multiple cells
836 		// e.g. the whole week if we are in week mode
837 		var firstCellIdx = Math.floor(this._getDayCellIndex(this._selectedDayElId) / 7) * 7;
838 
839 		for (var i = 0; i < 7; i++) {
840 			className = this._origDayClassName;
841 			var aCell = document.getElementById(this._getDayCellId(firstCellIdx++));
842 			aCell.className = this._setCellClassName(aCell, className, mode);
843 		}
844 		return;
845 	}
846 
847 	cell.className = this._setCellClassName(cell, className, mode);
848 };
849 
850 DwtCalendar.prototype._setTitle =
851 function(month, year) {
852 	var cell = document.getElementById(this._monthCell);
853 	var formatter = DwtCalendar.getMonthFormatter();
854 	var date = new Date(year, month);
855 	cell.innerHTML = formatter.format(date);
856 };
857 
858 DwtCalendar.prototype._init =
859 function() {
860 	var html = new Array(100);
861 	var idx = 0;
862 	this._monthCell = "t:" + this._uuid;
863 
864 	// Construct the header row with the prev/next year and prev/next month
865 	// icons as well as the month/year title cell
866 	html[idx++] =	"<table width=100%>";
867 	html[idx++] =		"<tr><td class='DwtCalendarTitlebar'>";
868 	html[idx++] =			"<table>";
869 	html[idx++] =				"<tr>";
870 	html[idx++] =					"<td align='center' class='";
871 	html[idx++] =						DwtCalendar._BUTTON_CLASS;
872 	html[idx++] =						"' id='b:py:";
873 	html[idx++] =						this._uuid;
874 	html[idx++] =						"'>";
875 	html[idx++] =						AjxImg.getImageHtml("FastRevArrowSmall", null, ["id='b:py:img:", this._uuid, "'"].join(""));
876 	html[idx++] =					"</td>";
877 	html[idx++] =					"<td align='center' class='";
878 	html[idx++] =						DwtCalendar._BUTTON_CLASS;
879 	html[idx++] =						"' id='b:pm:";
880 	html[idx++] =						this._uuid;
881 	html[idx++] =						"'>";
882 	html[idx++] =						AjxImg.getImageHtml("RevArrowSmall", null, ["id='b:pm:img:", this._uuid, "'"].join(""));
883 	html[idx++] =					"</td>";
884 	html[idx++] =					"<td class='DwtCalendarTitleCell' 'nowrap' style='width: 60%'><span class='";
885 	html[idx++] =						DwtCalendar._TITLE_CLASS;
886 	html[idx++] = 						"' id='";
887 	html[idx++] =						this._monthCell;
888 	html[idx++] =					"'></span></td>";
889 	html[idx++] =					"<td align='center' class='";
890 	html[idx++] =						DwtCalendar._BUTTON_CLASS;
891 	html[idx++] =						"' id='b:nm:";
892 	html[idx++] =						this._uuid;
893 	html[idx++] =						"'>";
894 	html[idx++] =						AjxImg.getImageHtml("FwdArrowSmall", null, ["id='b:nm:img:", this._uuid, "'"].join(""));
895 	html[idx++] =					"</td>";
896 	html[idx++] =					"<td align='center' class='";
897 	html[idx++] =						DwtCalendar._BUTTON_CLASS;
898 	html[idx++] =						"' id='b:ny:";
899 	html[idx++] =						this._uuid;
900 	html[idx++] =						"'>";
901 	html[idx++] =						AjxImg.getImageHtml("FastFwdArrowSmall", null, ["id='b:ny:img:", this._uuid, "'"].join(""));
902 	html[idx++] =					"</td>";
903 	html[idx++] =				"</tr>";
904 	html[idx++] =			"</table>";
905 	html[idx++] =		"</td></tr>";
906 	html[idx++] =	"<tr><td class='DwtCalendarBody'>";
907 	html[idx++] =		"<table width='100%' style='border-collapse:separate;' cellspacing='0'>";
908 	html[idx++] =			"<tr>";
909 
910 	if (this._showWeekNumber) {
911 		html[idx++] = "<td class='DwtCalendarWeekNoTitle' width='14%' id='";
912 		html[idx++] = this._getWeekNumberCellId('kw');
913 		html[idx++] = "'>";
914 		html[idx++] = AjxMsg.calendarWeekTitle;
915 		html[idx++] = "</td>";
916 	}
917 
918 	for (var i = 0; i < 7; i++) {
919 		html[idx++] = "<td class='DwtCalendarDow' width='";
920 		html[idx++] = (i < 5 ? "14%" : "15%");
921 		html[idx++] = "' id='";
922 		html[idx++] = this._getDOWCellId(i);
923 		html[idx++] = "'> </td>";
924 	}
925 	html[idx++] = "</tr>";
926 
927 	for (var i = 0; i < 6; i++) {
928 		html[idx++] = "<tr>";
929 		if (this._showWeekNumber) {
930 			html[idx++] = "<td class='DwtCalendarWeekNo' id='" + this._getWeekNumberCellId('kw' + i * 7) + "'> </td>";
931 		}
932 		for (var j = 0; j < 7; j++) {
933 			html[idx++] = "<td id='";
934 			html[idx++] = this._getDayCellId(i * 7 + j);
935 			html[idx++] = "'> </td>";
936 		}
937 		html[idx++] ="</tr>";
938 	}
939 
940 	html[idx++] = "</td></tr></table></table>";
941 
942 	this.getHtmlElement().innerHTML = html.join("");
943 	if (!this._readOnly) {
944 		document.getElementById("b:py:img:" + this._uuid)._origClassName = AjxImg.getClassForImage("FastRevArrowSmall");
945 		document.getElementById("b:pm:img:" + this._uuid)._origClassName = AjxImg.getClassForImage("RevArrowSmall");
946 		document.getElementById("b:nm:img:" + this._uuid)._origClassName = AjxImg.getClassForImage("FwdArrowSmall");
947 		document.getElementById("b:ny:img:" + this._uuid)._origClassName = AjxImg.getClassForImage("FastFwdArrowSmall");
948 	}
949 
950 	this._calWidgetInited = true;
951 };
952 
953 /**
954  * Sets the mouse over day callback.
955  * 
956  * @param	{AjxCallback}		callback		the callback
957  */
958 DwtCalendar.prototype.setMouseOverDayCallback =
959 function(callback) {
960 	this._mouseOverDayCB = callback;
961 };
962 
963 /**
964  * Sets the mouse out day callback.
965  * 
966  * @param	{AjxCallback}		callback		the callback
967  */
968 DwtCalendar.prototype.setMouseOutDayCallback =
969 function(callback) {
970 	this._mouseOutDayCB = callback;
971 };
972 
973 /**
974  * Gets the date value for the last cell that the most recent
975  * Drag-and-drop operation occurred over. Typically it will be called by a DwtDropTarget
976  * listener when an item is dropped onto the mini calendar
977  * 
978  * @return	{Date}		the date or <code>null</code> for none
979  */
980 DwtCalendar.prototype.getDndDate =
981 function() {
982 	var dayCell = this._lastDndCell;
983 	if (dayCell) {
984 		return new Date(dayCell._year, dayCell._month, dayCell._day);
985 	}
986 
987 	return null;
988 };
989 
990 // Temp date used for callback in mouseOverListener
991 DwtCalendar._tmpDate = new Date();
992 DwtCalendar._tmpDate.setHours(0, 0, 0, 0);
993 
994 DwtCalendar.prototype._mouseOverListener = 
995 function(ev) {
996 	var target = ev.target;
997 	if (target.id.charAt(0) == 'c') {
998 		this._setClassName(target, DwtCalendar._HOVERED);
999 		// If a mouse over callback has been registered, then call it to give it
1000 		// chance do work like setting the tooltip content
1001 		if (this._mouseOverDayCB) {
1002 			DwtCalendar._tmpDate.setFullYear(target._year, target._month, target._day);
1003 			this._mouseOverDayCB.run(this, DwtCalendar._tmpDate);
1004 		}
1005 	} else if (target.id.charAt(0) == 't') {
1006 		// Dont activate title for now
1007 		return;
1008 	} else if (target.id.charAt(0) == 'b') {
1009 		var img;
1010 		if (target.firstChild == null) {
1011 			img = target;
1012 			AjxImg.getParentElement(target).className = DwtCalendar._BUTTON_HOVERED_CLASS;
1013 		} else {
1014 			target.className = DwtCalendar._BUTTON_HOVERED_CLASS;
1015 			img = AjxImg.getImageElement(target);
1016 		}
1017 		img.className = img._origClassName;
1018 	}
1019 
1020 	ev._stopPropagation = true;
1021 };
1022 
1023 DwtCalendar.prototype._mouseOutListener = 
1024 function(ev) {
1025 	this.setToolTipContent(null);
1026 	var target = ev.target;
1027 	if (target.id.charAt(0) == 'c') {
1028 		this._setClassName(target, DwtCalendar._NORMAL);
1029 		if (this._mouseOutDayCB) {
1030 			this._mouseOutDayCB.run(this);
1031 		}
1032 	} else if (target.id.charAt(0) == 'b') {
1033 		var img;
1034 		target.className = DwtCalendar._BUTTON_CLASS;
1035 		if (target.firstChild == null) {
1036 			img = target;
1037 			AjxImg.getParentElement(target).className = DwtCalendar._BUTTON_CLASS;
1038 		} else {
1039 			target.className = DwtCalendar._BUTTON_CLASS;
1040 			img = AjxImg.getImageElement(target);
1041 		}
1042 		img.className = img._origClassName;
1043 	}
1044 };
1045 
1046 DwtCalendar.prototype._mouseDownListener = 
1047 function(ev) {
1048 	if (ev.button == DwtMouseEvent.LEFT) {
1049 		var target = ev.target;
1050 		if (target.id.charAt(0) == 'c') {
1051 			this._setClassName(target, DwtCalendar._ACTIVE);
1052 		} else if (target.id.charAt(0) == 't') {
1053 			target.className = DwtCalendar._TITLE_ACTIVE_CLASS;
1054 		} else if (target.id.charAt(0) == 'b') {
1055 			var img;
1056 			if (target.firstChild == null) {
1057 				img = target;
1058 				AjxImg.getParentElement(target).className = DwtCalendar._BUTTON_ACTIVE_CLASS;
1059 			} else {
1060 				target.className = DwtCalendar._BUTTON_ACTIVE_CLASS;
1061 				img = AjxImg.getImageElement(target);
1062 			}
1063 			img.className = img._origClassName;
1064 		} else if (target.id.charAt(0) == 'w') {
1065 		}
1066 	}
1067 };
1068 
1069 DwtCalendar.prototype._mouseUpListener = 
1070 function(ev) {
1071 	var target = ev.target;
1072 	if (ev.button == DwtMouseEvent.LEFT) {
1073 		if (target.id.charAt(0) == 'c') {
1074 			// If our parent is a menu then we need to have it close
1075 			if (this.parent instanceof DwtMenu)
1076 				DwtMenu.closeActiveMenu();
1077 
1078             var sDate = new Date(target._year, target._month, target._day);
1079             if(sDate.getDate() != target._day) {
1080                 sDate.setDate(target._day);                 
1081             }
1082 			if (this.setDate(sDate)) { return; }
1083 
1084 			this._setClassName(target, DwtCalendar._HOVERED);
1085 		} else if (target.id.charAt(0) == 'b') {
1086 			var img;
1087 			if (target.firstChild == null) {
1088 				img = target;
1089 				AjxImg.getParentElement(target).className = DwtCalendar._BUTTON_HOVERED_CLASS;
1090 			} else {
1091 				target.className = DwtCalendar._BUTTON_HOVERED_CLASS;
1092 				img = AjxImg.getImageElement(target);
1093 			}
1094 			img.className = img._origClassName;
1095 			
1096 			if (img.id.indexOf("py") != -1) {
1097 				this._prevYear();
1098 			} else if (img.id.indexOf("pm") != -1) {
1099 				this._prevMonth();
1100 			} else if (img.id.indexOf("nm") != -1) {
1101 				this._nextMonth();
1102 			} else {
1103 				this._nextYear();
1104 			}
1105 		} else if (target.id.charAt(0) == 't') {
1106 			// TODO POPUP MENU
1107 			target.className = DwtCalendar._TITLE_HOVERED_CLASS;
1108 			this.setDate(new Date(), this._skipNotifyOnPage);
1109 			// If our parent is a menu then we need to have it close
1110 			if (this.parent instanceof DwtMenu) {
1111 				DwtMenu.closeActiveMenu();
1112 			}
1113 		}
1114 	} else if (ev.button == DwtMouseEvent.RIGHT && target.id.charAt(0) == 'c') {
1115 		this._notifyListeners(DwtEvent.ACTION, 0, new Date(target._year, target._month, target._day), ev);
1116 	}
1117 };
1118 
1119 DwtCalendar.prototype._doubleClickListener =
1120 function(ev) {
1121 	var target = ev.target;
1122 	if (this._selectionEvent) {
1123 		this._selectionEvent.type = DwtCalendar.DATE_DBL_CLICKED;
1124 	}
1125 	if (target.id.charAt(0) == 'c') {
1126 		// If our parent is a menu then we need to have it close
1127 		if (this.parent instanceof DwtMenu) {
1128 			DwtMenu.closeActiveMenu();
1129 		}
1130 		this.setDate(new Date(target._year, target._month, target._day), false, false, true)
1131 	}
1132 };
1133 
1134 DwtCalendar.prototype._prevMonth = 
1135 function(ev) {
1136 	var d = new Date(this._date.getTime());
1137 	this.setDate(AjxDateUtil.roll(d, AjxDateUtil.MONTH, -1), this._skipNotifyOnPage);
1138 };
1139 
1140 DwtCalendar.prototype._nextMonth = 
1141 function(ev) {
1142 	var d = new Date(this._date.getTime());
1143 	this.setDate(AjxDateUtil.roll(d, AjxDateUtil.MONTH, 1), this._skipNotifyOnPage);
1144 };
1145 
1146 DwtCalendar.prototype._prevYear = 
1147 function(ev) {
1148 	var d = new Date(this._date.getTime());
1149 	this.setDate(AjxDateUtil.roll(d, AjxDateUtil.YEAR, -1), this._skipNotifyOnPage);
1150 };
1151 
1152 DwtCalendar.prototype._nextYear = 
1153 function(ev) {
1154 	var d = new Date(this._date.getTime());
1155 	this.setDate(AjxDateUtil.roll(d, AjxDateUtil.YEAR, 1), this._skipNotifyOnPage);
1156 };
1157 
1158 /**
1159  * Gets the date formatter.
1160  * 
1161  * @return	{AjxDateFormat}		the date formatter
1162  * 
1163  * @private
1164  */
1165 DwtCalendar.getDateFormatter =
1166 function() {
1167 	if (!DwtCalendar._dateFormatter) {
1168 		DwtCalendar._dateFormatter = new AjxDateFormat(AjxMsg.formatCalDate);
1169 	}
1170 	return DwtCalendar._dateFormatter;
1171 };
1172 
1173 /**
1174  * Gets the date long formatter.
1175  * 
1176  * @return	{AjxDateFormat}		the date formatter
1177  * 
1178  * @private
1179  */
1180 DwtCalendar.getDateLongFormatter =
1181 function() {
1182 	if (!DwtCalendar._dateLongFormatter) {
1183 		DwtCalendar._dateLongFormatter = new AjxDateFormat(AjxMsg.formatCalDateLong);
1184 	}
1185 	return DwtCalendar._dateLongFormatter;
1186 };
1187 
1188 /**
1189  * Gets the date full formatter.
1190  * 
1191  * @return	{AjxDateFormat}		the date formatter
1192  * 
1193  * @private
1194  */
1195 DwtCalendar.getDateFullFormatter =
1196 function() {
1197 	if (!DwtCalendar._dateFullFormatter) {
1198 		DwtCalendar._dateFullFormatter = new AjxDateFormat(AjxMsg.formatCalDateFull);
1199 	}
1200 	return DwtCalendar._dateFullFormatter;
1201 };
1202 
1203 /**
1204  * Gets the hour formatter.
1205  * 
1206  * @return	{AjxDateFormat}		the date formatter
1207  * 
1208  * @private
1209  */
1210 DwtCalendar.getHourFormatter =
1211 function() {
1212 	if (!DwtCalendar._hourFormatter) {
1213 		DwtCalendar._hourFormatter = new AjxMessageFormat(AjxMsg.formatCalHour);
1214 	}
1215 	return DwtCalendar._hourFormatter;
1216 };
1217 
1218 /**
1219  * Gets the day formatter.
1220  * 
1221  * @return	{AjxDateFormat}		the date formatter
1222  * 
1223  * @private
1224  */
1225 DwtCalendar.getDayFormatter =
1226 function() {
1227 	if (!DwtCalendar._dayFormatter) {
1228 		DwtCalendar._dayFormatter = new AjxDateFormat(AjxMsg.formatCalDay);
1229 	}
1230 	return DwtCalendar._dayFormatter;
1231 };
1232 
1233 /**
1234  * Gets the month formatter.
1235  * 
1236  * @return	{AjxDateFormat}		the date formatter
1237  * 
1238  * @private
1239  */
1240 DwtCalendar.getMonthFormatter =
1241 function() {
1242 	if (!DwtCalendar._monthFormatter) {
1243 		DwtCalendar._monthFormatter = new AjxDateFormat(AjxMsg.formatCalMonth);
1244 	}
1245 	return DwtCalendar._monthFormatter;
1246 };
1247 
1248 /**
1249  * Gets the short month formatter.
1250  * 
1251  * @return	{AjxDateFormat}		the date formatter
1252  * 
1253  * @private
1254  */
1255 DwtCalendar.getShortMonthFormatter =
1256 function() {
1257 	if (!DwtCalendar._shortMonthFormatter) {
1258 		DwtCalendar._shortMonthFormatter = new AjxDateFormat(AjxMsg.formatShortCalMonth);
1259 	}
1260 	return DwtCalendar._shortMonthFormatter;
1261 };
1262 
1263 DwtCalendar.prototype._dragEnter =
1264 function(ev) {
1265 };
1266 
1267 DwtCalendar.prototype._dragHover =
1268 function(ev) {
1269 };
1270 
1271 DwtCalendar.prototype._dragOver =
1272 function(ev) {
1273 	var target = ev.target;
1274 	if (target.id.charAt(0) == 'c') {
1275 		this._setClassName(target, DwtCalendar._HOVERED);
1276 		this._lastDndCell = target;
1277 	} else {
1278 		this._lastDndCell = null;
1279 	}
1280 };
1281 
1282 DwtCalendar.prototype._dragLeave =
1283 function(ev) {
1284 };
1285