Array.prototype.contains = function(obj) {
for (var i=0; i<this.length; i++) {
if (this[i].toString() == obj.toString()) return i;
}
return "undefined";
}


/*
Copyright (c) 2006, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
Version 0.11.3
*/

var YAHOO = window.YAHOO || {};


/**
 * Returns the namespace specified and creates it if it doesn't exist
 *
 * YAHOO.namespace("property.package");
 * YAHOO.namespace("YAHOO.property.package");
 *
 * Either of the above would create YAHOO.property, then
 * YAHOO.property.package
 *
 * @param{String} sNameSpace String representation of the desired 
 * namespace
 * @return {Object}A reference to the namespace object
 */
YAHOO.namespace = function( sNameSpace ) {

if (!sNameSpace || !sNameSpace.length) {
return null;
}

var levels = sNameSpace.split(".");

var currentNS = YAHOO;

// YAHOO is implied, so it is ignored if it is included
for (var i=(levels[0] == "YAHOO") ? 1 : 0; i<levels.length; ++i) {
currentNS[levels[i]] = currentNS[levels[i]] || {};
currentNS = currentNS[levels[i]];
}

return currentNS;
};

/**
 * Global log method.
 */
YAHOO.log = function(sMsg,sCategory) {
if(YAHOO.widget.Logger) {
YAHOO.widget.Logger.log(null, sMsg, sCategory);
} else {
return false;
}
};

YAHOO.namespace("util");
YAHOO.namespace("widget");
YAHOO.namespace("example");


/**
 * The CustomEvent class lets you define events for your application
 * that can be subscribed to by one or more independent component.
 *
 * @param {String} type The type of event, which is passed to the callback
 * when the event fires
 * @param {Object} oScope The context the event will fire from."this" will
 * refer to this object in the callback.Default value: 
 * the window object.The listener can override this.
 * @constructor
 */
YAHOO.util.CustomEvent = function(type, oScope) {
/**
 * The type of event, returned to subscribers when the event fires
 * @type string
 */
this.type = type;

/**
 * The scope the the event will fire from by default.Defaults to the window 
 * obj
 * @type object
 */
this.scope = oScope || window;

/**
 * The subscribers to this event
 * @type Subscriber[]
 */
this.subscribers = [];

// Register with the event utility for automatic cleanup.Made optional
// so that CustomEvent can be used independently of pe.event
if (YAHOO.util.Event) { 
YAHOO.util.Event.regCE(this);
}
};

YAHOO.util.CustomEvent.prototype = {
/**
 * Subscribes the caller to this event
 * @param {Function} fn The function to execute
 * @param {Object} objAn object to be passed along when the event fires
 * @param {boolean}bOverride If true, the obj passed in becomes the execution
 *scope of the listener
 */
subscribe: function(fn, obj, bOverride) {
this.subscribers.push( new YAHOO.util.Subscriber(fn, obj, bOverride) );
},

/**
 * Unsubscribes the caller from this event
 * @param {Function} fnThe function to execute
 * @param {Object} obj An object to be passed along when the event fires
 * @return {boolean} True if the subscriber was found and detached.
 */
unsubscribe: function(fn, obj) {
var found = false;
for (var i=0, len=this.subscribers.length; i<len; ++i) {
var s = this.subscribers[i];
if (s && s.contains(fn, obj)) {
this._delete(i);
found = true;
}
}

return found;
},

/**
 * Notifies the subscribers.The callback functions will be executed
 * from the scope specified when the event was created, and with the following
 * parameters:
 * <pre>
 * - The type of event
 * - All of the arguments fire() was executed with as an array
 * - The custom object (if any) that was passed into the subscribe() method
 * </pre>
 * 
 * @param {Array} an arbitrary set of parameters to pass to the handler
 */
fire: function() {
for (var i=0, len=this.subscribers.length; i<len; ++i) {
var s = this.subscribers[i];
if (s) {
var scope = (s.override) ? s.obj : this.scope;
s.fn.call(scope, this.type, arguments, s.obj);
}
}
},

/**
 * Removes all listeners
 */
unsubscribeAll: function() {
for (var i=0, len=this.subscribers.length; i<len; ++i) {
this._delete(i);
}
},

/**
 * @private
 */
_delete: function(index) {
var s = this.subscribers[index];
if (s) {
delete s.fn;
delete s.obj;
}

delete this.subscribers[index];
}
};

/////////////////////////////////////////////////////////////////////

/**
 * @class Stores the subscriber information to be used when the event fires.
 * @param {Function} fn The function to execute
 * @param {Object} objAn object to be passed along when the event fires
 * @param {boolean}bOverride If true, the obj passed in becomes the execution
 *scope of the listener
 * @constructor
 */
YAHOO.util.Subscriber = function(fn, obj, bOverride) {
/**
 * The callback that will be execute when the event fires
 * @type function
 */
this.fn = fn;

/**
 * An optional custom object that will passed to the callback when
 * the event fires
 * @type object
 */
this.obj = obj || null;

/**
 * The default execution scope for the event listener is defined when the
 * event is created (usually the object which contains the event).
 * By setting override to true, the execution scope becomes the custom
 * object passed in by the subscriber
 * @type boolean
 */
this.override = (bOverride);
};

/**
 * Returns true if the fn and obj match this objects properties.
 * Used by the unsubscribe method to match the right subscriber.
 *
 * @param {Function} fn the function to execute
 * @param {Object} obj an object to be passed along when the event fires
 * @return {boolean} true if the supplied arguments match this 
 * subscriber's signature.
 */
YAHOO.util.Subscriber.prototype.contains = function(fn, obj) {
return (this.fn == fn && this.obj == obj);
};



// Only load this library once.If it is loaded a second time, existing
// events cannot be detached.
if (!YAHOO.util.Event) {

/**
 * @class
 * The event utility provides functions to add and remove event listeners,
 * event cleansing.It also tries to automatically remove listeners it
 * registers during the unload event.
 * @constructor
 */
YAHOO.util.Event = function() {

/**
 * True after the onload event has fired
 * @type boolean
 * @private
 */
var loadComplete =false;

/**
 * Cache of wrapped listeners
 * @type array
 * @private
 */
var listeners = [];

/**
 * Listeners that will be attached during the onload event
 * @type array
 * @private
 */
var delayedListeners = [];

/**
 * User-defined unload function that will be fired before all events
 * are detached
 * @type array
 * @private
 */
var unloadListeners = [];

/**
 * Cache of the custom events that have been defined.Used for
 * automatic cleanup
 * @type array
 * @private
 */
var customEvents = [];

/**
 * Cache of DOM0 event handlers to work around issues with DOM2 events
 * in Safari
 * @private
 */
var legacyEvents = [];

/**
 * Listener stack for DOM0 events
 * @private
 */
var legacyHandlers = [];

/**
 * The number of times to poll after window.onload.This number is
 * increased if additional late-bound handlers are requested after
 * the page load.
 * @private
 */
var retryCount = 0;

/**
 * onAvailable listeners
 * @private
 */
var onAvailStack = [];

/**
 * Lookup table for legacy events
 * @private
 */
var legacyMap = [];

/**
 * Counter for auto id generation
 * @private
 */
var counter = 0;

return { // PREPROCESS

/**
 * The number of times we should look for elements that are not
 * in the DOM at the time the event is requested after the document
 * has been loaded.The default is 200@50 ms, so it will poll
 * for 10 seconds or until all outstanding handlers are bound
 * (whichever comes first).
 * @type int
 */
POLL_RETRYS: 200,

/**
 * The poll interval in milliseconds
 * @type int
 */
POLL_INTERVAL: 50,

/**
 * Element to bind, int constant
 * @type int
 */
EL: 0,

/**
 * Type of event, int constant
 * @type int
 */
TYPE: 1,

/**
 * Function to execute, int constant
 * @type int
 */
FN: 2,

/**
 * Function wrapped for scope correction and cleanup, int constant
 * @type int
 */
WFN: 3,

/**
 * Object passed in by the user that will be returned as a 
 * parameter to the callback, int constant
 * @type int
 */
SCOPE: 3,

/**
 * Adjusted scope, either the element we are registering the event
 * on or the custom object passed in by the listener, int constant
 * @type int
 */
ADJ_SCOPE: 4,

/**
 * Safari detection is necessary to work around the preventDefault
 * bug that makes it so you can't cancel a href click from the 
 * handler.There is not a capabilities check we can use here.
 * @private
 */
isSafari: (/Safari|Konqueror|KHTML/gi).test(navigator.userAgent),

/**
 * IE detection needed to properly calculate pageX and pageY.
 * capabilities checking didn't seem to work because another 
 * browser that does not provide the properties have the values 
 * calculated in a different manner than IE.
 * @private
 */
isIE: (!this.isSafari && !navigator.userAgent.match(/opera/gi) && 
navigator.userAgent.match(/msie/gi)),

/**
 * @private
 */
addDelayedListener: function(el, sType, fn, oScope, bOverride) {
delayedListeners[delayedListeners.length] =
[el, sType, fn, oScope, bOverride];

// If this happens after the inital page load, we need to
// reset the poll counter so that we continue to search for
// the element for a fixed period of time.
if (loadComplete) {
retryCount = this.POLL_RETRYS;
this.startTimeout(0);
// this._tryPreloadAttach();
}
},

/**
 * @private
 */
startTimeout: function(interval) {
var i = (interval || interval === 0) ? interval : this.POLL_INTERVAL;
var self = this;
var callback = function() { self._tryPreloadAttach(); };
this.timeout = setTimeout(callback, i);
},

/**
 * Executes the supplied callback when the item with the supplied
 * id is found.This is meant to be used to execute behavior as
 * soon as possible as the page loads.If you use this after the
 * initial page load it will poll for a fixed time for the element.
 * The number of times it will poll and the frequency are
 * configurable.By default it will poll for 10 seconds.
 * @param {string} p_id the id of the element to look for.
 * @param {function} p_fn what to execute when the element is found.
 * @param {object} p_obj an optional object to be passed back as
 * a parameter to p_fn.
 * @param {boolean} p_override If set to true, p_fn will execute
 * in the scope of p_obj
 *
 */
onAvailable: function(p_id, p_fn, p_obj, p_override) {
onAvailStack.push( { id: p_id, 
 fn: p_fn, 
 obj:p_obj, 
 override: p_override } );

retryCount = this.POLL_RETRYS;
this.startTimeout(0);
// this._tryPreloadAttach();
},

/**
 * Appends an event handler
 *
 * @param {Object} elThe html element to assign the 
 * event to
 * @param {String} sType The type of event to append
 * @param {Function} fnThe method the event invokes
 * @param {Object} oScopeAn arbitrary object that will be 
 * passed as a parameter to the handler
 * @param {boolean}bOverride If true, the obj passed in becomes
 * the execution scope of the listener
 * @return {boolean} True if the action was successful or defered,
 *false if one or more of the elements 
 *could not have the event bound to it.
 */
addListener: function(el, sType, fn, oScope, bOverride) {

if (!fn || !fn.call) {
return false;
}

// The el argument can be an array of elements or element ids.
if ( this._isValidCollection(el)) {
var ok = true;
for (var i=0,len=el.length; i<len; ++i) {
ok = ( this.on(el[i], 
 sType, 
 fn, 
 oScope, 
 bOverride) && ok );
}
return ok;

} else if (typeof el == "string") {
var oEl = this.getEl(el);
// If the el argument is a string, we assume it is 
// actually the id of the element.If the page is loaded
// we convert el to the actual element, otherwise we 
// defer attaching the event until onload event fires

// check to see if we need to delay hooking up the event 
// until after the page loads.
if (loadComplete && oEl) {
el = oEl;
} else {
// defer adding the event until onload fires
this.addDelayedListener(el, 
sType, 
fn, 
oScope, 
bOverride);

return true;
}
}

// Element should be an html element or an array if we get 
// here.
if (!el) {
return false;
}

// we need to make sure we fire registered unload events 
// prior to automatically unhooking them.So we hang on to 
// these instead of attaching them to the window and fire the
// handles explicitly during our one unload event.
if ("unload" == sType && oScope !== this) {
unloadListeners[unloadListeners.length] =
[el, sType, fn, oScope, bOverride];
return true;
}


// if the user chooses to override the scope, we use the custom
// object passed in, otherwise the executing scope will be the
// HTML element that the event is registered on
var scope = (bOverride) ? oScope : el;

// wrap the function so we can return the oScope object when
// the event fires;
var wrappedFn = function(e) {
return fn.call(scope, YAHOO.util.Event.getEvent(e), 
oScope);
};

var li = [el, sType, fn, wrappedFn, scope];
var index = listeners.length;
// cache the listener so we can try to automatically unload
listeners[index] = li;

if (this.useLegacyEvent(el, sType)) {
var legacyIndex = this.getLegacyIndex(el, sType);
if (legacyIndex == -1) {

legacyIndex = legacyEvents.length;
legacyMap[el.id + sType] = legacyIndex;

// cache the signature for the DOM0 event, and 
// include the existing handler for the event, if any
legacyEvents[legacyIndex] = 
[el, sType, el["on" + sType]];
legacyHandlers[legacyIndex] = [];

el["on" + sType] = 
function(e) {
YAHOO.util.Event.fireLegacyEvent(
YAHOO.util.Event.getEvent(e), legacyIndex);
};
}

// add a reference to the wrapped listener to our custom
// stack of events
legacyHandlers[legacyIndex].push(index);

// DOM2 Event model
} else if (el.addEventListener) {
el.addEventListener(sType, wrappedFn, false);
// Internet Explorer abstraction
} else if (el.attachEvent) {
el.attachEvent("on" + sType, wrappedFn);
}

return true;

},

/**
 * Shorthand for YAHOO.util.Event.addListener
 * @type function
 */
// on: this.addListener,

/**
 * When using legacy events, the handler is routed to this object
 * so we can fire our custom listener stack.
 * @private
 */
fireLegacyEvent: function(e, legacyIndex) {
var ok = true;

var le = legacyHandlers[legacyIndex];
for (var i=0,len=le.length; i<len; ++i) {
var index = le[i];
if (index) {
var li = listeners[index];
if ( li && li[this.WFN] ) {
var scope = li[this.ADJ_SCOPE];
var ret = li[this.WFN].call(scope, e);
ok = (ok && ret);
} else {
// This listener was removed, so delete it from
// the array
delete le[i];
}
}
}

return ok;
},

/**
 * Returns the legacy event index that matches the supplied 
 * signature
 * @private
 */
getLegacyIndex: function(el, sType) {
/*
for (var i=0,len=legacyEvents.length; i<len; ++i) {
var le = legacyEvents[i];
if (le && le[0] === el && le[1] === sType) {
return i;
}
}
return -1;
*/

var key = this.generateId(el) + sType;
if (typeof legacyMap[key] == "undefined") { 
return -1;
} else {
return legacyMap[key];
}

},

/**
 * Logic that determines when we should automatically use legacy
 * events instead of DOM2 events.
 * @private
 */
useLegacyEvent: function(el, sType) {

if (!el.addEventListener && !el.attachEvent) {
return true;
} else if (this.isSafari) {
if ("click" == sType || "dblclick" == sType) {
return true;
}
}

return false;
},

/**
 * Removes an event handler
 *
 * @param {Object} el the html element or the id of the element to 
 * assign the event to.
 * @param {String} sType the type of event to remove
 * @param {Function} fn the method the event invokes
 * @return {boolean} true if the unbind was successful, false 
 * otherwise
 */
removeListener: function(el, sType, fn, index) {

if (!fn || !fn.call) {
return false;
}

// The el argument can be a string
if (typeof el == "string") {
el = this.getEl(el);
// The el argument can be an array of elements or element ids.
} else if ( this._isValidCollection(el)) {
var ok = true;
for (var i=0,len=el.length; i<len; ++i) {
ok = ( this.removeListener(el[i], sType, fn) && ok );
}
return ok;
}

if ("unload" == sType) {

for (i=0, len=unloadListeners.length; i<len; i++) {
var li = unloadListeners[i];
if (li && 
li[0] == el && 
li[1] == sType && 
li[2] == fn) {
delete unloadListeners[i];
return true;
}
}

return false;
}

var cacheItem = null;

if ("undefined" == typeof index) {
index = this._getCacheIndex(el, sType, fn);
}

if (index >= 0) {
cacheItem = listeners[index];
}

if (!el || !cacheItem) {
return false;
}


if (el.removeEventListener) {
el.removeEventListener(sType, cacheItem[this.WFN], false);
} else if (el.detachEvent) {
el.detachEvent("on" + sType, cacheItem[this.WFN]);
}

// removed the wrapped handler
delete listeners[index][this.WFN];
delete listeners[index][this.FN];
delete listeners[index];

return true;

},

/**
 * Returns the event's target element
 * @param {Event} ev the event
 * @param {boolean} resolveTextNode when set to true the target's
 *parent will be returned if the target is a 
 *text node
 * @return {HTMLElement} the event's target
 */
getTarget: function(ev, resolveTextNode) {
var t = ev.target || ev.srcElement;

if (resolveTextNode && t && "#text" == t.nodeName) {
return t.parentNode;
} else {
return t;
}
},

/**
 * Returns the event's pageX
 * @param {Event} ev the event
 * @return {int} the event's pageX
 */
getPageX: function(ev) {
var x = ev.pageX;
if (!x && 0 !== x) {
x = ev.clientX || 0;

if ( this.isIE ) {
x += this._getScrollLeft();
}
}

return x;
},

/**
 * Returns the event's pageY
 * @param {Event} ev the event
 * @return {int} the event's pageY
 */
getPageY: function(ev) {
var y = ev.pageY;
if (!y && 0 !== y) {
y = ev.clientY || 0;

if ( this.isIE ) {
y += this._getScrollTop();
}
}

return y;
},

/**
 * Returns the pageX and pageY properties as an indexed array.
 * @type int[]
 */
getXY: function(ev) {
return [this.getPageX(ev), this.getPageY(ev)];
},

/**
 * Returns the event's related target 
 * @param {Event} ev the event
 * @return {HTMLElement} the event's relatedTarget
 */
getRelatedTarget: function(ev) {
var t = ev.relatedTarget;
if (!t) {
if (ev.type == "mouseout") {
t = ev.toElement;
} else if (ev.type == "mouseover") {
t = ev.fromElement;
}
}

return t;
},

/**
 * Returns the time of the event.If the time is not included, the
 * event is modified using the current time.
 * @param {Event} ev the event
 * @return {Date} the time of the event
 */
getUTCTime: function(ev) {
if (!ev.time) {
var t = new Date().getUTCTime();
try {
ev.time = t;
} catch(e) { 
// can't set the time property
return t;
}
}

return ev.time;
},

/**
 * Convenience method for stopPropagation + preventDefault
 * @param {Event} ev the event
 */
stopEvent: function(ev) {
this.stopPropagation(ev);
this.preventDefault(ev);
},

/**
 * Stops event propagation
 * @param {Event} ev the event
 */
stopPropagation: function(ev) {
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
ev.cancelBubble = true;
}
},

/**
 * Prevents the default behavior of the event
 * @param {Event} ev the event
 */
preventDefault: function(ev) {
if (ev.preventDefault) {
ev.preventDefault();
} else {
ev.returnValue = false;
}
},
 
/**
 * Finds the event in the window object, the caller's arguments, or
 * in the arguments of another method in the callstack.This is
 * executed automatically for events registered through the event
 * manager, so the implementer should not normally need to execute
 * this function at all.
 * @param {Event} the event parameter from the handler
 * @return {Event} the event 
 */
getEvent: function(e) {
var ev = e || window.event;

if (!ev) {
var c = this.getEvent.caller;
while (c) {
ev = c.arguments[0];
if (ev && Event == ev.constructor) {
break;
}
c = c.caller;
}
}

return ev;
},

/**
 * Returns the charcode for an event
 * @param {Event} ev the event
 * @return {int} the event's charCode
 */
getCharCode: function(ev) {
return ev.charCode || ((ev.type == "keypress") ? ev.keyCode : 0);
},

/**
 * @private
 * Locating the saved event handler data by function ref
 */
_getCacheIndex: function(el, sType, fn) {
for (var i=0,len=listeners.length; i<len; ++i) {
var li = listeners[i];
if ( li && 
 li[this.FN] == fn&& 
 li[this.EL] == el&& 
 li[this.TYPE] == sType ) {
return i;
}
}

return -1;
},

/**
 * Generates an unique ID for the element if it does not already 
 * have one.
 * @param el the element
 * @return {string} the id of the element
 */
generateId: function(el) {
var id = el.id;

if (!id) {
id = "yuievtautoid-" + (counter++);
el.id = id;
}

return id;
},

/**
 * We want to be able to use getElementsByTagName as a collection
 * to attach a group of events to.Unfortunately, different 
 * browsers return different types of collections.This function
 * tests to determine if the object is array-like.It will also 
 * fail if the object is an array, but is empty.
 * @param o the object to test
 * @return {boolean} true if the object is array-like and populated
 * @private
 */
_isValidCollection: function(o) {

return ( o&& // o is something
 o.length && // o is indexed
 typeof o != "string" && // o is not a string
 !o.tagName && // o is not an HTML element
 !o.alert && // o is not a window
 typeof o[0] != "undefined" );

},

/**
 * @private
 * DOM element cache
 */
elCache: {},

/**
 * We cache elements bound by id because when the unload event 
 * fires, we can no longer use document.getElementById
 * @private
 */
getEl: function(id) {
return document.getElementById(id);
},

/**
 * Clears the element cache
 * @deprecated
 * @private
 */
clearCache: function() { },

/**
 * Called by CustomEvent instances to provide a handle to the 
 * event * that can be removed later on.Should be package 
 * protected.
 * @private
 */
regCE: function(ce) {
customEvents.push(ce);
},

/**
 * @private
 * hook up any deferred listeners
 */
_load: function(e) {
loadComplete = true;
},

/**
 * Polling function that runs before the onload event fires, 
 * attempting * to attach to DOM Nodes as soon as they are 
 * available
 * @private
 */
_tryPreloadAttach: function() {

if (this.locked) {
return false;
}

this.locked = true;


// keep trying until after the page is loaded.We need to 
// check the page load state prior to trying to bind the 
// elements so that we can be certain all elements have been 
// tested appropriately
var tryAgain = !loadComplete;
if (!tryAgain) {
tryAgain = (retryCount > 0);
}

// Delayed listeners
var stillDelayed = [];

for (var i=0,len=delayedListeners.length; i<len; ++i) {
var d = delayedListeners[i];
// There may be a race condition here, so we need to 
// verify the array element is usable.
if (d) {

// el will be null if document.getElementById did not
// work
var el = this.getEl(d[this.EL]);

if (el) {
this.on(el, d[this.TYPE], d[this.FN], 
d[this.SCOPE], d[this.ADJ_SCOPE]);
delete delayedListeners[i];
} else {
stillDelayed.push(d);
}
}
}

delayedListeners = stillDelayed;

// onAvailable
notAvail = [];
for (i=0,len=onAvailStack.length; i<len ; ++i) {
var item = onAvailStack[i];
if (item) {
el = this.getEl(item.id);

if (el) {
var scope = (item.override) ? item.obj : el;
item.fn.call(scope, item.obj);
delete onAvailStack[i];
} else {
notAvail.push(item);
}
}
}

retryCount = (stillDelayed.length === 0 && 
notAvail.length === 0) ? 0 : retryCount - 1;

if (tryAgain) {
this.startTimeout();
}

this.locked = false;

},

/**
 * Removes all listeners registered by pe.event.Called 
 * automatically during the unload event.
 * @private
 */
_unload: function(e, me) {
for (var i=0,len=unloadListeners.length; i<len; ++i) {
var l = unloadListeners[i];
if (l) {
var scope = (l[this.ADJ_SCOPE]) ? l[this.SCOPE]: window;
l[this.FN].call(scope, this.getEvent(e), l[this.SCOPE] );
}
}

if (listeners && listeners.length > 0) {
for (i=0,len=listeners.length; i<len ; ++i) {
l = listeners[i];
if (l) {
this.removeListener(l[this.EL], l[this.TYPE], 
l[this.FN], i);
}
}

this.clearCache();
}

for (i=0,len=customEvents.length; i<len; ++i) {
customEvents[i].unsubscribeAll();
delete customEvents[i];
}

for (i=0,len=legacyEvents.length; i<len; ++i) {
// dereference the element
delete legacyEvents[i][0];
// delete the array item
delete legacyEvents[i];
}
},

/**
 * Returns scrollLeft
 * @private
 */
_getScrollLeft: function() {
return this._getScroll()[1];
},

/**
 * Returns scrollTop
 * @private
 */
_getScrollTop: function() {
return this._getScroll()[0];
},

/**
 * Returns the scrollTop and scrollLeft.Used to calculate the 
 * pageX and pageY in Internet Explorer
 * @private
 */
_getScroll: function() {
var dd = document.documentElement; db = document.body;
if (dd && dd.scrollTop) {
return [dd.scrollTop, dd.scrollLeft];
} else if (db) {
return [db.scrollTop, db.scrollLeft];
} else {
return [0, 0];
}
}
};
} ();

