[Jifty-commit] r1360 - in jifty/trunk: . share/web/static/css share/web/static/css/yui share/web/static/css/yui/calendar share/web/static/js share/web/static/js/yui

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Fri Jun 23 22:09:53 EDT 2006


Author: trs
Date: Fri Jun 23 22:09:51 2006
New Revision: 1360

Added:
   jifty/trunk/share/web/static/css/yui/
   jifty/trunk/share/web/static/css/yui/calendar/
   jifty/trunk/share/web/static/css/yui/calendar/calendar.css
   jifty/trunk/share/web/static/js/calendar.js
   jifty/trunk/share/web/static/js/formatDate.js   (contents, props changed)
   jifty/trunk/share/web/static/js/yui/
   jifty/trunk/share/web/static/js/yui/calendar.js
   jifty/trunk/share/web/static/js/yui/dom.js
   jifty/trunk/share/web/static/js/yui/event.js
   jifty/trunk/share/web/static/js/yui/yahoo.js
Modified:
   jifty/trunk/   (props changed)
   jifty/trunk/lib/Jifty/Web.pm
   jifty/trunk/share/web/static/css/main.css
   jifty/trunk/share/web/static/js/bps_util.js

Log:
 r13470 at zot:  tom | 2006-06-23 22:05:11 -0400
 Sexy calendar date selection!  (using Yahoo's calendar widget)


Modified: jifty/trunk/lib/Jifty/Web.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Web.pm	(original)
+++ jifty/trunk/lib/Jifty/Web.pm	Fri Jun 23 22:09:51 2006
@@ -43,8 +43,10 @@
     scriptaculous/builder.js
     scriptaculous/effects.js
     scriptaculous/controls.js
+    formatDate.js
     jifty.js
     jifty_utils.js
+    calendar.js
     dom-drag.js
     halo.js
     combobox.js
@@ -52,6 +54,10 @@
     context_menu.js
     bps_util.js
     rico.js
+    yui/yahoo.js
+    yui/dom.js
+    yui/event.js
+    yui/calendar.js
     app.js
     app_behaviour.js
 )]);

Modified: jifty/trunk/share/web/static/css/main.css
==============================================================================
--- jifty/trunk/share/web/static/css/main.css	(original)
+++ jifty/trunk/share/web/static/css/main.css	Fri Jun 23 22:09:51 2006
@@ -9,3 +9,4 @@
 @import "halos.css";
 @import "app.css";
 @import "autocomplete.css";
+ at import "yui/calendar/calendar.css";

