W. Hardy Interactive, Inc.

multilayercalendar  0.1

W. Hardy Interactive, Inc. > multilayercalendar > multilayercalendar.js (source view)
Search:
 
Filters
/**
* The multi-layer calendar provides year and decade navigation layers
* on top of standard month display.
* 
* @module multilayercalendar
* @requires yahoo, dom, event, animation, calendar
*/
(function() {
	var Lang = YAHOO.lang,
		Dom = YAHOO.util.Dom,
		Event = YAHOO.util.Event,
		Anim = YAHOO.util.Anim,
		Easing = YAHOO.util.Easing,
		Calendar = YAHOO.widget.Calendar;
	
	/**
	* The MultiLayerCalendar class implements the multi-layer calendar.
	* @namespace YAHOO.WHII.widget
	* @class MultiLayerCalendar
	* @extends YAHOO.widget.Calendar
	* @constructor
	* @param {String | HTMLElement} ctr HTML container element
	* @param {Object} cfg Configuration attributes
	*/
	function MultiLayerCalendar(ctr, cfg) {
		// Disable CalendarNavigator
		cfg = Lang.merge(cfg || {}, { navigator: false });
		
		// Call parent constructor
		MultiLayerCalendar.superclass.constructor.call(this, ctr, cfg);
		
		/**
		* Layer container DOM references
		* @property _layers
		* @private
		* @type Object
		*/
		this._layers = {
			year: null,
			decade: null
		};
		
		/**
		* Current layer
		* @property _layer
		* @private
		* @type Number
		*/
		this._layer = this.cfg.getProperty(_CONFIG.LAYER_INITIAL.key);
		
		/**
		* Temporarily prevents hover state on layer cells (see _onMonthCellClick)
		* @property _nohover
		* @private
		* @type Boolean
		*/
		this._nohover = false;
	}
	
	/**
	* Layer constants
	* @property _LAYERS
	* @private
	* @static
	* @final
	* @type Object
	*/
	MultiLayerCalendar._LAYERS = {
		MONTH: 0,
		YEAR: 1,
		DECADE: 2
	};
	
	/**
	* HTML classes
	* @property _CLASSES
	* @private
	* @static
	* @final
	* @type Object
	*/
	MultiLayerCalendar._CLASSES = {
		UP_LINK: 'uplink',
		TOP_UP_LINK: 'topuplink',
		LAYER: 'layer',
		YEAR_LAYER: 'year',
		YEAR_TABLE: 'year',
		MONTH_CELL: 'month',
		THIS_MONTH_CELL: 'current',
		DECADE_LAYER: 'decade',
		DECADE_TABLE: 'decade',
		YEAR_CELL: 'year',
		THIS_YEAR_CELL: 'current',
		OOD_CELL: 'ood',
		HOVER_CELL: 'hover'
	};
	
	/**
	* Default configuration values
	* @property _CONFIG_DEFAULTS
	* @private
	* @static
	* @final
	* @type Object
	*/
	MultiLayerCalendar._CONFIG_DEFAULTS = {
		LAYER_INITIAL: { key: 'layer_initial', value: MultiLayerCalendar._LAYERS.MONTH },
		LAYER_ZINDEX_BASE: { key: 'layer_zindex_base', value: 10 },
		LAYER_WIDTH_ADJUST: { key: 'layer_width_adjust', value: 1 },
		LAYER_HEIGHT_ADJUST: { key: 'layer_height_adjust', value: 1 },
		LAYER_FADING: { key: 'layer_fading', value: true },
		LAYER_FADE_DURATION: { key: 'layer_fade_duration', value: 0.2 }
	};
	
	var _LAYERS = MultiLayerCalendar._LAYERS,
		_CLASSES = MultiLayerCalendar._CLASSES,
		_CONFIG = MultiLayerCalendar._CONFIG_DEFAULTS;
	
	/**
	* Month layer
	* @property LAYER_MONTH
	* @static
	* @final
	* @type Number
	*/
	MultiLayerCalendar.LAYER_MONTH = _LAYERS.MONTH;
	
	/**
	* Year layer
	* @property LAYER_YEAR
	* @static
	* @final
	* @type Number
	*/
	MultiLayerCalendar.LAYER_YEAR = _LAYERS.YEAR;
	
	/**
	* Decade layer
	* @property LAYER_DECADE
	* @static
	* @final
	* @type Number
	*/
	MultiLayerCalendar.LAYER_DECADE = _LAYERS.DECADE;
	
	/**
	* Validates layer value
	* @method checkLayer
	* @static
	* @param {Number} v Value
	* @return {Boolean} True if valid layer value, false otherwise
	*/
	MultiLayerCalendar.checkLayer = function(v) {
		return ((!isNaN(v)) && _LAYERS.MONTH <= v && v <= _LAYERS.DECADE);
	};
	
	Lang.extend(MultiLayerCalendar, Calendar,
		{
			/**
			* Sets up configuration attributes
			* @method setupConfig
			*/
			setupConfig: function() {
				// Call parent method
				MultiLayerCalendar.superclass.setupConfig.call(this);
				
				var cfg = this.cfg;
				
				/**
				* Initial layer to display on first render
				* @config layer_initial
				* @type Number
				* @default MultiLayerCalendar.LAYER_MONTH
				*/
				cfg.addProperty(_CONFIG.LAYER_INITIAL.key, { value: _CONFIG.LAYER_INITIAL.value, validator: MultiLayerCalendar.checkLayer });
				
				/**
				* Layer base z-index in stacking context of container
				* @config layer_zindex_base
				* @type Number
				* @default 10
				*/
				cfg.addProperty(_CONFIG.LAYER_ZINDEX_BASE.key, { value: _CONFIG.LAYER_ZINDEX_BASE.value, validator: cfg.checkNumber });
				
				/**
				* Layer width adjustment. This increases or decreases the automatically
				* computed layer width by twice the specified number of pixels. Useful
				* for cross-browser adjustments or if stylization of the month table or
				* table cells includes larger borders.
				* @config layer_width_adjust
				* @type Number
				* @default 1
				*/
				cfg.addProperty(_CONFIG.LAYER_WIDTH_ADJUST.key, { value: _CONFIG.LAYER_WIDTH_ADJUST.value, validator: cfg.checkNumber });
				
				/**
				* Layer height adjustment. This increases or decreases the automatically
				* computed layer height by twice the specified number of pixels. Useful
				* for cross-browser adjustments or if stylization of the month table or
				* table cells includes larger borders.
				* @config layer_height_adjust
				* @type Number
				* @default 1
				*/
				cfg.addProperty(_CONFIG.LAYER_HEIGHT_ADJUST.key, { value: _CONFIG.LAYER_HEIGHT_ADJUST.value, validator: cfg.checkNumber });
				
				/**
				* Enables or disables animated fading of layers
				* @config layer_fading
				* @type Boolean
				* @default true
				*/
				cfg.addProperty(_CONFIG.LAYER_FADING.key, { value: _CONFIG.LAYER_FADING.value, validator: cfg.checkBoolean });
				
				/**
				* Layer fade duration in seconds
				* @config layer_fade_duration
				* @type Number
				* @default 0.2
				*/
				cfg.addProperty(_CONFIG.LAYER_FADE_DURATION.key, { value: _CONFIG.LAYER_FADE_DURATION.value, validator: cfg.checkNumber });
			},
			
			/**
			* Sets up custom events
			* @method initEvents
			*/
			initEvents: function() {
				// Call parent method
				MultiLayerCalendar.superclass.initEvents.call(this);
				
				this.renderEvent.subscribe(this._onRender, this, true);
			},
			
			/**
			* Dynamically calculates frame for layer containers
			* @method _getLayerFrame
			* @private
			* @returns {Object} Frame with left, top, width, height values
			*/
			_getLayerFrame: function() {
				var table = Dom.get(this.id),
					header = Dom.getElementsByClassName(Calendar._STYLES.CSS_HEADER_TEXT, 'th', table)[0],
					rgnTable = Dom.getRegion(table),
					rgnHeader = Dom.getRegion(header),
					pyHeader = rgnHeader.bottom - rgnHeader.top,
					cfg = this.cfg,
					pxAdjust = cfg.getProperty(_CONFIG.LAYER_WIDTH_ADJUST.key),
					pyAdjust = cfg.getProperty(_CONFIG.LAYER_HEIGHT_ADJUST.key),
					frame = {};
				
				frame.left = rgnTable.left - pxAdjust;
				frame.top = rgnTable.top + pyHeader - pyAdjust;
				frame.width = rgnTable.right - rgnTable.left + 2 * pxAdjust;
				frame.height = rgnTable.bottom - rgnTable.top - pyHeader + 2 * pyAdjust;
				
				return frame;
			},
			
			/**
			* Creates new layer container
			* @method _createLayer
			* @private
			* @param {String} classname HTML class name
			* @param {Object} frame Layer frame
			* @param {Number} level Layer z-index
			* @returns {HTMLElement} Layer container element
			*/
			_createLayer: function(classname, frame, level) {
				var layer = document.createElement('div'),
					zIndexBase = this.cfg.getProperty(_CONFIG.LAYER_ZINDEX_BASE.key);
				
				Dom.addClass(layer, _CLASSES.LAYER);
				Dom.addClass(layer, classname);
				Dom.setStyle(layer, 'position', 'absolute');
				Dom.setStyle(layer, 'zIndex', zIndexBase + (level - 1));
				Dom.setStyle(layer, 'width', frame.width + 'px');
				Dom.setStyle(layer, 'height', frame.height + 'px');
				Dom.setStyle(layer, 'visibility', (level <= this._layer) ? 'visible' : 'hidden');
				
				return layer;
			},
			
			/**
			* Generates HTML for year layer
			* @method _renderYearLayer
			* @private
			* @returns {String} HTML
			*/
			_renderYearLayer: function() {
				YAHOO.log('_renderYearLayer');
				
				var months = this.cfg.getProperty('MONTHS_SHORT'),
					now = new Date(),
					thisMonth = now.getMonth(),
					thisYear = now.getFullYear(),
					pageYear = this.cfg.getProperty('pagedate').getFullYear(),
					html = [],
					i, j, k, cls;
				
				html[html.length] = '<table class="' + _CLASSES.YEAR_TABLE + '">';
				for(i = 0; i < 3; i++) {
					html[html.length] = '<tr>';
					for(j = 0; j < 4; j++) {
						k = 4 * i + j;
						cls = (k === thisMonth && pageYear === thisYear ? ' ' + _CLASSES.THIS_MONTH_CELL : '');
						html[html.length] = '<td class="' + _CLASSES.MONTH_CELL + ' m' + (k + 1) + cls + '">' + months[k] + '</td>';
					}
					html[html.length] = '</tr>';
				}
				html[html.length] = '</table>';
				
				return html.join('');
			},
			
			/**
			* Synchronizes year layer (without full render)
			* @method _syncYearLayer
			* @private
			*/
			_syncYearLayer: function() {
				YAHOO.log('_syncYearLayer');
				
				var now = new Date(),
					thisMonth = now.getMonth(),
					thisYear = now.getFullYear(),
					pageYear = this.cfg.getProperty('pagedate').getFullYear(),
					cells = Dom.getElementsByClassName(_CLASSES.MONTH_CELL, 'td', this._layers.year),
					i, len, cell;
				
				for(i = 0, len = cells.length; i < len; i++) {
					cell = cells[i];
					if(i === thisMonth && pageYear === thisYear) {
						Dom.addClass(cell, _CLASSES.THIS_MONTH_CELL);
					}
					else {
						Dom.removeClass(cell, _CLASSES.THIS_MONTH_CELL);
					}
				}
			},
			
			/**
			* Generates HTML for decade layer
			* @method _renderDecadeLayer
			* @private
			* @returns {String} HTML
			*/
			_renderDecadeLayer: function() {
				YAHOO.log('_renderDecadeLayer');
				
				var now = new Date(),
					thisYear = now.getFullYear(),
					pageYear = this.cfg.getProperty('pagedate').getFullYear(),
					y = Math.floor(pageYear / 10) * 10 - 1,
					html = [],
					i, j, clsThisYear, clsOOD;
				
				html[html.length] = '<table class="' + _CLASSES.DECADE_TABLE + '">';
				for(i = 0; i < 3; i++) {
					html[html.length] = '<tr>';
					for(j = 0; j < 4; j++) {
						clsThisYear = (y === thisYear ? ' ' + _CLASSES.THIS_YEAR_CELL : '');
						clsOOD = ((i === 0 && j === 0) || (i === 2 && j === 3) ? ' ' + _CLASSES.OOD_CELL : '');
						html[html.length] = '<td class="' + _CLASSES.YEAR_CELL + clsThisYear + clsOOD + '">' + (y++) + '</td>';
					}
					html[html.length] = '</tr>';
				}
				html[html.length] = '</table>';
				
				return html.join('');
			},
			
			/**
			* Synchronizes decade layer (without full render)
			* @method _syncDecadeLayer
			* @private
			*/
			_syncDecadeLayer: function() {
				YAHOO.log('_syncDecadeLayer');
				
				var now = new Date(),
					thisYear = now.getFullYear(),
					pageYear = this.cfg.getProperty('pagedate').getFullYear(),
					y = Math.floor(pageYear / 10) * 10 - 1;
				
				Dom.getElementsByClassName(_CLASSES.YEAR_CELL, 'td', this._layers.decade,
					function(el) {
						if(y === thisYear) {
							Dom.addClass(el, _CLASSES.THIS_YEAR_CELL);
						}
						else {
							Dom.removeClass(el, _CLASSES.THIS_YEAR_CELL);
						}
						
						el.innerHTML = (y++);
					}
				);
			},
			
			/**
			* Attaches layer to document and positions it
			* @method _attachLayer
			* @private
			* @param {HTMLElement} layer Layer container element
			* @param {Object} frame Layer frame
			*/
			_attachLayer: function(layer, frame) {
				var ctr = this.oDomContainer;
				ctr.appendChild(layer);
				Dom.setXY(layer, [frame.left, frame.top]);
			},
			
			/**
			* Shows layer, using animation if so configured
			* @method _showLayer
			* @private
			* @param {HTMLElement} layer Layer container element
			*/
			_showLayer: function(layer) {
				YAHOO.log('_showLayer');
				
				var cfg = this.cfg;
				if(Anim && cfg.getProperty(_CONFIG.LAYER_FADING.key)) {
					Dom.setStyle(layer, 'opacity', 0);
					Dom.setStyle(layer, 'visibility', 'visible');
					
					var duration = cfg.getProperty(_CONFIG.LAYER_FADE_DURATION.key),
						anim = new Anim(layer, { opacity: { to: 1 } }, duration, Easing.easeNone);
					
					anim.animate();
				}
				else {
					Dom.setStyle(layer, 'visibility', 'visible');
				}
			},
			
			/**
			* Hides layer, using animation if so configured
			* @method _hideLayer
			* @private
			* @param {HTMLElement} layer Layer container element
			*/
			_hideLayer: function(layer) {
				YAHOO.log('_hideLayer');
				
				var cfg = this.cfg;
				if(Anim && cfg.getProperty(_CONFIG.LAYER_FADING.key)) {
					var duration = cfg.getProperty(_CONFIG.LAYER_FADE_DURATION.key),
						anim = new Anim(layer, { opacity: { from: 1, to: 0 } }, duration, Easing.easeNone),
						cal = this;
					
					anim.onComplete.subscribe(
						function() {
							Dom.setStyle(layer, 'visibility', 'hidden');
							cal._nohover = false;
						}
					);
					
					anim.animate();
				}
				else {
					Dom.setStyle(layer, 'visibility', 'hidden');
					this._nohover = false;
				}
			},
			
			/**
			* Generates calendar label text
			* @method _renderLabel
			* @private
			* @returns {String} Label text
			*/
			_renderLabel: function() {
				switch(this._layer) {
					case _LAYERS.MONTH:
						return MultiLayerCalendar.superclass.buildMonthLabel.call(this);
						
					case _LAYERS.YEAR:
						var year = this.cfg.getProperty('pagedate').getFullYear(),
							suffix = this.cfg.getProperty('my_label_year_suffix');
						
						return year + suffix;
						
					case _LAYERS.DECADE:
						var year = this.cfg.getProperty('pagedate').getFullYear(),
							lower = Math.floor(year / 10) * 10,
							upper = lower + 9,
							suffix = this.cfg.getProperty('my_label_year_suffix'),
							delim = this.cfg.getProperty('date_range_delimiter');
						
						return lower + suffix + delim + upper + suffix;
				}
			},
			
			/**
			* Synchronizes calendar label text (without full render)
			* @method _syncLabel
			* @private
			*/
			_syncLabel: function() {
				YAHOO.log('_syncLabel');
				
				var cal = this;
				Dom.getElementsByClassName(_CLASSES.UP_LINK, 'a', this.oDomContainer,
					function(el) {
						if(cal._layer === _LAYERS.DECADE) {
							Dom.addClass(el, _CLASSES.TOP_UP_LINK);
						}
						else {
							Dom.removeClass(el, _CLASSES.TOP_UP_LINK);
						}
						
						el.innerHTML = cal._renderLabel();
					}
				);
			},
			
			/**
			* Attaches DOM event listeners for calendar label and layers
			* @method _applyLayerListeners
			* @private
			*/
			_applyLayerListeners: function() {
				var cal = this;
				
				// Header link
				Dom.getElementsByClassName(_CLASSES.UP_LINK, 'a', this.oDomContainer,
					function(el) {
						Event.on(el, 'click', cal._onUpLinkClick, cal, true);
					}
				);
				
				// Year layer cells
				Dom.getElementsByClassName(_CLASSES.MONTH_CELL, 'td', this._layers.year,
					function(el) {
						Event.on(el, 'mouseover', cal._onCellMouseover, cal, true);
						Event.on(el, 'mouseout', cal._onCellMouseout, cal, true);
						Event.on(el, 'click', cal._onMonthCellClick, cal, true);
					}
				);
				
				// Decade layer cells
				Dom.getElementsByClassName(_CLASSES.YEAR_CELL, 'td', this._layers.decade,
					function(el) {
						Event.on(el, 'mouseover', cal._onCellMouseover, cal, true);
						Event.on(el, 'mouseout', cal._onCellMouseout, cal, true);
						Event.on(el, 'click', cal._onYearCellClick, cal, true);
					}
				);
			},
			
			/**
			* Generates HTML for calendar label
			* @method buildMonthLabel
			* @returns {String} HTML
			*/
			buildMonthLabel: function() {
				var html = [],
					clsTop = (this._layer === _LAYERS.DECADE ? ' ' + _CLASSES.TOP_UP_LINK : '');
				
				html[html.length] = '<a class="' + _CLASSES.UP_LINK + clsTop + '" href="javascript:void(0);">';
				html[html.length] = this._renderLabel();
				html[html.length] = '</a>';
				
				return html.join('');
			},
			
			/**
			* Render handler
			* @method _onRender
			* @private
			*/
			_onRender: function() {
				YAHOO.log('_onRender');
				
				// Create layers
				var frame = this._getLayerFrame(),
					yearLayer = this._createLayer(_CLASSES.YEAR_LAYER, frame, _LAYERS.YEAR),
					decadeLayer = this._createLayer(_CLASSES.DECADE_LAYER, frame, _LAYERS.DECADE);
				
				// Render layers
				yearLayer.innerHTML = this._renderYearLayer();
				decadeLayer.innerHTML = this._renderDecadeLayer();
				
				// Attach layers
				this._attachLayer(yearLayer, frame);
				this._attachLayer(decadeLayer, frame);
				
				// Store DOM references
				this._layers.year = yearLayer;
				this._layers.decade = decadeLayer;
				
				// Apply event listeners
				this._applyLayerListeners();
			},
			
			/**
			* Returns current layer
			* @method getCurrentLayer
			* @returns {Number} Current layer
			*/
			getCurrentLayer: function() {
				return this._layer;
			},
			
			/**
			* Moves up one layer
			* @method upLayer
			* @returns {Number} New current layer
			*/
			upLayer: function() {
				YAHOO.log('upLayer');
				
				switch(this._layer) {
					case _LAYERS.MONTH:
						this._layer++;
						this.changePageEvent.fire();
						this._showLayer(this._layers.year);
						break;
					case _LAYERS.YEAR:
						this._layer++;
						this.changePageEvent.fire();
						this._showLayer(this._layers.decade);
						break;
				}
				
				return this._layer;
			},
			
			/**
			* Moves down one layer
			* @method downLayer
			* @returns {Number} New current layer
			*/
			downLayer: function() {
				YAHOO.log('downLayer');
				
				switch(this._layer) {
					case _LAYERS.YEAR:
						/* We need to render the month layer while this layer is still up,
							so fire changePageEvent now, then render, then go down */
						this.changePageEvent.fire();
						this.render();
						this._layer--;
						this._syncLabel();
						this._hideLayer(this._layers.year);
						break;
					case _LAYERS.DECADE:
						this._layer--;
						this.changePageEvent.fire();
						this._hideLayer(this._layers.decade);
						break;
				}
				
				return this._layer;
			},
			
			/**
			* Handles page left navigation
			* @method doPreviousMonthNav
			* @param {Object} e Event object
			*/
			doPreviousMonthNav: function(e) {
				switch(this._layer) {
					case _LAYERS.MONTH:
						return MultiLayerCalendar.superclass.doPreviousMonthNav.apply(this, arguments);
					case _LAYERS.YEAR:
						Event.preventDefault(e);
						this.previousYear();
						return;
					case _LAYERS.DECADE:
						Event.preventDefault(e);
						this.subtractYears(10);
						return;
				}
			},
			
			/**
			* Handles page right navigation
			* @method doNextMonthNav
			* @param {Object} e Event object
			*/
			doNextMonthNav: function(e) {
				switch(this._layer) {
					case _LAYERS.MONTH:
						return MultiLayerCalendar.superclass.doNextMonthNav.apply(this, arguments);
					case _LAYERS.YEAR:
						Event.preventDefault(e);
						this.nextYear();
						return;
					case _LAYERS.DECADE:
						Event.preventDefault(e);
						this.addYears(10);
						return;
				}
			},
			
			/**
			* Handles page change event
			* @method onChangePage
			*/
			onChangePage: function() {
				YAHOO.log('onChangePage');
				
				switch(this._layer) {
					case _LAYERS.MONTH:
						return MultiLayerCalendar.superclass.onChangePage.apply(this, arguments);
					case _LAYERS.YEAR:
						this._syncYearLayer();
						this._syncLabel();
						return;
					case _LAYERS.DECADE:
						this._syncDecadeLayer();
						this._syncLabel();
						return;
				}
			},
			
			/**
			* Handles layer table cell mouseover
			* @method _onCellMouseover
			* @private
			* @param {Object} e Event object
			*/
			_onCellMouseover: function(e) {
				if(!this._nohover) {
					Dom.addClass(Event.getTarget(e), _CLASSES.HOVER_CELL);
				}
			},
			
			/**
			* Handles layer table cell mouseout
			* @method _onCellMouseout
			* @private
			* @param {Object} e Event object
			*/
			_onCellMouseout: function(e) {
				Dom.removeClass(Event.getTarget(e), _CLASSES.HOVER_CELL);
			},
			
			/**
			* Handles header link click
			* @method _onUpLinkClick
			* @private
			* @param {Object} e Event object
			*/
			_onUpLinkClick: function(e) {
				Event.preventDefault(e);
				Event.getTarget(e).blur();
				this.upLayer();
			},
			
			/**
			* Handles year layer month cell click
			* @method _onMonthCellClick
			* @private
			* @param {Object} e Event object
			*/
			_onMonthCellClick: function(e) {
				// Determine month clicked from cell HTML class
				var target = Event.getTarget(e),
					match = target.className.match(/m(\d{1,2})/);
				
				if(match) {
					var month = parseInt(match[1], 10) - 1,
						pageDate = this.cfg.getProperty('pagedate'),
						cal = this;
					
					// Change pagedate month
					pageDate.setMonth(month);
					this.cfg.setProperty('pagedate', pageDate);
					
					// Kill hover temporarily to prevent flicker on render
					this._nohover = true;
					Dom.removeClass(target, _CLASSES.HOVER_CELL);
					
					/* Move down to month layer, but do so in a timeout so that the
						current event can bubble up before the render removes the
						event target from DOM. */
					setTimeout(function() { cal.downLayer(); }, 0);
				}
			},
			
			/**
			* Handles decade layer year cell click
			* @method _onYearCellClick
			* @private
			* @param {Object} e Event Object
			*/
			_onYearCellClick: function(e) {
				// Determine year clicked
				var target = Event.getTarget(e),
					targetYear = parseInt(target.innerHTML, 10),
					pageDate = this.cfg.getProperty('pagedate');
				
				// Change pagedate year
				pageDate.setFullYear(targetYear);
				this.cfg.setProperty('pagedate', pageDate);
				
				// Move down to year layer (no render)
				this.downLayer();
			},
			
			configNavigator: function() {
				// Ignore
			}
		}
	);
	
	// Export
	YAHOO.namespace('WHII.widget');
	YAHOO.WHII.widget.MultiLayerCalendar = MultiLayerCalendar;
	
	// Register
	YAHOO.register('multilayercalendar', MultiLayerCalendar, { version: 0.1, build: 1 });
})();

Copyright © 2009 Yahoo! Inc. All rights reserved.