/**
 * @private
 */
YAHOO.util.Event.on = YAHOO.util.Event.addListener;

if (document && document.body) {
YAHOO.util.Event._load();
} else {
YAHOO.util.Event.on(window, "load", YAHOO.util.Event._load, 
YAHOO.util.Event, true);
}

YAHOO.util.Event.on(window, "unload", YAHOO.util.Event._unload, 
YAHOO.util.Event, true);

YAHOO.util.Event._tryPreloadAttach();

}


//dom.js


/*
Copyright (c) 2006, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
*/

/**
 * @class Provides helper methods for DOM elements.
 */
YAHOO.util.Dom = function() {
 var ua = navigator.userAgent.toLowerCase();
 var isOpera = (ua.indexOf('opera') != -1);
 var isIE = (ua.indexOf('msie') != -1 && !isOpera); // not opera spoof
 var id_counter = 0;
 
 return {
/**
 * Returns an HTMLElement reference
 * @param {String/HTMLElement/Array} el Accepts a string to use as an ID for getting a DOM reference, an actual DOM reference, or an Array of IDs and/or HTMLElements.
 * @return {HTMLElement/Array} A DOM reference to an HTML element or an array of HTMLElements.
 */
get: function(el) {
 if (typeof el != 'string' && !(el instanceof Array) )
 { // assuming HTMLElement or HTMLCollection, so pass back as is
return el;
 }
 
 if (typeof el == 'string') 
 { // ID
return document.getElementById(el);
 }
 else
 { // array of ID's and/or elements
var collection = [];
for (var i = 0, len = el.length; i < len; ++i)
{
 collection[collection.length] = this.get(el[i]);
}

return collection;
 }

 return null; // safety, should never happen
},
 
/**
 * Normalizes currentStyle and ComputedStyle.
 * @param {String/HTMLElement/Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
 * @param {String} property The style property whose value is returned.
 * @return {String/Array} The current value of the style property for the element(s).
 */
getStyle: function(el, property) {
 var f = function(el) {
var value = null;
var dv = document.defaultView;

if (property == 'opacity' && el.filters) 
{// IE opacity
 value = 1;
 try {
value = el.filters.item('DXImageTransform.Microsoft.Alpha').opacity / 100;
 } catch(e) {
try {
 value = el.filters.item('alpha').opacity / 100;
} catch(e) {}
 }
}
else if (el.style[property]) 
{
 value = el.style[property];
}
else if (el.currentStyle && el.currentStyle[property]) {
 value = el.currentStyle[property];
}
else if ( dv && dv.getComputedStyle )
{// convert camelCase to hyphen-case
 
 var converted = '';
 for(var i = 0, len = property.length;i < len; ++i) {
if (property.charAt(i) == property.charAt(i).toUpperCase()) 
{
 converted = converted + '-' + property.charAt(i).toLowerCase();
} else {
 converted = converted + property.charAt(i);
}
 }
 
 if (dv.getComputedStyle(el, '') && dv.getComputedStyle(el, '').getPropertyValue(converted)) {
value = dv.getComputedStyle(el, '').getPropertyValue(converted);
 }
}

return value;
 };
 
 return this.batch(el, f, this, true);
},
 
/**
 * Wrapper for setting style properties of HTMLElements.Normalizes "opacity" across modern browsers.
 * @param {String/HTMLElement/Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
 * @param {String} property The style property to be set.
 * @param {String} val The value to apply to the given property.
 */
setStyle: function(el, property, val) {
 var f = function(el) {
switch(property) {
 case 'opacity' :
if (isIE && typeof el.style.filter == 'string') { // in case not appended
 el.style.filter = 'alpha(opacity=' + val * 100 + ')';
 
 if (!el.currentStyle || !el.currentStyle.hasLayout) {
el.style.zoom = 1; // when no layout or cant tell
 }
} else {
 el.style.opacity = val;
 el.style['-moz-opacity'] = val;
 el.style['-khtml-opacity'] = val;
}

break;
 default :
el.style[property] = val;
}

 };
 
 this.batch(el, f, this, true);
},

/**
 * Gets the current position of an element based on page coordinates.Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 * @param {String/HTMLElement/Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
 @ return {Array} The XY position of the element(s)
 */
getXY: function(el) {
 var f = function(el) {
 
 // has to be part of document to have pageXY
if (el.parentNode === null || this.getStyle(el, 'display') == 'none') {
 return false;
}

var parent = null;
var pos = [];
var box;

if (el.getBoundingClientRect) { // IE
 box = el.getBoundingClientRect();
 var scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
 var scrollLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft);
 
 return [box.left + scrollLeft, box.top + scrollTop];
}
else if (document.getBoxObjectFor) { // gecko
 box = document.getBoxObjectFor(el);
 
 var borderLeft = parseInt(this.getStyle(el, 'borderLeftWidth'));
 var borderTop = parseInt(this.getStyle(el, 'borderTopWidth'));
 
 pos = [box.x - borderLeft, box.y - borderTop];
}
else { // safari & opera
 pos = [el.offsetLeft, el.offsetTop];
 parent = el.offsetParent;
 if (parent != el) {
while (parent) {
 pos[0] += parent.offsetLeft;
 pos[1] += parent.offsetTop;
 parent = parent.offsetParent;
}
 }
 if (
ua.indexOf('opera') != -1 
|| ( ua.indexOf('safari') != -1 && this.getStyle(el, 'position') == 'absolute' ) 
 ) {
pos[0] -= document.body.offsetLeft;
pos[1] -= document.body.offsetTop;
 } 
}

if (el.parentNode) { parent = el.parentNode; }
else { parent = null; }

while (parent && parent.tagName != 'BODY' && parent.tagName != 'HTML') 
{ // account for any scrolled ancestors
 pos[0] -= parent.scrollLeft;
 pos[1] -= parent.scrollTop;

 if (parent.parentNode) { parent = parent.parentNode; } 
 else { parent = null; }
}

return pos;
 };
 
 return this.batch(el, f, this, true);
},

/**
 * Gets the current X position of an element based on page coordinates.The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 * @param {String/HTMLElement/Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
 * @return {String/Array} The X position of the element(s)
 */
getX: function(el) {
 return this.getXY(el)[0];
},

/**
 * Gets the current Y position of an element based on page coordinates.Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 * @param {String/HTMLElement/Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
 * @return {String/Array} The Y position of the element(s)
 */
getY: function(el) {
 return this.getXY(el)[1];
},

/**
 * Set the position of an html element in page coordinates, regardless of how the element is positioned.
 * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 * @param {String/HTMLElement/Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
 * @param {Array} pos Contains X & Y values for new position (coordinates are page-based)
 * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
 */
setXY: function(el, pos, noRetry) {
 var f = function(el) {
 
var style_pos = this.getStyle(el, 'position');
if (style_pos == 'static') { // default to relative
 this.setStyle(el, 'position', 'relative');
 style_pos = 'relative';
}

var pageXY = YAHOO.util.Dom.getXY(el);
if (pageXY === false) { return false; } // has to be part of doc to have pageXY

var delta = [
 parseInt( YAHOO.util.Dom.getStyle(el, 'left'), 10 ),
 parseInt( YAHOO.util.Dom.getStyle(el, 'top'), 10 )
];
 
if ( isNaN(delta[0]) ) // defaults to 'auto'
{ 
 delta[0] = (style_pos == 'relative') ? 0 : el.offsetLeft;
} 
if ( isNaN(delta[1]) ) // defaults to 'auto'
{ 
 delta[1] = (style_pos == 'relative') ? 0 : el.offsetTop;
} 

if (pos[0] !== null) { el.style.left = pos[0] - pageXY[0] + delta[0] + 'px'; }
if (pos[1] !== null) { el.style.top = pos[1] - pageXY[1] + delta[1] + 'px'; }

var newXY = this.getXY(el);

// if retry is true, try one more time if we miss
if (!noRetry && (newXY[0] != pos[0] || newXY[1] != pos[1]) ) {
 var retry = function() { YAHOO.util.Dom.setXY(el, pos, true); };
 setTimeout(retry, 0); // "delay" for IE resize timing issue
}
 };
 
 this.batch(el, f, this, true);
},

/**
 * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 * @param {String/HTMLElement/Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
 * @param {Int} x to use as the X coordinate for the element(s).
 */
setX: function(el, x) {
 this.setXY(el, [x, null]);
},

/**
 * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 * @param {String/HTMLElement/Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
 * @param {Int} x to use as the Y coordinate for the element(s).
 */
setY: function(el, y) {
 this.setXY(el, [null, y]);
},

/**
 * Returns the region position of the given element.
 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
 * @param {String/HTMLElement/Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
 * @return {Region/Array} A Region or array of Region instances containing "top, left, bottom, right" member data.
 */
getRegion: function(el) {
 var f = function(el) {
return new YAHOO.util.Region.getRegion(el);
 };
 
 return this.batch(el, f, this, true);
},

/**
 * Returns the width of the client (viewport).
 * Now using getViewportWidth.This interface left intact for back compat.
 * @return {Int} The width of the viewable area of the page.
 */
getClientWidth: function() {
 return this.getViewportWidth();
},

/**
 * Returns the height of the client (viewport).
 * Now using getViewportHeight.This interface left intact for back compat.
 * @return {Int} The height of the viewable area of the page.
 */
getClientHeight: function() {
 return this.getViewportHeight();
},

/**
 * Returns a array of HTMLElements with the given class
 * For optimized performance, include a tag and/or root node if possible
 * @param {String} className The class name to match against
 * @param {String} tag (optional) The tag name of the elements being collected
 * @param {String/HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
 * @return {Array} An array of elements that have the given class name
 */
getElementsByClassName: function(className, tag, root) {
 var re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
 
 var method = function(el) { return re.test(el['className']); };
 
 return this.getElementsBy(method, tag, root);
},

/**
 * Determines whether an HTMLElement has the given className
 * @param {String/HTMLElement/Array} el The element or collection to test
 * @param {String} className the class name to search for
 * @return {Boolean/Array} A boolean value or array of boolean values
 */
hasClass: function(el, className) {
 var f = function(el) {
var re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
return re.test(el['className']);
 };
 
 return this.batch(el, f, this, true);
},
 
/**
 * Adds a class name to a given element or collection of elements
 * @param {String/HTMLElement/Array} el The element or collection to add the class to
 * @param {String} className the class name to add to the class attribute
 */
addClass: function(el, className) {
 var f = function(el) {
if (this.hasClass(el, className)) { return; } // already present

el['className'] = [el['className'], className].join(' ');
 };
 
 this.batch(el, f, this, true);
},
 
/**
 * Removes a class name from a given element or collection of elements
 * @param {String/HTMLElement/Array} el The element or collection to remove the class from
 * @param {String} className the class name to remove from the class attribute
 */
removeClass: function(el, className) {
 var f = function(el) {
if (!this.hasClass(el, className)) { return; } // not present

var re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', 'g');
var c = el['className'];

el['className'] = c.replace( re, ' ');
 };
 
 this.batch(el, f, this, true);
},

/**
 * Replace a class with another class for a given element or collection of elements.
 * If no oldClassName is present, the newClassName is simply added.
 * @param {String/HTMLElement/Array} el The element or collection to remove the class from
 * @param {String} oldClassName the class name to be replaced
 * @param {String} newClassName the class name that will be replacing the old class name
 */
replaceClass: function(el, oldClassName, newClassName) {
 var f = function(el) {
this.removeClass(el, oldClassName);
this.addClass(el, newClassName);
 };
 
 this.batch(el, f, this, true);
},

/**
 * Generates a unique ID
 * @param {String/HTMLElement/Array} el (optional) An optional element array of elements to add an ID to (no ID is added if one is already present)
 * @param {String} prefix (optional) an optional prefix to use (defaults to "yui-gen")
 * @return {String/Array} The generated ID, or array of generated IDs (or original ID if already present on an element)
 */
generateId: function(el, prefix) {
 prefix = prefix || 'yui-gen';
 
 var f = function(el) {
el = el || {}; // just generating ID in this case

if (!el.id) { el.id = prefix + id_counter++; } // dont override existing

return el.id;
 };
 
 return this.batch(el, f, this, true);
},

/**
 * Determines whether an HTMLElement is an ancestor of another HTML element in the DOM hierarchy
 * @param {String/HTMLElement} haystack The possible ancestor
 * @param {String/HTMLElement} needle The possible descendent
 * @return {Boolean} Whether or not the haystack is an ancestor of needle
 */
isAncestor: function(haystack, needle) {
 haystack = this.get(haystack);
 if (!haystack || !needle) { return false; }
 
 var f = function(needle) {
if (haystack.contains && ua.indexOf('safari') < 0) 
{ // safari "contains" is broken
 return haystack.contains(needle);
}
else if ( haystack.compareDocumentPosition ) 
{
 return !!(haystack.compareDocumentPosition(needle) & 16);
}
else 
{ // loop up and test each parent
 var parent = needle.parentNode;
 
 while (parent) {
if (parent == haystack) {
 return true;
}
else if (parent.tagName == 'HTML') {
 return false;
}

parent = parent.parentNode;
 }
 
 return false;
}
 };
 
 return this.batch(needle, f, this, true); 
},

/**
 * Determines whether an HTMLElement is present in the current document
 * @param {String/HTMLElement} el The element to search for
 * @return {Boolean} Whether or not the element is present in the current document
 */
inDocument: function(el) {
 var f = function(el) {
return this.isAncestor(document.documentElement, el);
 };
 
 return this.batch(el, f, this, true);
},

/**
 * Returns a array of HTMLElements that pass the test applied by supplied boolean method
 * For optimized performance, include a tag and/or root node if possible
 * @param {Function} method A boolean method to test elements with
 * @param {String} tag (optional) The tag name of the elements being collected
 * @param {String/HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
 */
getElementsBy: function(method, tag, root) {
 tag = tag || '*';
 root = this.get(root) || document;
 
 var nodes = [];
 var elements = root.getElementsByTagName(tag);
 
 if ( !elements.length && (tag == '*' && root.all) ) {
elements = root.all; // IE < 6
 }
 
 for (var i = 0, len = elements.length; i < len; ++i) 
 {
if ( method(elements[i]) ) { nodes[nodes.length] = elements[i]; }
 }

 return nodes;
},

/**
 * Returns an array of elements that have had the supplied method applied.
 * The method is called with the element(s) as the first arg, and the optional param as the second ( method(el, o) )
 * @param {String/HTMLElement/Array} el (optional) An element or array of elements to apply the method to
 * @param {Function} method The method to apply to the element(s)
 * @param {Generic} (optional) o An optional arg that is passed to the supplied method
 * @param {Boolean} (optional) override Whether or not to override the scope of "method" with "o"
 * @return {HTMLElement/Array} The element(s) with the method applied
 */
batch: function(el, method, o, override) {
 el = this.get(el);
 var scope = (override) ? o : window;
 
 if (!el || el.tagName || !el.length) 
 { // is null or not a collection (tagName for SELECT and others that can be both an element and a collection)
return method.call(scope, el, o);
 } 
 
 var collection = [];
 
 for (var i = 0, len = el.length; i < len; ++i)
 {
collection[collection.length] = method.call(scope, el[i], o);
 }
 
 return collection;
},

/**
 * Returns the height of the document.
 * @return {Int} The height of the actual document (which includes the body and its margin).
 */
getDocumentHeight: function() {
 var scrollHeight=-1,windowHeight=-1,bodyHeight=-1;
 var marginTop = parseInt(this.getStyle(document.body, 'marginTop'), 10);
 var marginBottom = parseInt(this.getStyle(document.body, 'marginBottom'), 10);
 
 var mode = document.compatMode;
 
 if ( (mode || isIE) && !isOpera ) { // (IE, Gecko)
switch (mode) {
 case 'CSS1Compat': // Standards mode
scrollHeight = ((window.innerHeight && window.scrollMaxY) ?window.innerHeight+window.scrollMaxY : -1);
windowHeight = [document.documentElement.clientHeight,self.innerHeight||-1].sort(function(a, b){return(a-b);})[1];
bodyHeight = document.body.offsetHeight + marginTop + marginBottom;
break;
 
 default: // Quirks
scrollHeight = document.body.scrollHeight;
bodyHeight = document.body.clientHeight;
}
 } else { // Safari & Opera
scrollHeight = document.documentElement.scrollHeight;
windowHeight = self.innerHeight;
bodyHeight = document.documentElement.clientHeight;
 }

 var h = [scrollHeight,windowHeight,bodyHeight].sort(function(a, b){return(a-b);});
 return h[2];
},

/**
 * Returns the width of the document.
 * @return {Int} The width of the actual document (which includes the body and its margin).
 */
getDocumentWidth: function() {
 var docWidth=-1,bodyWidth=-1,winWidth=-1;
 var marginRight = parseInt(this.getStyle(document.body, 'marginRight'), 10);
 var marginLeft = parseInt(this.getStyle(document.body, 'marginLeft'), 10);
 
 var mode = document.compatMode;
 
 if (mode || isIE) { // (IE, Gecko, Opera)
switch (mode) {
 case 'CSS1Compat': // Standards mode
docWidth = document.documentElement.clientWidth;
bodyWidth = document.body.offsetWidth + marginLeft + marginRight;
winWidth = self.innerWidth || -1;
break;

 default: // Quirks
bodyWidth = document.body.clientWidth;
winWidth = document.body.scrollWidth;
break;
}
 } else { // Safari
docWidth = document.documentElement.clientWidth;
bodyWidth = document.body.offsetWidth + marginLeft + marginRight;
winWidth = self.innerWidth;
 }

 var w = [docWidth,bodyWidth,winWidth].sort(function(a, b){return(a-b);});
 return w[2];
},

/**
 * Returns the current height of the viewport.
 * @return {Int} The height of the viewable area of the page (excludes scrollbars).
 */
getViewportHeight: function() {
 var height = -1;
 var mode = document.compatMode;

 if ( (mode || isIE) && !isOpera ) {
switch (mode) { // (IE, Gecko)
 case 'CSS1Compat': // Standards mode
height = document.documentElement.clientHeight;
break;

 default: // Quirks
height = document.body.clientHeight;
}
 } else { // Safari, Opera
height = self.innerHeight;
 }

 return height;
},

/**
 * Returns the current width of the viewport.
 * @return {Int} The width of the viewable area of the page (excludes scrollbars).
 */

getViewportWidth: function() {
 var width = -1;
 var mode = document.compatMode;
 
 if (mode || isIE) { // (IE, Gecko, Opera)
switch (mode) {
case 'CSS1Compat': // Standards mode 
 width = document.documentElement.clientWidth;
 break;
 
default: // Quirks
 width = document.body.clientWidth;
}
 } else { // Safari
width = self.innerWidth;
 }
 
 return width;
}
 };
}();