Added: jifty/trunk/share/web/static/css/yui/calendar/calendar.css
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/css/yui/calendar/calendar.css	Fri Jun 23 22:09:51 2006
@@ -0,0 +1,168 @@
+/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
+/* Container Styles */
+
+.calendar {
+    font-size: 0.9em;
+    margin-top: 0;
+}
+
+.calcontainer {*height:1%;} /* IE */
+.calcontainer:after {content:'.';clear:both;display:block;visibility:hidden;height:0;} /* others */
+
+.calbordered {
+	float:left;
+	padding:5px;
+	background-color:#F7F9FB;
+	border:1px solid #7B9EBD;
+}
+
+.calbordered .title {
+	font-family: Arial,Helvetica,sans-serif;
+	color:#000;
+	font-weight:bold;
+	margin-bottom:5px;
+	height:auto;
+	width:304px;
+	position:relative;
+}
+
+.title .close-icon {
+	position:absolute;
+	right:0;
+	top:0;
+	border:none;
+}
+
+.cal2up {
+	float:left;
+}
+
+.calnavleft {
+	position:absolute;
+	top:0;
+	bottom:0;
+	height:12px;   
+	left:2px;
+}
+
+.calnavright {
+	position:absolute;
+	top:0;
+	bottom:0;
+	height:12px;  
+	right:2px;
+}
+
+/* Calendar element styles */
+
+.calendar {
+	font-family: Arial,Helvetica,sans-serif;
+	text-align:center;
+	border-spacing:0;
+}
+
+td.calcell {
+	width:1.5em;
+	height:1em;
+	border:1px solid #E0E0E0;
+	background-color:#FFF;
+}
+
+.calendar.wait td.calcell {
+	color:#999;
+	background-color:#CCC;
+}
+
+.calendar.wait td.calcell a {
+	color:#999;
+	font-style:italic;
+}
+
+td.calcell a {
+	color:#003DB8;
+	text-decoration:none;
+}
+
+td.calcell.today {
+	border:1px solid #000;
+}
+
+td.calcell.oom {
+	cursor:default;
+	color:#999;
+	background-color:#EEE;
+	border:1px solid #E0E0E0;
+}
+
+td.calcell.selected {
+	color:#003DB8;
+	background-color:#FFF19F;
+	border:1px solid #FF9900;
+}
+
+td.calcell.calcellhover {
+	cursor:pointer;
+	color:#FFF;
+	background-color:#FF9900;
+	border:1px solid #FF9900;
+}
+
+/* Added to perform some correction for Opera 8.5
+   hover redraw bug */
+table:hover {
+	background-color:#FFF;
+}
+
+td.calcell.calcellhover a {
+	color:#FFF;
+}
+
+td.calcell.restricted {
+	text-decoration:line-through;
+}
+
+td.calcell.previous {
+	color:#CCC;
+}
+
+td.calcell.highlight1 { background-color:#CCFF99; }
+td.calcell.highlight2 { background-color:#99CCFF; }
+td.calcell.highlight3 { background-color:#FFCCCC; }
+td.calcell.highlight4 { background-color:#CCFF99; }
+
+
+.calhead {
+	border:1px solid #E0E0E0;
+	vertical-align:middle;
+	background-color:#FFF;
+}
+
+.calheader {
+	position:relative;
+	width:100%;
+}
+
+.calheader img {
+	border:none;
+}
+
+.calweekdaycell {
+	color:#666;
+	font-weight:normal;
+}
+
+.calfoot {
+	background-color:#EEE;
+}
+
+.calrowhead, .calrowfoot {
+	color:#666;
+	font-size:9px;
+	font-style:italic;
+	font-weight:normal;
+	width:15px;
+}
+
+.calrowhead {
+	border-right-width:2px;
+}

Modified: jifty/trunk/share/web/static/js/bps_util.js
==============================================================================
--- jifty/trunk/share/web/static/js/bps_util.js	(original)
+++ jifty/trunk/share/web/static/js/bps_util.js	Fri Jun 23 22:09:51 2006
@@ -5,11 +5,6 @@
     if (e) e.focus();
 }
 
-function openCalWindow(field) {
-    var objWindow = window.open('/helpers/calendar.html?field='+field, 'Calendar', 'height=200,width=235,scrollbars=1');
-    objWindow.focus();
-}
-
 function updateParentField(field, value) {
     if (window.opener) {
         window.opener.document.getElementById(field).value = value;
@@ -17,26 +12,8 @@
     }
 }
 
-function createCalendarLink(input) {
-    var e = $(input);
-    if (e) {
-        var link = document.createElement("a");
-        link.setAttribute("href", "javascript:openCalWindow('"+e.id+"')");
-        Element.addClassName(link, "calendar_link");
-        
-        var img = document.createElement("img");
-        img.setAttribute("src", "/static/images/silk/calendar.png");
-        img.setAttribute("border", 0);
-        link.appendChild(img);
-        
-        var space = document.createTextNode(" ");
-        
-        e.parentNode.insertBefore(link, e.nextSibling);
-        e.parentNode.insertBefore(space, e.nextSibling);
-        
-        return true;
-    }
-    return false;
+function createCalendarLink(id) {
+    return Jifty.Calendar.registerDateWidget( id );
 }
 
 function buttonToLink(input) {

Added: jifty/trunk/share/web/static/js/calendar.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/calendar.js	Fri Jun 23 22:09:51 2006
@@ -0,0 +1,78 @@
+JSAN.use("DOM.Events");
+
+if ( typeof Jifty == "undefined" ) Jifty = { };
+
+Jifty.Calendar = {
+    registerDateWidget: function(id) {
+        var input = $(id);
+        
+        if ( !input ) return false;
+
+        DOM.Events.addListener( input, "click", Jifty.Calendar.toggleCalendar );
+        return true;
+    },
+
+    toggleCalendar: function(ev) {
+        var calId  = "cal_" + ev.target.id;
+        var wrapId = calId + "_wrap";
+        var wrap   = $(wrapId);
+        var input  = ev.target;
+
+        if ( Jifty.Calendar.openCalendar == wrapId ) {
+            Jifty.Calendar.hideOpenCalendar();
+            return;
+        }
+
+        Jifty.Calendar.hideOpenCalendar();
+        
+        if ( wrap ) {
+            wrap.style.display = "block";
+            Jifty.Calendar.openCalendar = wrapId;
+            return;
+        }
+
+        wrap = document.createElement("div");
+        wrap.setAttribute( "id", wrapId );
+        
+        wrap.style.position = "absolute";
+        wrap.style.left     = Jifty.Utils.findPosX( input );
+        wrap.style.top      = Jifty.Utils.findPosY( input ) + input.offsetHeight;
+        
+        input.parentNode.insertBefore( wrap, input.nextSibling );
+
+        var cal;
+        
+        if ( /^(\d{4})-(\d{2})-(\d{2})/.test(input.value) ) {
+            var bits = input.value.match(/^(\d{4})-(\d{2})-(\d{2})/);
+            cal = new YAHOO.widget.Calendar( calId,
+                                             wrapId,
+                                             bits[2]+"/"+bits[1],
+                                             bits[2]+"/"+bits[3]+"/"+bits[1]
+                                            );
+        }
+        else {
+            cal = new YAHOO.widget.Calendar( calId, wrapId );
+        }
+        
+        cal["onSelect"] = function() {
+            input.value = cal.getSelectedDates()[0].formatDate("Y-m-d");
+            Jifty.Calendar.hideOpenCalendar();
+        };
+        cal.render();
+        
+        Jifty.Calendar.openCalendar = wrapId;
+        /*Jifty.Calendar.preventStutter = wrapId;*/
+    },
+
+    openCalendar: "",
+
+    hideOpenCalendar: function() {
+        if ( Jifty.Calendar.openCalendar ) {
+            $( Jifty.Calendar.openCalendar ).style.display = "none";
+            Jifty.Calendar.openCalendar = "";
+        }
+    }
+};
+
+/*DOM.Events.addListener( window, "click", Jifty.Calendar.hideOpenCalendar );*/
+

Added: jifty/trunk/share/web/static/js/formatDate.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/formatDate.js	Fri Jun 23 22:09:51 2006
@@ -0,0 +1,356 @@
+// formatDate :
+// a PHP date like function, for formatting date strings
+// authored by Svend Tofte <www.svendtofte.com>
+// the code is in the public domain
+//
+// see http://www.svendtofte.com/code/date_format/
+// and http://www.php.net/date
+//
+// thanks to 
+//  - Daniel Berlin <mail at daniel-berlin.de>,
+//    major overhaul and improvements
+//  - Matt Bannon,
+//    correcting some stupid bugs in my days-in-the-months list!
+//
+// input : format string
+// time : epoch time (seconds, and optional)
+//
+// if time is not passed, formatting is based on 
+// the current "this" date object's set time.
+//
+// supported switches are
+// a, A, B, c, d, D, F, g, G, h, H, i, I (uppercase i), j, l (lowecase L), 
+// L, m, M, n, N, O, P, r, s, S, t, U, w, W, y, Y, z, Z
+// 
+// unsupported (as compared to date in PHP 5.1.3)
+// T, e, o
+
+Date.prototype.formatDate = function (input,time) {
+    
+    var daysLong =    ["Sunday", "Monday", "Tuesday", "Wednesday", 
+                       "Thursday", "Friday", "Saturday"];
+    var daysShort =   ["Sun", "Mon", "Tue", "Wed", 
+                       "Thu", "Fri", "Sat"];
+    var monthsShort = ["Jan", "Feb", "Mar", "Apr",
+                       "May", "Jun", "Jul", "Aug", "Sep",
+                       "Oct", "Nov", "Dec"];
+    var monthsLong =  ["January", "February", "March", "April",
+                       "May", "June", "July", "August", "September",
+                       "October", "November", "December"];
+
+    var switches = { // switches object
+        
+        a : function () {
+            // Lowercase Ante meridiem and Post meridiem
+            return date.getHours() > 11? "pm" : "am";
+        },
+        
+        A : function () {
+            // Uppercase Ante meridiem and Post meridiem
+            return (this.a().toUpperCase ());
+        },
+    
+        B : function (){
+            // Swatch internet time. code simply grabbed from ppk,
+            // since I was feeling lazy:
+            // http://www.xs4all.nl/~ppk/js/beat.html
+            var off = (date.getTimezoneOffset() + 60)*60;
+            var theSeconds = (date.getHours() * 3600) + 
+                             (date.getMinutes() * 60) + 
+                              date.getSeconds() + off;
+            var beat = Math.floor(theSeconds/86.4);
+            if (beat > 1000) beat -= 1000;
+            if (beat < 0) beat += 1000;
+            if ((String(beat)).length == 1) beat = "00"+beat;
+            if ((String(beat)).length == 2) beat = "0"+beat;
+            return beat;
+        },
+        
+        c : function () {
+            // ISO 8601 date (e.g.: "2004-02-12T15:19:21+00:00"), as per
+            // http://www.cl.cam.ac.uk/~mgk25/iso-time.html
+            return (this.Y() + "-" + this.m() + "-" + this.d() + "T" + 
+                    this.h() + ":" + this.i() + ":" + this.s() + this.P());
+        },
+        
+        d : function () {
+            // Day of the month, 2 digits with leading zeros
+            var j = String(this.j());
+            return (j.length == 1 ? "0"+j : j);
+        },
+        
+        D : function () {
+            // A textual representation of a day, three letters
+            return daysShort[date.getDay()];
+        },
+        
+        F : function () {
+            // A full textual representation of a month
+            return monthsLong[date.getMonth()];
+        },
+        
+        g : function () {
+            // 12-hour format of an hour without leading zeros
+            return date.getHours() > 12? date.getHours()-12 : date.getHours();
+        },
+        
+        G : function () {
+            // 24-hour format of an hour without leading zeros
+            return date.getHours();
+        },
+        
+        h : function () {
+            // 12-hour format of an hour with leading zeros
+            var g = String(this.g());
+            return (g.length == 1 ? "0"+g : g);
+        },
+        
+        H : function () {
+            // 24-hour format of an hour with leading zeros
+            var G = String(this.G());
+            return (G.length == 1 ? "0"+G : G);
+        },
+        
+        i : function () {
+            // Minutes with leading zeros
+            var min = String (date.getMinutes ());
+            return (min.length == 1 ? "0" + min : min);
+        },
+        
+        I : function () {
+            // Whether or not the date is in daylight saving time (DST)
+            // note that this has no bearing in actual DST mechanics,
+            // and is just a pure guess. buyer beware.
+            var noDST = new Date ("January 1 " + this.Y() + " 00:00:00");
+            return (noDST.getTimezoneOffset () == 
+                    date.getTimezoneOffset () ? 0 : 1);
+        },
+        
+        j : function () {
+            // Day of the month without leading zeros
+            return date.getDate();
+        },
+        
+        l : function () {
+            // A full textual representation of the day of the week
+            return daysLong[date.getDay()];
+        },
+        
+        L : function () {
+            // leap year or not. 1 if leap year, 0 if not.
+            // the logic should match iso's 8601 standard.
+            // http://www.uic.edu/depts/accc/software/isodates/leapyear.html
+            var Y = this.Y();
+            if (         
+                (Y % 4 == 0 && Y % 100 != 0) ||
+                (Y % 4 == 0 && Y % 100 == 0 && Y % 400 == 0)
+                ) {
+                return 1;
+            } else {
+                return 0;
+            }
+        },
+        
+        m : function () {
+            // Numeric representation of a month, with leading zeros
+            var n = String(this.n());
+            return (n.length == 1 ? "0"+n : n);
+        },
+        
+        M : function () {
+            // A short textual representation of a month, three letters
+            return monthsShort[date.getMonth()];
+        },
+        
+        n : function () {
+            // Numeric representation of a month, without leading zeros
+            return date.getMonth()+1;
+        },
+        
+        N : function () {
+            // ISO-8601 numeric representation of the day of the week
+            var w = this.w();
+            return (w == 0 ? 7 : w);
+        },
+        
+        O : function () {
+            // Difference to Greenwich time (GMT) in hours
+            var os = Math.abs(date.getTimezoneOffset());
+            var h = String(Math.floor(os/60));
+            var m = String(os%60);
+            h.length == 1? h = "0"+h:1;
+            m.length == 1? m = "0"+m:1;
+            return date.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m;
+        },
+        
+        P : function () {
+            // Difference to GMT, with colon between hours and minutes
+            var O = this.O();
+            return (O.substr(0, 3) + ":" + O.substr(3, 2));
+        },      
+        
+        r : function () {
+            // RFC 822 formatted date
+            var r; // result
+            //  Thu         ,     21               Dec              2000
+            r = this.D() + ", " + this.d() + " " + this.M() + " " + this.Y() +
+            //    16          :    01          :    07               0200
+            " " + this.H() + ":" + this.i() + ":" + this.s() + " " + this.O();
+            return r;
+        },
+
+        s : function () {
+            // Seconds, with leading zeros
+            var sec = String (date.getSeconds ());
+            return (sec.length == 1 ? "0" + sec : sec);
+        },        
+        
+        S : function () {
+            // English ordinal suffix for the day of the month, 2 characters
+            switch (date.getDate ()) {
+                case  1: return ("st"); 
+                case  2: return ("nd"); 
+                case  3: return ("rd");
+                case 21: return ("st"); 
+                case 22: return ("nd"); 
+                case 23: return ("rd");
+                case 31: return ("st");
+                default: return ("th");
+            }
+        },
+        
+        t : function () {
+            // thanks to Matt Bannon for some much needed code-fixes here!
+            var daysinmonths = [null,31,28,31,30,31,30,31,31,30,31,30,31];
+            if (this.L()==1 && this.n()==2) return 29; // ~leap day
+            return daysinmonths[this.n()];
+        },
+        
+        U : function () {
+            // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
+            return Math.round(date.getTime()/1000);
+        },
+
+        w : function () {
+            // Numeric representation of the day of the week
+            return date.getDay();
+        },
+        
+        W : function () {
+            // Weeknumber, as per ISO specification:
+            // http://www.cl.cam.ac.uk/~mgk25/iso-time.html
+        
+            var DoW = this.N ();
+            var DoY = this.z ();
+
+            // If the day is 3 days before New Year's Eve and is Thursday or earlier,
+            // it's week 1 of next year.
+            var daysToNY = 364 + this.L () - DoY;
+            if (daysToNY <= 2 && DoW <= (3 - daysToNY)) {
+                return 1;
+            }
+
+            // If the day is within 3 days after New Year's Eve and is Friday or later,
+            // it belongs to the old year.
+            if (DoY <= 2 && DoW >= 5) {
+                return new Date (this.Y () - 1, 11, 31).formatDate ("W");
+            }
+            
+            var nyDoW = new Date (this.Y (), 0, 1).getDay ();
+            nyDoW = nyDoW != 0 ? nyDoW - 1 : 6;
+
+            if (nyDoW <= 3) { // First day of the year is a Thursday or earlier
+                return (1 + Math.floor ((DoY + nyDoW) / 7));
+            } else {  // First day of the year is a Friday or later
+                return (1 + Math.floor ((DoY - (7 - nyDoW)) / 7));
+            }
+        },
+        
+        y : function () {
+            // A two-digit representation of a year
+            var y = String(this.Y());
+            return y.substring(y.length-2,y.length);
+        },        
+        
+        Y : function () {
+            // A full numeric representation of a year, 4 digits
+    
+            // we first check, if getFullYear is supported. if it
+            // is, we just use that. ppks code is nice, but wont
+            // work with dates outside 1900-2038, or something like that
+            if (date.getFullYear) {
+                var newDate = new Date("January 1 2001 00:00:00 +0000");
+                var x = newDate .getFullYear();
+                if (x == 2001) {              
+                    // i trust the method now
+                    return date.getFullYear();
+                }
+            }
+            // else, do this:
+            // codes thanks to ppk:
+            // http://www.xs4all.nl/~ppk/js/introdate.html
+            var x = date.getYear();
+            var y = x % 100;
+            y += (y < 38) ? 2000 : 1900;
+            return y;
+        },
+
+        
+        z : function () {
+            // The day of the year, zero indexed! 0 through 366
+            var t = new Date("January 1 " + this.Y() + " 00:00:00");
+            var diff = date.getTime() - t.getTime();
+            return Math.floor(diff/1000/60/60/24);
+        },
+
+        Z : function () {
+            // Timezone offset in seconds
+            return (date.getTimezoneOffset () * -60);
+        }        
+    
+    }
+
+    function getSwitch(str) {
+        if (switches[str] != undefined) {
+            return switches[str]();
+        } else {
+            return str;
+        }
+    }
+
+    var date;
+    if (time) {
+        var date = new Date (time);
+    } else {
+        var date = this;
+    }
+
+    var formatString = input.split("");
+    var i = 0;
+    while (i < formatString.length) {
+        if (formatString[i] == "\\") {
+            // this is our way of allowing users to escape stuff
+            formatString.splice(i,1);
+        } else {
+            formatString[i] = getSwitch(formatString[i]);
+        }
+        i++;
+    }
+    
+    return formatString.join("");
+}
+
+
+// Some (not all) predefined format strings from PHP 5.1.1, which 
+// offer standard date representations.
+// See: http://www.php.net/manual/en/ref.datetime.php#datetime.constants
+//
+
+// Atom      "2005-08-15T15:52:01+00:00"
+Date.DATE_ATOM    = "Y-m-d\\TH:i:sP";
+// ISO-8601  "2005-08-15T15:52:01+0000"
+Date.DATE_ISO8601 = "Y-m-d\\TH:i:sO";
+// RFC 2822  "Mon, 15 Aug 2005 15:52:01 +0000"
+Date.DATE_RFC2822 = "D, d M Y H:i:s O";
+// W3C       "2005-08-15T15:52:01+00:00"
+Date.DATE_W3C     = "Y-m-d\\TH:i:sP";

Added: jifty/trunk/share/web/static/js/yui/calendar.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/yui/calendar.js	Fri Jun 23 22:09:51 2006
@@ -0,0 +1,2825 @@
+/*
+Copyright (c) 2006, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 0.10.0
+*/
+/**
+* @class
+* <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} date	The JavaScript Date object to perform addition on
+	* @param {string} field	The this field constant to be used for performing addition.
+	* @param {Integer} amount	The number of units (measured in the field constant) to add to the date.
+	*/
+	this.add = function(date, field, amount) {
+		var d = new Date(date.getTime());
+		switch (field)
+		{
+			case this.MONTH:
+				var newMonth = date.getMonth() + 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.setMonth(newMonth);
+				d.setFullYear(date.getFullYear() + years);
+				break;
+			case this.DAY:
+				d.setDate(date.getDate() + amount);
+				break;
+			case this.YEAR:
+				d.setFullYear(date.getFullYear() + amount);
+				break;
+			case this.WEEK:
+				d.setDate(date.getDate() + 7);
+				break;
+		}
+		return d;
+	};
+
+	/**
+	* Subtracts the specified amount of time from the this instance.
+	* @param {Date} date	The JavaScript Date object to perform subtraction on
+	* @param {Integer} field	The this field constant to be used for performing subtraction.
+	* @param {Integer} amount	The 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} date		The Date object to compare with the compare argument
+	* @param {Date} compareTo	The 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 = compareTo.getTime();
+		if (date.getTime() < ms) {
+			return true;
+		} else {
+			return false;
+		}
+	};
+
+	/**
+	* Determines whether a given date is after another date on the calendar.
+	* @param {Date} date		The Date object to compare with the compare argument
+	* @param {Date} compareTo	The 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 = compareTo.getTime();
+		if (date.getTime() > ms) {
+			return true;
+		} else {
+			return false;
+		}
+	};
+
+	/**
+	* Retrieves a JavaScript Date object representing January 1 of any given year.
+	* @param {Integer} calendarYear		The calendar year for which to retrieve January 1
+	* @return {Date}	January 1 of the calendar year specified.
+	*/
+	this.getJan1 = function(calendarYear) {
+		return new Date(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}	date	The JavaScript date for which to find the offset
+	* @param {Integer} calendarYear	The calendar year to use for determining the offset
+	* @return {Integer}	The number of days since January 1 of the given year
+	*/
+	this.getDayOffset = 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.getTime()-beginYear.getTime()) / 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}	date	The JavaScript date for which to find the week number
+	* @param {Integer} calendarYear	OPTIONAL - The calendar year to use for determining the week number. Default is
+	*											the calendar year of parameter "date".
+	* @param {Integer} weekStartsOn	OPTIONAL - 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) {
+		if (! weekStartsOn) {
+			weekStartsOn = 0;
+		}
+		if (! calendarYear) {
+			calendarYear = date.getFullYear();
+		}
+		var weekNum = -1;
+		
+		var jan1 = this.getJan1(calendarYear);
+		var jan1DayOfWeek = jan1.getDay();
+		
+		var month = date.getMonth();
+		var day = date.getDate();
+		var year = date.getFullYear();
+		
+		var dayOffset = this.getDayOffset(date, calendarYear); // Days since Jan 1, Calendar Year
+			
+		if (dayOffset < 0 && dayOffset >= (-1 * jan1DayOfWeek)) {
+			weekNum = 1;
+		} else {
+			weekNum = 1;
+			var testDate = this.getJan1(calendarYear);
+			
+			while (testDate.getTime() < date.getTime() && testDate.getFullYear() == calendarYear) {
+				weekNum += 1;
+				testDate = this.add(testDate, this.WEEK, 1);
+			}
+		}
+		
+		return weekNum;
+	};
+
+	/**
+	* Determines if a given week overlaps two different years.
+	* @param {Date}	weekBeginDate	The 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.getFullYear() != weekBeginDate.getFullYear()) {
+			overlaps = true;
+		}
+		return overlaps;
+	};
+
+	/**
+	* Determines if a given week overlaps two different months.
+	* @param {Date}	weekBeginDate	The 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.getMonth() != weekBeginDate.getMonth()) {
+			overlaps = true;
+		}
+		return overlaps;
+	};
+
+	/**
+	* Gets the first day of a month containing a given date.
+	* @param {Date}	date	The 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.getFullYear(), date.getMonth(), 1);
+		return start;
+	};
+
+	/**
+	* Gets the last day of a month containing a given date.
+	* @param {Date}	date	The 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}	date	The 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;
+	};
+}
+
+/**
+* @class
+* <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}	id			The id of the table element that will represent the calendar widget
+* @param {String}	containerId	The id of the container element that will contain the calendar table
+* @param {String}	monthyear	The month/year string used to set the current calendar page
+* @param {String}	selected	A 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);
+	}
+}
+
+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}	id			The id of the table element that will represent the calendar widget
+* @param {String}	containerId	The id of the container element that will contain the calendar table
+* @param {String}	monthyear	The month/year string used to set the current calendar page
+* @param {String}	selected	A 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.getMonth()+1;
+		year = this.today.getFullYear();
+	}
+
+	this.pageDate = new Date(year, month-1, 1);
+	this._pageDate = new Date(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. 
+	* @param	e		The event
+	* @param	cal		A 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(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
+	* @param	e		The event
+	* @param	cal		A 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(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.widget.Calendar_Core.prependCssClass(cell, cal.Style.CSS_CELL_HOVER);
+		}
+	}
+
+	/**
+	* The event that is executed when the user moves the mouse out of a cell
+	* @param	e		The event
+	* @param	cal		A reference to the calendar passed by the Event utility
+	* @private
+	*/
+	this.doCellMouseOut = function(e, cal) {
+		YAHOO.widget.Calendar_Core.removeCssClass(this, cal.Style.CSS_CELL_HOVER);
+	}
+	
+	/**
+	* A wrapper event that executes the nextMonth method through a DOM event
+	* @param	e		The event
+	* @param	cal		A 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
+	* @param	e		The event
+	* @param	cal		A 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><strong>Config.Style</strong> - Defines the CSS classes used for different calendar elements</p>
+<blockquote>
+	<div><em>CSS_CALENDAR</em> : Container table</div>
+	<div><em>CSS_HEADER</em> : </div>
+	<div><em>CSS_HEADER_TEXT</em> : Calendar header</div>
+	<div><em>CSS_FOOTER</em> : Calendar footer</div>
+	<div><em>CSS_CELL</em> : Calendar day cell</div>
+	<div><em>CSS_CELL_OOM</em> : Calendar OOM (out of month) cell</div>
+	<div><em>CSS_CELL_SELECTED</em> : Calendar selected cell</div>
+	<div><em>CSS_CELL_RESTRICTED</em> : Calendar restricted cell</div>
+	<div><em>CSS_CELL_TODAY</em> : Calendar cell for today's date</div>
+	<div><em>CSS_ROW_HEADER</em> : The cell preceding a row (used for week number by default)</div>
+	<div><em>CSS_ROW_FOOTER</em> : The cell following a row (not implemented by default)</div>
+	<div><em>CSS_WEEKDAY_CELL</em> : The cells used for labeling weekdays</div>
+	<div><em>CSS_WEEKDAY_ROW</em> : The row containing the weekday label cells</div>
+	<div><em>CSS_BORDER</em> : The border style used for the default UED rendering</div>
+	<div><em>CSS_CONTAINER</em> : Special container class used to properly adjust the sizing and float</div>
+	<div><em>CSS_NAV_LEFT</em> : Left navigation arrow</div>
+	<div><em>CSS_NAV_RIGHT</em> : Right navigation arrow</div>
+	<div><em>CSS_CELL_TOP</em> : Outlying cell along the top row</div>
+	<div><em>CSS_CELL_LEFT</em> : Outlying cell along the left row</div>
+	<div><em>CSS_CELL_RIGHT</em> : Outlying cell along the right row</div>
+	<div><em>CSS_CELL_BOTTOM</em> : Outlying cell along the bottom row</div>
+	<div><em>CSS_CELL_HOVER</em> : Cell hover style</div>
+	<div><em>CSS_CELL_HIGHLIGHT1</em> : Highlight color 1 for styling cells</div>
+	<div><em>CSS_CELL_HIGHLIGHT2</em> : Highlight color 2 for styling cells</div>
+	<div><em>CSS_CELL_HIGHLIGHT3</em> : Highlight color 3 for styling cells</div>
+	<div><em>CSS_CELL_HIGHLIGHT4</em> : Highlight color 4 for styling cells</div>
+
+</blockquote>
+<p><strong>Config.Locale</strong> - Defines the locale string arrays used for localization</p>
+<blockquote>
+	<div><em>MONTHS_SHORT</em> : Array of 12 months in short format ("Jan", "Feb", etc.)</div>
+	<div><em>MONTHS_LONG</em> : Array of 12 months in short format ("Jan", "Feb", etc.)</div>
+	<div><em>WEEKDAYS_1CHAR</em> : Array of 7 days in 1-character format ("S", "M", etc.)</div>
+	<div><em>WEEKDAYS_SHORT</em> : Array of 7 days in short format ("Su", "Mo", etc.)</div>
+	<div><em>WEEKDAYS_MEDIUM</em> : Array of 7 days in medium format ("Sun", "Mon", etc.)</div>
+	<div><em>WEEKDAYS_LONG</em> : Array of 7 days in long format ("Sunday", "Monday", etc.)</div>
+	<div><em>DATE_DELIMITER</em> : The value used to delimit series of multiple dates (Default: ",")</div>
+	<div><em>DATE_FIELD_DELIMITER</em> : The value used to delimit date fields (Default: "/")</div>
+	<div><em>DATE_RANGE_DELIMITER</em> : The value used to delimit date fields (Default: "-")</div>
+	<div><em>MY_MONTH_POSITION</em> : The value used to determine the position of the month in a month/year combo (e.g. 12/2005) (Default: 1)</div>
+	<div><em>MY_YEAR_POSITION</em> : The value used to determine the position of the year in a month/year combo (e.g. 12/2005) (Default: 2)</div>	
+	<div><em>MD_MONTH_POSITION</em> : The value used to determine the position of the month in a month/day combo (e.g. 12/25) (Default: 1)</div>
+	<div><em>MD_DAY_POSITION</em> : The value used to determine the position of the day in a month/day combo (e.g. 12/25) (Default: 2)</div>
+	<div><em>MDY_MONTH_POSITION</em> : 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><em>MDY_DAY_POSITION</em> : 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><em>MDY_YEAR_POSITION</em> : The value used to determine the position of the year in a month/day/year combo (e.g. 12/25/2005) (Default: 3)</div>
+</blockquote>
+<p><strong>Config.Options</strong> - Defines other configurable calendar widget options</p>
+<blockquote>
+	<div><em>SHOW_WEEKDAYS</em> : Boolean, determines whether to display the weekday headers (defaults to true)</div>
+	<div><em>LOCALE_MONTHS</em> : Array, points to the desired Config.Locale array (defaults to Config.Locale.MONTHS_LONG)</div>
+	<div><em>LOCALE_WEEKDAYS</em> : Array, points to the desired Config.Locale array (defaults to Config.Locale.WEEKDAYS_SHORT)</div>
+	<div><em>START_WEEKDAY</em> : Integer, 0-6, representing the day that a week begins on</div>
+	<div><em>SHOW_WEEK_HEADER</em> : Boolean, determines whether to display row headers</div>
+	<div><em>SHOW_WEEK_FOOTER</em> : Boolean, determines whether to display row footers</div>
+	<div><em>HIDE_BLANK_WEEKS</em> : Boolean, determines whether to hide extra weeks that are completely OOM</div>
+	<div><em>NAV_ARROW_LEFT</em> : String, the image path used for the left navigation arrow</div>
+	<div><em>NAV_ARROW_RIGHT</em> : String, the image path used for the right navigation arrow</div>
+</blockquote>
+*/
+YAHOO.widget.Calendar_Core.prototype.setupConfig = function() {
+	/**
+	* Container for the CSS style configuration variables.
+	*/
+	this.Config = new Object();
+	
+	this.Config.Style = {
+		// Style variables
+		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:
+* <blockquote><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></blockquote>
+*/
+YAHOO.widget.Calendar_Core.prototype.customConfig = function() { };
+
+/**
+* Builds the date label that will be displayed in the calendar header or
+* footer, depending on configuration.
+* @return	The formatted calendar month label
+* @type String
+*/
+YAHOO.widget.Calendar_Core.prototype.buildMonthLabel = function() {
+	var text = this.Options.LOCALE_MONTHS[this.pageDate.getMonth()] + " " + this.pageDate.getFullYear();
+	return text;
+};
+
+/**
+* Builds the date digit that will be displayed in calendar cells
+* @return	The formatted day label
+* @type	String
+*/
+YAHOO.widget.Calendar_Core.prototype.buildDayLabel = function(workingDate) {
+	var day = workingDate.getDate();
+	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}	workingDate	The current working Date object being used to generate the calendar
+*/
+YAHOO.widget.Calendar_Core.prototype.renderBody = function(workingDate) {
+
+	this.preMonthDays = workingDate.getDay();
+	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).getDate();
+	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.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()]; // Add this date to cellDates
+
+		if (workingDate.getDay() == 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.getFullYear()	== this.today.getFullYear() &&
+			workingDate.getMonth()		== this.today.getMonth() &&
+			workingDate.getDate()		== this.today.getDate())
+		{
+			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.getMonth()+1 == month && workingDate.getDate() == day && workingDate.getFullYear() == 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.getMonth()+1 == month && workingDate.getDate() == 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(d1year, d1month-1, d1day);
+
+						var d2month = date2[1];
+						var d2day = date2[2];
+						var d2year = date2[0];
+
+						var d2 = new Date(d2year, d2month-1, d2day);
+
+						if (workingDate.getTime() >= d1.getTime() && workingDate.getTime() <= d2.getTime())
+						{
+							renderer = rArray[2];
+
+							if (workingDate.getTime()==d2.getTime()) { 
+								this.renderStack.splice(r,1);
+							}
+						}
+						break;
+					case YAHOO.widget.Calendar_Core.WEEKDAY:
+						
+						var weekday = rArray[1][0];
+						if (workingDate.getDay()+1 == weekday)
+						{
+							renderer = rArray[2];
+						}
+						break;
+					case YAHOO.widget.Calendar_Core.MONTH:
+						
+						month = rArray[1][0];
+						if (workingDate.getMonth()+1 == month)
+						{
+							renderer = rArray[2];
+						}
+						break;
+				}
+				
+				if (renderer) {
+					cellRenderers[cellRenderers.length]=renderer;
+				}
+			}
+
+		}
+
+		if (this._indexOfSelectedFieldArray([workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()]) > -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.getDay() == this.Options.START_WEEKDAY) {
+			weekRowIndex += 1;
+		}
+
+		YAHOO.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL);
+		if (c >= 0 && c <= 6) {
+			YAHOO.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_TOP);
+		}
+		if ((c % 7) == 0) {
+			YAHOO.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_LEFT);
+		}
+		if (((c+1) % 7) == 0) {
+			YAHOO.widget.Calendar_Core.addCssClass(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.widget.Calendar_Core.addCssClass(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.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_OOB);
+	cell.innerHTML = workingDate.getDate();
+	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}					workingDate		The current working Date object (beginning of the week) being used to generate the calendar
+* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
+*/
+YAHOO.widget.Calendar_Core.prototype.renderRowHeader = function(workingDate, cell) {
+	YAHOO.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_ROW_HEADER);
+	
+	var useYear = this.pageDate.getFullYear();
+	
+	if (! YAHOO.widget.DateMath.isYearOverlapWeek(workingDate)) {
+		useYear = workingDate.getFullYear();
+	}
+	
+	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.widget.Calendar_Core.addCssClass(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}					workingDate		The current working Date object (beginning of the week) being used to generate the calendar
+* @param {HTMLTableCellElement}	cell			The current working cell in the calendar
+*/
+YAHOO.widget.Calendar_Core.prototype.renderRowFooter = function(workingDate, cell) {
+	YAHOO.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_ROW_FOOTER);
+	
+	if (this.isDateOOM(workingDate) && ! YAHOO.widget.DateMath.isMonthOverlapWeek(workingDate)) {
+		YAHOO.widget.Calendar_Core.addCssClass(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}					workingDate		The current working Date object being used to generate the calendar
+* @param {HTMLTableCellElement}	cell			The 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.getFullYear()+"_"+(workingDate.getMonth()+1)+"_"+workingDate.getDate();
+
+	link.appendChild(document.createTextNode(this.buildDayLabel(workingDate)));
+	cell.appendChild(link);
+};
+
+YAHOO.widget.Calendar_Core.prototype.renderCellStyleHighlight1 = function(workingDate, cell) {
+	YAHOO.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_HIGHLIGHT1);
+};
+YAHOO.widget.Calendar_Core.prototype.renderCellStyleHighlight2 = function(workingDate, cell) {
+	YAHOO.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_HIGHLIGHT2);
+};
+YAHOO.widget.Calendar_Core.prototype.renderCellStyleHighlight3 = function(workingDate, cell) {
+	YAHOO.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_HIGHLIGHT3);
+};
+YAHOO.widget.Calendar_Core.prototype.renderCellStyleHighlight4 = function(workingDate, cell) {
+	YAHOO.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_HIGHLIGHT4);
+};
+
+/**
+* Applies the default style used for rendering today's date to the current calendar cell
+* @param {Date}					workingDate		The current working Date object being used to generate the calendar
+* @param {HTMLTableCellElement}	cell			The 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.renderCellStyleToday = function(workingDate, cell) {
+	YAHOO.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_TODAY);
+};
+
+/**
+* Applies the default style used for rendering selected dates to the current calendar cell
+* @param {Date}					workingDate		The current working Date object being used to generate the calendar
+* @param {HTMLTableCellElement}	cell			The 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.renderCellStyleSelected = function(workingDate, cell) {
+	YAHOO.widget.Calendar_Core.addCssClass(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}					workingDate		The current working Date object being used to generate the calendar
+* @param {HTMLTableCellElement}	cell			The 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.renderCellNotThisMonth = function(workingDate, cell) {
+	YAHOO.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_OOM);
+	cell.innerHTML=workingDate.getDate();
+	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}					workingDate		The current working Date object being used to generate the calendar
+* @param {HTMLTableCellElement}	cell			The 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.renderBodyCellRestricted = function(workingDate, cell) {
+	YAHOO.widget.Calendar_Core.setCssClasses(cell, [this.Style.CSS_CELL,this.Style.CSS_CELL_RESTRICTED]);
+	cell.innerHTML=workingDate.getDate();
+	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}	count	The 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}	count	The 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}	count	The 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}	count	The 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.getTime());
+	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.getTime());
+	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[]}	date	The 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.
+* @return						Array 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();
+
+	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}	cellIndex	The index of the cell to select in the current calendar. 
+* @return							Array 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();
+	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[]}	date	The 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.	
+* @return						Array 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();
+	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}	cellIndex	The index of the cell to deselect in the current calendar. 
+* @return							Array 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.getMonth() == dCellDate.getMonth() &&
+			this.pageDate.getFullYear() == dCellDate.getFullYear())
+		{
+			YAHOO.widget.Calendar_Core.removeCssClass(cell, this.Style.CSS_CELL_SELECTED);
+		}
+
+		this.selectedDates.splice(cellDateIndex, 1);
+	}
+
+
+	if (this.parent) {
+		this.parent.sync(this);
+	}
+
+	this.onDeselect();
+	return this.getSelectedDates();
+};
+
+/**
+* Deselects all dates on the current calendar.
+* @return				Array 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;
+	this.selectedDates.length = 0;
+
+	if (this.parent) {
+		this.parent.sync(this);
+	}
+	
+	if (count > 0) {
+		this.onDeselect();
+	}
+
+	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[]}	date	The 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.	
+* @return						Array 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.getFullYear(), date.getMonth()+1, date.getDate()]];
+	} 
+	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.getFullYear(),d.getMonth()+1,d.getDate()];
+		}
+	}
+	
+	return returnDate;
+};
+
+/**
+* Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
+* @private
+* @param	{Integer[]}		dateFieldArray	The date field array to convert to a JavaScript Date.
+* @return					JavaScript 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(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[]}	array1	The first date field array to compare
+* @param	{Integer[]}	array2	The first date field array to compare
+* @return						The 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[]}		find	The date field array to search for
+* @return					The 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}	date	The 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.getMonth() != this.pageDate.getMonth()) {
+		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.
+*/
+YAHOO.widget.Calendar_Core.prototype.onSelect = function() { };
+
+/**
+* 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.
+*/
+YAHOO.widget.Calendar_Core.prototype.onDeselect = function() { };
+
+/**
+* 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.
+* @return	Should 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}	sDate			Date string. Valid formats are mm/dd and mm/dd/yyyy.
+* @return				A 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}	sDates		Date string with one or more comma-delimited dates. Valid formats are mm/dd, mm/dd/yyyy, mm/dd/yyyy-mm/dd/yyyy
+* @return							An 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[]}	startDate	Date field array representing the first date in the range
+* @param	{Integer[]}	endDate		Date field array representing the last date in the range
+* @return							An array of date field arrays
+* @type Array[](Integer[])
+*/
+YAHOO.widget.Calendar_Core.prototype._parseRange = function(startDate, endDate) {
+	var dStart   = new Date(startDate[0],startDate[1]-1,startDate[2]);
+	var dCurrent = YAHOO.widget.DateMath.add(new Date(startDate[0],startDate[1]-1,startDate[2]),YAHOO.widget.DateMath.DAY,1);
+	var dEnd     = new Date(endDate[0],  endDate[1]-1,  endDate[2]);
+
+	var results = new Array();
+	results.push(startDate);
+	while (dCurrent.getTime() <= dEnd.getTime())
+	{
+		results.push([dCurrent.getFullYear(),dCurrent.getMonth()+1,dCurrent.getDate()]);
+		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}	sDates		A 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}	fnRender	The 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}	type		The 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}		aDates		An array of dates used to construct the renderer. The format varies based
+*									on the renderer type
+* @param	{Function}	fnRender	The 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}	month		The month (1-12) to associate with this renderer
+* @param	{Function}	fnRender	The 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}	weekay		The weekday (1-7) to associate with this renderer
+* @param	{Function}	fnRender	The 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 *******************************************/
+/**
+* Adds the specified CSS class to the element passed to this method.
+* @param	{HTMLElement}	element		The element to which the CSS class should be applied
+* @param	{String}	style		The CSS class name to add to the referenced element
+*/
+YAHOO.widget.Calendar_Core.addCssClass = function(element, style) {
+	if (element.className.length === 0)
+	{
+		element.className += style;
+	} else {
+		element.className += " " + style;
+	}
+};
+
+YAHOO.widget.Calendar_Core.prependCssClass = function(element, style) {
+	element.className = style + " " + element.className;
+}
+
+/**
+* Removed the specified CSS class from the element passed to this method.
+* @param	{HTMLElement}	element		The element from which the CSS class should be removed
+* @param	{String}	style		The CSS class name to remove from the referenced element
+*/
+YAHOO.widget.Calendar_Core.removeCssClass = function(element, style) {
+	var aStyles = element.className.split(" ");
+	for (var s=0;s<aStyles.length;++s)
+	{
+		if (aStyles[s] == style)
+		{
+			aStyles.splice(s,1);
+			break;
+		}
+	}
+	YAHOO.widget.Calendar_Core.setCssClasses(element, aStyles);
+};
+
+/**
+* Sets the specified array of CSS classes into the referenced element
+* @param	{HTMLElement}	element		The element to set the CSS classes into
+* @param	{String[]}		aStyles		An 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.widget.Calendar_Core.removeCssClass(this.cells[c],style);
+	}
+};
+
+/***************** END CSS METHODS *********************************************/
+
+/***************** BEGIN GETTER/SETTER METHODS *********************************/
+/**
+* Sets the calendar's month explicitly.
+* @param {Integer}	month		The numeric month, from 1 (January) to 12 (December)
+*/
+YAHOO.widget.Calendar_Core.prototype.setMonth = function(month) {
+	this.pageDate.setMonth(month);
+};
+
+/**
+* Sets the calendar's year explicitly.
+* @param {Integer}	year		The numeric 4-digit year
+*/
+YAHOO.widget.Calendar_Core.prototype.setYear = function(year) {
+	this.pageDate.setFullYear(year);
+};
+
+/**
+* Gets the list of currently selected dates from the calendar.
+* @return	An 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(dateArray[0],dateArray[1]-1,dateArray[2]);
+		returnDates.push(date);
+	}
+
+	returnDates.sort();
+	return returnDates;
+};
+
+/***************** END GETTER/SETTER METHODS *********************************/
+
+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;
+}
+
+
+YAHOO.widget.Cal_Core = YAHOO.widget.Calendar_Core;
+
+/**
+* @class
+* 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}	id			The id of the table element that will represent the calendar widget
+* @param {String}	containerId	The id of the container element that will contain the calendar table
+* @param {String}	monthyear	The month/year string used to set the current calendar page
+* @param {String}	selected	A 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 = 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_BORDER;
+	
+	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;
+	
+    var ourthis = this;
+    
+    /* So, Yahoo hooked into previous/nextMonth() via a javascript: "URL",
+       but they did it in a way that didn't work (!).  So I swapped that out
+       for an onclick. */
+	var linkLeft = document.createElement("A");
+    linkLeft.href = "#";
+    linkLeft.onclick = function(ev) { ourthis.previousMonth(); return false; }; 
+	var imgLeft = document.createElement("IMG");
+	imgLeft.src = this.Options.NAV_ARROW_LEFT;
+	imgLeft.className = this.Style.CSS_NAV_LEFT;
+	linkLeft.appendChild(imgLeft);
+	
+	var linkRight = document.createElement("A");
+    linkRight.href = "#";
+    linkRight.onclick = function(ev) { ourthis.nextMonth(); return false; };
+	var imgRight = document.createElement("IMG");
+	imgRight.src = this.Options.NAV_ARROW_RIGHT;
+	imgRight.className = this.Style.CSS_NAV_RIGHT;
+	linkRight.appendChild(imgRight);
+	
+	headerContainer.appendChild(linkLeft);
+	headerContainer.appendChild(document.createTextNode(this.buildMonthLabel()));
+	headerContainer.appendChild(linkRight);
+	
+	this.headerCell.appendChild(headerContainer);
+};
+
+YAHOO.widget.Cal = YAHOO.widget.Calendar;
+
+/**
+* @class
+* <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}		pageCount	The number of pages that this calendar should display.
+* @param {String}		id			The id of the element that will be inserted into the DOM.
+* @param {String}	containerId	The id of the container element that the calendar will be inserted into.
+* @param {String}		monthyear	The month/year string used to set the current calendar page
+* @param {String}		selected	A 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}		pageCount	The number of pages that this calendar should display.
+* @param {String}		id			The id of the element that will be inserted into the DOM.
+* @param {String}		containerId	The id of the container element that the calendar will be inserted into.
+* @param {String}		monthyear	The month/year string used to set the current calendar page
+* @param {String}		selected	A 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) {
+	//var self=this;
+	
+	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.setMonth(cal.pageDate.getMonth()+p);
+		cal._pageDate = new Date(cal.pageDate.getFullYear(),cal.pageDate.getMonth(),cal.pageDate.getDate());
+		this.pages.push(cal);
+	}
+	
+	this.doNextMonth = function(e, calGroup) {
+		calGroup.nextMonth();
+	}
+	
+	this.doPreviousMonth = function(e, calGroup) {
+		calGroup.previousMonth();
+	}
+};
+
+YAHOO.widget.CalendarGroup.prototype.setChildFunction = function(fnName, fn) {
+	for (var p=0;p<this.pageCount;++p) {
+		this.pages[p][fnName] = fn;
+	}
+}
+
+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}		id			The id of the element that will be inserted into the DOM.
+* @param {String}		containerId	The id of the container element that the calendar will be inserted into.
+* @param {String}		monthyear	The month/year string used to set the current calendar page
+* @param {String}		selected	A 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"
+* @return							The 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}	month		The numeric month, from 1 (January) to 12 (December)
+*/
+YAHOO.widget.CalendarGroup.prototype.setMonth = function(month) {
+	for (var p=0;p<this.pages.length;++p)
+	{
+		var cal = this.pages[p];
+		cal.setMonth(month+p);
+	}
+};
+
+/**
+* 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}	year		The 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.getMonth()+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}	caller		The YAHOO.widget.Calendar_Core that is initiating the call to sync().
+* @return								Array 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.
+* @return			An 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(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}	eventName	The name of the event to handler to set within all child calendars.
+* @param	{Function}	fn			The 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;
+	}
+};
+
+YAHOO.widget.CalGrp = YAHOO.widget.CalendarGroup;
+
+/**
+* @class
+* 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}	id			The id of the table element that will represent the calendar widget
+* @param {String}	containerId	The id of the container element that will contain the calendar table
+* @param {String}	monthyear	The month/year string used to set the current calendar page
+* @param {String}	selected	A 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.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) {
+		var linkLeft = document.createElement("A");
+		linkLeft.href = "javascript:void(null)";
+		YAHOO.util.Event.addListener(linkLeft, "click", this.parent.doPreviousMonth, this.parent);
+		var imgLeft = document.createElement("IMG");
+		imgLeft.src = this.Options.NAV_ARROW_LEFT;
+		imgLeft.className = this.Style.CSS_NAV_LEFT;
+		linkLeft.appendChild(imgLeft);
+		headerContainer.appendChild(linkLeft);
+	}
+	
+	headerContainer.appendChild(document.createTextNode(this.buildMonthLabel()));
+	
+	if (this.index == 1) {
+		var linkRight = document.createElement("A");
+		linkRight.href = "javascript:void(null)";
+		YAHOO.util.Event.addListener(linkRight, "click", this.parent.doNextMonth, this.parent);
+		var imgRight = document.createElement("IMG");
+		imgRight.src = this.Options.NAV_ARROW_RIGHT;
+		imgRight.className = this.Style.CSS_NAV_RIGHT;
+		linkRight.appendChild(imgRight);
+		headerContainer.appendChild(linkRight);
+	}
+	
+	this.headerCell.appendChild(headerContainer);
+};
+
+
+
+
+/**
+* @class
+* 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}	id			The id of the table element that will represent the calendar widget
+* @param {String}	containerId	The id of the container element that will contain the calendar table
+* @param {String}	monthyear	The month/year string used to set the current calendar page
+* @param {String}	selected	A 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.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();
+
+/**
+* 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} containerId	The id of the outer container element.
+*/
+YAHOO.widget.Calendar2up.prototype.buildWrapper = function(containerId) {
+	var outerContainer = document.getElementById(containerId);
+	
+	outerContainer.className = "calcontainer";
+	
+	var innerContainer = document.createElement("DIV");
+	innerContainer.className = "calbordered";
+	innerContainer.id = containerId + "_inner";
+	
+	var cal1Container = document.createElement("DIV");
+	cal1Container.id = containerId + "_0";
+	cal1Container.className = "cal2up";
+	cal1Container.style.marginRight = "10px";
+	
+	var cal2Container = document.createElement("DIV");
+	cal2Container.id = containerId + "_1"; 
+	cal2Container.className = "cal2up";
+	
+	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 = "title";
+	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 + "us/my/bn/x_d.gif";
+		imgClose.className = "close-icon";
+
+		linkClose.appendChild(imgClose);
+
+		this.linkClose = linkClose;
+
+		this.titleDiv.appendChild(linkClose);
+	}
+
+	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;