/*
Copyright (c) 2006, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
*/

YAHOO.util.Region = function(t, r, b, l) {


this.top = t;


this[1] = t;


this.right = r;


this.bottom = b;


this.left = l;


this[0] = l;
};


YAHOO.util.Region.prototype.contains = function(region) {
return ( region.left >= this.left && 
 region.right<= this.right&& 
 region.top>= this.top&& 
 region.bottom <= this.bottom);

// this.logger.debug("does " + this + " contain " + region + " ... " + ret);
};


YAHOO.util.Region.prototype.getArea = function() {
return ( (this.bottom - this.top) * (this.right - this.left) );
};


YAHOO.util.Region.prototype.intersect = function(region) {
var t = Math.max( this.top,region.top);
var r = Math.min( this.right,region.right);
var b = Math.min( this.bottom, region.bottom );
var l = Math.max( this.left, region.left );

if (b >= t && r >= l) {
return new YAHOO.util.Region(t, r, b, l);
} else {
return null;
}
};


YAHOO.util.Region.prototype.union = function(region) {
var t = Math.min( this.top,region.top);
var r = Math.max( this.right,region.right);
var b = Math.max( this.bottom, region.bottom );
var l = Math.min( this.left, region.left );

return new YAHOO.util.Region(t, r, b, l);
};


YAHOO.util.Region.prototype.toString = function() {
return ( "Region {" +
 "t: "+ this.top+ 
 ", r: "+ this.right+ 
 ", b: "+ this.bottom + 
 ", l: "+ this.left + 
 "}" );
};


YAHOO.util.Region.getRegion = function(el) {
var p = YAHOO.util.Dom.getXY(el);

var t = p[1];
var r = p[0] + el.offsetWidth;
var b = p[1] + el.offsetHeight;
var l = p[0];

return new YAHOO.util.Region(t, r, b, l);
};

/////////////////////////////////////////////////////////////////////////////



YAHOO.util.Point = function(x, y) {

this.x= x;


this.y= y;
this.top= y;
this[1] = y;

this.right= x;
this.bottom = y;
this.left = x;
this[0] = x;
};

YAHOO.util.Point.prototype = new YAHOO.util.Region();





/**
* <p>YAHOO.widget.DateMath is used for simple date manipulation. The class is a static utility
* used for adding, subtracting, and comparing dates.</p>
*/
YAHOO.widget.DateMath = new function() {

/**
* Constant field representing Day
* @type String
*/
this.DAY = "D";

/**
* Constant field representing Week
* @type String
*/
this.WEEK = "W";

/**
* Constant field representing Year
* @type String
*/
this.YEAR = "Y";

/**
* Constant field representing Month
* @type String
*/
this.MONTH = "M";

/**
* Constant field representing one day, in milliseconds
* @type Integer
*/
this.ONE_DAY_MS = 1000*60*60*24;

/**
* Adds the specified amount of time to the this instance.
* @param {Date} dateThe JavaScript Date object to perform addition on
* @param {string} fieldThe this field constant to be used for performing addition.
* @param {Integer} amountThe number of units (measured in the field constant) to add to the date.
*/
this.add = function(date, field, amount) {
var d = new Date(date.toUTCString());
//alert(d.getUTCMonth());
switch (field) {
case this.MONTH:
var newMonth = date.getUTCMonth() + amount;
var years = 0;


if (newMonth < 0) {
while (newMonth < 0) {
newMonth += 12;
years -= 1;
}
} else if (newMonth > 11) {
while (newMonth > 11) {
newMonth -= 12;
years += 1;
}
}

d.setUTCMonth(newMonth);
d.setUTCFullYear(date.getUTCFullYear() + years);
break;
case this.DAY:
d.setUTCDate(date.getUTCDate() + amount);
break;
case this.YEAR:
d.setUTCFullYear(date.getUTCFullYear() + amount);
break;
case this.WEEK:
d.setUTCDate(date.getUTCDate() + (amount * 7));
break;
}
return d;
};

/**
* Subtracts the specified amount of time from the this instance.
* @param {Date} dateThe JavaScript Date object to perform subtraction on
* @param {Integer} fieldThe this field constant to be used for performing subtraction.
* @param {Integer} amountThe number of units (measured in the field constant) to subtract from the date.
*/
this.subtract = function(date, field, amount) {
return this.add(date, field, (amount*-1));
};

/**
* Determines whether a given date is before another date on the calendar.
* @param {Date} dateThe Date object to compare with the compare argument
* @param {Date} compareToThe Date object to use for the comparison
* @return {Boolean} true if the date occurs before the compared date; false if not.
*/
this.before = function(date, compareTo) {
var ms = Date.UTC(compareTo);
if (Date.UTC(date) < ms) {
return true;
} else {
return false;
}
};

/**
* Determines whether a given date is after another date on the calendar.
* @param {Date} dateThe Date object to compare with the compare argument
* @param {Date} compareToThe Date object to use for the comparison
* @return {Boolean} true if the date occurs after the compared date; false if not.
*/
this.after = function(date, compareTo) {
var ms = Date.UTC(compareTo);
if (Date.UTC(date) > ms) {
return true;
} else {
return false;
}
};

/**
* Determines whether a given date is between two other dates on the calendar.
* @param {Date} dateThe date to check for
* @param {Date} dateBeginThe start of the range
* @param {Date} dateEndThe end of the range
* @return {Boolean} true if the date occurs between the compared dates; false if not.
*/
this.between = function(date, dateBegin, dateEnd) {
if (this.after(date, dateBegin) && this.before(date, dateEnd)) {
return true;
} else {
return false;
}
};

/**
* Retrieves a JavaScript Date object representing January 1 of any given year.
* @param {Integer} calendarYearThe calendar year for which to retrieve January 1
* @return {Date}January 1 of the calendar year specified.
*/
this.getJan1 = function(calendarYear) {
return new Date(Date.UTC(calendarYear,0,1)); 
};

/**
* Calculates the number of days the specified date is from January 1 of the specified calendar year.
* Passing January 1 to this function would return an offset value of zero.
* @param {Date}dateThe JavaScript date for which to find the offset
* @param {Integer} calendarYearThe calendar year to use for determining the offset
* @return {Integer}The number of days since January 1 of the given year
*/
this.getUTCDayOffset = function(date, calendarYear) {
var beginYear = this.getJan1(calendarYear); // Find the start of the year. This will be in week 1.

// Find the number of days the passed in date is away from the calendar year start
var dayOffset = Math.ceil((date.getUTCTime()-beginYear.getUTCTime()) / this.ONE_DAY_MS);
return dayOffset;
};

/**
* Calculates the week number for the given date. This function assumes that week 1 is the
* week in which January 1 appears, regardless of whether the week consists of a full 7 days.
* The calendar year can be specified to help find what a the week number would be for a given
* date if the date overlaps years. For instance, a week may be considered week 1 of 2005, or
* week 53 of 2004. Specifying the optional calendarYear allows one to make this distinction
* easily.
* @param {Date}dateThe JavaScript date for which to find the week number
* @param {Integer} calendarYearOPTIONAL - The calendar year to use for determining the week number. Default is
*the calendar year of parameter "date".
* @param {Integer} weekStartsOnOPTIONAL - The integer (0-6) representing which day a week begins on. Default is 0 (for Sunday).
* @return {Integer}The week number of the given date.
*/
this.getWeekNumber = function(date, calendarYear, weekStartsOn) {
date.setHours(12,0,0,0);

if (! weekStartsOn) {
weekStartsOn = 0;
}
if (! calendarYear) {
calendarYear = date.getUTCFullYear();
}

var weekNum = -1;

var jan1 = this.getJan1(calendarYear);

var jan1Offset = jan1.getUTCDay() - weekStartsOn;
var jan1DayOfWeek = (jan1Offset >= 0 ? jan1Offset : (7 + jan1Offset));

var endOfWeek1 = this.add(jan1, this.DAY, (6 - jan1DayOfWeek));
endOfWeek1.setHours(23,59,59,999);

var month = date.getUTCMonth();
var day = date.getUTCDate();
var year = date.getUTCFullYear();

var dayOffset = this.getUTCDayOffset(date, calendarYear); // Days since Jan 1, Calendar Year

if (dayOffset < 0 || this.before(date, endOfWeek1)) {
weekNum = 1;
} else {
weekNum = 2;
var weekBegin = new Date(endOfWeek1.getUTCTime() + 1);
var weekEnd = this.add(weekBegin, this.WEEK, 1);

while (! this.between(date, weekBegin, weekEnd)) {
weekBegin = this.add(weekBegin, this.WEEK, 1);
weekEnd = this.add(weekEnd, this.WEEK, 1);
weekNum += 1;
}
}

return weekNum;
};

/**
* Determines if a given week overlaps two different years.
* @param {Date}weekBeginDateThe JavaScript Date representing the first day of the week.
* @return {Boolean}true if the date overlaps two different years.
*/
this.isYearOverlapWeek = function(weekBeginDate) {
var overlaps = false;
var nextWeek = this.add(weekBeginDate, this.DAY, 6);
if (nextWeek.getUTCFullYear() != weekBeginDate.getUTCFullYear()) {
overlaps = true;
}
return overlaps;
};

/**
* Determines if a given week overlaps two different months.
* @param {Date}weekBeginDateThe JavaScript Date representing the first day of the week.
* @return {Boolean}true if the date overlaps two different months.
*/
this.isMonthOverlapWeek = function(weekBeginDate) {
var overlaps = false;
var nextWeek = this.add(weekBeginDate, this.DAY, 6);
if (nextWeek.getUTCMonth() != weekBeginDate.getUTCMonth()) {
overlaps = true;
}
return overlaps;
};

/**
* Gets the first day of a month containing a given date.
* @param {Date}dateThe JavaScript Date used to calculate the month start
* @return {Date}The JavaScript Date representing the first day of the month
*/
this.findMonthStart = function(date) {
var start = new Date(date.toUTCString());
start.setUTCDate(1);
return start;
};

/**
* Gets the last day of a month containing a given date.
* @param {Date}dateThe JavaScript Date used to calculate the month end
* @return {Date}The JavaScript Date representing the last day of the month
*/
this.findMonthEnd = function(date) {
var start = this.findMonthStart(date);
var nextMonth = this.add(start, this.MONTH, 1);
var end = this.subtract(nextMonth, this.DAY, 1);
return end;
};

/**
* Clears the time fields from a given date, effectively setting the time to midnight.
* @param {Date}dateThe JavaScript Date for which the time fields will be cleared
* @return {Date}The JavaScript Date cleared of all time fields
*/
this.clearTime = function(date) {
date.setHours(0,0,0,0);
return date;
};
}
/**
* <p>Calendar_Core is the base class for the Calendar widget. In its most basic
* implementation, it has the ability to render a calendar widget on the page
* that can be manipulated to select a single date, move back and forth between
* months and years.</p>
* <p>To construct the placeholder for the calendar widget, the code is as
* follows:
*<xmp>
*<div id="cal1Container"></div>
*</xmp>
* Note that the table can be replaced with any kind of element.
* </p>
* @constructor
* @param {String}idThe id of the table element that will represent the calendar widget
* @param {String}containerIdThe id of the container element that will contain the calendar table
* @param {String}monthyearThe month/year string used to set the current calendar page
* @param {String}selectedA string of date values formatted using the date parser. The built-in
default date format is MM/DD/YYYY. Ranges are defined using
MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
Any combination of these can be combined by delimiting the string with
commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
*/
YAHOO.widget.Calendar_Core = function(id, containerId, monthyear, selected) {
if (arguments.length > 0) {
this.init(id, containerId, monthyear, selected);
}
}

/**
* The path to be used for images loaded for the Calendar
* @type String
*/
YAHOO.widget.Calendar_Core.IMG_ROOT =""; // for WCT use

//YAHOO.widget.Calendar_Core.IMG_ROOT = (window.location.href.toLowerCase().indexOf("https") == 0 ? "https://a248.e.akamai.net/sec.yimg.com/i/" : "http://us.i1.yimg.com/us.yimg.com/i/");

/**
* Type constant used for renderers to represent an individual date (M/D/Y)
* @final
* @type String
*/
YAHOO.widget.Calendar_Core.DATE = "D";

/**
* Type constant used for renderers to represent an individual date across any year (M/D)
* @final
* @type String
*/
YAHOO.widget.Calendar_Core.MONTH_DAY = "MD";

/**
* Type constant used for renderers to represent a weekday
* @final
* @type String
*/
YAHOO.widget.Calendar_Core.WEEKDAY = "WD";

/**
* Type constant used for renderers to represent a range of individual dates (M/D/Y-M/D/Y)
* @final
* @type String
*/
YAHOO.widget.Calendar_Core.RANGE = "R";

/**
* Type constant used for renderers to represent a month across any year
* @final
* @type String
*/
YAHOO.widget.Calendar_Core.MONTH = "M";

/**
* Constant that represents the total number of date cells that are displayed in a given month
* including 
* @final
* @type Integer
*/
YAHOO.widget.Calendar_Core.DISPLAY_DAYS = 42;

/**
* Constant used for halting the execution of the remainder of the render stack
* @final
* @type String
*/
YAHOO.widget.Calendar_Core.STOP_RENDER = "S";

YAHOO.widget.Calendar_Core.prototype = {

/**
* The configuration object used to set up the calendars various locale and style options.
* @type Object
*/
Config : null,

/**
* The parent CalendarGroup, only to be set explicitly by the parent group
* @type CalendarGroup
*/
parent : null,

/**
* The index of this item in the parent group
* @type Integer
*/
index : -1,

/**
* The collection of calendar table cells
* @type HTMLTableCellElement[]
*/
cells : null,

/**
* The collection of calendar week header cells
* @type HTMLTableCellElement[]
*/
weekHeaderCells : null,

/**
* The collection of calendar week footer cells
* @type HTMLTableCellElement[]
*/
weekFooterCells : null,

/**
* The collection of calendar cell dates that is parallel to the cells collection. The array contains dates field arrays in the format of [YYYY, M, D].
* @type Array[](Integer[])
*/
cellDates : null,

/**
* The id that uniquely identifies this calendar. This id should match the id of the placeholder element on the page.
* @type String
*/
id : null,

/**
* The DOM element reference that points to this calendar's container element. The calendar will be inserted into this element when the shell is rendered.
* @type HTMLElement
*/
oDomContainer : null,

/**
* A Date object representing today's date.
* @type Date
*/
today : null,

/**
* The list of render functions, along with required parameters, used to render cells. 
* @type Array[]
*/
renderStack : null,

/**
* A copy of the initial render functions created before rendering.
* @type Array
* @private
*/
_renderStack : null,

/**
* A Date object representing the month/year that the calendar is currently set to
* @type Date
*/
pageDate : null,

/**
* A Date object representing the month/year that the calendar is initially set to
* @type Date
* @private
*/
_pageDate : null,

/**
* A Date object representing the minimum selectable date
* @type Date
*/
minDate : null,

/**
* A Date object representing the maximum selectable date
* @type Date
*/
maxDate : null,

/**
* The list of currently selected dates. The data format for this local collection is 
* an array of date field arrays, e.g:
* [
*[2004,5,25],
*[2004,5,26]
* ]
* @type Array[](Integer[])
*/
selectedDates : null,

/**
* The private list of initially selected dates.
* @type Array
* @private
*/
_selectedDates : null,

/**
* A boolean indicating whether the shell of the calendar has already been rendered to the page
* @type Boolean
*/
shellRendered : false,

/**
* The HTML table element that represents this calendar
* @type HTMLTableElement
*/
table : null,

/**
* The HTML cell element that represents the main header cell TH used in the calendar table
* @type HTMLTableCellElement
*/
headerCell : null
};



/**
* Initializes the calendar widget. This method must be called by all subclass constructors.
* @param {String}idThe id of the table element that will represent the calendar widget
* @param {String}containerIdThe id of the container element that will contain the calendar table
* @param {String}monthyearThe month/year string used to set the current calendar page
* @param {String}selectedA string of date values formatted using the date parser. The built-in
default date format is MM/DD/YYYY. Ranges are defined using
MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
Any combination of these can be combined by delimiting the string with
commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
*/
YAHOO.widget.Calendar_Core.prototype.init = function(id, containerId, monthyear, selected) {
this.setupConfig();

this.id = id;

this.cellDates = new Array();

this.cells = new Array();

this.renderStack = new Array();
this._renderStack = new Array();

this.oDomContainer = document.getElementById(containerId);

this.today = new Date();
YAHOO.widget.DateMath.clearTime(this.today);

var month;
var year;

if (monthyear) {
var aMonthYear = monthyear.split(this.Locale.DATE_FIELD_DELIMITER);
month = parseInt(aMonthYear[this.Locale.MY_MONTH_POSITION-1]);
year = parseInt(aMonthYear[this.Locale.MY_YEAR_POSITION-1]);
} else {
month = this.today.getUTCMonth()+1;
year = this.today.getUTCFullYear();
}

this.pageDate = new Date(Date.UTC(year, month-1, 1));
//alert(this.pageDate.toUTCString());
this._pageDate = new Date(this.pageDate.toUTCString());
//this._pageDate.setUTCTime(this.pageDate.getTime());

if (selected) {
this.selectedDates = this._parseDates(selected);
this._selectedDates = this.selectedDates.concat();
} else {
this.selectedDates = new Array();
this._selectedDates = new Array();
}

this.wireDefaultEvents();
this.wireCustomEvents();
};


/**
* Wires the local DOM events for the Calendar, including cell selection, hover, and
* default navigation that is used for moving back and forth between calendar pages.
*/
YAHOO.widget.Calendar_Core.prototype.wireDefaultEvents = function() {

/**
* The default event function that is attached to a date link within a calendar cell
* when the calendar is rendered. 
* @parameThe event
* @paramcalA reference to the calendar passed by the Event utility
*/
this.doSelectCell = function(e, cal) {
var cell = this;
var index = cell.index;
var d = cal.cellDates[index];
var date = new Date(Date.UTC(d[0],d[1]-1,d[2]));

if (! cal.isDateOOM(date) && ! YAHOO.util.Dom.hasClass(cell, cal.Style.CSS_CELL_RESTRICTED) && ! YAHOO.util.Dom.hasClass(cell, cal.Style.CSS_CELL_OOB)) {
if (cal.Options.MULTI_SELECT) {
var link = cell.getElementsByTagName("A")[0];
link.blur();

var cellDate = cal.cellDates[index];
var cellDateIndex = cal._indexOfSelectedFieldArray(cellDate);

if (cellDateIndex > -1) {
cal.deselectCell(index);
} else {
cal.selectCell(index);
}

} else {
var link = cell.getElementsByTagName("A")[0];
link.blur()
cal.selectCell(index);
}
}
}

/**
* The event that is executed when the user hovers over a cell
* @parameThe event
* @paramcalA reference to the calendar passed by the Event utility
* @private
*/
this.doCellMouseOver = function(e, cal) {
var cell = this;
var index = cell.index;
var d = cal.cellDates[index];
var date = new Date(Date.UTC(d[0],d[1]-1,d[2]));

if (! cal.isDateOOM(date) && ! YAHOO.util.Dom.hasClass(cell, cal.Style.CSS_CELL_RESTRICTED) && ! YAHOO.util.Dom.hasClass(cell, cal.Style.CSS_CELL_OOB)) {
YAHOO.util.Dom.addClass(cell, cal.Style.CSS_CELL_HOVER);
}
}

/**
* The event that is executed when the user moves the mouse out of a cell
* @parameThe event
* @paramcalA reference to the calendar passed by the Event utility
* @private
*/
this.doCellMouseOut = function(e, cal) {
YAHOO.util.Dom.removeClass(this, cal.Style.CSS_CELL_HOVER);
}

/**
* A wrapper event that executes the nextMonth method through a DOM event
* @parameThe event
* @paramcalA reference to the calendar passed by the Event utility
* @private
*/
this.doNextMonth = function(e, cal) {
cal.nextMonth();
}

/**
* A wrapper event that executes the previousMonth method through a DOM event
* @parameThe event
* @paramcalA reference to the calendar passed by the Event utility
* @private
*/
this.doPreviousMonth = function(e, cal) {
cal.previousMonth();
}
}

/**
* This function can be extended by subclasses to attach additional DOM events to
* the calendar. By default, this method is unimplemented.
*/
YAHOO.widget.Calendar_Core.prototype.wireCustomEvents = function() { }

/**
This method is called to initialize the widget configuration variables, including
style, localization, and other display and behavioral options.
<p>Config: Container for the CSS style configuration variables.</p>
<p><b>Config.Style</b> - Defines the CSS classes used for different calendar elements</p>
<ul>
<div><i>CSS_CALENDAR</i> : Container table</div>
<div><i>CSS_HEADER</i> : </div>
<div><i>CSS_HEADER_TEXT</i> : Calendar header</div>
<div><i>CSS_FOOTER</i> : Calendar footer</div>
<div><i>CSS_CELL</i> : Calendar day cell</div>
<div><i>CSS_CELL_OOM</i> : Calendar OOM (out of month) cell</div>
<div><i>CSS_CELL_SELECTED</i> : Calendar selected cell</div>
<div><i>CSS_CELL_RESTRICTED</i> : Calendar restricted cell</div>
<div><i>CSS_CELL_TODAY</i> : Calendar cell for today's date</div>
<div><i>CSS_ROW_HEADER</i> : The cell preceding a row (used for week number by default)</div>
<div><i>CSS_ROW_FOOTER</i> : The cell following a row (not implemented by default)</div>
<div><i>CSS_WEEKDAY_CELL</i> : The cells used for labeling weekdays</div>
<div><i>CSS_WEEKDAY_ROW</i> : The row containing the weekday label cells</div>
<div><i>CSS_CONTAINER</i> : The border style used for the default UED rendering</div>
<div><i>CSS_2UPWRAPPER</i> : Special container class used to properly adjust the sizing and float</div>
<div><i>CSS_NAV_LEFT</i> : Left navigation arrow</div>
<div><i>CSS_NAV_RIGHT</i> : Right navigation arrow</div>
<div><i>CSS_CELL_TOP</i> : Outlying cell along the top row</div>
<div><i>CSS_CELL_LEFT</i> : Outlying cell along the left row</div>
<div><i>CSS_CELL_RIGHT</i> : Outlying cell along the right row</div>
<div><i>CSS_CELL_BOTTOM</i> : Outlying cell along the bottom row</div>
<div><i>CSS_CELL_HOVER</i> : Cell hover style</div>
<div><i>CSS_CELL_HIGHLIGHT1</i> : Highlight color 1 for styling cells</div>
<div><i>CSS_CELL_HIGHLIGHT2</i> : Highlight color 2 for styling cells</div>
<div><i>CSS_CELL_HIGHLIGHT3</i> : Highlight color 3 for styling cells</div>
<div><i>CSS_CELL_HIGHLIGHT4</i> : Highlight color 4 for styling cells</div>

</ul>
<p><b>Config.Locale</b> - Defines the locale string arrays used for localization</p>
<ul>
<div><i>MONTHS_SHORT</i> : Array of 12 months in short format ("Jan", "Feb", etc.)</div>
<div><i>MONTHS_LONG</i> : Array of 12 months in short format ("Jan", "Feb", etc.)</div>
<div><i>WEEKDAYS_1CHAR</i> : Array of 7 days in 1-character format ("S", "M", etc.)</div>
<div><i>WEEKDAYS_SHORT</i> : Array of 7 days in short format ("Su", "Mo", etc.)</div>
<div><i>WEEKDAYS_MEDIUM</i> : Array of 7 days in medium format ("Sun", "Mon", etc.)</div>
<div><i>WEEKDAYS_LONG</i> : Array of 7 days in long format ("Sunday", "Monday", etc.)</div>
<div><i>DATE_DELIMITER</i> : The value used to delimit series of multiple dates (Default: ",")</div>
<div><i>DATE_FIELD_DELIMITER</i> : The value used to delimit date fields (Default: "/")</div>
<div><i>DATE_RANGE_DELIMITER</i> : The value used to delimit date fields (Default: "-")</div>
<div><i>MY_MONTH_POSITION</i> : The value used to determine the position of the month in a month/year combo (e.g. 12/2005) (Default: 1)</div>
<div><i>MY_YEAR_POSITION</i> : The value used to determine the position of the year in a month/year combo (e.g. 12/2005) (Default: 2)</div>
<div><i>MD_MONTH_POSITION</i> : The value used to determine the position of the month in a month/day combo (e.g. 12/25) (Default: 1)</div>
<div><i>MD_DAY_POSITION</i> : The value used to determine the position of the day in a month/day combo (e.g. 12/25) (Default: 2)</div>
<div><i>MDY_MONTH_POSITION</i> : The value used to determine the position of the month in a month/day/year combo (e.g. 12/25/2005) (Default: 1)</div>
<div><i>MDY_DAY_POSITION</i> : The value used to determine the position of the day in a month/day/year combo (e.g. 12/25/2005) (Default: 2)</div>
<div><i>MDY_YEAR_POSITION</i> : The value used to determine the position of the year in a month/day/year combo (e.g. 12/25/2005) (Default: 3)</div>
</ul>
<p><b>Config.Options</b> - Defines other configurable calendar widget options</p>
<ul>
<div><i>SHOW_WEEKDAYS</i> : Boolean, determines whether to display the weekday headers (defaults to true)</div>
<div><i>LOCALE_MONTHS</i> : Array, points to the desired Config.Locale array (defaults to Config.Locale.MONTHS_LONG)</div>
<div><i>LOCALE_WEEKDAYS</i> : Array, points to the desired Config.Locale array (defaults to Config.Locale.WEEKDAYS_SHORT)</div>
<div><i>START_WEEKDAY</i> : Integer, 0-6, representing the day that a week begins on</div>
<div><i>SHOW_WEEK_HEADER</i> : Boolean, determines whether to display row headers</div>
<div><i>SHOW_WEEK_FOOTER</i> : Boolean, determines whether to display row footers</div>
<div><i>HIDE_BLANK_WEEKS</i> : Boolean, determines whether to hide extra weeks that are completely OOM</div>
<div><i>NAV_ARROW_LEFT</i> : String, the image path used for the left navigation arrow</div>
<div><i>NAV_ARROW_RIGHT</i> : String, the image path used for the right navigation arrow</div>
</ul>
*/
YAHOO.widget.Calendar_Core.prototype.setupConfig = function() {
/**
* Container for the CSS style configuration variables.
*/
this.Config = new Object();

this.Config.Style = {

CSS_ROW_HEADER: "calrowhead",
CSS_ROW_FOOTER: "calrowfoot",
CSS_CELL : "calcell",
CSS_CELL_SELECTED : "selected",
CSS_CELL_RESTRICTED : "restricted",
CSS_CELL_TODAY : "today",
CSS_CELL_OOM : "oom",
CSS_CELL_OOB : "previous",
CSS_HEADER : "calheader",
CSS_HEADER_TEXT : "calhead",
CSS_WEEKDAY_CELL : "calweekdaycell",
CSS_WEEKDAY_ROW : "calweekdayrow",
CSS_FOOTER : "calfoot",
CSS_CALENDAR : "calendar",
CSS_BORDER : "calbordered",
CSS_CONTAINER : "calcontainer", 
CSS_NAV_LEFT : "calnavleft",
CSS_NAV_RIGHT : "calnavright",
CSS_CELL_TOP : "calcelltop",
CSS_CELL_LEFT : "calcellleft",
CSS_CELL_RIGHT : "calcellright",
CSS_CELL_BOTTOM : "calcellbottom",
CSS_CELL_HOVER : "calcellhover",
CSS_CELL_HIGHLIGHT1 : "highlight1",
CSS_CELL_HIGHLIGHT2 : "highlight2",
CSS_CELL_HIGHLIGHT3 : "highlight3",
CSS_CELL_HIGHLIGHT4 : "highlight4"
};

this.Style = this.Config.Style;

this.Config.Locale = {
// Locale definition
MONTHS_SHORT : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
MONTHS_LONG : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
WEEKDAYS_1CHAR : ["S", "M", "T", "W", "T", "F", "S"],
WEEKDAYS_SHORT : ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
WEEKDAYS_MEDIUM : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
WEEKDAYS_LONG : ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
DATE_DELIMITER : ",",
DATE_FIELD_DELIMITER : "/",
DATE_RANGE_DELIMITER : "-",
MY_MONTH_POSITION : 1,
MY_YEAR_POSITION : 2,
MD_MONTH_POSITION : 1,
MD_DAY_POSITION : 2,
MDY_MONTH_POSITION : 1,
MDY_DAY_POSITION : 2,
MDY_YEAR_POSITION : 3
};

this.Locale = this.Config.Locale;

this.Config.Options = {
// Configuration variables
MULTI_SELECT : false,
SHOW_WEEKDAYS : true,
START_WEEKDAY : 0,
SHOW_WEEK_HEADER : false,
SHOW_WEEK_FOOTER : false,
HIDE_BLANK_WEEKS : false,
NAV_ARROW_LEFT : YAHOO.widget.Calendar_Core.IMG_ROOT + "us/tr/callt.gif",
NAV_ARROW_RIGHT : YAHOO.widget.Calendar_Core.IMG_ROOT + "us/tr/calrt.gif"
};

this.Options = this.Config.Options;

this.customConfig();

if (! this.Options.LOCALE_MONTHS) {
this.Options.LOCALE_MONTHS=this.Locale.MONTHS_LONG;
}
if (! this.Options.LOCALE_WEEKDAYS) {
this.Options.LOCALE_WEEKDAYS=this.Locale.WEEKDAYS_SHORT;
}

// If true, reconfigure weekday arrays to place Mondays first
if (this.Options.START_WEEKDAY > 0) {
for (var w=0;w<this.Options.START_WEEKDAY;++w) {
this.Locale.WEEKDAYS_SHORT.push(this.Locale.WEEKDAYS_SHORT.shift());
this.Locale.WEEKDAYS_MEDIUM.push(this.Locale.WEEKDAYS_MEDIUM.shift());
this.Locale.WEEKDAYS_LONG.push(this.Locale.WEEKDAYS_LONG.shift());
}
}
};

/**
* This method is called when subclasses need to override configuration variables
* or create new ones. Values can be explicitly set as follows:
* <ul><code>
*this.Config.Style.CSS_CELL = "newcalcell";
*this.Config.Locale.MONTHS_SHORT = ["Jan", "Fv", "Mars", "Avr", "Mai", "Juin", "Juil", "Aot", "Sept", "Oct", "Nov", "Dc"];
* </code></ul>
*/
YAHOO.widget.Calendar_Core.prototype.customConfig = function() { };

/**
* Builds the date label that will be displayed in the calendar header or
* footer, depending on configuration.
* @returnThe formatted calendar month label
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.buildMonthLabel = function() {
var text = this.Options.LOCALE_MONTHS[this.pageDate.getUTCMonth()] + " " + this.pageDate.getUTCFullYear();
return text;
};

/**
* Builds the date digit that will be displayed in calendar cells
* @returnThe formatted day label
* @typeString
*/
YAHOO.widget.Calendar_Core.prototype.buildDayLabel = function(workingDate) {
var day = workingDate.getUTCDate();
return day;
};



/**
* Builds the calendar table shell that will be filled in with dates and formatting.
* This method calls buildShellHeader, buildShellBody, and buildShellFooter (in that order) 
* to construct the pieces of the calendar table. The construction of the shell should
* only happen one time when the calendar is initialized.
*/
YAHOO.widget.Calendar_Core.prototype.buildShell = function() {

this.table = document.createElement("TABLE");
this.table.cellSpacing = 0;
YAHOO.widget.Calendar_Core.setCssClasses(this.table, [this.Style.CSS_CALENDAR]);

this.table.id = this.id;

this.buildShellHeader();
this.buildShellBody();
this.buildShellFooter();

YAHOO.util.Event.addListener(window, "unload", this._unload, this);
};

/**
* Builds the calendar shell header by inserting a THEAD into the local calendar table.
*/
YAHOO.widget.Calendar_Core.prototype.buildShellHeader = function() {
var head = document.createElement("THEAD");
var headRow = document.createElement("TR");

var headerCell = document.createElement("TH");

var colSpan = 7;
if (this.Config.Options.SHOW_WEEK_HEADER) {
this.weekHeaderCells = new Array();
colSpan += 1;
}
if (this.Config.Options.SHOW_WEEK_FOOTER) {
this.weekFooterCells = new Array();
colSpan += 1;
}

headerCell.colSpan = colSpan;

YAHOO.widget.Calendar_Core.setCssClasses(headerCell,[this.Style.CSS_HEADER_TEXT]);

this.headerCell = headerCell;

headRow.appendChild(headerCell);
head.appendChild(headRow);

// Append day labels, if needed
if (this.Options.SHOW_WEEKDAYS) {
var row = document.createElement("TR");
var fillerCell;

YAHOO.widget.Calendar_Core.setCssClasses(row,[this.Style.CSS_WEEKDAY_ROW]);

if (this.Config.Options.SHOW_WEEK_HEADER) {
fillerCell = document.createElement("TH");
YAHOO.widget.Calendar_Core.setCssClasses(fillerCell,[this.Style.CSS_WEEKDAY_CELL]);
row.appendChild(fillerCell);
}

for(var i=0;i<this.Options.LOCALE_WEEKDAYS.length;++i) {
var cell = document.createElement("TH");
YAHOO.widget.Calendar_Core.setCssClasses(cell,[this.Style.CSS_WEEKDAY_CELL]);
cell.innerHTML=this.Options.LOCALE_WEEKDAYS[i];
row.appendChild(cell);
}

if (this.Config.Options.SHOW_WEEK_FOOTER) {
fillerCell = document.createElement("TH");
YAHOO.widget.Calendar_Core.setCssClasses(fillerCell,[this.Style.CSS_WEEKDAY_CELL]);
row.appendChild(fillerCell);
}

head.appendChild(row);
}

this.table.appendChild(head);
};

/**
* Builds the calendar shell body (6 weeks by 7 days)
*/
YAHOO.widget.Calendar_Core.prototype.buildShellBody = function() {
// This should only get executed once
this.tbody = document.createElement("TBODY");

for (var r=0;r<6;++r) {
var row = document.createElement("TR");

for (var c=0;c<this.headerCell.colSpan;++c) {
var cell;
if (this.Config.Options.SHOW_WEEK_HEADER && c===0) { // Row header
cell = document.createElement("TH");
this.weekHeaderCells[this.weekHeaderCells.length] = cell;
} else if (this.Config.Options.SHOW_WEEK_FOOTER && c==(this.headerCell.colSpan-1)){ // Row footer
cell = document.createElement("TH");
this.weekFooterCells[this.weekFooterCells.length] = cell;
} else {
cell = document.createElement("TD");
this.cells[this.cells.length] = cell;
YAHOO.widget.Calendar_Core.setCssClasses(cell, [this.Style.CSS_CELL]);
YAHOO.util.Event.addListener(cell, "click", this.doSelectCell, this);

YAHOO.util.Event.addListener(cell, "mouseover", this.doCellMouseOver, this);
YAHOO.util.Event.addListener(cell, "mouseout", this.doCellMouseOut, this);
}
row.appendChild(cell);
}
this.tbody.appendChild(row);
}

this.table.appendChild(this.tbody);
};

/**
* Builds the calendar shell footer. In the default implementation, there is
* no footer.
*/
YAHOO.widget.Calendar_Core.prototype.buildShellFooter = function() { };

/**
* Outputs the calendar shell to the DOM, inserting it into the placeholder element.
*/
YAHOO.widget.Calendar_Core.prototype.renderShell = function() {
this.oDomContainer.appendChild(this.table);
this.shellRendered = true;
};

/**
* Renders the calendar after it has been configured. The render() method has a specific call chain that will execute
* when the method is called: renderHeader, renderBody, renderFooter.
* Refer to the documentation for those methods for information on 
* individual render tasks.
*/
YAHOO.widget.Calendar_Core.prototype.render = function() {
if (! this.shellRendered) {
this.buildShell();
this.renderShell();
}

this.resetRenderers();

this.cellDates.length = 0;

// Find starting day of the current month
var workingDate = YAHOO.widget.DateMath.findMonthStart(this.pageDate);

this.renderHeader();
this.renderBody(workingDate);
this.renderFooter();

this.onRender();
};



/**
* Appends the header contents into the widget header.
*/
YAHOO.widget.Calendar_Core.prototype.renderHeader = function() {
this.headerCell.innerHTML = "";

var headerContainer = document.createElement("DIV");
headerContainer.className = this.Style.CSS_HEADER;

headerContainer.appendChild(document.createTextNode(this.buildMonthLabel()));

this.headerCell.appendChild(headerContainer);
};