Added: jifty/trunk/share/web/static/js/yui/dom.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/yui/dom.js	Fri Jun 23 22:09:51 2006
@@ -0,0 +1,827 @@
+/*
+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
+*/
+
+/**
+ * @class A region is a representation of an object on a grid.  It is defined
+ * by the top, right, bottom, left extents, so is rectangular by default.  If 
+ * other shapes are required, this class could be extended to support it.
+ *
+ * @param {int} t the top extent
+ * @param {int} r the right extent
+ * @param {int} b the bottom extent
+ * @param {int} l the left extent
+ * @constructor
+ */
+YAHOO.util.Region = function(t, r, b, l) {
+
+    /**
+     * The region's top extent
+     * @type int
+     */
+    this.top = t;
+    
+    /**
+     * The region's top extent as index, for symmetry with set/getXY
+     * @type int
+     */
+    this[1] = t;
+
+    /**
+     * The region's right extent
+     * @type int
+     */
+    this.right = r;
+
+    /**
+     * The region's bottom extent
+     * @type int
+     */
+    this.bottom = b;
+
+    /**
+     * The region's left extent
+     * @type int
+     */
+    this.left = l;
+    
+    /**
+     * The region's left extent as index, for symmetry with set/getXY
+     * @type int
+     */
+    this[0] = l;
+};
+
+/**
+ * Returns true if this region contains the region passed in
+ *
+ * @param  {Region}  region The region to evaluate
+ * @return {boolean}        True if the region is contained with this region, 
+ *                          else false
+ */
+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);
+};
+
+/**
+ * Returns the area of the region
+ *
+ * @return {int} the region's area
+ */
+YAHOO.util.Region.prototype.getArea = function() {
+    return ( (this.bottom - this.top) * (this.right - this.left) );
+};
+
+/**
+ * Returns the region where the passed in region overlaps with this one
+ *
+ * @param  {Region} region The region that intersects
+ * @return {Region}        The overlap region, or null if there is no overlap
+ */
+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;
+    }
+};
+
+/**
+ * Returns the region representing the smallest region that can contain both
+ * the passed in region and this region.
+ *
+ * @param  {Region} region The region that to create the union with
+ * @return {Region}        The union region
+ */
+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);
+};
+
+/**
+ * toString
+ * @return string the region properties
+ */
+YAHOO.util.Region.prototype.toString = function() {
+    return ( "Region {" +
+             "t: "    + this.top    + 
+             ", r: "    + this.right  + 
+             ", b: "    + this.bottom + 
+             ", l: "    + this.left   + 
+             "}" );
+};
+
+/**
+ * Returns a region that is occupied by the DOM element
+ *
+ * @param  {HTMLElement} el The element
+ * @return {Region}         The region that the element occupies
+ * @static
+ */
+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);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * @class
+ *
+ * A point is a region that is special in that it represents a single point on 
+ * the grid.
+ *
+ * @param {int} x The X position of the point
+ * @param {int} y The Y position of the point
+ * @constructor
+ * @extends Region
+ */
+YAHOO.util.Point = function(x, y) {
+    /**
+     * The X position of the point
+     * @type int
+     */
+    this.x      = x;
+
+    /**
+     * The Y position of the point
+     * @type int
+     */
+    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();
+

Added: jifty/trunk/share/web/static/js/yui/event.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/yui/event.js	Fri Jun 23 22:09:51 2006
@@ -0,0 +1,1096 @@
+/*                                                                                                                                                      
+Copyright (c) 2006, Yahoo! Inc. All rights reserved.                                                                                                    
+Code licensed under the BSD License:                                                                                                                    
+http://developer.yahoo.net/yui/license.txt                                                                                                              
+version: 0.10.0                                                                                                                                         
+*/ 
+
+/**
+ * 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}   obj      An 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} fn  The 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}   obj      An 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);
+};
+
+/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
+
+// 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 at 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}   el        The html element to assign the 
+             *                             event to
+             * @param {String}   sType     The type of event to append
+             * @param {Function} fn        The method the event invokes
+             * @param {Object}   oScope    An 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
+             */
+            getTime: function(ev) {
+                if (!ev.time) {
+                    var t = new Date().getTime();
+                    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();
+
+}
+

Added: jifty/trunk/share/web/static/js/yui/yahoo.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/yui/yahoo.js	Fri Jun 23 22:09:51 2006
@@ -0,0 +1,61 @@
+/*                                                                                                                                                      
+Copyright (c) 2006, Yahoo! Inc. All rights reserved.                                                                                                    
+Code licensed under the BSD License:                                                                                                                    
+http://developer.yahoo.net/yui/license.txt                                                                                                              
+version: 0.10.0                                                                                                                                         
+*/ 
+
+/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
+
+/**
+ * The Yahoo global namespace
+ * @constructor
+ */
+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");


More information about the Jifty-commit mailing list