/**
* Appends the calendar body. The default implementation calculates the number of
* OOM (out of month) cells that need to be rendered at the start of the month, renders those, 
* and renders all the day cells using the built-in cell rendering methods.
*
* While iterating through all of the cells, the calendar checks for renderers in the
* local render stack that match the date of the current cell, and then applies styles
* as necessary.
* 
* @param {Date}workingDateThe current working Date object being used to generate the calendar
*/
YAHOO.widget.Calendar_Core.prototype.renderBody = function(workingDate) {

this.preMonthDays = workingDate.getUTCDay();
if (this.Options.START_WEEKDAY > 0) {
this.preMonthDays -= this.Options.START_WEEKDAY;
}
if (this.preMonthDays < 0) {
this.preMonthDays += 7;
}

this.monthDays = YAHOO.widget.DateMath.findMonthEnd(workingDate).getUTCDate();
this.postMonthDays = YAHOO.widget.Calendar_Core.DISPLAY_DAYS-this.preMonthDays-this.monthDays;

workingDate = YAHOO.widget.DateMath.subtract(workingDate, YAHOO.widget.DateMath.DAY, this.preMonthDays);

var weekRowIndex = 0;

for (var c=0;c<this.cells.length;++c) {
var cellRenderers = new Array();

var cell = this.cells[c];
this.clearElement(cell);

cell.index = c;
cell.id = this.id + "_cell" + c;

this.cellDates[this.cellDates.length]=[workingDate.getUTCFullYear(),workingDate.getUTCMonth()+1,workingDate.getUTCDate()]; // Add this date to cellDates

if (workingDate.getUTCDay() == this.Options.START_WEEKDAY) {
var rowHeaderCell = null;
var rowFooterCell = null;

if (this.Options.SHOW_WEEK_HEADER) {
rowHeaderCell = this.weekHeaderCells[weekRowIndex];
this.clearElement(rowHeaderCell);
}

if (this.Options.SHOW_WEEK_FOOTER) {
rowFooterCell = this.weekFooterCells[weekRowIndex];
this.clearElement(rowFooterCell);
}

if (this.Options.HIDE_BLANK_WEEKS && this.isDateOOM(workingDate) && ! YAHOO.widget.DateMath.isMonthOverlapWeek(workingDate)) {
// The first day of the week is not in this month, and it's not an overlap week
continue;
} else {
if (rowHeaderCell) {
this.renderRowHeader(workingDate, rowHeaderCell);
}
if (rowFooterCell) {
this.renderRowFooter(workingDate, rowFooterCell);
}
}
}



var renderer = null;

if (workingDate.getUTCFullYear()== this.today.getUTCFullYear() &&
workingDate.getUTCMonth()== this.today.getUTCMonth() &&
workingDate.getUTCDate()== this.today.getUTCDate()) {
cellRenderers[cellRenderers.length]=this.renderCellStyleToday;
}

if (this.isDateOOM(workingDate)) {
cellRenderers[cellRenderers.length]=this.renderCellNotThisMonth;
} else {
for (var r=0;r<this.renderStack.length;++r) {
var rArray = this.renderStack[r];
var type = rArray[0];

var month;
var day;
var year;

switch (type) {
case YAHOO.widget.Calendar_Core.DATE:
month = rArray[1][1];
day = rArray[1][2];
year = rArray[1][0];

if (workingDate.getUTCMonth()+1 == month && workingDate.getUTCDate() == day && workingDate.getUTCFullYear() == year) {
renderer = rArray[2];
this.renderStack.splice(r,1);
}
break;
case YAHOO.widget.Calendar_Core.MONTH_DAY:
month = rArray[1][0];
day = rArray[1][1];

if (workingDate.getUTCMonth()+1 == month && workingDate.getUTCDate() == day) {
renderer = rArray[2];
this.renderStack.splice(r,1);
}
break;
case YAHOO.widget.Calendar_Core.RANGE:
var date1 = rArray[1][0];
var date2 = rArray[1][1];

var d1month = date1[1];
var d1day = date1[2];
var d1year = date1[0];

var d1 = new Date(Date.UTC(d1year, d1month-1, d1day));

var d2month = date2[1];
var d2day = date2[2];
var d2year = date2[0];

var d2 = new Date(Date.UTC(d2year, d2month-1, d2day));

if (Date.UTC(workingDate) >= Date.UTC(d1) && Date.UTC(workingDate) <= Date.UTC(d2)) {
renderer = rArray[2];

if (Date.UTC(workingDate)==Date.UTC(d2)) { 
this.renderStack.splice(r,1);
}
}
break;
case YAHOO.widget.Calendar_Core.WEEKDAY:

var weekday = rArray[1][0];
if (workingDate.getUTCDay()+1 == weekday) {
renderer = rArray[2];
}
break;
case YAHOO.widget.Calendar_Core.MONTH:

month = rArray[1][0];
if (workingDate.getUTCMonth()+1 == month) {
renderer = rArray[2];
}
break;
}

if (renderer) {
cellRenderers[cellRenderers.length]=renderer;
}
}

}

if (this._indexOfSelectedFieldArray([workingDate.getUTCFullYear(),workingDate.getUTCMonth()+1,workingDate.getUTCDate()]) > -1) {
cellRenderers[cellRenderers.length]=this.renderCellStyleSelected; 
}

if (this.minDate) {
this.minDate = YAHOO.widget.DateMath.clearTime(this.minDate);
}
if (this.maxDate) {
this.maxDate = YAHOO.widget.DateMath.clearTime(this.maxDate);
}

if (
(this.minDate && (workingDate.getTime() < this.minDate.getTime())) ||
(this.maxDate && (workingDate.getTime() > this.maxDate.getTime()))
) {
cellRenderers[cellRenderers.length]=this.renderOutOfBoundsDate;
} else {
cellRenderers[cellRenderers.length]=this.renderCellDefault;
}

for (var x=0;x<cellRenderers.length;++x) {
var ren = cellRenderers[x];
if (ren.call(this,workingDate,cell) == YAHOO.widget.Calendar_Core.STOP_RENDER) {
break;
}
}

workingDate = YAHOO.widget.DateMath.add(workingDate, YAHOO.widget.DateMath.DAY, 1); // Go to the next day
if (workingDate.getUTCDay() == this.Options.START_WEEKDAY) {
weekRowIndex += 1;
}

YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL);

if (c >= 0 && c <= 6) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_TOP);
}
if ((c % 7) == 0) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_LEFT);
}
if (((c+1) % 7) == 0) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_RIGHT);
}

var postDays = this.postMonthDays; 
if (postDays >= 7 && this.Options.HIDE_BLANK_WEEKS) {
var blankWeeks = Math.floor(postDays/7);
for (var p=0;p<blankWeeks;++p) {
postDays -= 7;
}
}

if (c >= ((this.preMonthDays+postDays+this.monthDays)-7)) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_BOTTOM);
}
}

};

/**
* Appends the contents of the calendar widget footer into the shell. By default, 
* the calendar does not contain a footer, and this method must be implemented by 
* subclassing the widget.
*/
YAHOO.widget.Calendar_Core.prototype.renderFooter = function() { };

/**
* @private
*/
YAHOO.widget.Calendar_Core.prototype._unload = function(e, cal) {
for (var c in cal.cells) {
c = null;
}

cal.cells = null;

cal.tbody = null;
cal.oDomContainer = null;
cal.table = null;
cal.headerCell = null;

cal = null;
};


/****************** BEGIN BUILT-IN TABLE CELL RENDERERS ************************************/

YAHOO.widget.Calendar_Core.prototype.renderOutOfBoundsDate = function(workingDate, cell) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOB);
cell.innerHTML = workingDate.getUTCDate();
return YAHOO.widget.Calendar_Core.STOP_RENDER;
}

/**
* Renders the row header for a week. The date passed in should be
* the first date of the given week.
* @param {Date}workingDateThe current working Date object (beginning of the week) being used to generate the calendar
* @param {HTMLTableCellElement}cellThe current working cell in the calendar
*/
YAHOO.widget.Calendar_Core.prototype.renderRowHeader = function(workingDate, cell) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_ROW_HEADER);

var useYear = this.pageDate.getUTCFullYear();

if (! YAHOO.widget.DateMath.isYearOverlapWeek(workingDate)) {
useYear = workingDate.getUTCFullYear();
}

var weekNum = YAHOO.widget.DateMath.getWeekNumber(workingDate, useYear, this.Options.START_WEEKDAY);
cell.innerHTML = weekNum;

if (this.isDateOOM(workingDate) && ! YAHOO.widget.DateMath.isMonthOverlapWeek(workingDate)) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOM);
}
};

/**
* Renders the row footer for a week. The date passed in should be
* the first date of the given week.
* @param {Date}workingDateThe current working Date object (beginning of the week) being used to generate the calendar
* @param {HTMLTableCellElement}cellThe current working cell in the calendar
*/
YAHOO.widget.Calendar_Core.prototype.renderRowFooter = function(workingDate, cell) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_ROW_FOOTER);

if (this.isDateOOM(workingDate) && ! YAHOO.widget.DateMath.isMonthOverlapWeek(workingDate)) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOM);
}
};

/**
* Renders a single standard calendar cell in the calendar widget table.
* All logic for determining how a standard default cell will be rendered is 
* encapsulated in this method, and must be accounted for when extending the
* widget class.
* @param {Date}workingDateThe current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}cellThe current working cell in the calendar
* @return YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellDefault = function(workingDate, cell) {
cell.innerHTML = "";
var link = document.createElement("a");

link.href="javascript:void(null);";
link.name=this.id+"__"+workingDate.getUTCFullYear()+"_"+(workingDate.getUTCMonth()+1)+"_"+workingDate.getUTCDate();

link.appendChild(document.createTextNode(this.buildDayLabel(workingDate)));
cell.appendChild(link);
};

/**
* Renders a single standard calendar cell using the CSS hightlight1 style
* @param {Date}workingDateThe current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}cellThe current working cell in the calendar
* @return YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellStyleHighlight1 = function(workingDate, cell) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT1);
};

/**
* Renders a single standard calendar cell using the CSS hightlight2 style
* @param {Date}workingDateThe current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}cellThe current working cell in the calendar
* @return YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellStyleHighlight2 = function(workingDate, cell) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT2);
};

/**
* Renders a single standard calendar cell using the CSS hightlight3 style
* @param {Date}workingDateThe current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}cellThe current working cell in the calendar
* @return YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellStyleHighlight3 = function(workingDate, cell) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT3);
};

/**
* Renders a single standard calendar cell using the CSS hightlight4 style
* @param {Date}workingDateThe current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}cellThe current working cell in the calendar
* @return YAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellStyleHighlight4 = function(workingDate, cell) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT4);
};

/**
* Applies the default style used for rendering today's date to the current calendar cell
* @param {Date}workingDateThe current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}cellThe current working cell in the calendar
* @returnYAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellStyleToday = function(workingDate, cell) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_TODAY);
};

/**
* Applies the default style used for rendering selected dates to the current calendar cell
* @param {Date}workingDateThe current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}cellThe current working cell in the calendar
* @returnYAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellStyleSelected = function(workingDate, cell) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_SELECTED);
};

/**
* Applies the default style used for rendering dates that are not a part of the current
* month (preceding or trailing the cells for the current month)
* @param {Date}workingDateThe current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}cellThe current working cell in the calendar
* @returnYAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderCellNotThisMonth = function(workingDate, cell) {
YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOM);
cell.innerHTML=workingDate.getUTCDate();
return YAHOO.widget.Calendar_Core.STOP_RENDER;
};

/**
* Renders the current calendar cell as a non-selectable "black-out" date using the default
* restricted style.
* @param {Date}workingDateThe current working Date object being used to generate the calendar
* @param {HTMLTableCellElement}cellThe current working cell in the calendar
* @returnYAHOO.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
*should not be terminated
* @type String
*/
YAHOO.widget.Calendar_Core.prototype.renderBodyCellRestricted = function(workingDate, cell) {
YAHOO.widget.Calendar_Core.setCssClasses(cell, [this.Style.CSS_CELL,this.Style.CSS_CELL_RESTRICTED]);
cell.innerHTML=workingDate.getUTCDate();
return YAHOO.widget.Calendar_Core.STOP_RENDER;
};
/******************** END BUILT-IN TABLE CELL RENDERERS ************************************/

/******************** BEGIN MONTH NAVIGATION METHODS ************************************/
/**
* Adds the designated number of months to the current calendar month, and sets the current
* calendar page date to the new month.
* @param {Integer}countThe number of months to add to the current calendar
*/
YAHOO.widget.Calendar_Core.prototype.addMonths = function(count) {
this.pageDate = YAHOO.widget.DateMath.add(this.pageDate, YAHOO.widget.DateMath.MONTH, count);
this.resetRenderers();
this.onChangePage();
};

/**
* Subtracts the designated number of months from the current calendar month, and sets the current
* calendar page date to the new month.
* @param {Integer}countThe number of months to subtract from the current calendar
*/
YAHOO.widget.Calendar_Core.prototype.subtractMonths = function(count) {
this.pageDate = YAHOO.widget.DateMath.subtract(this.pageDate, YAHOO.widget.DateMath.MONTH, count);
this.resetRenderers();
this.onChangePage();
};

/**
* Adds the designated number of years to the current calendar, and sets the current
* calendar page date to the new month.
* @param {Integer}countThe number of years to add to the current calendar
*/
YAHOO.widget.Calendar_Core.prototype.addYears = function(count) {
this.pageDate = YAHOO.widget.DateMath.add(this.pageDate, YAHOO.widget.DateMath.YEAR, count);
this.resetRenderers();
this.onChangePage();
};

/**
* Subtcats the designated number of years from the current calendar, and sets the current
* calendar page date to the new month.
* @param {Integer}countThe number of years to subtract from the current calendar
*/
YAHOO.widget.Calendar_Core.prototype.subtractYears = function(count) {
this.pageDate = YAHOO.widget.DateMath.subtract(this.pageDate, YAHOO.widget.DateMath.YEAR, count);
this.resetRenderers();
this.onChangePage();
};

/**
* Navigates to the next month page in the calendar widget.
*/
YAHOO.widget.Calendar_Core.prototype.nextMonth = function() {
this.addMonths(1);
};

/**
* Navigates to the previous month page in the calendar widget.
*/
YAHOO.widget.Calendar_Core.prototype.previousMonth = function() {
this.subtractMonths(1);
};

/**
* Navigates to the next year in the currently selected month in the calendar widget.
*/
YAHOO.widget.Calendar_Core.prototype.nextYear = function() {
this.addYears(1);
};

/**
* Navigates to the previous year in the currently selected month in the calendar widget.
*/
YAHOO.widget.Calendar_Core.prototype.previousYear = function() {
this.subtractYears(1);
};

/****************** END MONTH NAVIGATION METHODS ************************************/

/************* BEGIN SELECTION METHODS *************************************************************/

/**
* Resets the calendar widget to the originally selected month and year, and 
* sets the calendar to the initial selection(s).
*/
YAHOO.widget.Calendar_Core.prototype.reset = function() {
this.selectedDates.length = 0;
this.selectedDates = this._selectedDates.concat();

this.pageDate = new Date(this._pageDate.toUTCString());
this.onReset();
};

/**
* Clears the selected dates in the current calendar widget and sets the calendar
* to the current month and year.
*/
YAHOO.widget.Calendar_Core.prototype.clear = function() {
this.selectedDates.length = 0;
this.pageDate = new Date(this.today.toUTCString());
this.onClear();
};

/**
* Selects a date or a collection of dates on the current calendar. This method, by default,
* does not call the render method explicitly. Once selection has completed, render must be 
* called for the changes to be reflected visually.
* @param{String/Date/Date[]}dateThe date string of dates to select in the current calendar. Valid formats are
*individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
*Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
*This method can also take a JavaScript Date object or an array of Date objects.
* @returnArray of JavaScript Date objects representing all individual dates that are currently selected.
* @type Date[]
*/
YAHOO.widget.Calendar_Core.prototype.select = function(date) {
this.onBeforeSelect();

var aToBeSelected = this._toFieldArray(date);

for (var a=0;a<aToBeSelected.length;++a) {
var toSelect = aToBeSelected[a]; // For each date item in the list of dates we're trying to select
if (this._indexOfSelectedFieldArray(toSelect) == -1) { // not already selected?
this.selectedDates[this.selectedDates.length]=toSelect;
}
}

if (this.parent) {
this.parent.sync(this);
}

this.onSelect(aToBeSelected);

return this.getSelectedDates();
};

/**
* Selects a date on the current calendar by referencing the index of the cell that should be selected.
* This method is used to easily select a single cell (usually with a mouse click) without having to do
* a full render. The selected style is applied to the cell directly.
* @param{Integer}cellIndexThe index of the cell to select in the current calendar. 
* @returnArray of JavaScript Date objects representing all individual dates that are currently selected.
* @type Date[]
*/
YAHOO.widget.Calendar_Core.prototype.selectCell = function(cellIndex) {
this.onBeforeSelect();

this.cells = this.tbody.getElementsByTagName("TD");

var cell = this.cells[cellIndex];
var cellDate = this.cellDates[cellIndex];

var dCellDate = this._toDate(cellDate);

var selectDate = cellDate.concat();

this.selectedDates.push(selectDate);

if (this.parent) {
this.parent.sync(this);
}

this.renderCellStyleSelected(dCellDate,cell);

this.onSelect([selectDate]);
this.doCellMouseOut.call(cell, null, this);

return this.getSelectedDates();
};

/**
* Deselects a date or a collection of dates on the current calendar. This method, by default,
* does not call the render method explicitly. Once deselection has completed, render must be 
* called for the changes to be reflected visually.
* @param{String/Date/Date[]}dateThe date string of dates to deselect in the current calendar. Valid formats are
*individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
*Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
*This method can also take a JavaScript Date object or an array of Date objects.
* @returnArray of JavaScript Date objects representing all individual dates that are currently selected.
* @type Date[]
*/
YAHOO.widget.Calendar_Core.prototype.deselect = function(date) {
this.onBeforeDeselect();

var aToBeSelected = this._toFieldArray(date);

for (var a=0;a<aToBeSelected.length;++a) {
var toSelect = aToBeSelected[a]; // For each date item in the list of dates we're trying to select
var index = this._indexOfSelectedFieldArray(toSelect);

if (index != -1) {
this.selectedDates.splice(index,1);
}
}

if (this.parent) {
this.parent.sync(this);
} 

this.onDeselect(aToBeSelected);
return this.getSelectedDates();
};

/**
* Deselects a date on the current calendar by referencing the index of the cell that should be deselected.
* This method is used to easily deselect a single cell (usually with a mouse click) without having to do
* a full render. The selected style is removed from the cell directly.
* @param{Integer}cellIndexThe index of the cell to deselect in the current calendar. 
* @returnArray of JavaScript Date objects representing all individual dates that are currently selected.
* @type Date[]
*/
YAHOO.widget.Calendar_Core.prototype.deselectCell = function(i) {
this.onBeforeDeselect();
this.cells = this.tbody.getElementsByTagName("TD");

var cell = this.cells[i];
var cellDate = this.cellDates[i];
var cellDateIndex = this._indexOfSelectedFieldArray(cellDate);

var dCellDate = this._toDate(cellDate);

var selectDate = cellDate.concat();

if (cellDateIndex > -1) {
if (this.pageDate.getUTCMonth() == dCellDate.getUTCMonth() &&
this.pageDate.getUTCFullYear() == dCellDate.getUTCFullYear()) {
YAHOO.util.Dom.removeClass(cell, this.Style.CSS_CELL_SELECTED);
}

this.selectedDates.splice(cellDateIndex, 1);
}


if (this.parent) {
this.parent.sync(this);
}

this.onDeselect(selectDate);
return this.getSelectedDates();
};

/**
* Deselects all dates on the current calendar.
* @returnArray of JavaScript Date objects representing all individual dates that are currently selected.
*Assuming that this function executes properly, the return value should be an empty array.
*However, the empty array is returned for the sake of being able to check the selection status
*of the calendar.
* @type Date[]
*/
YAHOO.widget.Calendar_Core.prototype.deselectAll = function() {
this.onBeforeDeselect();
var count = this.selectedDates.length;
var sel = this.selectedDates.concat();
this.selectedDates.length = 0;

if (this.parent) {
this.parent.sync(this);
}

if (count > 0) {
this.onDeselect(sel);
}

return this.getSelectedDates();
};
/************* END SELECTION METHODS *************************************************************/


/************* BEGIN TYPE CONVERSION METHODS ****************************************************/

/**
* Converts a date (either a JavaScript Date object, or a date string) to the internal data structure
* used to represent dates: [[yyyy,mm,dd],[yyyy,mm,dd]].
* @private
* @param{String/Date/Date[]}dateThe date string of dates to deselect in the current calendar. Valid formats are
*individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
*Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
*This method can also take a JavaScript Date object or an array of Date objects.
* @returnArray of date field arrays
* @type Array[](Integer[])
*/
YAHOO.widget.Calendar_Core.prototype._toFieldArray = function(date) {
var returnDate = new Array();

if (date instanceof Date) {
returnDate = [[date.getUTCFullYear(), date.getUTCMonth()+1, date.getUTCDate()]];
} else if (typeof date == 'string') {
returnDate = this._parseDates(date);
} else if (date instanceof Array) {
for (var i=0;i<date.length;++i) {
var d = date[i];
returnDate[returnDate.length] = [d.getUTCFullYear(),d.getUTCMonth()+1,d.getUTCDate()];
}
}

return returnDate;
};

/**
* Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
* @private
* @param{Integer[]}dateFieldArrayThe date field array to convert to a JavaScript Date.
* @returnJavaScript Date object representing the date field array
* @type Date
*/
YAHOO.widget.Calendar_Core.prototype._toDate = function(dateFieldArray) {
if (dateFieldArray instanceof Date) {
return dateFieldArray;
} else {
return new Date(Date.UTC(dateFieldArray[0],dateFieldArray[1]-1,dateFieldArray[2]));
}
};
/************* END TYPE CONVERSION METHODS ******************************************************/


/************* BEGIN UTILITY METHODS ****************************************************/
/**
* Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
* @private
* @param{Integer[]}array1The first date field array to compare
* @param{Integer[]}array2The first date field array to compare
* @returnThe boolean that represents the equality of the two arrays
* @type Boolean
*/
YAHOO.widget.Calendar_Core.prototype._fieldArraysAreEqual = function(array1, array2) {
var match = false;

if (array1[0]==array2[0]&&array1[1]==array2[1]&&array1[2]==array2[2]) {
match=true;
}

return match;
};

/**
* Gets the index of a date field array [yyyy,mm,dd] in the current list of selected dates.
* @private
* @param{Integer[]}findThe date field array to search for
* @returnThe index of the date field array within the collection of selected dates.
*-1 will be returned if the date is not found.
* @type Integer
*/
YAHOO.widget.Calendar_Core.prototype._indexOfSelectedFieldArray = function(find) {
var selected = -1;

for (var s=0;s<this.selectedDates.length;++s) {
var sArray = this.selectedDates[s];
if (find[0]==sArray[0]&&find[1]==sArray[1]&&find[2]==sArray[2]) {
selected = s;
break;
}
}

return selected;
};

/**
* Determines whether a given date is OOM (out of month).
* @param{Date}dateThe JavaScript Date object for which to check the OOM status
* @return{Boolean}true if the date is OOM
*/
YAHOO.widget.Calendar_Core.prototype.isDateOOM = function(date) {
var isOOM = false;
if (date.getUTCMonth() != this.pageDate.getUTCMonth()) {
isOOM = true;
}
return isOOM;
};

/************* END UTILITY METHODS *******************************************************/

/************* BEGIN EVENT HANDLERS ******************************************************/

/**
* Event executed before a date is selected in the calendar widget.
*/
YAHOO.widget.Calendar_Core.prototype.onBeforeSelect = function() {
if (! this.Options.MULTI_SELECT) {
this.clearAllBodyCellStyles(this.Style.CSS_CELL_SELECTED);
this.deselectAll();
}
};

/**
* Event executed when a date is selected in the calendar widget.
* @param{Array}selectedAn array of date field arrays representing which date or dates were selected. Example: [ [2006,8,6],[2006,8,7],[2006,8,8] ]
*/
YAHOO.widget.Calendar_Core.prototype.onSelect = function(selected) { };

/**
* Event executed before a date is deselected in the calendar widget.
*/
YAHOO.widget.Calendar_Core.prototype.onBeforeDeselect = function() { };

/**
* Event executed when a date is deselected in the calendar widget.
* @param{Array}selectedAn array of date field arrays representing which date or dates were deselected. Example: [ [2006,8,6],[2006,8,7],[2006,8,8] ]
*/
YAHOO.widget.Calendar_Core.prototype.onDeselect = function(deselected) { };

/**
* Event executed when the user navigates to a different calendar page.
*/
YAHOO.widget.Calendar_Core.prototype.onChangePage = function() {
var me = this;

this.renderHeader();
if (this.renderProcId) {
clearTimeout(this.renderProcId);
}
this.renderProcId = setTimeout(function() {
me.render();
me.renderProcId = null;
}, 1);
};

/**
* Event executed when the calendar widget is rendered.
*/
YAHOO.widget.Calendar_Core.prototype.onRender = function() { };

/**
* Event executed when the calendar widget is reset to its original state.
*/
YAHOO.widget.Calendar_Core.prototype.onReset = function() { this.render(); };

/**
* Event executed when the calendar widget is completely cleared to the current month with no selections.
*/
YAHOO.widget.Calendar_Core.prototype.onClear = function() { this.render(); };

/**
* Validates the calendar widget. This method has no default implementation
* and must be extended by subclassing the widget.
* @returnShould return true if the widget validates, and false if
* it doesn't.
* @type Boolean
*/
YAHOO.widget.Calendar_Core.prototype.validate = function() { return true; };

/************* END EVENT HANDLERS *********************************************************/


/************* BEGIN DATE PARSE METHODS ***************************************************/


/**
* Converts a date string to a date field array
* @private
* @param{String}sDateDate string. Valid formats are mm/dd and mm/dd/yyyy.
* @returnA date field array representing the string passed to the method
* @type Array[](Integer[])
*/
YAHOO.widget.Calendar_Core.prototype._parseDate = function(sDate) {
var aDate = sDate.split(this.Locale.DATE_FIELD_DELIMITER);
var rArray;

if (aDate.length == 2) {
rArray = [aDate[this.Locale.MD_MONTH_POSITION-1],aDate[this.Locale.MD_DAY_POSITION-1]];
rArray.type = YAHOO.widget.Calendar_Core.MONTH_DAY;
} else {
rArray = [aDate[this.Locale.MDY_YEAR_POSITION-1],aDate[this.Locale.MDY_MONTH_POSITION-1],aDate[this.Locale.MDY_DAY_POSITION-1]];
rArray.type = YAHOO.widget.Calendar_Core.DATE;
}
return rArray;
};

/**
* Converts a multi or single-date string to an array of date field arrays
* @private
* @param{String}sDatesDate string with one or more comma-delimited dates. Valid formats are mm/dd, mm/dd/yyyy, mm/dd/yyyy-mm/dd/yyyy
* @returnAn array of date field arrays
* @type Array[](Integer[])
*/
YAHOO.widget.Calendar_Core.prototype._parseDates = function(sDates) {
var aReturn = new Array();
var aDates = sDates.split(this.Locale.DATE_DELIMITER);

for (var d=0;d<aDates.length;++d) {
var sDate = aDates[d];

if (sDate.indexOf(this.Locale.DATE_RANGE_DELIMITER) != -1) {
// This is a range
var aRange = sDate.split(this.Locale.DATE_RANGE_DELIMITER);

var dateStart = this._parseDate(aRange[0]);
var dateEnd = this._parseDate(aRange[1]);

var fullRange = this._parseRange(dateStart, dateEnd);
aReturn = aReturn.concat(fullRange);
} else {
// This is not a range
var aDate = this._parseDate(sDate);
aReturn.push(aDate);
}
}
return aReturn;
};

/**
* Converts a date range to the full list of included dates
* @private
* @param{Integer[]}startDateDate field array representing the first date in the range
* @param{Integer[]}endDateDate field array representing the last date in the range
* @returnAn array of date field arrays
* @type Array[](Integer[])
*/
YAHOO.widget.Calendar_Core.prototype._parseRange = function(startDate, endDate) {
var dStart = new Date(Date.UTC(startDate[0],startDate[1]-1,startDate[2]));
var dCurrent = YAHOO.widget.DateMath.add(new Date(Date.UTC(startDate[0],startDate[1]-1,startDate[2])),YAHOO.widget.DateMath.DAY,1);
var dEnd = new Date(Date.UTC(endDate[0],endDate[1]-1,endDate[2]));
 
var results = new Array();
results.push(startDate);
while (Date.UTC(dCurrent) <= Date.UTC(dEnd)) {
results.push([dCurrent.getUTCFullYear(),dCurrent.getUTCMonth()+1,dCurrent.getUTCDate()]);
dCurrent = YAHOO.widget.DateMath.add(dCurrent,YAHOO.widget.DateMath.DAY,1);
}
return results;
};

/************* END DATE PARSE METHODS *****************************************************/

/************* BEGIN RENDERER METHODS *****************************************************/

/**
* Resets the render stack of the current calendar to its original pre-render value.
*/
YAHOO.widget.Calendar_Core.prototype.resetRenderers = function() {
this.renderStack = this._renderStack.concat();
};

/**
* Clears the inner HTML, CSS class and style information from the specified cell.
* @param{HTMLTableCellElement}The cell to clear
*/ 
YAHOO.widget.Calendar_Core.prototype.clearElement = function(cell) {
cell.innerHTML = "&nbsp;";
cell.className="";
};

/**
* Adds a renderer to the render stack. The function reference passed to this method will be executed
* when a date cell matches the conditions specified in the date string for this renderer.
* @param{String}sDatesA date string to associate with the specified renderer. Valid formats
*include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
* @param{Function}fnRenderThe function executed to render cells that match the render rules for this renderer.
*/
YAHOO.widget.Calendar_Core.prototype.addRenderer = function(sDates, fnRender) {
var aDates = this._parseDates(sDates);
for (var i=0;i<aDates.length;++i) {
var aDate = aDates[i];
if (aDate.length == 2) { // this is either a range or a month/day combo
if (aDate[0] instanceof Array) { // this is a range
this._addRenderer(YAHOO.widget.Calendar_Core.RANGE,aDate,fnRender);
} else { // this is a month/day combo
this._addRenderer(YAHOO.widget.Calendar_Core.MONTH_DAY,aDate,fnRender);
}
} else if (aDate.length == 3) {
this._addRenderer(YAHOO.widget.Calendar_Core.DATE,aDate,fnRender);
}
}
};

/**
* The private method used for adding cell renderers to the local render stack.
* This method is called by other methods that set the renderer type prior to the method call.
* @private
* @param{String}typeThe type string that indicates the type of date renderer being added.
*Values are YAHOO.widget.Calendar_Core.DATE, YAHOO.widget.Calendar_Core.MONTH_DAY, YAHOO.widget.Calendar_Core.WEEKDAY,
*YAHOO.widget.Calendar_Core.RANGE, YAHOO.widget.Calendar_Core.MONTH
* @param{Array}aDatesAn array of dates used to construct the renderer. The format varies based
*on the renderer type
* @param{Function}fnRenderThe function executed to render cells that match the render rules for this renderer.
*/
YAHOO.widget.Calendar_Core.prototype._addRenderer = function(type, aDates, fnRender) {
var add = [type,aDates,fnRender];
this.renderStack.unshift(add);

this._renderStack = this.renderStack.concat();
};

/**
* Adds a month to the render stack. The function reference passed to this method will be executed
* when a date cell matches the month passed to this method.
* @param{Integer}monthThe month (1-12) to associate with this renderer
* @param{Function}fnRenderThe function executed to render cells that match the render rules for this renderer.
*/
YAHOO.widget.Calendar_Core.prototype.addMonthRenderer = function(month, fnRender) {
this._addRenderer(YAHOO.widget.Calendar_Core.MONTH,[month],fnRender);
};

/**
* Adds a weekday to the render stack. The function reference passed to this method will be executed
* when a date cell matches the weekday passed to this method.
* @param{Integer}weekayThe weekday (1-7) to associate with this renderer
* @param{Function}fnRenderThe function executed to render cells that match the render rules for this renderer.
*/
YAHOO.widget.Calendar_Core.prototype.addWeekdayRenderer = function(weekday, fnRender) {
this._addRenderer(YAHOO.widget.Calendar_Core.WEEKDAY,[weekday],fnRender);
};
//// END RENDERER METHODS ////

//// BEGIN CSS METHODS ////

/**
* Sets the specified array of CSS classes into the referenced element
* @param{HTMLElement}elementThe element to set the CSS classes into
* @param{String[]}aStylesAn array of CSS class names
*/
YAHOO.widget.Calendar_Core.setCssClasses = function(element, aStyles) {
element.className = "";
var className = aStyles.join(" ");
element.className = className;
};

/**
* Removes all styles from all body cells in the current calendar table.
* @param{style}The CSS class name to remove from all calendar body cells
*/
YAHOO.widget.Calendar_Core.prototype.clearAllBodyCellStyles = function(style) {
for (var c=0;c<this.cells.length;++c) {
YAHOO.util.Dom.removeClass(this.cells[c],style);
}
};

//// END CSS METHODS ////

//// BEGIN GETTER/SETTER METHODS ////
/**
* Sets the calendar's month explicitly.
* @param {Integer}monthThe numeric month, from 1 (January) to 12 (December)
*/
YAHOO.widget.Calendar_Core.prototype.setUTCMonth = function(month) {
this.pageDate.setUTCMonth(month);
};

/**
* Sets the calendar's year explicitly.
* @param {Integer}yearThe numeric 4-digit year
*/
YAHOO.widget.Calendar_Core.prototype.setYear = function(year) {
this.pageDate.setUTCFullYear(year);
};

/**
* Gets the list of currently selected dates from the calendar.
* @returnAn array of currently selected JavaScript Date objects.
* @type Date[]
*/
YAHOO.widget.Calendar_Core.prototype.getSelectedDates = function() {
var returnDates = new Array();

for (var d=0;d<this.selectedDates.length;++d) {
var dateArray = this.selectedDates[d];

var date = new Date(Date.UTC(dateArray[0],dateArray[1]-1,dateArray[2]));
returnDates.push(date);
}

returnDates.sort();
return returnDates;
};

/// END GETTER/SETTER METHODS ///

/**
* Returns a string representing the current browser.
* @type String
*/
YAHOO.widget.Calendar_Core._getBrowser = function() {
/**
 * UserAgent
 * @private
 * @type String
 */
var ua = navigator.userAgent.toLowerCase();

if (ua.indexOf('opera')!=-1) // Opera (check first in case of spoof)
 return 'opera';
else if (ua.indexOf('msie')!=-1) // IE
 return 'ie';
else if (ua.indexOf('safari')!=-1) // Safari (check before Gecko because it includes "like Gecko")
 return 'safari';
else if (ua.indexOf('gecko') != -1) // Gecko
 return 'gecko';
 else
return false;
}

/**
* Returns a string representation of the object.
* @type string
*/
YAHOO.widget.Calendar_Core.prototype.toString = function() {
return "Calendar_Core " + this.id;
}

YAHOO.widget.Cal_Core = YAHOO.widget.Calendar_Core;
/**
* Calendar is the default implementation of the YAHOO.widget.Calendar_Core base class.
* This class is the UED-approved version of the calendar selector widget. For all documentation
* on the implemented methods listed here, see the documentation for YAHOO.widget.Calendar_Core.
* @constructor
* @param {String}idThe id of the table element that will represent the calendar widget
* @param {String}containerIdThe id of the container element that will contain the calendar table
* @param {String}monthyearThe month/year string used to set the current calendar page
* @param {String}selectedA string of date values formatted using the date parser. The built-in
default date format is MM/DD/YYYY. Ranges are defined using
MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
Any combination of these can be combined by delimiting the string with
commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
* @extends YAHOO.widget.Calendar_Core
*/
YAHOO.widget.Calendar = function(id, containerId, monthyear, selected) {
if (arguments.length > 0) {
this.init(id, containerId, monthyear, selected);
}
}

YAHOO.widget.Calendar.prototype = new YAHOO.widget.Calendar_Core();

YAHOO.widget.Calendar.prototype.buildShell = function() {
this.border = document.createElement("DIV");
this.border.className =this.Style.CSS_CONTAINER;

this.table = document.createElement("TABLE");
this.table.cellSpacing = 0;

YAHOO.widget.Calendar_Core.setCssClasses(this.table, [this.Style.CSS_CALENDAR]);

this.border.id = this.id;

this.buildShellHeader();
this.buildShellBody();
this.buildShellFooter();
};

YAHOO.widget.Calendar.prototype.renderShell = function() {
this.border.appendChild(this.table);
this.oDomContainer.appendChild(this.border);
this.shellRendered = true;
};

YAHOO.widget.Calendar.prototype.renderHeader = function() {
this.headerCell.innerHTML = "";

var headerContainer = document.createElement("DIV");
headerContainer.className = this.Style.CSS_HEADER;

if (this.linkLeft) {
YAHOO.util.Event.removeListener(this.linkLeft, "mousedown", this.previousMonth);
}
this.linkLeft = document.createElement("A");
this.linkLeft.innerHTML = "&nbsp;";
YAHOO.util.Event.addListener(this.linkLeft, "mousedown", this.previousMonth, this, true);
//this.linkLeft.style.backgroundImage ="url(" + this.Options.NAV_ARROW_LEFT + ")";
this.linkLeft.className = this.Style.CSS_NAV_LEFT;

if (this.linkRight) {
YAHOO.util.Event.removeListener(this.linkRight, "mousedown", this.nextMonth);
}
this.linkRight = document.createElement("A");
this.linkRight.innerHTML = "&nbsp;";
YAHOO.util.Event.addListener(this.linkRight, "mousedown", this.nextMonth, this, true);
//this.linkRight.style.backgroundImage = "url(" + this.Options.NAV_ARROW_RIGHT + ")";/* remove for wct use */
this.linkRight.className = this.Style.CSS_NAV_RIGHT;

headerContainer.appendChild(this.linkLeft);
headerContainer.appendChild(document.createTextNode(this.buildMonthLabel()));
headerContainer.appendChild(this.linkRight);

this.headerCell.appendChild(headerContainer);
};

YAHOO.widget.Cal = YAHOO.widget.Calendar;
/**
* <p>YAHOO.widget.CalendarGroup is a special container class for YAHOO.widget.Calendar_Core. This class facilitates
* the ability to have multi-page calendar views that share a single dataset and are
* dependent on each other.</p>
* 
* <p>The calendar group instance will refer to each of its elements using a 0-based index.
* For example, to construct the placeholder for a calendar group widget with id "cal1" and
* containerId of "cal1Container", the markup would be as follows:
*<xmp>
*<div id="cal1Container_0"></div>
*<div id="cal1Container_1"></div>
*</xmp>
* The tables for the calendars ("cal1_0" and "cal1_1") will be inserted into those containers.
* </p>
* @constructor
* @param {Integer}pageCountThe number of pages that this calendar should display.
* @param {String}idThe id of the element that will be inserted into the DOM.
* @param {String}containerIdThe id of the container element that the calendar will be inserted into.
* @param {String}monthyearThe month/year string used to set the current calendar page
* @param {String}selectedA string of date values formatted using the date parser. The built-in
default date format is MM/DD/YYYY. Ranges are defined using
MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
Any combination of these can be combined by delimiting the string with
commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
*/
YAHOO.widget.CalendarGroup = function(pageCount, id, containerId, monthyear, selected) {
if (arguments.length > 0) {
this.init(pageCount, id, containerId, monthyear, selected);
}
}

/**
* Initializes the calendar group. All subclasses must call this method in order for the
* group to be initialized properly.
* @param {Integer}pageCountThe number of pages that this calendar should display.
* @param {String}idThe id of the element that will be inserted into the DOM.
* @param {String}containerIdThe id of the container element that the calendar will be inserted into.
* @param {String}monthyearThe month/year string used to set the current calendar page
* @param {String}selectedA string of date values formatted using the date parser. The built-in
default date format is MM/DD/YYYY. Ranges are defined using
MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
Any combination of these can be combined by delimiting the string with
commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
*/
YAHOO.widget.CalendarGroup.prototype.init = function(pageCount, id, containerId, monthyear, selected) {
this.id = id;
this.selectedDates = new Array();
this.containerId = containerId;

this.pageCount = pageCount;

this.pages = new Array();

for (var p=0;p<pageCount;++p) {
var cal = this.constructChild(id + "_" + p, this.containerId + "_" + p , monthyear, selected);

cal.parent = this;

cal.index = p;

cal.pageDate.setUTCMonth(cal.pageDate.getUTCMonth()+p);
cal._pageDate = new Date(Date.UTC(cal.pageDate.getUTCFullYear(),cal.pageDate.getUTCMonth(),cal.pageDate.getUTCDate()));
this.pages.push(cal);
}

this.sync();

this.doNextMonth = function(e, calGroup) {
calGroup.nextMonth();
};

this.doPreviousMonth = function(e, calGroup) {
calGroup.previousMonth();
};
};

/**
* Adds a function to all child Calendars within this CalendarGroup.
* @param {String}fnNameThe name of the function
* @param {Function}fnThe function to apply to each Calendar page object
*/
YAHOO.widget.CalendarGroup.prototype.setChildFunction = function(fnName, fn) {
for (var p=0;p<this.pageCount;++p) {
this.pages[p][fnName] = fn;
}
}

/**
* Calls a function within all child Calendars within this CalendarGroup.
* @param {String}fnNameThe name of the function
* @param {Array}argsThe arguments to pass to the function
*/
YAHOO.widget.CalendarGroup.prototype.callChildFunction = function(fnName, args) {
for (var p=0;p<this.pageCount;++p) {
var page = this.pages[p];
if (page[fnName]) {
var fn = page[fnName];
fn.call(page, args);
}
}
}

/**
* Constructs a child calendar. This method can be overridden if a subclassed version of the default
* calendar is to be used.
* @param {String}idThe id of the element that will be inserted into the DOM.
* @param {String}containerIdThe id of the container element that the calendar will be inserted into.
* @param {String}monthyearThe month/year string used to set the current calendar page
* @param {String}selectedA string of date values formatted using the date parser. The built-in
default date format is MM/DD/YYYY. Ranges are defined using
MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
Any combination of these can be combined by delimiting the string with
commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
* @returnThe YAHOO.widget.Calendar_Core instance that is constructed
* @type YAHOO.widget.Calendar_Core
*/
YAHOO.widget.CalendarGroup.prototype.constructChild = function(id,containerId,monthyear,selected) {
return new YAHOO.widget.Calendar_Core(id,containerId,monthyear,selected);
};


/**
* Sets the calendar group's month explicitly. This month will be set into the first
* page of the multi-page calendar, and all other months will be iterated appropriately.
* @param {Integer}monthThe numeric month, from 1 (January) to 12 (December)
*/
YAHOO.widget.CalendarGroup.prototype.setUTCMonth = function(month) {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.setUTCMonth(month+p);
}
};

/**
* Sets the calendar group's minimum month. 
* @param {Date()}monthThe numeric month, from 1 (January) to 12 (December)
*/
YAHOO.widget.CalendarGroup.prototype.minDate = function(date) {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.minDate=date;
}
};


/**
* Sets the calendar group's year explicitly. This year will be set into the first
* page of the multi-page calendar, and all other months will be iterated appropriately.
* @param {Integer}yearThe numeric 4-digit year
*/
YAHOO.widget.CalendarGroup.prototype.setYear = function(year) {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
if ((cal.pageDate.getUTCMonth()+1) == 1 && p>0)
{
year+=1;
}
cal.setYear(year);
}
};

/**
* Calls the render function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.render = function() {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.render();
}
};

/**
* Calls the select function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.select = function(date) {
var ret;
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
ret = cal.select(date);
}
return ret;
};

/**
* Calls the selectCell function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.selectCell = function(cellIndex) {
var ret;
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
ret = cal.selectCell(cellIndex);
}
return ret;
};

/**
* Calls the deselect function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.deselect = function(date) {
var ret;
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
ret = cal.deselect(date);
}
return ret;
};

/**
* Calls the deselectAll function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.deselectAll = function() {
var ret;
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
ret = cal.deselectAll();
}
return ret;
};

/**
* Calls the deselectAll function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.deselectCell = function(cellIndex) {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.deselectCell(cellIndex);
}
return this.getSelectedDates();
};

/**
* Calls the reset function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.reset = function() {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.reset();
}
};

/**
* Calls the clear function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.clear = function() {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.clear();
}
};

/**
* Calls the nextMonth function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.nextMonth = function() {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.nextMonth();
}
};

/**
* Calls the previousMonth function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.previousMonth = function() {
for (var p=this.pages.length-1;p>=0;--p)
{
var cal = this.pages[p];
cal.previousMonth();
}
};

/**
* Calls the nextYear function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.nextYear = function() {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.nextYear();
}
};

/**
* Calls the previousYear function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.previousYear = function() {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.previousYear();
}
};

/**
* Synchronizes the data values for all child calendars within the group. If the sync
* method is called passing in the caller object, the values of all children will be set
* to the values of the caller. If the argument is ommitted, the values from all children
* will be combined into one distinct list and set into each child.
* @param{YAHOO.widget.Calendar_Core}callerThe YAHOO.widget.Calendar_Core that is initiating the call to sync().
* @returnArray of selected dates, in JavaScript Date object form.
* @type Date[]
*/
YAHOO.widget.CalendarGroup.prototype.sync = function(caller) {
var calendar;

if (caller)
{
this.selectedDates = caller.selectedDates.concat();
} else {
var hash = new Object();
var combinedDates = new Array();

for (var p=0;p<this.pages.length;++p)
{
calendar = this.pages[p];

var values = calendar.selectedDates;

for (var v=0;v<values.length;++v)
{
var valueArray = values[v];
hash[valueArray.toString()] = valueArray;
}
}

for (var val in hash)
{
combinedDates[combinedDates.length]=hash[val];
}

this.selectedDates = combinedDates.concat();
}

// Set all the values into the children
for (p=0;p<this.pages.length;++p)
{
calendar = this.pages[p];
if (! calendar.Options.MULTI_SELECT) {
calendar.clearAllBodyCellStyles(calendar.Config.Style.CSS_CELL_SELECTED);
}
calendar.selectedDates = this.selectedDates.concat();

}

return this.getSelectedDates();
};

/**
* Gets the list of currently selected dates from the calendar.
* @returnAn array of currently selected JavaScript Date objects.
* @type Date[]
*/
YAHOO.widget.CalendarGroup.prototype.getSelectedDates = function() { 
var returnDates = new Array();

for (var d=0;d<this.selectedDates.length;++d)
{
var dateArray = this.selectedDates[d];

var date = new Date(Date.UTC(dateArray[0],dateArray[1]-1,dateArray[2]));
returnDates.push(date);
}

returnDates.sort();
return returnDates;
};

/**
* Calls the addRenderer function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.addRenderer = function(sDates, fnRender) {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.addRenderer(sDates, fnRender);
}
};


/**
* Calls the addMonthRenderer function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.addMonthRenderer = function(month, fnRender) {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.addMonthRenderer(month, fnRender);
}
};

/**
* Calls the addWeekdayRenderer function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.addWeekdayRenderer = function(weekday, fnRender) {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.addWeekdayRenderer(weekday, fnRender);
}
};

/**
* Sets an event handler universally across all child calendars within the group. For instance,
* to set the onSelect handler for all child calendars to a function called fnSelect, the call would be:
* <code>
* calGroup.wireEvent("onSelect", fnSelect);
* </code>
* @param{String}eventNameThe name of the event to handler to set within all child calendars.
* @param{Function}fnThe function to set into the specified event handler.
*/
YAHOO.widget.CalendarGroup.prototype.wireEvent = function(eventName, fn) {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal[eventName] = fn;
}
};

/**
* Returns a string representation of the object.
* @type string
*/ 
YAHOO.widget.CalendarGroup.prototype.toString = function() {
return "CalendarGroup " + this.id;
}

YAHOO.widget.CalGrp = YAHOO.widget.CalendarGroup;
/**
* Calendar2up_Cal is the default implementation of the Calendar_Core base class, when used
* in a 2-up view. This class is the UED-approved version of the calendar selector widget. For all documentation
* on the implemented methods listed here, see the documentation for Calendar_Core. This class
* has some special attributes that only apply to calendars rendered within the calendar group implementation. 
* There should be no reason to instantiate this class directly.
* @constructor
* @param {String}idThe id of the table element that will represent the calendar widget
* @param {String}containerIdThe id of the container element that will contain the calendar table
* @param {String}monthyearThe month/year string used to set the current calendar page
* @param {String}selectedA string of date values formatted using the date parser. The built-in
default date format is MM/DD/YYYY. Ranges are defined using
MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
Any combination of these can be combined by delimiting the string with
commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
* @extends YAHOO.widget.Calendar_Core
*/
YAHOO.widget.Calendar2up_Cal = function(id, containerId, monthyear, selected) {
if (arguments.length > 0)
{
this.init(id, containerId, monthyear, selected);
}
}

YAHOO.widget.Calendar2up_Cal.prototype = new YAHOO.widget.Calendar_Core();

/**
* Renders the header for each individual calendar in the 2-up view. More
* specifically, this method handles the placement of left and right arrows for
* navigating between calendar pages.
*/
YAHOO.widget.Calendar2up_Cal.prototype.renderHeader = function() {
this.headerCell.innerHTML = "";

var headerContainer = document.createElement("DIV");
headerContainer.className = this.Style.CSS_HEADER;

if (this.index == 0) {

if (this.linkLeft) {
YAHOO.util.Event.removeListener(this.linkLeft, "mousedown", this.parent.doPreviousMonth);
}
this.linkLeft = document.createElement("A");
this.linkLeft.innerHTML = "&nbsp;";
// this.linkLeft.style.backgroundImage ="url(" + this.Options.NAV_ARROW_LEFT + ")"; /* remove for wct use */
this.linkLeft.className = this.Style.CSS_NAV_LEFT;

YAHOO.util.Event.addListener(this.linkLeft, "mousedown", this.parent.doPreviousMonth, this.parent);
headerContainer.appendChild(this.linkLeft);
}

headerContainer.appendChild(document.createTextNode(this.buildMonthLabel()));

if (this.index == 1) {

if (this.linkRight) {
YAHOO.util.Event.removeListener(this.linkRight, "mousedown", this.parent.doNextMonth);
}
this.linkRight = document.createElement("A");
this.linkRight.innerHTML = "&nbsp;";
// this.linkRight.style.backgroundImage = "url(" + this.Options.NAV_ARROW_RIGHT + ")"; /* remove for WCT use */
this.linkRight.className = this.Style.CSS_NAV_RIGHT;

YAHOO.util.Event.addListener(this.linkRight, "mousedown", this.parent.doNextMonth, this.parent);
headerContainer.appendChild(this.linkRight);
}

this.headerCell.appendChild(headerContainer);
};




/**
* Calendar2up is the default implementation of the CalendarGroup base class, when used
* in a 2-up view. This class is the UED-approved version of the 2-up calendar selector widget. For all documentation
* on the implemented methods listed here, see the documentation for CalendarGroup. 
* @constructor
* @param {String}idThe id of the table element that will represent the calendar widget
* @param {String}containerIdThe id of the container element that will contain the calendar table
* @param {String}monthyearThe month/year string used to set the current calendar page
* @param {String}selectedA string of date values formatted using the date parser. The built-in
default date format is MM/DD/YYYY. Ranges are defined using
MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
Any combination of these can be combined by delimiting the string with
commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
* @extends YAHOO.widget.CalendarGroup
*/
YAHOO.widget.Calendar2up = function(id, containerId, monthyear, selected) {
if (arguments.length > 0)
{
this.buildWrapper(containerId);
this.init(2, id, containerId, monthyear, selected);
}
}

YAHOO.widget.Calendar2up.prototype = new YAHOO.widget.CalendarGroup();

/**
* CSS class representing the wrapper for the 2-up calendar
* @type string
*/
YAHOO.widget.Calendar2up.CSS_2UPWRAPPER = "cal2upwrapper";

/**
* CSS class representing the container for the calendar
* @type string
*/
YAHOO.widget.Calendar2up.CSS_CONTAINER = "calcontainer";

/**
* CSS class representing the container for the 2-up calendar
* @type string
*/
YAHOO.widget.Calendar2up.CSS_2UPCONTAINER = "cal2up";

/**
* CSS class representing the title for the 2-up calendar
* @type string
*/
YAHOO.widget.Calendar2up.CSS_2UPTITLE = "title";

/**
* CSS class representing the close icon for the 2-up calendar
* @type string
*/
YAHOO.widget.Calendar2up.CSS_2UPCLOSE = "close-icon";

/**
* Implementation of CalendarGroup.constructChild that ensures that child calendars of 
* Calendar2up will be of type Calendar2up_Cal.
*/
YAHOO.widget.Calendar2up.prototype.constructChild = function(id,containerId,monthyear,selected) {
var cal = new YAHOO.widget.Calendar2up_Cal(id,containerId,monthyear,selected);
return cal;
};

/**
* Builds the wrapper container for the 2-up calendar.
* @param {String} containerIdThe id of the outer container element.
*/
YAHOO.widget.Calendar2up.prototype.buildWrapper = function(containerId) {
var outerContainer = document.getElementById(containerId);

outerContainer.className = YAHOO.widget.Calendar2up.CSS_2UPWRAPPER;

var innerContainer = document.createElement("DIV");
innerContainer.className = YAHOO.widget.Calendar2up.CSS_CONTAINER;
innerContainer.id = containerId + "_inner";

var cal1Container = document.createElement("DIV");
cal1Container.id = containerId + "_0";
cal1Container.className = YAHOO.widget.Calendar2up.CSS_2UPCONTAINER;
cal1Container.style.marginRight = "10px";

var cal2Container = document.createElement("DIV");
cal2Container.id = containerId + "_1"; 
cal2Container.className = YAHOO.widget.Calendar2up.CSS_2UPCONTAINER;

outerContainer.appendChild(innerContainer);
innerContainer.appendChild(cal1Container);
innerContainer.appendChild(cal2Container);

this.innerContainer = innerContainer;
this.outerContainer = outerContainer;
}

/**
* Renders the 2-up calendar.
*/
YAHOO.widget.Calendar2up.prototype.render = function() {
this.renderHeader();
YAHOO.widget.CalendarGroup.prototype.render.call(this);
this.renderFooter();
};

/**
* Renders the header located at the top of the container for the 2-up calendar.
*/
YAHOO.widget.Calendar2up.prototype.renderHeader = function() {
if (! this.title) {
this.title = "";
}
if (! this.titleDiv)
{
this.titleDiv = document.createElement("DIV");
if (this.title == "")
{
this.titleDiv.style.display="none";
}
}

this.titleDiv.className = YAHOO.widget.Calendar2up.CSS_2UPTITLE;
this.titleDiv.innerHTML = this.title;

if (this.outerContainer.style.position == "absolute")
{
var linkClose = document.createElement("A");
linkClose.href = "javascript:void(null)";
YAHOO.util.Event.addListener(linkClose, "click", this.hide, this);

var imgClose = document.createElement("IMG");
imgClose.src = YAHOO.widget.Calendar_Core.IMG_ROOT + "/cal/2/images/x_d.gif";
imgClose.className = YAHOO.widget.Calendar2up.CSS_2UPCLOSE;

linkClose.appendChild(imgClose);

this.linkClose = linkClose;

this.titleDiv.appendChild(linkClose);
}

if (this.titleDiv != this.innerContainer.firstChild) {
this.innerContainer.insertBefore(this.titleDiv, this.innerContainer.firstChild);
}
}

/**
* Hides the 2-up calendar's outer container from view.
*/
YAHOO.widget.Calendar2up.prototype.hide = function(e, cal) {
if (! cal)
{
cal = this;
}
cal.outerContainer.style.display = "none";
}

/**
* Renders a footer for the 2-up calendar container. By default, this method is
* unimplemented.
*/
YAHOO.widget.Calendar2up.prototype.renderFooter = function() {}

YAHOO.widget.Cal2up = YAHOO.widget.Calendar2up;

/***************************** WCT custom function **************************/

YAHOO.widget.Calendar2up.prototype.setParent = function(p)
{
this.parentMain = p;// stores wct calendar reference
}

YAHOO.widget.Calendar.prototype.setParent = function(p)
{
this.parentMain = p;// stores wct calendar reference
}


/**
* Remove a renderer to the render stack. The function reference passed to this method will be executed
* when a date cell matches the conditions specified in the date string for this renderer.
* @param{String}sDatesA date string to associate with the specified renderer. Valid formats
*include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
* @param{Function}fnRenderThe function executed to render cells that match the render rules for this renderer.
*/

YAHOO.widget.Calendar_Core.prototype.removeRenderer = function(sDates, fnRender) {
var aDates = this._parseDates(sDates);
for (var i=0;i<aDates.length;++i) {
var aDate = aDates[i];

if (aDate.length == 2) { // this is either a range or a month/day combo
if (aDate[0] instanceof Array) { // this is a range
this._removeRenderer(YAHOO.widget.Calendar_Core.RANGE,aDate,fnRender);
} else { // this is a month/day combo
this._removeRenderer(YAHOO.widget.Calendar_Core.MONTH_DAY,aDate,fnRender);
}
} else if (aDate.length == 3) {
this._removeRenderer(YAHOO.widget.Calendar_Core.DATE,aDate,fnRender);
}
}
};

/**
* The private method used for remove cell renderers to the local render stack.
* This method is called by other methods that set the renderer type prior to the method call.
* @private
* @param{String}typeThe type string that indicates the type of date renderer being added.
*Values are YAHOO.widget.Calendar_Core.DATE, YAHOO.widget.Calendar_Core.MONTH_DAY, YAHOO.widget.Calendar_Core.WEEKDAY,
*YAHOO.widget.Calendar_Core.RANGE, YAHOO.widget.Calendar_Core.MONTH
* @param{Array}aDatesAn array of dates used to construct the renderer. The format varies based
*on the renderer type
* @param{Function}fnRenderThe function executed to render cells that match the render rules for this renderer.
*/
YAHOO.widget.Calendar_Core.prototype._removeRenderer = function(type, aDates, fnRender) {
var remove = [type,aDates,fnRender];
var searchIndex = this.renderStack.contains(remove);
if(searchIndex != "undefined")
{
this.renderStack.splice(searchIndex,1);
this._renderStack = this.renderStack.concat();
}
};

/**
* Calls the removeRenderer function of all child calendars within the group.
*/
YAHOO.widget.CalendarGroup.prototype.removeRenderer = function(sDates, fnRender) {
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.removeRenderer(sDates, fnRender);
}
};


YAHOO.widget.Calendar_Core.prototype.clearRenderer = function(){
delete this.renderStack;
delete this._renderStack;

this.renderStack = new Array();
this._renderStack = new Array();
}

YAHOO.widget.CalendarGroup.prototype.clearRenderer = function(){
for (var p=0;p<this.pages.length;++p)
{
var cal = this.pages[p];
cal.clearRenderer();
}
}

YAHOO.widget.Calendar_Core.prototype.isMaxDateReached = function(){
return (this.pageDate.getUTCTime() == this.maxDate.getUTCTime());
}

YAHOO.widget.CalendarGroup.prototype.isMaxDateReached = function(){
/*for (var p=0;p<this.pages.length;++p)
{ */
var cal = this.pages[1];
return cal.isMaxDateReached();
//}
}


/********************** Single calendar customization ******************************/
/* Begin Japanese 2up Calendar */

YAHOO.widget.Calendar_Itnl_Cal = function(id, containerId, monthyear, selected) {
if (arguments.length > 0)
{
this.init(id, containerId, monthyear, selected);
}
}

YAHOO.widget.Calendar_Itnl_Cal.prototype = new YAHOO.widget.Calendar();
/***************************** end WCT custom function **************************/


var WCT = {};
//WCT.calendar.returnDateField = "undefined";
returnDate= "undefined";
var over_cal = false;// variable to determine if mouse is over calendar container

// TYPE = doa for from-date (check-in), and dod for to-date (check-out)
WCT.calendar = function (id,container,parentContainerId,dateInput,type) 
{
if (arguments.length > 0)
{
var defaultSelectedDate = null;

if(type){
var calType = type.toLowerCase();
if(calType == "doa" || calType == "dod")
{
this.calType = calType;
}
}

if (parentContainerId)
{
if (typeof (parentContainerId) != "object")
{
this.parentContainerId = document.getElementById(parentContainerId);
}
else
{
this.parentContainerId = parentContainerId;
}
}

if (dateInput)
{
if (typeof (dateInput) != "object")
{
this.dateInput = document.getElementById(dateInput);
}
else
{
this.dateInput = dateInput;
}
}

this.today = new Date();

/* Begin Calendar Customization*/

YAHOO.widget.Calendar2up_CU_Cal = function(id, containerId, monthyear, selected) {
if (arguments.length > 0)
{
this.init(id, containerId, monthyear, selected);
}
}

YAHOO.widget.Calendar2up_CU_Cal.prototype = new YAHOO.widget.Calendar2up_Cal();

/*YAHOO.widget.Calendar2up_CU_Cal.prototype.customConfig = function() {
//this.Config.Options.START_WEEKDAY = 1;
}*/

/*************************************/

YAHOO.widget.Calendar2up_Itnl_Custom = function(id, containerId, monthyear, selected) {
if (arguments.length > 0)
{
this.buildWrapper(containerId);
this.init(2, id, containerId, monthyear, selected);
}
}

YAHOO.widget.Calendar2up_Itnl_Custom.prototype = new YAHOO.widget.Calendar2up();

YAHOO.widget.Calendar2up_Itnl_Custom.prototype.constructChild = function(id,containerId,monthyear,selected) {
var cal = new YAHOO.widget.Calendar2up_CU_Cal(id,containerId,monthyear,selected);
return cal;
};

/********************** Single calendar customization ******************************/
/* Begin Japanese 2up Calendar */
/*
YAHOO.widget.Calendar_Itnl_Cal = function(id, containerId, monthyear, selected) {
if (arguments.length > 0)
{
this.init(id, containerId, monthyear, selected);
}
}

YAHOO.widget.Calendar_Itnl_Cal.prototype = new YAHOO.widget.Calendar();

YAHOO.widget.Calendar_Itnl_Cal.prototype.customConfig = function() {
this.Config.Locale.MONTHS_SHORT = ["1\u6708", "2\u6708", "3\u6708", "4\u6708", "5\u6708", "6\u6708", "7\u6708", "8\u6708", "9\u6708", "10\u6708", "11\u6708", "12\u6708"];
this.Config.Locale.MONTHS_LONG = ["1\u6708", "2\u6708", "3\u6708", "4\u6708", "5\u6708", "6\u6708", "7\u6708", "8\u6708", "9\u6708", "10\u6708", "11\u6708", "12\u6708"];
this.Config.Locale.WEEKDAYS_1CHAR = ["\u65E5", "\u6708", "\u706B", "\u6C34", "\u6728", "\u91D1", "\u571F"];
this.Config.Locale.WEEKDAYS_SHORT = ["\u65E5", "\u6708", "\u706B", "\u6C34", "\u6728", "\u91D1", "\u571F"];
this.Config.Locale.WEEKDAYS_MEDIUM = ["\u65E5", "\u6708", "\u706B", "\u6C34", "\u6728", "\u91D1", "\u571F"];
this.Config.Locale.WEEKDAYS_LONG = ["\u65E5", "\u6708", "\u706B", "\u6C34", "\u6728", "\u91D1", "\u571F"];

this.Config.Options.START_WEEKDAY = 1;
}*/
/********************* End customization of 1 calendar*************/

//this.cal = new YAHOO.widget.Calendar2up_Custom("WCT.calendar."+id,container);
this.wctinit(id,container);
}
}

WCT.calendar.prototype.wctinit = function(id, container, monthyear, selected)
{
if(arguments.length >0)
{
var maxDate = YAHOO.widget.DateMath.add(new Date(), YAHOO.widget.DateMath.DAY, 330);
this.cal = new YAHOO.widget.Calendar_Itnl_Cal("WCT.calendar."+id,container);
this.calClass = this.cal.toString();
if(this.calClass.match("Calendar_Core"))
{
this.cal.minDate=new Date();
this.cal.maxDate=maxDate;
this.cal.onSelect = this.selectedDate;
}
else{
this.minDate(new Date());
this.maxDate(maxDate);
this.cal.setChildFunction("onSelect",this.selectedDate);
}

this.cal.setParent(this);
this.cal.render();
this.prevRenderer = new Array();
this.dateFormat = "US";
}
}

//WCT.calendar.prototype = new WCT.calendar();

WCT.calendar.prototype.showCalendar = function (hideCalendar,el){

// hideCalendar can be type of Array, single calendar object, or string.This is the calendar that you want to hide.
// displayCalendar is a single calendar object to display. 
if (typeof(hideCalendar.length) == "number")
{
var index = 0;
for (index=0; index <hideCalendar.length; index++)
{
var calObj;
if (typeof(hideCalendar[index]) == "object")
{
calObj = hideCalendar[index];
}
else if (typeof(hideCalendar[index]) == "string")
{
calObj = document.getElementById(hideCalendar[index]);
}
try
{
calObj.hideCal();
}
catch(e){};
}
}
else if (typeof(hideCalendar) == "object")
{
hideCalendar.hideCal();
}

if(el)
{

var pos = YAHOO.util.Dom.getXY(el);
returnDate=this.dateInput;

//pre-select the calendar if there is a date in the input field
if(this.dateInput.value.toLowerCase() != "mm/dd/aaaa" && this.dateInput.value.toLowerCase() != "mm/dd/yyyy" && (this.dateInput.value.toLowerCase() != "dd.mm.yyyy" && this.dateInput.value.toLowerCase() != "dd/mm/yyyy"))
{
if(!this.cal.getSelectedDates()[0])
{
var inputDateArray = this.dateInput.value.split('/');
var typedDate = new Date();
if(inputDateArray.length == 3)
{
if (isNaN(parseInt(inputDateArray[0])) || isNaN(parseInt(inputDateArray[1])) || isNaN(parseInt(inputDateArray[2])))
{
this.dateInput.value="Invalid Date";
this.dateInput.style.background = "#fff url(http://images.wctravel.com/images-general/warningBG.gif) 100% 50% no-repeat";
this.cal.render();
return false;
}
else{
this.dateInput.style.background = "";
if (this.dateFormat.toUpperCase() == "EU")
{
typedDate.setUTCFullYear(inputDateArray[2],inputDateArray[1]-1,inputDateArray[0]);
this.cal.setUTCMonth(inputDateArray[1]-1);
}
else
{
typedDate.setUTCFullYear(inputDateArray[2],inputDateArray[0]-1,inputDateArray[1]);
this.cal.setUTCMonth(inputDateArray[0]-1);
}
this.cal.select(typedDate);
this.cal.setYear(inputDateArray[2]);
}
this.cal.render();
}
}
}

this.highlightCell();

var ie=document.all
var ieNOTopera=document.all&&navigator.userAgent.indexOf("Opera")==-1
//getting current view port size
var dsoctop=ie? document.body.scrollTop : pageYOffset// amt of pixel from top of document to top of window.
var window_height=ieNOTopera? document.body.clientHeight : window.innerHeight// window height.

if (this.parentContainerId)
{
this.parentContainerId.style.display='block';
if(this.calendarPosition == 'relative')
{
var calendarHeight = this.parentContainerId.offsetHeight;
if(eval(parseInt(pos[1])+parseInt(el.offsetHeight+1)+calendarHeight) < eval(parseInt(dsoctop)+parseInt(window_height)))
YAHOO.util.Dom.setXY(this.parentContainerId, [pos[0],pos[1]+el.offsetHeight+1]);
else
{
YAHOO.util.Dom.setXY(this.parentContainerId, [pos[0],eval(pos[1]-calendarHeight-1)]);
}
}
}
else
{
this.outerContainer.style.display='block';
if(this.calendarPosition == 'relative')
{
var calendarHeight = this.parentContainerId.offsetHeight;
if(eval(parseInt(pos[1])+parseInt(el.offsetHeight+1)+calendarHeight) < eval(parseInt(dsoctop)+parseInt(window_height)))
YAHOO.util.Dom.setXY(this.outerContainer, [pos[0],pos[1]+el.offsetHeight+1]);
else
{
YAHOO.util.Dom.setXY(this.outerContainer, [pos[0],eval(pos[1]-calendarHeight-1)]);
}
}
}

// hide select box from appearing on top of calendar
if (YAHOO.util.Event.isIE) // boolean set if we're on IE
{
this.hiddenSelects = new Array();

// Get the rectangle occupied by this calendar.
if (this.parentContainerId)
{
var r = YAHOO.util.Dom.getRegion(this.parentContainerId);
}
else
{
var r = YAHOO.util.Dom.getRegion(this.outerContainer);
}

// Hide any selects which intersect
var selects = document.getElementsByTagName("select");
for (var i = 0; i < selects.length; i++)
{
if (r.intersect(YAHOO.util.Dom.getRegion(selects[i])))
{
selects[i].style.visibility = "hidden";
this.hiddenSelects.push(selects[i]);
}
}
}

// auto hide calendar if click out side of current date field
YAHOO.util.Event.addListener(el, 'blur', onUnfocus,this)
YAHOO.util.Event.addListener(this.parentContainerId, 'mouseover', overCal);
YAHOO.util.Event.addListener(this.parentContainerId, 'mouseout', outCal);
// attach onChange event to automatically update the calendar if user type in date
YAHOO.util.Event.addListener(this.dateInput, 'change', this.updateCalendar,this);
}
}

WCT.calendar.prototype.highlightCell = function()
{
if (this.linkedCal)
{
var linkC = this.linkedCal.getInternalCal();
var extSelectedDate = linkC.getSelectedDates()[0];
var selectedDate = this.cal.getSelectedDates()[0];
var adjustInvalidDate = 0;
if (extSelectedDate)
{
var month = extSelectedDate.getUTCMonth() +1;
var date= extSelectedDate.getUTCDate();
var year= extSelectedDate.getUTCFullYear();
var extdateString = month+"/"+ date+"/"+year;

if(extSelectedDate > this.cal.getSelectedDates()[0] && this.calType == "dod")
{// if external calendar date is greater than this date, default this to external calendar date +1
var newDate = YAHOO.widget.DateMath.add(extSelectedDate, YAHOO.widget.DateMath.DAY, 1);
this.cal.select(newDate);
selectedDate = this.cal.getSelectedDates()[0];
adjustInvalidDate = 1;
}

// clear previous calendar style
if(this.prevRenderer.length > 0)
{
this.cal.clearRenderer();
}

if(this.calType == "dod")
{
this.cal.setUTCMonth(month-1);
this.cal.setYear(year);
if(this.calClass.match("Calendar_Core"))
{
this.cal.minDate=extSelectedDate;
}
else
{
this.minDate(new Date());
}
}

/* Highlight a date range */
var dateStringRange;//valid format:12/1/2004-1/1/2005
if(selectedDate && adjustInvalidDate != 1)
{
var newHighlightDate;
var newHighlightDateExt;

// calculating date range 
// ie:currectDate - futureDate.The range will be (currectDate +1) - (futureDate-1)

if (this.calType == "doa" && selectedDate < extSelectedDate)
{// this.selected date - external cal selected date
newHighlightDate = YAHOO.widget.DateMath.add(selectedDate, YAHOO.widget.DateMath.DAY, 1);
newHighlightDateExt = YAHOO.widget.DateMath.subtract(extSelectedDate, YAHOO.widget.DateMath.DAY, 1);
}
else if (this.calType == "dod")
{
newHighlightDate = YAHOO.widget.DateMath.add(extSelectedDate, YAHOO.widget.DateMath.DAY, 1);
newHighlightDateExt = YAHOO.widget.DateMath.subtract(selectedDate, YAHOO.widget.DateMath.DAY, 1);

}

if (newHighlightDate && newHighlightDateExt)
{
var highlightMonth = newHighlightDate.getUTCMonth()+1;
var highlightDate= newHighlightDate.getUTCDate();
var highlightYear= newHighlightDate.getUTCFullYear();
var highLightDateStart = highlightMonth+"/"+highlightDate+"/"+highlightYear;
var highlightDateExtMonth = newHighlightDateExt.getUTCMonth()+1;
var highlightDateExtDate = newHighlightDateExt.getUTCDate();
var highlightDateExtYear = newHighlightDateExt.getUTCFullYear();
var highlightDateEnd = highlightDateExtMonth+"/"+highlightDateExtDate+"/"+highlightDateExtYear;
var dateStringRange = highLightDateStart+"-"+highlightDateEnd;

this.cal.addRenderer(dateStringRange,this.renderHighlight);
this.prevRenderer.push([dateStringRange,this.renderHighlight]);
}
}/* end Highlight a date range */
this.cal.addRenderer(extdateString, this.renderSelected);
this.prevRenderer.push([extdateString,this.renderSelected]);
if(selectedDate)
{
// default calendar view to the selected month
var currSelectedMonth = selectedDate.getUTCMonth();
var currSelectedYear= selectedDate.getUTCFullYear();
this.cal.setUTCMonth(currSelectedMonth);
this.cal.setYear(currSelectedYear);
}

this.cal.render();
}
}
}

WCT.calendar.prototype.getInternalCal = function()
{
return this.cal;
}

/* Localization setting
 Return selected date based on format 
 
 Param: USfor mm/dd/yyyy or
 EUfor dd/mm/yyyy
*/

WCT.calendar.prototype.returnDateFormat = function(dformat)
{
if (arguments.length > 0)
{
if(dformat.toUpperCase() == "US" || dformat.toUpperCase() == "EU")
{
this.dateFormat = dformat.toUpperCase();
}
}
else{
returnthis.dateFormat
}
}
 
WCT.calendar.prototype.selectedDate = function(selected)
{
var cal = this.parent;
var selectedCalDate=null;
if (cal)
{
selectedCalDate = cal.getSelectedDates()[0];
}
else
{
selectedCalDate = this.getSelectedDates()[0];
}
var month = selectedCalDate.getUTCMonth() +1;
var date= selectedCalDate.getUTCDate();
var year= selectedCalDate.getUTCFullYear();

if(month.toString().length < 2)
{
month = "0" + month + "";
}

if(date.toString().length < 2)
{
date = "0" + date + "";
}

if (cal) 
{
if (cal.parentMain.dateFormat.toUpperCase() == "EU")
{
returnDate.value = date +"."+ month+"."+ year;
}
else
{
returnDate.value = month+"/"+ date +"/"+ year;
}
cal.parentMain.dateInput.style.background = "";
cal.parentMain.hideCal();
}
else
{
if (this.parentMain.dateFormat.toUpperCase() == "EU")
{
returnDate.value = date +"."+ month+"."+ year;
}
else
{
returnDate.value = month+"/"+ date +"/"+ year;
}
this.parentMain.dateInput.style.background = "";
this.parentMain.hideCal();
}

}

WCT.calendar.prototype.hideCal = function() 
{
if(this.hiddenSelects)
{
if (YAHOO.util.Event.isIE)
{
for (var i = 0; i < this.hiddenSelects.length; i++)
this.hiddenSelects[i].style.visibility = "";
}
}


if (this.parentContainerId)
{
this.parentContainerId.style.display = "none";
}
else 
{
this.cal.hide();
}
}

WCT.calendar.prototype.addLinkedCal = function(linkedCal)
{
this.linkedCal = linkedCal;
}

WCT.calendar.prototype.renderSelected = function(workingDate, cell) {
YAHOO.util.Dom.addClass(cell, "selected");
}

WCT.calendar.prototype.renderHighlight = function(workingDate, cell) {
YAHOO.util.Dom.addClass(cell, "highlight");
}


WCT.calendar.prototype.previousMonth = function(){
this.cal.previousMonth()
}

WCT.calendar.prototype.nextMonth = function(){
this.cal.nextMonth()
}

WCT.calendar.prototype.getSelectedDates = function()
{
this.cal.getSelectedDates();
}

WCT.calendar.prototype.updateCalendar = function(e,obj)
{
var dstring = obj.dateInput.value;

if (dstring.length <1)
{
return false;
}
obj.cal.deselectAll();
var splitstring = '/';
if (obj.dateFormat.toUpperCase() == "EU")
{
splitstring = '.';
}
var inputDateArray = dstring.split(splitstring);
var typedDate = new Date();
var currentDate = new Date();
if (inputDateArray.length == 2) inputDateArray[2]=""; // missing year
try{
if(inputDateArray[2].toString().length < 4)
{
inputDateArray[2] = currentDate.getUTCFullYear();
}
}
catch(e){}

if (isNaN(parseInt(inputDateArray[0])) || isNaN(parseInt(inputDateArray[1])) || isNaN(parseInt(inputDateArray[2])))
{
obj.dateInput.value = "Invalid Date";
obj.dateInput.style.background = "#fff url(http://images.wctravel.com/images-general/warningBG.gif) 100% 50% no-repeat";
return false;
}
else
{
obj.dateInput.style.background = "";
}
if (obj.dateFormat.toUpperCase() == "EU")
{
if(inputDateArray.length == 3)
{
typedDate.setUTCFullYear(inputDateArray[2],inputDateArray[1]-1,inputDateArray[0]);
}
}
else
{
if(inputDateArray.length == 3)
{
typedDate.setUTCFullYear(inputDateArray[2],inputDateArray[0]-1,inputDateArray[1]);
}

}
obj.cal.select(typedDate);
obj.cal.render();
}

/**
* Sets the calendar group's minimum month. 
* @param {Date()}monthThe numeric month, from 1 (January) to 12 (December)
*/
WCT.calendar.prototype.minDate = function(date) {
if(typeof(this.cal.pages) != "undefined")
{
// Multiple calendar pages (2up)
for (var p=0;p<this.cal.pages.length;++p)
{
var cal = this.cal.pages[p];
cal.minDate=date;
}
}
else
{
this.cal.minDate=date;
}
};


/**
* Sets the calendar group's minimum month. 
* @param {Date()}monthThe numeric month, from 1 (January) to 12 (December)
*/
WCT.calendar.prototype.maxDate = function(date) {
if(typeof(this.cal.pages) != "undefined")
{
// 2 calendar side by side
for (var p=0;p<this.cal.pages.length;++p)
{
var cal = this.cal.pages[p];
cal.maxDate=date;
}
}
else
{
this.cal.maxDate=date;
}
};

/* set calendar positioning type 
Params: pos = string of the following (Case insensitive).
Default: absolute

1.absolute--- this will be controlled via css
2.relative--- this will be relative to an html element
*/
WCT.calendar.prototype.position = function(pos)
{
this.calendarPosition = pos.toLowerCase();
}

/*
Return selected calendar date into separate date field.
return true if success.
*/

WCT.calendar.prototype.returnSelectedDate = function(year,month,day)
{
if (arguments.length > 0)
{
var searchForm = this.dateInput.form;
if (typeof(year) !="object")
{
year = searchForm.elements[year];
}
if (typeof(month) !="object")
{
month = searchForm.elements[month];
}

if (typeof(day) !="object")
{
day = searchForm.elements[day];
}

if(this.dateInput.value.toLowerCase() != "mm/dd/aaaa" && this.dateInput.value.toLowerCase() != "mm/dd/yyyy" && (this.dateInput.value.toLowerCase() != "dd.mm.yyyy" && this.dateInput.value.toLowerCase() != "dd/mm/yyyy") )
{
var splitstring = '/';
if (this.dateFormat.toUpperCase() == "EU")
{
splitstring = '.';
}
var inputDateArray = this.dateInput.value.split(splitstring);
if(inputDateArray.length == 3)
{
if (isNaN(parseInt(inputDateArray[0])) || isNaN(parseInt(inputDateArray[1])) || isNaN(parseInt(inputDateArray[2])))
{
this.dateInput.value="Invalid Date";
this.dateInput.style.background = "#fff url(http://images.wctravel.com/images-general/warningBG.gif) 100% 50% no-repeat";
return false;
}
else
{
this.dateInput.style.background = "";
if (this.dateFormat.toUpperCase() == "EU")
{
(year) ? year.value = inputDateArray[2]:"";
month.value = inputDateArray[1];
day.value = inputDateArray[0];
}
else
{
(year) ? year.value = inputDateArray[2]: "";
month.value = inputDateArray[0];
day.value = inputDateArray[1];
}
//alert(month.value+"/"+day.value);
return true;
}
}
}
else
{
this.dateInput.value="Enter Date";
this.dateInput.style.background = "#fff url(http://images.wctravel.com/images-general/warningBG.gif) 100% 50% no-repeat";
return false;
}
}
return false;
}

WCT.calendar.prototype.clear = function()
{
this.cal.minDate=new Date();
this.cal.clearRenderer();
this.cal.reset();
}

WCT.calendar.prototype.toString = function()
{
return this.cal.id;
}

onUnfocus = function (el,obj)
{
if (!over_cal) 
{
obj.hideCal();
}
}

function overCal() 
{
over_cal = true;
}

function outCal() 
{
over_cal = false;
}




/* Fixes flickering css background image issue*/
/* not needed for now */
/*
try {
document.execCommand('BackgroundImageCache', false, true);
} catch(e) {}*/
