[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/2in3/2.9.0/build/yui2-calendar/ -> yui2-calendar.js (source)

   1  YUI.add('yui2-datemath', function(Y) { Y.use('yui2-calendar'); }, '3.3.0' ,{"requires": ["yui2-yahoo"]});
   2  YUI.add('yui2-calendar', function(Y) {
   3      var YAHOO    = Y.YUI2;
   4      /*
   5  Copyright (c) 2011, Yahoo! Inc. All rights reserved.
   6  Code licensed under the BSD License:
   7  http://developer.yahoo.com/yui/license.html
   8  version: 2.9.0
   9  */
  10  (function () {
  11  
  12      /**
  13      * Config is a utility used within an Object to allow the implementer to
  14      * maintain a list of local configuration properties and listen for changes 
  15      * to those properties dynamically using CustomEvent. The initial values are 
  16      * also maintained so that the configuration can be reset at any given point 
  17      * to its initial state.
  18      * @namespace YAHOO.util
  19      * @class Config
  20      * @constructor
  21      * @param {Object} owner The owner Object to which this Config Object belongs
  22      */
  23      YAHOO.util.Config = function (owner) {
  24  
  25          if (owner) {
  26              this.init(owner);
  27          }
  28  
  29  
  30      };
  31  
  32  
  33      var Lang = YAHOO.lang,
  34          CustomEvent = YAHOO.util.CustomEvent,
  35          Config = YAHOO.util.Config;
  36  
  37  
  38      /**
  39       * Constant representing the CustomEvent type for the config changed event.
  40       * @property YAHOO.util.Config.CONFIG_CHANGED_EVENT
  41       * @private
  42       * @static
  43       * @final
  44       */
  45      Config.CONFIG_CHANGED_EVENT = "configChanged";
  46      
  47      /**
  48       * Constant representing the boolean type string
  49       * @property YAHOO.util.Config.BOOLEAN_TYPE
  50       * @private
  51       * @static
  52       * @final
  53       */
  54      Config.BOOLEAN_TYPE = "boolean";
  55      
  56      Config.prototype = {
  57       
  58          /**
  59          * Object reference to the owner of this Config Object
  60          * @property owner
  61          * @type Object
  62          */
  63          owner: null,
  64          
  65          /**
  66          * Boolean flag that specifies whether a queue is currently 
  67          * being executed
  68          * @property queueInProgress
  69          * @type Boolean
  70          */
  71          queueInProgress: false,
  72          
  73          /**
  74          * Maintains the local collection of configuration property objects and 
  75          * their specified values
  76          * @property config
  77          * @private
  78          * @type Object
  79          */ 
  80          config: null,
  81          
  82          /**
  83          * Maintains the local collection of configuration property objects as 
  84          * they were initially applied.
  85          * This object is used when resetting a property.
  86          * @property initialConfig
  87          * @private
  88          * @type Object
  89          */ 
  90          initialConfig: null,
  91          
  92          /**
  93          * Maintains the local, normalized CustomEvent queue
  94          * @property eventQueue
  95          * @private
  96          * @type Object
  97          */ 
  98          eventQueue: null,
  99          
 100          /**
 101          * Custom Event, notifying subscribers when Config properties are set 
 102          * (setProperty is called without the silent flag
 103          * @event configChangedEvent
 104          */
 105          configChangedEvent: null,
 106      
 107          /**
 108          * Initializes the configuration Object and all of its local members.
 109          * @method init
 110          * @param {Object} owner The owner Object to which this Config 
 111          * Object belongs
 112          */
 113          init: function (owner) {
 114      
 115              this.owner = owner;
 116      
 117              this.configChangedEvent = 
 118                  this.createEvent(Config.CONFIG_CHANGED_EVENT);
 119      
 120              this.configChangedEvent.signature = CustomEvent.LIST;
 121              this.queueInProgress = false;
 122              this.config = {};
 123              this.initialConfig = {};
 124              this.eventQueue = [];
 125          
 126          },
 127          
 128          /**
 129          * Validates that the value passed in is a Boolean.
 130          * @method checkBoolean
 131          * @param {Object} val The value to validate
 132          * @return {Boolean} true, if the value is valid
 133          */ 
 134          checkBoolean: function (val) {
 135              return (typeof val == Config.BOOLEAN_TYPE);
 136          },
 137          
 138          /**
 139          * Validates that the value passed in is a number.
 140          * @method checkNumber
 141          * @param {Object} val The value to validate
 142          * @return {Boolean} true, if the value is valid
 143          */
 144          checkNumber: function (val) {
 145              return (!isNaN(val));
 146          },
 147          
 148          /**
 149          * Fires a configuration property event using the specified value. 
 150          * @method fireEvent
 151          * @private
 152          * @param {String} key The configuration property's name
 153          * @param {value} Object The value of the correct type for the property
 154          */ 
 155          fireEvent: function ( key, value ) {
 156              var property = this.config[key];
 157          
 158              if (property && property.event) {
 159                  property.event.fire(value);
 160              } 
 161          },
 162          
 163          /**
 164          * Adds a property to the Config Object's private config hash.
 165          * @method addProperty
 166          * @param {String} key The configuration property's name
 167          * @param {Object} propertyObject The Object containing all of this 
 168          * property's arguments
 169          */
 170          addProperty: function ( key, propertyObject ) {
 171              key = key.toLowerCase();
 172          
 173              this.config[key] = propertyObject;
 174          
 175              propertyObject.event = this.createEvent(key, { scope: this.owner });
 176              propertyObject.event.signature = CustomEvent.LIST;
 177              
 178              
 179              propertyObject.key = key;
 180          
 181              if (propertyObject.handler) {
 182                  propertyObject.event.subscribe(propertyObject.handler, 
 183                      this.owner);
 184              }
 185          
 186              this.setProperty(key, propertyObject.value, true);
 187              
 188              if (! propertyObject.suppressEvent) {
 189                  this.queueProperty(key, propertyObject.value);
 190              }
 191              
 192          },
 193          
 194          /**
 195          * Returns a key-value configuration map of the values currently set in  
 196          * the Config Object.
 197          * @method getConfig
 198          * @return {Object} The current config, represented in a key-value map
 199          */
 200          getConfig: function () {
 201          
 202              var cfg = {},
 203                  currCfg = this.config,
 204                  prop,
 205                  property;
 206                  
 207              for (prop in currCfg) {
 208                  if (Lang.hasOwnProperty(currCfg, prop)) {
 209                      property = currCfg[prop];
 210                      if (property && property.event) {
 211                          cfg[prop] = property.value;
 212                      }
 213                  }
 214              }
 215  
 216              return cfg;
 217          },
 218          
 219          /**
 220          * Returns the value of specified property.
 221          * @method getProperty
 222          * @param {String} key The name of the property
 223          * @return {Object}  The value of the specified property
 224          */
 225          getProperty: function (key) {
 226              var property = this.config[key.toLowerCase()];
 227              if (property && property.event) {
 228                  return property.value;
 229              } else {
 230                  return undefined;
 231              }
 232          },
 233          
 234          /**
 235          * Resets the specified property's value to its initial value.
 236          * @method resetProperty
 237          * @param {String} key The name of the property
 238          * @return {Boolean} True is the property was reset, false if not
 239          */
 240          resetProperty: function (key) {
 241              key = key.toLowerCase();
 242  
 243              var property = this.config[key];
 244  
 245              if (property && property.event) {
 246                  if (key in this.initialConfig) {
 247                      this.setProperty(key, this.initialConfig[key]);
 248                      return true;
 249                  }
 250              } else {
 251                  return false;
 252              }
 253          },
 254          
 255          /**
 256          * Sets the value of a property. If the silent property is passed as 
 257          * true, the property's event will not be fired.
 258          * @method setProperty
 259          * @param {String} key The name of the property
 260          * @param {String} value The value to set the property to
 261          * @param {Boolean} silent Whether the value should be set silently, 
 262          * without firing the property event.
 263          * @return {Boolean} True, if the set was successful, false if it failed.
 264          */
 265          setProperty: function (key, value, silent) {
 266          
 267              var property;
 268          
 269              key = key.toLowerCase();
 270          
 271              if (this.queueInProgress && ! silent) {
 272                  // Currently running through a queue... 
 273                  this.queueProperty(key,value);
 274                  return true;
 275      
 276              } else {
 277                  property = this.config[key];
 278                  if (property && property.event) {
 279                      if (property.validator && !property.validator(value)) {
 280                          return false;
 281                      } else {
 282                          property.value = value;
 283                          if (! silent) {
 284                              this.fireEvent(key, value);
 285                              this.configChangedEvent.fire([key, value]);
 286                          }
 287                          return true;
 288                      }
 289                  } else {
 290                      return false;
 291                  }
 292              }
 293          },
 294          
 295          /**
 296          * Sets the value of a property and queues its event to execute. If the 
 297          * event is already scheduled to execute, it is
 298          * moved from its current position to the end of the queue.
 299          * @method queueProperty
 300          * @param {String} key The name of the property
 301          * @param {String} value The value to set the property to
 302          * @return {Boolean}  true, if the set was successful, false if 
 303          * it failed.
 304          */ 
 305          queueProperty: function (key, value) {
 306          
 307              key = key.toLowerCase();
 308          
 309              var property = this.config[key],
 310                  foundDuplicate = false,
 311                  iLen,
 312                  queueItem,
 313                  queueItemKey,
 314                  queueItemValue,
 315                  sLen,
 316                  supercedesCheck,
 317                  qLen,
 318                  queueItemCheck,
 319                  queueItemCheckKey,
 320                  queueItemCheckValue,
 321                  i,
 322                  s,
 323                  q;
 324                                  
 325              if (property && property.event) {
 326      
 327                  if (!Lang.isUndefined(value) && property.validator && 
 328                      !property.validator(value)) { // validator
 329                      return false;
 330                  } else {
 331          
 332                      if (!Lang.isUndefined(value)) {
 333                          property.value = value;
 334                      } else {
 335                          value = property.value;
 336                      }
 337          
 338                      foundDuplicate = false;
 339                      iLen = this.eventQueue.length;
 340          
 341                      for (i = 0; i < iLen; i++) {
 342                          queueItem = this.eventQueue[i];
 343          
 344                          if (queueItem) {
 345                              queueItemKey = queueItem[0];
 346                              queueItemValue = queueItem[1];
 347  
 348                              if (queueItemKey == key) {
 349      
 350                                  /*
 351                                      found a dupe... push to end of queue, null 
 352                                      current item, and break
 353                                  */
 354      
 355                                  this.eventQueue[i] = null;
 356      
 357                                  this.eventQueue.push(
 358                                      [key, (!Lang.isUndefined(value) ? 
 359                                      value : queueItemValue)]);
 360      
 361                                  foundDuplicate = true;
 362                                  break;
 363                              }
 364                          }
 365                      }
 366                      
 367                      // this is a refire, or a new property in the queue
 368      
 369                      if (! foundDuplicate && !Lang.isUndefined(value)) { 
 370                          this.eventQueue.push([key, value]);
 371                      }
 372                  }
 373          
 374                  if (property.supercedes) {
 375  
 376                      sLen = property.supercedes.length;
 377  
 378                      for (s = 0; s < sLen; s++) {
 379  
 380                          supercedesCheck = property.supercedes[s];
 381                          qLen = this.eventQueue.length;
 382  
 383                          for (q = 0; q < qLen; q++) {
 384                              queueItemCheck = this.eventQueue[q];
 385  
 386                              if (queueItemCheck) {
 387                                  queueItemCheckKey = queueItemCheck[0];
 388                                  queueItemCheckValue = queueItemCheck[1];
 389  
 390                                  if (queueItemCheckKey == 
 391                                      supercedesCheck.toLowerCase() ) {
 392  
 393                                      this.eventQueue.push([queueItemCheckKey, 
 394                                          queueItemCheckValue]);
 395  
 396                                      this.eventQueue[q] = null;
 397                                      break;
 398  
 399                                  }
 400                              }
 401                          }
 402                      }
 403                  }
 404  
 405  
 406                  return true;
 407              } else {
 408                  return false;
 409              }
 410          },
 411          
 412          /**
 413          * Fires the event for a property using the property's current value.
 414          * @method refireEvent
 415          * @param {String} key The name of the property
 416          */
 417          refireEvent: function (key) {
 418      
 419              key = key.toLowerCase();
 420          
 421              var property = this.config[key];
 422      
 423              if (property && property.event && 
 424      
 425                  !Lang.isUndefined(property.value)) {
 426      
 427                  if (this.queueInProgress) {
 428      
 429                      this.queueProperty(key);
 430      
 431                  } else {
 432      
 433                      this.fireEvent(key, property.value);
 434      
 435                  }
 436      
 437              }
 438          },
 439          
 440          /**
 441          * Applies a key-value Object literal to the configuration, replacing  
 442          * any existing values, and queueing the property events.
 443          * Although the values will be set, fireQueue() must be called for their 
 444          * associated events to execute.
 445          * @method applyConfig
 446          * @param {Object} userConfig The configuration Object literal
 447          * @param {Boolean} init  When set to true, the initialConfig will 
 448          * be set to the userConfig passed in, so that calling a reset will 
 449          * reset the properties to the passed values.
 450          */
 451          applyConfig: function (userConfig, init) {
 452          
 453              var sKey,
 454                  oConfig;
 455  
 456              if (init) {
 457                  oConfig = {};
 458                  for (sKey in userConfig) {
 459                      if (Lang.hasOwnProperty(userConfig, sKey)) {
 460                          oConfig[sKey.toLowerCase()] = userConfig[sKey];
 461                      }
 462                  }
 463                  this.initialConfig = oConfig;
 464              }
 465  
 466              for (sKey in userConfig) {
 467                  if (Lang.hasOwnProperty(userConfig, sKey)) {
 468                      this.queueProperty(sKey, userConfig[sKey]);
 469                  }
 470              }
 471          },
 472          
 473          /**
 474          * Refires the events for all configuration properties using their 
 475          * current values.
 476          * @method refresh
 477          */
 478          refresh: function () {
 479  
 480              var prop;
 481  
 482              for (prop in this.config) {
 483                  if (Lang.hasOwnProperty(this.config, prop)) {
 484                      this.refireEvent(prop);
 485                  }
 486              }
 487          },
 488          
 489          /**
 490          * Fires the normalized list of queued property change events
 491          * @method fireQueue
 492          */
 493          fireQueue: function () {
 494          
 495              var i, 
 496                  queueItem,
 497                  key,
 498                  value,
 499                  property;
 500          
 501              this.queueInProgress = true;
 502              for (i = 0;i < this.eventQueue.length; i++) {
 503                  queueItem = this.eventQueue[i];
 504                  if (queueItem) {
 505          
 506                      key = queueItem[0];
 507                      value = queueItem[1];
 508                      property = this.config[key];
 509  
 510                      property.value = value;
 511  
 512                      // Clear out queue entry, to avoid it being 
 513                      // re-added to the queue by any queueProperty/supercedes
 514                      // calls which are invoked during fireEvent
 515                      this.eventQueue[i] = null;
 516  
 517                      this.fireEvent(key,value);
 518                  }
 519              }
 520              
 521              this.queueInProgress = false;
 522              this.eventQueue = [];
 523          },
 524          
 525          /**
 526          * Subscribes an external handler to the change event for any 
 527          * given property. 
 528          * @method subscribeToConfigEvent
 529          * @param {String} key The property name
 530          * @param {Function} handler The handler function to use subscribe to 
 531          * the property's event
 532          * @param {Object} obj The Object to use for scoping the event handler 
 533          * (see CustomEvent documentation)
 534          * @param {Boolean} overrideContext Optional. If true, will override
 535          * "this" within the handler to map to the scope Object passed into the
 536          * method.
 537          * @return {Boolean} True, if the subscription was successful, 
 538          * otherwise false.
 539          */ 
 540          subscribeToConfigEvent: function (key, handler, obj, overrideContext) {
 541      
 542              var property = this.config[key.toLowerCase()];
 543      
 544              if (property && property.event) {
 545                  if (!Config.alreadySubscribed(property.event, handler, obj)) {
 546                      property.event.subscribe(handler, obj, overrideContext);
 547                  }
 548                  return true;
 549              } else {
 550                  return false;
 551              }
 552      
 553          },
 554          
 555          /**
 556          * Unsubscribes an external handler from the change event for any 
 557          * given property. 
 558          * @method unsubscribeFromConfigEvent
 559          * @param {String} key The property name
 560          * @param {Function} handler The handler function to use subscribe to 
 561          * the property's event
 562          * @param {Object} obj The Object to use for scoping the event 
 563          * handler (see CustomEvent documentation)
 564          * @return {Boolean} True, if the unsubscription was successful, 
 565          * otherwise false.
 566          */
 567          unsubscribeFromConfigEvent: function (key, handler, obj) {
 568              var property = this.config[key.toLowerCase()];
 569              if (property && property.event) {
 570                  return property.event.unsubscribe(handler, obj);
 571              } else {
 572                  return false;
 573              }
 574          },
 575          
 576          /**
 577          * Returns a string representation of the Config object
 578          * @method toString
 579          * @return {String} The Config object in string format.
 580          */
 581          toString: function () {
 582              var output = "Config";
 583              if (this.owner) {
 584                  output += " [" + this.owner.toString() + "]";
 585              }
 586              return output;
 587          },
 588          
 589          /**
 590          * Returns a string representation of the Config object's current 
 591          * CustomEvent queue
 592          * @method outputEventQueue
 593          * @return {String} The string list of CustomEvents currently queued 
 594          * for execution
 595          */
 596          outputEventQueue: function () {
 597  
 598              var output = "",
 599                  queueItem,
 600                  q,
 601                  nQueue = this.eventQueue.length;
 602                
 603              for (q = 0; q < nQueue; q++) {
 604                  queueItem = this.eventQueue[q];
 605                  if (queueItem) {
 606                      output += queueItem[0] + "=" + queueItem[1] + ", ";
 607                  }
 608              }
 609              return output;
 610          },
 611  
 612          /**
 613          * Sets all properties to null, unsubscribes all listeners from each 
 614          * property's change event and all listeners from the configChangedEvent.
 615          * @method destroy
 616          */
 617          destroy: function () {
 618  
 619              var oConfig = this.config,
 620                  sProperty,
 621                  oProperty;
 622  
 623  
 624              for (sProperty in oConfig) {
 625              
 626                  if (Lang.hasOwnProperty(oConfig, sProperty)) {
 627  
 628                      oProperty = oConfig[sProperty];
 629  
 630                      oProperty.event.unsubscribeAll();
 631                      oProperty.event = null;
 632  
 633                  }
 634              
 635              }
 636              
 637              this.configChangedEvent.unsubscribeAll();
 638              
 639              this.configChangedEvent = null;
 640              this.owner = null;
 641              this.config = null;
 642              this.initialConfig = null;
 643              this.eventQueue = null;
 644          
 645          }
 646  
 647      };
 648      
 649      
 650      
 651      /**
 652      * Checks to determine if a particular function/Object pair are already 
 653      * subscribed to the specified CustomEvent
 654      * @method YAHOO.util.Config.alreadySubscribed
 655      * @static
 656      * @param {YAHOO.util.CustomEvent} evt The CustomEvent for which to check 
 657      * the subscriptions
 658      * @param {Function} fn The function to look for in the subscribers list
 659      * @param {Object} obj The execution scope Object for the subscription
 660      * @return {Boolean} true, if the function/Object pair is already subscribed 
 661      * to the CustomEvent passed in
 662      */
 663      Config.alreadySubscribed = function (evt, fn, obj) {
 664      
 665          var nSubscribers = evt.subscribers.length,
 666              subsc,
 667              i;
 668  
 669          if (nSubscribers > 0) {
 670              i = nSubscribers - 1;
 671              do {
 672                  subsc = evt.subscribers[i];
 673                  if (subsc && subsc.obj == obj && subsc.fn == fn) {
 674                      return true;
 675                  }
 676              }
 677              while (i--);
 678          }
 679  
 680          return false;
 681  
 682      };
 683  
 684      YAHOO.lang.augmentProto(Config, YAHOO.util.EventProvider);
 685  
 686  }());
 687  /**
 688  * The datemath module provides utility methods for basic JavaScript Date object manipulation and 
 689  * comparison. 
 690  * 
 691  * @module datemath
 692  */
 693  
 694  /**
 695  * YAHOO.widget.DateMath is used for simple date manipulation. The class is a static utility
 696  * used for adding, subtracting, and comparing dates.
 697  * @namespace YAHOO.widget
 698  * @class DateMath
 699  */
 700  YAHOO.widget.DateMath = {
 701      /**
 702      * Constant field representing Day
 703      * @property DAY
 704      * @static
 705      * @final
 706      * @type String
 707      */
 708      DAY : "D",
 709  
 710      /**
 711      * Constant field representing Week
 712      * @property WEEK
 713      * @static
 714      * @final
 715      * @type String
 716      */
 717      WEEK : "W",
 718  
 719      /**
 720      * Constant field representing Year
 721      * @property YEAR
 722      * @static
 723      * @final
 724      * @type String
 725      */
 726      YEAR : "Y",
 727  
 728      /**
 729      * Constant field representing Month
 730      * @property MONTH
 731      * @static
 732      * @final
 733      * @type String
 734      */
 735      MONTH : "M",
 736  
 737      /**
 738      * Constant field representing one day, in milliseconds
 739      * @property ONE_DAY_MS
 740      * @static
 741      * @final
 742      * @type Number
 743      */
 744      ONE_DAY_MS : 1000*60*60*24,
 745      
 746      /**
 747       * Constant field representing the date in first week of January
 748       * which identifies the first week of the year.
 749       * <p>
 750       * In the U.S, Jan 1st is normally used based on a Sunday start of week.
 751       * ISO 8601, used widely throughout Europe, uses Jan 4th, based on a Monday start of week.
 752       * </p>
 753       * @property WEEK_ONE_JAN_DATE
 754       * @static
 755       * @type Number
 756       */
 757      WEEK_ONE_JAN_DATE : 1,
 758  
 759      /**
 760      * Adds the specified amount of time to the this instance.
 761      * @method add
 762      * @param {Date} date The JavaScript Date object to perform addition on
 763      * @param {String} field The field constant to be used for performing addition.
 764      * @param {Number} amount The number of units (measured in the field constant) to add to the date.
 765      * @return {Date} The resulting Date object
 766      */
 767      add : function(date, field, amount) {
 768          var d = new Date(date.getTime());
 769          switch (field) {
 770              case this.MONTH:
 771                  var newMonth = date.getMonth() + amount;
 772                  var years = 0;
 773  
 774                  if (newMonth < 0) {
 775                      while (newMonth < 0) {
 776                          newMonth += 12;
 777                          years -= 1;
 778                      }
 779                  } else if (newMonth > 11) {
 780                      while (newMonth > 11) {
 781                          newMonth -= 12;
 782                          years += 1;
 783                      }
 784                  }
 785  
 786                  d.setMonth(newMonth);
 787                  d.setFullYear(date.getFullYear() + years);
 788                  break;
 789              case this.DAY:
 790                  this._addDays(d, amount);
 791                  // d.setDate(date.getDate() + amount);
 792                  break;
 793              case this.YEAR:
 794                  d.setFullYear(date.getFullYear() + amount);
 795                  break;
 796              case this.WEEK:
 797                  this._addDays(d, (amount * 7));
 798                  // d.setDate(date.getDate() + (amount * 7));
 799                  break;
 800          }
 801          return d;
 802      },
 803  
 804      /**
 805       * Private helper method to account for bug in Safari 2 (webkit < 420)
 806       * when Date.setDate(n) is called with n less than -128 or greater than 127.
 807       * <p>
 808       * Fix approach and original findings are available here:
 809       * http://brianary.blogspot.com/2006/03/safari-date-bug.html
 810       * </p>
 811       * @method _addDays
 812       * @param {Date} d JavaScript date object
 813       * @param {Number} nDays The number of days to add to the date object (can be negative)
 814       * @private
 815       */
 816      _addDays : function(d, nDays) {
 817          if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420) {
 818              if (nDays < 0) {
 819                  // Ensure we don't go below -128 (getDate() is always 1 to 31, so we won't go above 127)
 820                  for(var min = -128; nDays < min; nDays -= min) {
 821                      d.setDate(d.getDate() + min);
 822                  }
 823              } else {
 824                  // Ensure we don't go above 96 + 31 = 127
 825                  for(var max = 96; nDays > max; nDays -= max) {
 826                      d.setDate(d.getDate() + max);
 827                  }
 828              }
 829              // nDays should be remainder between -128 and 96
 830          }
 831          d.setDate(d.getDate() + nDays);
 832      },
 833  
 834      /**
 835      * Subtracts the specified amount of time from the this instance.
 836      * @method subtract
 837      * @param {Date} date The JavaScript Date object to perform subtraction on
 838      * @param {Number} field The this field constant to be used for performing subtraction.
 839      * @param {Number} amount The number of units (measured in the field constant) to subtract from the date.
 840      * @return {Date} The resulting Date object
 841      */
 842      subtract : function(date, field, amount) {
 843          return this.add(date, field, (amount*-1));
 844      },
 845  
 846      /**
 847      * Determines whether a given date is before another date on the calendar.
 848      * @method before
 849      * @param {Date} date  The Date object to compare with the compare argument
 850      * @param {Date} compareTo The Date object to use for the comparison
 851      * @return {Boolean} true if the date occurs before the compared date; false if not.
 852      */
 853      before : function(date, compareTo) {
 854          var ms = compareTo.getTime();
 855          if (date.getTime() < ms) {
 856              return true;
 857          } else {
 858              return false;
 859          }
 860      },
 861  
 862      /**
 863      * Determines whether a given date is after another date on the calendar.
 864      * @method after
 865      * @param {Date} date  The Date object to compare with the compare argument
 866      * @param {Date} compareTo The Date object to use for the comparison
 867      * @return {Boolean} true if the date occurs after the compared date; false if not.
 868      */
 869      after : function(date, compareTo) {
 870          var ms = compareTo.getTime();
 871          if (date.getTime() > ms) {
 872              return true;
 873          } else {
 874              return false;
 875          }
 876      },
 877  
 878      /**
 879      * Determines whether a given date is between two other dates on the calendar.
 880      * @method between
 881      * @param {Date} date  The date to check for
 882      * @param {Date} dateBegin The start of the range
 883      * @param {Date} dateEnd  The end of the range
 884      * @return {Boolean} true if the date occurs between the compared dates; false if not.
 885      */
 886      between : function(date, dateBegin, dateEnd) {
 887          if (this.after(date, dateBegin) && this.before(date, dateEnd)) {
 888              return true;
 889          } else {
 890              return false;
 891          }
 892      },
 893      
 894      /**
 895      * Retrieves a JavaScript Date object representing January 1 of any given year.
 896      * @method getJan1
 897      * @param {Number} calendarYear  The calendar year for which to retrieve January 1
 898      * @return {Date} January 1 of the calendar year specified.
 899      */
 900      getJan1 : function(calendarYear) {
 901          return this.getDate(calendarYear,0,1);
 902      },
 903  
 904      /**
 905      * Calculates the number of days the specified date is from January 1 of the specified calendar year.
 906      * Passing January 1 to this function would return an offset value of zero.
 907      * @method getDayOffset
 908      * @param {Date} date The JavaScript date for which to find the offset
 909      * @param {Number} calendarYear The calendar year to use for determining the offset
 910      * @return {Number} The number of days since January 1 of the given year
 911      */
 912      getDayOffset : function(date, calendarYear) {
 913          var beginYear = this.getJan1(calendarYear); // Find the start of the year. This will be in week 1.
 914          
 915          // Find the number of days the passed in date is away from the calendar year start
 916          var dayOffset = Math.ceil((date.getTime()-beginYear.getTime()) / this.ONE_DAY_MS);
 917          return dayOffset;
 918      },
 919  
 920      /**
 921      * Calculates the week number for the given date. Can currently support standard
 922      * U.S. week numbers, based on Jan 1st defining the 1st week of the year, and 
 923      * ISO8601 week numbers, based on Jan 4th defining the 1st week of the year.
 924      * 
 925      * @method getWeekNumber
 926      * @param {Date} date The JavaScript date for which to find the week number
 927      * @param {Number} firstDayOfWeek The index of the first day of the week (0 = Sun, 1 = Mon ... 6 = Sat).
 928      * Defaults to 0
 929      * @param {Number} janDate The date in the first week of January which defines week one for the year
 930      * Defaults to the value of YAHOO.widget.DateMath.WEEK_ONE_JAN_DATE, which is 1 (Jan 1st). 
 931      * For the U.S, this is normally Jan 1st. ISO8601 uses Jan 4th to define the first week of the year.
 932      * 
 933      * @return {Number} The number of the week containing the given date.
 934      */
 935      getWeekNumber : function(date, firstDayOfWeek, janDate) {
 936  
 937          // Setup Defaults
 938          firstDayOfWeek = firstDayOfWeek || 0;
 939          janDate = janDate || this.WEEK_ONE_JAN_DATE;
 940  
 941          var targetDate = this.clearTime(date),
 942              startOfWeek,
 943              endOfWeek;
 944  
 945          if (targetDate.getDay() === firstDayOfWeek) { 
 946              startOfWeek = targetDate;
 947          } else {
 948              startOfWeek = this.getFirstDayOfWeek(targetDate, firstDayOfWeek);
 949          }
 950  
 951          var startYear = startOfWeek.getFullYear();
 952  
 953          // DST shouldn't be a problem here, math is quicker than setDate();
 954          endOfWeek = new Date(startOfWeek.getTime() + 6*this.ONE_DAY_MS);
 955  
 956          var weekNum;
 957          if (startYear !== endOfWeek.getFullYear() && endOfWeek.getDate() >= janDate) {
 958              // If years don't match, endOfWeek is in Jan. and if the 
 959              // week has WEEK_ONE_JAN_DATE in it, it's week one by definition.
 960              weekNum = 1;
 961          } else {
 962              // Get the 1st day of the 1st week, and 
 963              // find how many days away we are from it.
 964              var weekOne = this.clearTime(this.getDate(startYear, 0, janDate)),
 965                  weekOneDayOne = this.getFirstDayOfWeek(weekOne, firstDayOfWeek);
 966  
 967              // Round days to smoothen out 1 hr DST diff
 968              var daysDiff  = Math.round((targetDate.getTime() - weekOneDayOne.getTime())/this.ONE_DAY_MS);
 969  
 970              // Calc. Full Weeks
 971              var rem = daysDiff % 7;
 972              var weeksDiff = (daysDiff - rem)/7;
 973              weekNum = weeksDiff + 1;
 974          }
 975          return weekNum;
 976      },
 977  
 978      /**
 979       * Get the first day of the week, for the give date. 
 980       * @param {Date} dt The date in the week for which the first day is required.
 981       * @param {Number} startOfWeek The index for the first day of the week, 0 = Sun, 1 = Mon ... 6 = Sat (defaults to 0)
 982       * @return {Date} The first day of the week
 983       */
 984      getFirstDayOfWeek : function (dt, startOfWeek) {
 985          startOfWeek = startOfWeek || 0;
 986          var dayOfWeekIndex = dt.getDay(),
 987              dayOfWeek = (dayOfWeekIndex - startOfWeek + 7) % 7;
 988  
 989          return this.subtract(dt, this.DAY, dayOfWeek);
 990      },
 991  
 992      /**
 993      * Determines if a given week overlaps two different years.
 994      * @method isYearOverlapWeek
 995      * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week.
 996      * @return {Boolean} true if the date overlaps two different years.
 997      */
 998      isYearOverlapWeek : function(weekBeginDate) {
 999          var overlaps = false;
1000          var nextWeek = this.add(weekBeginDate, this.DAY, 6);
1001          if (nextWeek.getFullYear() != weekBeginDate.getFullYear()) {
1002              overlaps = true;
1003          }
1004          return overlaps;
1005      },
1006  
1007      /**
1008      * Determines if a given week overlaps two different months.
1009      * @method isMonthOverlapWeek
1010      * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week.
1011      * @return {Boolean} true if the date overlaps two different months.
1012      */
1013      isMonthOverlapWeek : function(weekBeginDate) {
1014          var overlaps = false;
1015          var nextWeek = this.add(weekBeginDate, this.DAY, 6);
1016          if (nextWeek.getMonth() != weekBeginDate.getMonth()) {
1017              overlaps = true;
1018          }
1019          return overlaps;
1020      },
1021  
1022      /**
1023      * Gets the first day of a month containing a given date.
1024      * @method findMonthStart
1025      * @param {Date} date The JavaScript Date used to calculate the month start
1026      * @return {Date}  The JavaScript Date representing the first day of the month
1027      */
1028      findMonthStart : function(date) {
1029          var start = this.getDate(date.getFullYear(), date.getMonth(), 1);
1030          return start;
1031      },
1032  
1033      /**
1034      * Gets the last day of a month containing a given date.
1035      * @method findMonthEnd
1036      * @param {Date} date The JavaScript Date used to calculate the month end
1037      * @return {Date}  The JavaScript Date representing the last day of the month
1038      */
1039      findMonthEnd : function(date) {
1040          var start = this.findMonthStart(date);
1041          var nextMonth = this.add(start, this.MONTH, 1);
1042          var end = this.subtract(nextMonth, this.DAY, 1);
1043          return end;
1044      },
1045  
1046      /**
1047      * Clears the time fields from a given date, effectively setting the time to 12 noon.
1048      * @method clearTime
1049      * @param {Date} date The JavaScript Date for which the time fields will be cleared
1050      * @return {Date}  The JavaScript Date cleared of all time fields
1051      */
1052      clearTime : function(date) {
1053          date.setHours(12,0,0,0);
1054          return date;
1055      },
1056  
1057      /**
1058       * Returns a new JavaScript Date object, representing the given year, month and date. Time fields (hr, min, sec, ms) on the new Date object
1059       * are set to 0, except the hour which is set to 12 (noon) to avoid issues around the start of Daylight Savings Time. The method allows Date
1060       * instances to be created with the a year less than 100. "new Date(year, month, date)" implementations set the year to 19xx if a year (xx)
1061       * which is less than 100 is provided.
1062       * <p>
1063       * <em>NOTE:</em>Validation on argument values is not performed. It is the caller's responsibility to ensure
1064       * arguments are valid as per the ECMAScript-262 Date object specification for the new Date(year, month[, date]) constructor.
1065       * </p>
1066       * @method getDate
1067       * @param {Number} y Year.
1068       * @param {Number} m Month index from 0 (Jan) to 11 (Dec).
1069       * @param {Number} d (optional) Date from 1 to 31. If not provided, defaults to 1.
1070       * @return {Date} The JavaScript date object with year, month, date set as provided.
1071       */
1072      getDate : function(y, m, d) {
1073          var dt = null;
1074          if (YAHOO.lang.isUndefined(d)) {
1075              d = 1;
1076          }
1077          if (y >= 100) {
1078              dt = new Date(y, m, d, 12);
1079          } else {
1080              dt = new Date();
1081              dt.setFullYear(y);
1082              dt.setMonth(m);
1083              dt.setDate(d);
1084              dt.setHours(12,0,0,0);
1085          }
1086          return dt;
1087      }
1088  };
1089  /**
1090  * The Calendar component is a UI control that enables users to choose one or more dates from a graphical calendar presented in a one-month or
1091  * multi-month interface. Calendars are generated entirely via script and can be navigated without any page refreshes.
1092  * @module    calendar
1093  * @title    Calendar
1094  * @namespace  YAHOO.widget
1095  * @requires  yahoo,dom,event
1096  */
1097  (function(){
1098  
1099      var Dom = YAHOO.util.Dom,
1100          Event = YAHOO.util.Event,
1101          Lang = YAHOO.lang,
1102          DateMath = YAHOO.widget.DateMath;
1103  
1104  /**
1105  * Calendar is the base class for the Calendar widget. In its most basic
1106  * implementation, it has the ability to render a calendar widget on the page
1107  * that can be manipulated to select a single date, move back and forth between
1108  * months and years.
1109  * <p>To construct the placeholder for the calendar widget, the code is as
1110  * follows:
1111  *   <xmp>
1112  *       <div id="calContainer"></div>
1113  *   </xmp>
1114  * </p>
1115  * <p>
1116  * <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong>
1117  * The Calendar can be constructed by simply providing a container ID string, 
1118  * or a reference to a container DIV HTMLElement (the element needs to exist 
1119  * in the document).
1120  * 
1121  * E.g.:
1122  *   <xmp>
1123  *       var c = new YAHOO.widget.Calendar("calContainer", configOptions);
1124  *   </xmp>
1125  * or:
1126  *   <xmp>
1127  *       var containerDiv = YAHOO.util.Dom.get("calContainer");
1128  *       var c = new YAHOO.widget.Calendar(containerDiv, configOptions);
1129  *   </xmp>
1130  * </p>
1131  * <p>
1132  * If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix.
1133  * For example if an ID is not provided, and the container's ID is "calContainer", the Calendar's ID will be set to "calContainer_t".
1134  * </p>
1135  * 
1136  * @namespace YAHOO.widget
1137  * @class Calendar
1138  * @constructor
1139  * @param {String} id optional The id of the table element that will represent the Calendar widget. As of 2.4.0, this argument is optional.
1140  * @param {String | HTMLElement} container The id of the container div element that will wrap the Calendar table, or a reference to a DIV element which exists in the document.
1141  * @param {Object} config optional The configuration object containing the initial configuration values for the Calendar.
1142  */
1143  function Calendar(id, containerId, config) {
1144      this.init.apply(this, arguments);
1145  }
1146  
1147  /**
1148  * The path to be used for images loaded for the Calendar
1149  * @property YAHOO.widget.Calendar.IMG_ROOT
1150  * @static
1151  * @deprecated   You can now customize images by overriding the calclose, calnavleft and calnavright default CSS classes for the close icon, left arrow and right arrow respectively
1152  * @type String
1153  */
1154  Calendar.IMG_ROOT = null;
1155  
1156  /**
1157  * Type constant used for renderers to represent an individual date (M/D/Y)
1158  * @property YAHOO.widget.Calendar.DATE
1159  * @static
1160  * @final
1161  * @type String
1162  */
1163  Calendar.DATE = "D";
1164  
1165  /**
1166  * Type constant used for renderers to represent an individual date across any year (M/D)
1167  * @property YAHOO.widget.Calendar.MONTH_DAY
1168  * @static
1169  * @final
1170  * @type String
1171  */
1172  Calendar.MONTH_DAY = "MD";
1173  
1174  /**
1175  * Type constant used for renderers to represent a weekday
1176  * @property YAHOO.widget.Calendar.WEEKDAY
1177  * @static
1178  * @final
1179  * @type String
1180  */
1181  Calendar.WEEKDAY = "WD";
1182  
1183  /**
1184  * Type constant used for renderers to represent a range of individual dates (M/D/Y-M/D/Y)
1185  * @property YAHOO.widget.Calendar.RANGE
1186  * @static
1187  * @final
1188  * @type String
1189  */
1190  Calendar.RANGE = "R";
1191  
1192  /**
1193  * Type constant used for renderers to represent a month across any year
1194  * @property YAHOO.widget.Calendar.MONTH
1195  * @static
1196  * @final
1197  * @type String
1198  */
1199  Calendar.MONTH = "M";
1200  
1201  /**
1202  * Constant that represents the total number of date cells that are displayed in a given month
1203  * @property YAHOO.widget.Calendar.DISPLAY_DAYS
1204  * @static
1205  * @final
1206  * @type Number
1207  */
1208  Calendar.DISPLAY_DAYS = 42;
1209  
1210  /**
1211  * Constant used for halting the execution of the remainder of the render stack
1212  * @property YAHOO.widget.Calendar.STOP_RENDER
1213  * @static
1214  * @final
1215  * @type String
1216  */
1217  Calendar.STOP_RENDER = "S";
1218  
1219  /**
1220  * Constant used to represent short date field string formats (e.g. Tu or Feb)
1221  * @property YAHOO.widget.Calendar.SHORT
1222  * @static
1223  * @final
1224  * @type String
1225  */
1226  Calendar.SHORT = "short";
1227  
1228  /**
1229  * Constant used to represent long date field string formats (e.g. Monday or February)
1230  * @property YAHOO.widget.Calendar.LONG
1231  * @static
1232  * @final
1233  * @type String
1234  */
1235  Calendar.LONG = "long";
1236  
1237  /**
1238  * Constant used to represent medium date field string formats (e.g. Mon)
1239  * @property YAHOO.widget.Calendar.MEDIUM
1240  * @static
1241  * @final
1242  * @type String
1243  */
1244  Calendar.MEDIUM = "medium";
1245  
1246  /**
1247  * Constant used to represent single character date field string formats (e.g. M, T, W)
1248  * @property YAHOO.widget.Calendar.ONE_CHAR
1249  * @static
1250  * @final
1251  * @type String
1252  */
1253  Calendar.ONE_CHAR = "1char";
1254  
1255  /**
1256  * The set of default Config property keys and values for the Calendar.
1257  *
1258  * <p>
1259  * NOTE: This property is made public in order to allow users to change 
1260  * the default values of configuration properties. Users should not 
1261  * modify the key string, unless they are overriding the Calendar implementation
1262  * </p>
1263  *
1264  * <p>
1265  * The property is an object with key/value pairs, the key being the 
1266  * uppercase configuration property name and the value being an object 
1267  * literal with a key string property, and a value property, specifying the 
1268  * default value of the property. To override a default value, you can set
1269  * the value property, for example, <code>YAHOO.widget.Calendar.DEFAULT_CONFIG.MULTI_SELECT.value = true;</code>
1270  * 
1271  * @property YAHOO.widget.Calendar.DEFAULT_CONFIG
1272  * @static
1273  * @type Object
1274  */
1275  
1276  Calendar.DEFAULT_CONFIG = {
1277      YEAR_OFFSET : {key:"year_offset", value:0, supercedes:["pagedate", "selected", "mindate","maxdate"]},
1278      TODAY : {key:"today", value:new Date(), supercedes:["pagedate"]}, 
1279      PAGEDATE : {key:"pagedate", value:null},
1280      SELECTED : {key:"selected", value:[]},
1281      TITLE : {key:"title", value:""},
1282      CLOSE : {key:"close", value:false},
1283      IFRAME : {key:"iframe", value:(YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) ? true : false},
1284      MINDATE : {key:"mindate", value:null},
1285      MAXDATE : {key:"maxdate", value:null},
1286      MULTI_SELECT : {key:"multi_select", value:false},
1287      OOM_SELECT : {key:"oom_select", value:false},
1288      START_WEEKDAY : {key:"start_weekday", value:0},
1289      SHOW_WEEKDAYS : {key:"show_weekdays", value:true},
1290      SHOW_WEEK_HEADER : {key:"show_week_header", value:false},
1291      SHOW_WEEK_FOOTER : {key:"show_week_footer", value:false},
1292      HIDE_BLANK_WEEKS : {key:"hide_blank_weeks", value:false},
1293      NAV_ARROW_LEFT: {key:"nav_arrow_left", value:null} ,
1294      NAV_ARROW_RIGHT : {key:"nav_arrow_right", value:null} ,
1295      MONTHS_SHORT : {key:"months_short", value:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]},
1296      MONTHS_LONG: {key:"months_long", value:["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]},
1297      WEEKDAYS_1CHAR: {key:"weekdays_1char", value:["S", "M", "T", "W", "T", "F", "S"]},
1298      WEEKDAYS_SHORT: {key:"weekdays_short", value:["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]},
1299      WEEKDAYS_MEDIUM: {key:"weekdays_medium", value:["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]},
1300      WEEKDAYS_LONG: {key:"weekdays_long", value:["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]},
1301      LOCALE_MONTHS:{key:"locale_months", value:"long"},
1302      LOCALE_WEEKDAYS:{key:"locale_weekdays", value:"short"},
1303      DATE_DELIMITER:{key:"date_delimiter", value:","},
1304      DATE_FIELD_DELIMITER:{key:"date_field_delimiter", value:"/"},
1305      DATE_RANGE_DELIMITER:{key:"date_range_delimiter", value:"-"},
1306      MY_MONTH_POSITION:{key:"my_month_position", value:1},
1307      MY_YEAR_POSITION:{key:"my_year_position", value:2},
1308      MD_MONTH_POSITION:{key:"md_month_position", value:1},
1309      MD_DAY_POSITION:{key:"md_day_position", value:2},
1310      MDY_MONTH_POSITION:{key:"mdy_month_position", value:1},
1311      MDY_DAY_POSITION:{key:"mdy_day_position", value:2},
1312      MDY_YEAR_POSITION:{key:"mdy_year_position", value:3},
1313      MY_LABEL_MONTH_POSITION:{key:"my_label_month_position", value:1},
1314      MY_LABEL_YEAR_POSITION:{key:"my_label_year_position", value:2},
1315      MY_LABEL_MONTH_SUFFIX:{key:"my_label_month_suffix", value:" "},
1316      MY_LABEL_YEAR_SUFFIX:{key:"my_label_year_suffix", value:""},
1317      NAV: {key:"navigator", value: null},
1318      STRINGS : { 
1319          key:"strings",
1320          value: {
1321              previousMonth : "Previous Month",
1322              nextMonth : "Next Month",
1323              close: "Close"
1324          },
1325          supercedes : ["close", "title"]
1326      }
1327  };
1328  
1329  /**
1330  * The set of default Config property keys and values for the Calendar
1331  * @property YAHOO.widget.Calendar._DEFAULT_CONFIG
1332  * @deprecated Made public. See the public DEFAULT_CONFIG property for details
1333  * @final
1334  * @static
1335  * @private
1336  * @type Object
1337  */
1338  Calendar._DEFAULT_CONFIG = Calendar.DEFAULT_CONFIG;
1339  
1340  var DEF_CFG = Calendar.DEFAULT_CONFIG;
1341  
1342  /**
1343  * The set of Custom Event types supported by the Calendar
1344  * @property YAHOO.widget.Calendar._EVENT_TYPES
1345  * @final
1346  * @static
1347  * @private
1348  * @type Object
1349  */
1350  Calendar._EVENT_TYPES = {
1351      BEFORE_SELECT : "beforeSelect", 
1352      SELECT : "select",
1353      BEFORE_DESELECT : "beforeDeselect",
1354      DESELECT : "deselect",
1355      CHANGE_PAGE : "changePage",
1356      BEFORE_RENDER : "beforeRender",
1357      RENDER : "render",
1358      BEFORE_DESTROY : "beforeDestroy",
1359      DESTROY : "destroy",
1360      RESET : "reset",
1361      CLEAR : "clear",
1362      BEFORE_HIDE : "beforeHide",
1363      HIDE : "hide",
1364      BEFORE_SHOW : "beforeShow",
1365      SHOW : "show",
1366      BEFORE_HIDE_NAV : "beforeHideNav",
1367      HIDE_NAV : "hideNav",
1368      BEFORE_SHOW_NAV : "beforeShowNav",
1369      SHOW_NAV : "showNav",
1370      BEFORE_RENDER_NAV : "beforeRenderNav",
1371      RENDER_NAV : "renderNav"
1372  };
1373  
1374  /**
1375  * The set of default style constants for the Calendar
1376  * @property YAHOO.widget.Calendar.STYLES
1377  * @static
1378  * @type Object An object with name/value pairs for the class name identifier/value.
1379  */
1380  Calendar.STYLES = {
1381      CSS_ROW_HEADER: "calrowhead",
1382      CSS_ROW_FOOTER: "calrowfoot",
1383      CSS_CELL : "calcell",
1384      CSS_CELL_SELECTOR : "selector",
1385      CSS_CELL_SELECTED : "selected",
1386      CSS_CELL_SELECTABLE : "selectable",
1387      CSS_CELL_RESTRICTED : "restricted",
1388      CSS_CELL_TODAY : "today",
1389      CSS_CELL_OOM : "oom",
1390      CSS_CELL_OOB : "previous",
1391      CSS_HEADER : "calheader",
1392      CSS_HEADER_TEXT : "calhead",
1393      CSS_BODY : "calbody",
1394      CSS_WEEKDAY_CELL : "calweekdaycell",
1395      CSS_WEEKDAY_ROW : "calweekdayrow",
1396      CSS_FOOTER : "calfoot",
1397      CSS_CALENDAR : "yui-calendar",
1398      CSS_SINGLE : "single",
1399      CSS_CONTAINER : "yui-calcontainer",
1400      CSS_NAV_LEFT : "calnavleft",
1401      CSS_NAV_RIGHT : "calnavright",
1402      CSS_NAV : "calnav",
1403      CSS_CLOSE : "calclose",
1404      CSS_CELL_TOP : "calcelltop",
1405      CSS_CELL_LEFT : "calcellleft",
1406      CSS_CELL_RIGHT : "calcellright",
1407      CSS_CELL_BOTTOM : "calcellbottom",
1408      CSS_CELL_HOVER : "calcellhover",
1409      CSS_CELL_HIGHLIGHT1 : "highlight1",
1410      CSS_CELL_HIGHLIGHT2 : "highlight2",
1411      CSS_CELL_HIGHLIGHT3 : "highlight3",
1412      CSS_CELL_HIGHLIGHT4 : "highlight4",
1413      CSS_WITH_TITLE: "withtitle",
1414      CSS_FIXED_SIZE: "fixedsize",
1415      CSS_LINK_CLOSE: "link-close"
1416  };
1417  
1418  /**
1419  * The set of default style constants for the Calendar
1420  * @property YAHOO.widget.Calendar._STYLES
1421  * @deprecated Made public. See the public STYLES property for details
1422  * @final
1423  * @static
1424  * @private
1425  * @type Object
1426  */
1427  Calendar._STYLES = Calendar.STYLES;
1428  
1429  Calendar.prototype = {
1430  
1431      /**
1432      * The configuration object used to set up the calendars various locale and style options.
1433      * @property Config
1434      * @private
1435      * @deprecated Configuration properties should be set by calling Calendar.cfg.setProperty.
1436      * @type Object
1437      */
1438      Config : null,
1439  
1440      /**
1441      * The parent CalendarGroup, only to be set explicitly by the parent group
1442      * @property parent
1443      * @type CalendarGroup
1444      */ 
1445      parent : null,
1446  
1447      /**
1448      * The index of this item in the parent group
1449      * @property index
1450      * @type Number
1451      */
1452      index : -1,
1453  
1454      /**
1455      * The collection of calendar table cells
1456      * @property cells
1457      * @type HTMLTableCellElement[]
1458      */
1459      cells : null,
1460  
1461      /**
1462      * 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].
1463      * @property cellDates
1464      * @type Array[](Number[])
1465      */
1466      cellDates : null,
1467  
1468      /**
1469      * The id that uniquely identifies this Calendar.
1470      * @property id
1471      * @type String
1472      */
1473      id : null,
1474  
1475      /**
1476      * The unique id associated with the Calendar's container
1477      * @property containerId
1478      * @type String
1479      */
1480      containerId: null,
1481  
1482      /**
1483      * 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.
1484      * @property oDomContainer
1485      * @type HTMLElement
1486      */
1487      oDomContainer : null,
1488  
1489      /**
1490      * A Date object representing today's date.
1491      * @deprecated Use the "today" configuration property
1492      * @property today
1493      * @type Date
1494      */
1495      today : null,
1496  
1497      /**
1498      * The list of render functions, along with required parameters, used to render cells. 
1499      * @property renderStack
1500      * @type Array[]
1501      */
1502      renderStack : null,
1503  
1504      /**
1505      * A copy of the initial render functions created before rendering.
1506      * @property _renderStack
1507      * @private
1508      * @type Array
1509      */
1510      _renderStack : null,
1511  
1512      /**
1513      * A reference to the CalendarNavigator instance created for this Calendar.
1514      * Will be null if the "navigator" configuration property has not been set
1515      * @property oNavigator
1516      * @type CalendarNavigator
1517      */
1518      oNavigator : null,
1519  
1520      /**
1521      * The private list of initially selected dates.
1522      * @property _selectedDates
1523      * @private
1524      * @type Array
1525      */
1526      _selectedDates : null,
1527  
1528      /**
1529      * A map of DOM event handlers to attach to cells associated with specific CSS class names
1530      * @property domEventMap
1531      * @type Object
1532      */
1533      domEventMap : null,
1534  
1535      /**
1536       * Protected helper used to parse Calendar constructor/init arguments.
1537       *
1538       * As of 2.4.0, Calendar supports a simpler constructor 
1539       * signature. This method reconciles arguments
1540       * received in the pre 2.4.0 and 2.4.0 formats.
1541       * 
1542       * @protected
1543       * @method _parseArgs
1544       * @param {Array} Function "arguments" array
1545       * @return {Object} Object with id, container, config properties containing
1546       * the reconciled argument values.
1547       **/
1548      _parseArgs : function(args) {
1549          /*
1550             2.4.0 Constructors signatures
1551  
1552             new Calendar(String)
1553             new Calendar(HTMLElement)
1554             new Calendar(String, ConfigObject)
1555             new Calendar(HTMLElement, ConfigObject)
1556  
1557             Pre 2.4.0 Constructor signatures
1558  
1559             new Calendar(String, String)
1560             new Calendar(String, HTMLElement)
1561             new Calendar(String, String, ConfigObject)
1562             new Calendar(String, HTMLElement, ConfigObject)
1563           */
1564          var nArgs = {id:null, container:null, config:null};
1565  
1566          if (args && args.length && args.length > 0) {
1567              switch (args.length) {
1568                  case 1:
1569                      nArgs.id = null;
1570                      nArgs.container = args[0];
1571                      nArgs.config = null;
1572                      break;
1573                  case 2:
1574                      if (Lang.isObject(args[1]) && !args[1].tagName && !(args[1] instanceof String)) {
1575                          nArgs.id = null;
1576                          nArgs.container = args[0];
1577                          nArgs.config = args[1];
1578                      } else {
1579                          nArgs.id = args[0];
1580                          nArgs.container = args[1];
1581                          nArgs.config = null;
1582                      }
1583                      break;
1584                  default: // 3+
1585                      nArgs.id = args[0];
1586                      nArgs.container = args[1];
1587                      nArgs.config = args[2];
1588                      break;
1589              }
1590          } else {
1591          }
1592          return nArgs;
1593      },
1594  
1595      /**
1596      * Initializes the Calendar widget.
1597      * @method init
1598      *
1599      * @param {String} id optional The id of the table element that will represent the Calendar widget. As of 2.4.0, this argument is optional.
1600      * @param {String | HTMLElement} container The id of the container div element that will wrap the Calendar table, or a reference to a DIV element which exists in the document.
1601      * @param {Object} config optional The configuration object containing the initial configuration values for the Calendar.
1602      */
1603      init : function(id, container, config) {
1604          // Normalize 2.4.0, pre 2.4.0 args
1605          var nArgs = this._parseArgs(arguments);
1606  
1607          id = nArgs.id;
1608          container = nArgs.container;
1609          config = nArgs.config;
1610  
1611          this.oDomContainer = Dom.get(container);
1612  
1613          this._oDoc = this.oDomContainer.ownerDocument;
1614  
1615          if (!this.oDomContainer.id) {
1616              this.oDomContainer.id = Dom.generateId();
1617          }
1618  
1619          if (!id) {
1620              id = this.oDomContainer.id + "_t";
1621          }
1622  
1623          this.id = id;
1624          this.containerId = this.oDomContainer.id;
1625  
1626          this.initEvents();
1627  
1628          /**
1629          * The Config object used to hold the configuration variables for the Calendar
1630          * @property cfg
1631          * @type YAHOO.util.Config
1632          */
1633          this.cfg = new YAHOO.util.Config(this);
1634  
1635          /**
1636          * The local object which contains the Calendar's options
1637          * @property Options
1638          * @type Object
1639          */
1640          this.Options = {};
1641  
1642          /**
1643          * The local object which contains the Calendar's locale settings
1644          * @property Locale
1645          * @type Object
1646          */
1647          this.Locale = {};
1648  
1649          this.initStyles();
1650  
1651          Dom.addClass(this.oDomContainer, this.Style.CSS_CONTAINER);
1652          Dom.addClass(this.oDomContainer, this.Style.CSS_SINGLE);
1653  
1654          this.cellDates = [];
1655          this.cells = [];
1656          this.renderStack = [];
1657          this._renderStack = [];
1658  
1659          this.setupConfig();
1660  
1661          if (config) {
1662              this.cfg.applyConfig(config, true);
1663          }
1664  
1665          this.cfg.fireQueue();
1666  
1667          this.today = this.cfg.getProperty("today");
1668      },
1669  
1670      /**
1671      * Default Config listener for the iframe property. If the iframe config property is set to true, 
1672      * renders the built-in IFRAME shim if the container is relatively or absolutely positioned.
1673      * 
1674      * @method configIframe
1675      */
1676      configIframe : function(type, args, obj) {
1677          var useIframe = args[0];
1678      
1679          if (!this.parent) {
1680              if (Dom.inDocument(this.oDomContainer)) {
1681                  if (useIframe) {
1682                      var pos = Dom.getStyle(this.oDomContainer, "position");
1683                      
1684                      if (pos == "absolute" || pos == "relative") {
1685                          
1686                          if (!Dom.inDocument(this.iframe)) {
1687                              this.iframe = document.createElement("iframe");
1688                              this.iframe.src = "javascript:false;";
1689      
1690                              Dom.setStyle(this.iframe, "opacity", "0");
1691      
1692                              if (YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) {
1693                                  Dom.addClass(this.iframe, this.Style.CSS_FIXED_SIZE);
1694                              }
1695      
1696                              this.oDomContainer.insertBefore(this.iframe, this.oDomContainer.firstChild);
1697                          }
1698                      }
1699                  } else {
1700                      if (this.iframe) {
1701                          if (this.iframe.parentNode) {
1702                              this.iframe.parentNode.removeChild(this.iframe);
1703                          }
1704                          this.iframe = null;
1705                      }
1706                  }
1707              }
1708          }
1709      },
1710  
1711      /**
1712      * Default handler for the "title" property
1713      * @method configTitle
1714      */
1715      configTitle : function(type, args, obj) {
1716          var title = args[0];
1717  
1718          // "" disables title bar
1719          if (title) {
1720              this.createTitleBar(title);
1721          } else {
1722              var close = this.cfg.getProperty(DEF_CFG.CLOSE.key);
1723              if (!close) {
1724                  this.removeTitleBar();
1725              } else {
1726                  this.createTitleBar("&#160;");
1727              }
1728          }
1729      },
1730      
1731      /**
1732      * Default handler for the "close" property
1733      * @method configClose
1734      */
1735      configClose : function(type, args, obj) {
1736          var close = args[0],
1737              title = this.cfg.getProperty(DEF_CFG.TITLE.key);
1738      
1739          if (close) {
1740              if (!title) {
1741                  this.createTitleBar("&#160;");
1742              }
1743              this.createCloseButton();
1744          } else {
1745              this.removeCloseButton();
1746              if (!title) {
1747                  this.removeTitleBar();
1748              }
1749          }
1750      },
1751  
1752      /**
1753      * Initializes Calendar's built-in CustomEvents
1754      * @method initEvents
1755      */
1756      initEvents : function() {
1757  
1758          var defEvents = Calendar._EVENT_TYPES,
1759              CE = YAHOO.util.CustomEvent,
1760              cal = this; // To help with minification
1761  
1762          /**
1763          * Fired before a date selection is made
1764          * @event beforeSelectEvent
1765          */
1766          cal.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT); 
1767  
1768          /**
1769          * Fired when a date selection is made
1770          * @event selectEvent
1771          * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
1772          */
1773          cal.selectEvent = new CE(defEvents.SELECT);
1774  
1775          /**
1776          * Fired before a date or set of dates is deselected
1777          * @event beforeDeselectEvent
1778          */
1779          cal.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT);
1780  
1781          /**
1782          * Fired when a date or set of dates is deselected
1783          * @event deselectEvent
1784          * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
1785          */
1786          cal.deselectEvent = new CE(defEvents.DESELECT);
1787      
1788          /**
1789          * Fired when the Calendar page is changed
1790          * @event changePageEvent
1791          * @param {Date} prevDate The date before the page was changed
1792          * @param {Date} newDate The date after the page was changed
1793          */
1794          cal.changePageEvent = new CE(defEvents.CHANGE_PAGE);
1795      
1796          /**
1797          * Fired before the Calendar is rendered
1798          * @event beforeRenderEvent
1799          */
1800          cal.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER);
1801      
1802          /**
1803          * Fired when the Calendar is rendered
1804          * @event renderEvent
1805          */
1806          cal.renderEvent = new CE(defEvents.RENDER);
1807  
1808          /**
1809          * Fired just before the Calendar is to be destroyed
1810          * @event beforeDestroyEvent
1811          */
1812          cal.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY);
1813  
1814          /**
1815          * Fired after the Calendar is destroyed. This event should be used
1816          * for notification only. When this event is fired, important Calendar instance
1817          * properties, dom references and event listeners have already been 
1818          * removed/dereferenced, and hence the Calendar instance is not in a usable 
1819          * state.
1820          *
1821          * @event destroyEvent
1822          */
1823          cal.destroyEvent = new CE(defEvents.DESTROY);
1824  
1825          /**
1826          * Fired when the Calendar is reset
1827          * @event resetEvent
1828          */
1829          cal.resetEvent = new CE(defEvents.RESET);
1830  
1831          /**
1832          * Fired when the Calendar is cleared
1833          * @event clearEvent
1834          */
1835          cal.clearEvent = new CE(defEvents.CLEAR);
1836  
1837          /**
1838          * Fired just before the Calendar is to be shown
1839          * @event beforeShowEvent
1840          */
1841          cal.beforeShowEvent = new CE(defEvents.BEFORE_SHOW);
1842  
1843          /**
1844          * Fired after the Calendar is shown
1845          * @event showEvent
1846          */
1847          cal.showEvent = new CE(defEvents.SHOW);
1848  
1849          /**
1850          * Fired just before the Calendar is to be hidden
1851          * @event beforeHideEvent
1852          */
1853          cal.beforeHideEvent = new CE(defEvents.BEFORE_HIDE);
1854  
1855          /**
1856          * Fired after the Calendar is hidden
1857          * @event hideEvent
1858          */
1859          cal.hideEvent = new CE(defEvents.HIDE);
1860  
1861          /**
1862          * Fired just before the CalendarNavigator is to be shown
1863          * @event beforeShowNavEvent
1864          */
1865          cal.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV);
1866      
1867          /**
1868          * Fired after the CalendarNavigator is shown
1869          * @event showNavEvent
1870          */
1871          cal.showNavEvent = new CE(defEvents.SHOW_NAV);
1872      
1873          /**
1874          * Fired just before the CalendarNavigator is to be hidden
1875          * @event beforeHideNavEvent
1876          */
1877          cal.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV);
1878      
1879          /**
1880          * Fired after the CalendarNavigator is hidden
1881          * @event hideNavEvent
1882          */
1883          cal.hideNavEvent = new CE(defEvents.HIDE_NAV);
1884  
1885          /**
1886          * Fired just before the CalendarNavigator is to be rendered
1887          * @event beforeRenderNavEvent
1888          */
1889          cal.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV);
1890  
1891          /**
1892          * Fired after the CalendarNavigator is rendered
1893          * @event renderNavEvent
1894          */
1895          cal.renderNavEvent = new CE(defEvents.RENDER_NAV);
1896  
1897          cal.beforeSelectEvent.subscribe(cal.onBeforeSelect, this, true);
1898          cal.selectEvent.subscribe(cal.onSelect, this, true);
1899          cal.beforeDeselectEvent.subscribe(cal.onBeforeDeselect, this, true);
1900          cal.deselectEvent.subscribe(cal.onDeselect, this, true);
1901          cal.changePageEvent.subscribe(cal.onChangePage, this, true);
1902          cal.renderEvent.subscribe(cal.onRender, this, true);
1903          cal.resetEvent.subscribe(cal.onReset, this, true);
1904          cal.clearEvent.subscribe(cal.onClear, this, true);
1905      },
1906  
1907      /**
1908      * The default event handler for clicks on the "Previous Month" navigation UI
1909      *
1910      * @method doPreviousMonthNav
1911      * @param {DOMEvent} e The DOM event
1912      * @param {Calendar} cal A reference to the calendar
1913      */
1914      doPreviousMonthNav : function(e, cal) {
1915          Event.preventDefault(e);
1916          // previousMonth invoked in a timeout, to allow
1917          // event to bubble up, with correct target. Calling
1918          // previousMonth, will call render which will remove 
1919          // HTML which generated the event, resulting in an 
1920          // invalid event target in certain browsers.
1921          setTimeout(function() {
1922              cal.previousMonth();
1923              var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_LEFT, "a", cal.oDomContainer);
1924              if (navs && navs[0]) {
1925                  try {
1926                      navs[0].focus();
1927                  } catch (ex) {
1928                      // ignore
1929                  }
1930              }
1931          }, 0);
1932      },
1933  
1934      /**
1935       * The default event handler for clicks on the "Next Month" navigation UI
1936       *
1937       * @method doNextMonthNav
1938       * @param {DOMEvent} e The DOM event
1939       * @param {Calendar} cal A reference to the calendar
1940       */
1941      doNextMonthNav : function(e, cal) {
1942          Event.preventDefault(e);
1943          setTimeout(function() {
1944              cal.nextMonth();
1945              var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_RIGHT, "a", cal.oDomContainer);
1946              if (navs && navs[0]) {
1947                  try {
1948                      navs[0].focus();
1949                  } catch (ex) {
1950                      // ignore
1951                  }
1952              }
1953          }, 0);
1954      },
1955  
1956      /**
1957      * The default event handler for date cell selection. Currently attached to 
1958      * the Calendar's bounding box, referenced by it's <a href="#property_oDomContainer">oDomContainer</a> property.
1959      *
1960      * @method doSelectCell
1961      * @param {DOMEvent} e The DOM event
1962      * @param {Calendar} cal A reference to the calendar
1963      */
1964      doSelectCell : function(e, cal) {
1965          var cell, d, date, index;
1966  
1967          var target = Event.getTarget(e),
1968              tagName = target.tagName.toLowerCase(),
1969              defSelector = false;
1970  
1971          while (tagName != "td" && !Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
1972  
1973              if (!defSelector && tagName == "a" && Dom.hasClass(target, cal.Style.CSS_CELL_SELECTOR)) {
1974                  defSelector = true;
1975              }
1976  
1977              target = target.parentNode;
1978              tagName = target.tagName.toLowerCase();
1979  
1980              if (target == this.oDomContainer || tagName == "html") {
1981                  return;
1982              }
1983          }
1984  
1985          if (defSelector) {
1986              // Stop link href navigation for default renderer
1987              Event.preventDefault(e);
1988          }
1989      
1990          cell = target;
1991  
1992          if (Dom.hasClass(cell, cal.Style.CSS_CELL_SELECTABLE)) {
1993              index = cal.getIndexFromId(cell.id);
1994              if (index > -1) {
1995                  d = cal.cellDates[index];
1996                  if (d) {
1997                      date = DateMath.getDate(d[0],d[1]-1,d[2]);
1998                  
1999                      var link;
2000  
2001                      if (cal.Options.MULTI_SELECT) {
2002                          link = cell.getElementsByTagName("a")[0];
2003                          if (link) {
2004                              link.blur();
2005                          }
2006  
2007                          var cellDate = cal.cellDates[index];
2008                          var cellDateIndex = cal._indexOfSelectedFieldArray(cellDate);
2009  
2010                          if (cellDateIndex > -1) { 
2011                              cal.deselectCell(index);
2012                          } else {
2013                              cal.selectCell(index);
2014                          }
2015  
2016                      } else {
2017                          link = cell.getElementsByTagName("a")[0];
2018                          if (link) {
2019                              link.blur();
2020                          }
2021                          cal.selectCell(index);
2022                      }
2023                  }
2024              }
2025          }
2026      },
2027  
2028      /**
2029      * The event that is executed when the user hovers over a cell
2030      * @method doCellMouseOver
2031      * @param {DOMEvent} e The event
2032      * @param {Calendar} cal A reference to the calendar passed by the Event utility
2033      */
2034      doCellMouseOver : function(e, cal) {
2035          var target;
2036          if (e) {
2037              target = Event.getTarget(e);
2038          } else {
2039              target = this;
2040          }
2041  
2042          while (target.tagName && target.tagName.toLowerCase() != "td") {
2043              target = target.parentNode;
2044              if (!target.tagName || target.tagName.toLowerCase() == "html") {
2045                  return;
2046              }
2047          }
2048  
2049          if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
2050              Dom.addClass(target, cal.Style.CSS_CELL_HOVER);
2051          }
2052      },
2053  
2054      /**
2055      * The event that is executed when the user moves the mouse out of a cell
2056      * @method doCellMouseOut
2057      * @param {DOMEvent} e The event
2058      * @param {Calendar} cal A reference to the calendar passed by the Event utility
2059      */
2060      doCellMouseOut : function(e, cal) {
2061          var target;
2062          if (e) {
2063              target = Event.getTarget(e);
2064          } else {
2065              target = this;
2066          }
2067  
2068          while (target.tagName && target.tagName.toLowerCase() != "td") {
2069              target = target.parentNode;
2070              if (!target.tagName || target.tagName.toLowerCase() == "html") {
2071                  return;
2072              }
2073          }
2074  
2075          if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
2076              Dom.removeClass(target, cal.Style.CSS_CELL_HOVER);
2077          }
2078      },
2079  
2080      setupConfig : function() {
2081  
2082          var cfg = this.cfg;
2083  
2084          /**
2085          * The date to use to represent "Today".
2086          *
2087          * @config today
2088          * @type Date
2089          * @default The client side date (new Date()) when the Calendar is instantiated.
2090          */
2091          cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler:this.configToday, suppressEvent:true } );
2092  
2093          /**
2094          * The month/year representing the current visible Calendar date (mm/yyyy)
2095          * @config pagedate
2096          * @type String | Date
2097          * @default Today's date
2098          */
2099          cfg.addProperty(DEF_CFG.PAGEDATE.key, { value: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), handler:this.configPageDate } );
2100  
2101          /**
2102          * The date or range of dates representing the current Calendar selection
2103          * @config selected
2104          * @type String
2105          * @default []
2106          */
2107          cfg.addProperty(DEF_CFG.SELECTED.key, { value:DEF_CFG.SELECTED.value.concat(), handler:this.configSelected } );
2108  
2109          /**
2110          * The title to display above the Calendar's month header. The title is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.   
2111          * @config title
2112          * @type HTML
2113          * @default ""
2114          */
2115          cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } );
2116  
2117          /**
2118          * Whether or not a close button should be displayed for this Calendar
2119          * @config close
2120          * @type Boolean
2121          * @default false
2122          */
2123          cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } );
2124  
2125          /**
2126          * Whether or not an iframe shim should be placed under the Calendar to prevent select boxes from bleeding through in Internet Explorer 6 and below.
2127          * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be 
2128          * enabled if required.
2129          * 
2130          * @config iframe
2131          * @type Boolean
2132          * @default true for IE6 and below, false for all other browsers
2133          */
2134          cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } );
2135  
2136          /**
2137          * The minimum selectable date in the current Calendar (mm/dd/yyyy)
2138          * @config mindate
2139          * @type String | Date
2140          * @default null
2141          */
2142          cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.configMinDate } );
2143  
2144          /**
2145          * The maximum selectable date in the current Calendar (mm/dd/yyyy)
2146          * @config maxdate
2147          * @type String | Date
2148          * @default null
2149          */
2150          cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.configMaxDate } );
2151  
2152          // Options properties
2153      
2154          /**
2155          * True if the Calendar should allow multiple selections. False by default.
2156          * @config MULTI_SELECT
2157          * @type Boolean
2158          * @default false
2159          */
2160          cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2161  
2162          /**
2163          * True if the Calendar should allow selection of out-of-month dates. False by default.
2164          * @config OOM_SELECT
2165          * @type Boolean
2166          * @default false
2167          */
2168          cfg.addProperty(DEF_CFG.OOM_SELECT.key, { value:DEF_CFG.OOM_SELECT.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2169  
2170          /**
2171          * The weekday the week begins on. Default is 0 (Sunday = 0, Monday = 1 ... Saturday = 6).
2172          * @config START_WEEKDAY
2173          * @type number
2174          * @default 0
2175          */
2176          cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.configOptions, validator:cfg.checkNumber  } );
2177      
2178          /**
2179          * True if the Calendar should show weekday labels. True by default.
2180          * @config SHOW_WEEKDAYS
2181          * @type Boolean
2182          * @default true
2183          */
2184          cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.configOptions, validator:cfg.checkBoolean  } );
2185      
2186          /**
2187          * True if the Calendar should show week row headers. False by default.
2188          * @config SHOW_WEEK_HEADER
2189          * @type Boolean
2190          * @default false
2191          */
2192          cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key, { value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2193      
2194          /**
2195          * True if the Calendar should show week row footers. False by default.
2196          * @config SHOW_WEEK_FOOTER
2197          * @type Boolean
2198          * @default false
2199          */ 
2200          cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2201      
2202          /**
2203          * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
2204          * @config HIDE_BLANK_WEEKS
2205          * @type Boolean
2206          * @default false
2207          */ 
2208          cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key, { value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.configOptions, validator:cfg.checkBoolean } );
2209          
2210          /**
2211          * The image URL that should be used for the left navigation arrow. The image URL is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2212          * @config NAV_ARROW_LEFT
2213          * @type String
2214          * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"  
2215          * @default null
2216          */ 
2217          cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.configOptions } );
2218      
2219          /**
2220          * The image URL that should be used for the right navigation arrow. The image URL is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2221          * @config NAV_ARROW_RIGHT
2222          * @type String
2223          * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
2224          * @default null
2225          */ 
2226          cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.configOptions } );
2227      
2228          // Locale properties
2229      
2230          /**
2231          * The short month labels for the current locale. The month labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2232          * @config MONTHS_SHORT
2233          * @type HTML[]
2234          * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
2235          */
2236          cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.configLocale } );
2237  
2238          /**
2239          * The long month labels for the current locale. The month labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2240          * @config MONTHS_LONG
2241          * @type HTML[]
2242          * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
2243          */ 
2244          cfg.addProperty(DEF_CFG.MONTHS_LONG.key,  { value:DEF_CFG.MONTHS_LONG.value, handler:this.configLocale } );
2245  
2246          /**
2247          * The 1-character weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2248          * @config WEEKDAYS_1CHAR
2249          * @type HTML[]
2250          * @default ["S", "M", "T", "W", "T", "F", "S"]
2251          */ 
2252          cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.configLocale } );
2253          
2254          /**
2255          * The short weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2256          * @config WEEKDAYS_SHORT
2257          * @type HTML[]
2258          * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
2259          */ 
2260          cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.configLocale } );
2261          
2262          /**
2263          * The medium weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2264          * @config WEEKDAYS_MEDIUM
2265          * @type HTML[]
2266          * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
2267          */ 
2268          cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.configLocale } );
2269          
2270          /**
2271          * The long weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2272          * @config WEEKDAYS_LONG
2273          * @type HTML[]
2274          * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
2275          */ 
2276          cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.configLocale } );
2277  
2278          /**
2279          * Refreshes the locale values used to build the Calendar.
2280          * @method refreshLocale
2281          * @private
2282          */
2283          var refreshLocale = function() {
2284              cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key);
2285              cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key);
2286          };
2287      
2288          cfg.subscribeToConfigEvent(DEF_CFG.START_WEEKDAY.key, refreshLocale, this, true);
2289          cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_SHORT.key, refreshLocale, this, true);
2290          cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_LONG.key, refreshLocale, this, true);
2291          cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_1CHAR.key, refreshLocale, this, true);
2292          cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_SHORT.key, refreshLocale, this, true);
2293          cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_MEDIUM.key, refreshLocale, this, true);
2294          cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_LONG.key, refreshLocale, this, true);
2295         
2296          /**
2297          * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
2298          * @config LOCALE_MONTHS
2299          * @type String
2300          * @default "long"
2301          */ 
2302          cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.configLocaleValues } );
2303          
2304          /**
2305          * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
2306          * @config LOCALE_WEEKDAYS
2307          * @type String
2308          * @default "short"
2309          */ 
2310          cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.configLocaleValues } );
2311  
2312          /**
2313          * The positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to 
2314          * be used when displaying and parsing dates. NOTE: All JS Date objects returned by methods, or expected as input by
2315          * methods will always represent the Gregorian year, in order to maintain date/month/week values. 
2316          *
2317          * @config YEAR_OFFSET
2318          * @type Number
2319          * @default 0
2320          */
2321          cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, handler:this.configLocale  } );
2322      
2323          /**
2324          * The value used to delimit individual dates in a date string passed to various Calendar functions.
2325          * @config DATE_DELIMITER
2326          * @type String
2327          * @default ","
2328          */ 
2329          cfg.addProperty(DEF_CFG.DATE_DELIMITER.key,  { value:DEF_CFG.DATE_DELIMITER.value, handler:this.configLocale } );
2330      
2331          /**
2332          * The value used to delimit date fields in a date string passed to various Calendar functions.
2333          * @config DATE_FIELD_DELIMITER
2334          * @type String
2335          * @default "/"
2336          */ 
2337          cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key, { value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.configLocale } );
2338      
2339          /**
2340          * The value used to delimit date ranges in a date string passed to various Calendar functions.
2341          * @config DATE_RANGE_DELIMITER
2342          * @type String
2343          * @default "-"
2344          */
2345          cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key, { value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.configLocale } );
2346      
2347          /**
2348          * The position of the month in a month/year date string
2349          * @config MY_MONTH_POSITION
2350          * @type Number
2351          * @default 1
2352          */
2353          cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2354      
2355          /**
2356          * The position of the year in a month/year date string
2357          * @config MY_YEAR_POSITION
2358          * @type Number
2359          * @default 2
2360          */
2361          cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2362      
2363          /**
2364          * The position of the month in a month/day date string
2365          * @config MD_MONTH_POSITION
2366          * @type Number
2367          * @default 1
2368          */
2369          cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2370      
2371          /**
2372          * The position of the day in a month/year date string
2373          * @config MD_DAY_POSITION
2374          * @type Number
2375          * @default 2
2376          */
2377          cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key,  { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2378      
2379          /**
2380          * The position of the month in a month/day/year date string
2381          * @config MDY_MONTH_POSITION
2382          * @type Number
2383          * @default 1
2384          */
2385          cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2386      
2387          /**
2388          * The position of the day in a month/day/year date string
2389          * @config MDY_DAY_POSITION
2390          * @type Number
2391          * @default 2
2392          */
2393          cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2394      
2395          /**
2396          * The position of the year in a month/day/year date string
2397          * @config MDY_YEAR_POSITION
2398          * @type Number
2399          * @default 3
2400          */
2401          cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2402          
2403          /**
2404          * The position of the month in the month year label string used as the Calendar header
2405          * @config MY_LABEL_MONTH_POSITION
2406          * @type Number
2407          * @default 1
2408          */
2409          cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2410      
2411          /**
2412          * The position of the year in the month year label string used as the Calendar header
2413          * @config MY_LABEL_YEAR_POSITION
2414          * @type Number
2415          * @default 2
2416          */
2417          cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
2418          
2419          /**
2420          * The suffix used after the month when rendering the Calendar header. The suffix is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2421          * @config MY_LABEL_MONTH_SUFFIX
2422          * @type HTML
2423          * @default " "
2424          */
2425          cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.configLocale } );
2426          
2427          /**
2428          * The suffix used after the year when rendering the Calendar header. The suffix is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2429          * @config MY_LABEL_YEAR_SUFFIX
2430          * @type HTML
2431          * @default ""
2432          */
2433          cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.configLocale } );
2434  
2435          /**
2436          * Configuration for the Month/Year CalendarNavigator UI which allows the user to jump directly to a 
2437          * specific Month/Year without having to scroll sequentially through months.
2438          * <p>
2439          * Setting this property to null (default value) or false, will disable the CalendarNavigator UI.
2440          * </p>
2441          * <p>
2442          * Setting this property to true will enable the CalendarNavigatior UI with the default CalendarNavigator configuration values.
2443          * </p>
2444          * <p>
2445          * This property can also be set to an object literal containing configuration properties for the CalendarNavigator UI.
2446          * The configuration object expects the the following case-sensitive properties, with the "strings" property being a nested object.
2447          * Any properties which are not provided will use the default values (defined in the CalendarNavigator class).
2448          * </p>
2449          * <dl>
2450          * <dt>strings</dt>
2451          * <dd><em>Object</em> :  An object with the properties shown below, defining the string labels to use in the Navigator's UI. The strings are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 
2452          *     <dl>
2453          *         <dt>month</dt><dd><em>HTML</em> : The markup to use for the month label. Defaults to "Month".</dd>
2454          *         <dt>year</dt><dd><em>HTML</em> : The markup to use for the year label. Defaults to "Year".</dd>
2455          *         <dt>submit</dt><dd><em>HTML</em> : The markup to use for the submit button label. Defaults to "Okay".</dd>
2456          *         <dt>cancel</dt><dd><em>HTML</em> : The markup to use for the cancel button label. Defaults to "Cancel".</dd>
2457          *         <dt>invalidYear</dt><dd><em>HTML</em> : The markup to use for invalid year values. Defaults to "Year needs to be a number".</dd>
2458          *     </dl>
2459          * </dd>
2460          * <dt>monthFormat</dt><dd><em>String</em> : The month format to use. Either YAHOO.widget.Calendar.LONG, or YAHOO.widget.Calendar.SHORT. Defaults to YAHOO.widget.Calendar.LONG</dd>
2461          * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
2462          * </dl>
2463          * <p>E.g.</p>
2464          * <pre>
2465          * var navConfig = {
2466          *   strings: {
2467          *    month:"Calendar Month",
2468          *    year:"Calendar Year",
2469          *    submit: "Submit",
2470          *    cancel: "Cancel",
2471          *    invalidYear: "Please enter a valid year"
2472          *   },
2473          *   monthFormat: YAHOO.widget.Calendar.SHORT,
2474          *   initialFocus: "month"
2475          * }
2476          * </pre>
2477          * @config navigator
2478          * @type {Object|Boolean}
2479          * @default null
2480          */
2481          cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } );
2482  
2483          /**
2484           * The map of UI strings which the Calendar UI uses.
2485           *
2486           * @config strings
2487           * @type {Object}
2488           * @default An object with the properties shown below:
2489           *     <dl>
2490           *         <dt>previousMonth</dt><dd><em>HTML</em> : The markup to use for the "Previous Month" navigation label. Defaults to "Previous Month". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd>
2491           *         <dt>nextMonth</dt><dd><em>HTML</em> : The markup to use for the "Next Month" navigation UI. Defaults to "Next Month". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd>
2492           *         <dt>close</dt><dd><em>HTML</em> : The markup to use for the close button label. Defaults to "Close". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd>
2493           *     </dl>
2494           */
2495          cfg.addProperty(DEF_CFG.STRINGS.key, { 
2496              value:DEF_CFG.STRINGS.value,
2497              handler:this.configStrings,
2498              validator: function(val) {
2499                  return Lang.isObject(val);
2500              },
2501              supercedes:DEF_CFG.STRINGS.supercedes
2502          });
2503      },
2504  
2505      /**
2506      * The default handler for the "strings" property
2507      * @method configStrings
2508      */
2509      configStrings : function(type, args, obj) {
2510          var val = Lang.merge(DEF_CFG.STRINGS.value, args[0]);
2511          this.cfg.setProperty(DEF_CFG.STRINGS.key, val, true);
2512      },
2513  
2514      /**
2515      * The default handler for the "pagedate" property
2516      * @method configPageDate
2517      */
2518      configPageDate : function(type, args, obj) {
2519          this.cfg.setProperty(DEF_CFG.PAGEDATE.key, this._parsePageDate(args[0]), true);
2520      },
2521  
2522      /**
2523      * The default handler for the "mindate" property
2524      * @method configMinDate
2525      */
2526      configMinDate : function(type, args, obj) {
2527          var val = args[0];
2528          if (Lang.isString(val)) {
2529              val = this._parseDate(val);
2530              this.cfg.setProperty(DEF_CFG.MINDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2]));
2531          }
2532      },
2533  
2534      /**
2535      * The default handler for the "maxdate" property
2536      * @method configMaxDate
2537      */
2538      configMaxDate : function(type, args, obj) {
2539          var val = args[0];
2540          if (Lang.isString(val)) {
2541              val = this._parseDate(val);
2542              this.cfg.setProperty(DEF_CFG.MAXDATE.key, DateMath.getDate(val[0],(val[1]-1),val[2]));
2543          }
2544      },
2545  
2546      /**
2547      * The default handler for the "today" property
2548      * @method configToday
2549      */
2550      configToday : function(type, args, obj) {
2551          // Only do this for initial set. Changing the today property after the initial
2552          // set, doesn't affect pagedate
2553          var val = args[0];
2554          if (Lang.isString(val)) {
2555              val = this._parseDate(val);
2556          }
2557          var today = DateMath.clearTime(val);
2558          if (!this.cfg.initialConfig[DEF_CFG.PAGEDATE.key]) {
2559              this.cfg.setProperty(DEF_CFG.PAGEDATE.key, today);
2560          }
2561          this.today = today;
2562          this.cfg.setProperty(DEF_CFG.TODAY.key, today, true);
2563      },
2564  
2565      /**
2566      * The default handler for the "selected" property
2567      * @method configSelected
2568      */
2569      configSelected : function(type, args, obj) {
2570          var selected = args[0],
2571              cfgSelected = DEF_CFG.SELECTED.key;
2572          
2573          if (selected) {
2574              if (Lang.isString(selected)) {
2575                  this.cfg.setProperty(cfgSelected, this._parseDates(selected), true);
2576              } 
2577          }
2578          if (! this._selectedDates) {
2579              this._selectedDates = this.cfg.getProperty(cfgSelected);
2580          }
2581      },
2582      
2583      /**
2584      * The default handler for all configuration options properties
2585      * @method configOptions
2586      */
2587      configOptions : function(type, args, obj) {
2588          this.Options[type.toUpperCase()] = args[0];
2589      },
2590  
2591      /**
2592      * The default handler for all configuration locale properties
2593      * @method configLocale
2594      */
2595      configLocale : function(type, args, obj) {
2596          this.Locale[type.toUpperCase()] = args[0];
2597  
2598          this.cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key);
2599          this.cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key);
2600      },
2601      
2602      /**
2603      * The default handler for all configuration locale field length properties
2604      * @method configLocaleValues
2605      */
2606      configLocaleValues : function(type, args, obj) {
2607  
2608          type = type.toLowerCase();
2609  
2610          var val = args[0],
2611              cfg = this.cfg,
2612              Locale = this.Locale;
2613  
2614          switch (type) {
2615              case DEF_CFG.LOCALE_MONTHS.key:
2616                  switch (val) {
2617                      case Calendar.SHORT:
2618                          Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_SHORT.key).concat();
2619                          break;
2620                      case Calendar.LONG:
2621                          Locale.LOCALE_MONTHS = cfg.getProperty(DEF_CFG.MONTHS_LONG.key).concat();
2622                          break;
2623                  }
2624                  break;
2625              case DEF_CFG.LOCALE_WEEKDAYS.key:
2626                  switch (val) {
2627                      case Calendar.ONE_CHAR:
2628                          Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_1CHAR.key).concat();
2629                          break;
2630                      case Calendar.SHORT:
2631                          Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_SHORT.key).concat();
2632                          break;
2633                      case Calendar.MEDIUM:
2634                          Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_MEDIUM.key).concat();
2635                          break;
2636                      case Calendar.LONG:
2637                          Locale.LOCALE_WEEKDAYS = cfg.getProperty(DEF_CFG.WEEKDAYS_LONG.key).concat();
2638                          break;
2639                  }
2640                  
2641                  var START_WEEKDAY = cfg.getProperty(DEF_CFG.START_WEEKDAY.key);
2642      
2643                  if (START_WEEKDAY > 0) {
2644                      for (var w=0; w < START_WEEKDAY; ++w) {
2645                          Locale.LOCALE_WEEKDAYS.push(Locale.LOCALE_WEEKDAYS.shift());
2646                      }
2647                  }
2648                  break;
2649          }
2650      },
2651  
2652      /**
2653       * The default handler for the "navigator" property
2654       * @method configNavigator
2655       */
2656      configNavigator : function(type, args, obj) {
2657          var val = args[0];
2658          if (YAHOO.widget.CalendarNavigator && (val === true || Lang.isObject(val))) {
2659              if (!this.oNavigator) {
2660                  this.oNavigator = new YAHOO.widget.CalendarNavigator(this);
2661                  // Cleanup DOM Refs/Events before innerHTML is removed.
2662                  this.beforeRenderEvent.subscribe(function () {
2663                      if (!this.pages) {
2664                          this.oNavigator.erase();
2665                      }
2666                  }, this, true);
2667              }
2668          } else {
2669              if (this.oNavigator) {
2670                  this.oNavigator.destroy();
2671                  this.oNavigator = null;
2672              }
2673          }
2674      },
2675  
2676      /**
2677      * Defines the class names used by Calendar when rendering to DOM. NOTE: The class names are added to the DOM as HTML and should be escaped by the implementor if coming from an external source. 
2678      * @method initStyles
2679      */
2680      initStyles : function() {
2681  
2682          var defStyle = Calendar.STYLES;
2683  
2684          this.Style = {
2685              /**
2686              * @property Style.CSS_ROW_HEADER
2687              */
2688              CSS_ROW_HEADER: defStyle.CSS_ROW_HEADER,
2689              /**
2690              * @property Style.CSS_ROW_FOOTER
2691              */
2692              CSS_ROW_FOOTER: defStyle.CSS_ROW_FOOTER,
2693              /**
2694              * @property Style.CSS_CELL
2695              */
2696              CSS_CELL : defStyle.CSS_CELL,
2697              /**
2698              * @property Style.CSS_CELL_SELECTOR
2699              */
2700              CSS_CELL_SELECTOR : defStyle.CSS_CELL_SELECTOR,
2701              /**
2702              * @property Style.CSS_CELL_SELECTED
2703              */
2704              CSS_CELL_SELECTED : defStyle.CSS_CELL_SELECTED,
2705              /**
2706              * @property Style.CSS_CELL_SELECTABLE
2707              */
2708              CSS_CELL_SELECTABLE : defStyle.CSS_CELL_SELECTABLE,
2709              /**
2710              * @property Style.CSS_CELL_RESTRICTED
2711              */
2712              CSS_CELL_RESTRICTED : defStyle.CSS_CELL_RESTRICTED,
2713              /**
2714              * @property Style.CSS_CELL_TODAY
2715              */
2716              CSS_CELL_TODAY : defStyle.CSS_CELL_TODAY,
2717              /**
2718              * @property Style.CSS_CELL_OOM
2719              */
2720              CSS_CELL_OOM : defStyle.CSS_CELL_OOM,
2721              /**
2722              * @property Style.CSS_CELL_OOB
2723              */
2724              CSS_CELL_OOB : defStyle.CSS_CELL_OOB,
2725              /**
2726              * @property Style.CSS_HEADER
2727              */
2728              CSS_HEADER : defStyle.CSS_HEADER,
2729              /**
2730              * @property Style.CSS_HEADER_TEXT
2731              */
2732              CSS_HEADER_TEXT : defStyle.CSS_HEADER_TEXT,
2733              /**
2734              * @property Style.CSS_BODY
2735              */
2736              CSS_BODY : defStyle.CSS_BODY,
2737              /**
2738              * @property Style.CSS_WEEKDAY_CELL
2739              */
2740              CSS_WEEKDAY_CELL : defStyle.CSS_WEEKDAY_CELL,
2741              /**
2742              * @property Style.CSS_WEEKDAY_ROW
2743              */
2744              CSS_WEEKDAY_ROW : defStyle.CSS_WEEKDAY_ROW,
2745              /**
2746              * @property Style.CSS_FOOTER
2747              */
2748              CSS_FOOTER : defStyle.CSS_FOOTER,
2749              /**
2750              * @property Style.CSS_CALENDAR
2751              */
2752              CSS_CALENDAR : defStyle.CSS_CALENDAR,
2753              /**
2754              * @property Style.CSS_SINGLE
2755              */
2756              CSS_SINGLE : defStyle.CSS_SINGLE,
2757              /**
2758              * @property Style.CSS_CONTAINER
2759              */
2760              CSS_CONTAINER : defStyle.CSS_CONTAINER,
2761              /**
2762              * @property Style.CSS_NAV_LEFT
2763              */
2764              CSS_NAV_LEFT : defStyle.CSS_NAV_LEFT,
2765              /**
2766              * @property Style.CSS_NAV_RIGHT
2767              */
2768              CSS_NAV_RIGHT : defStyle.CSS_NAV_RIGHT,
2769              /**
2770              * @property Style.CSS_NAV
2771              */
2772              CSS_NAV : defStyle.CSS_NAV,
2773              /**
2774              * @property Style.CSS_CLOSE
2775              */
2776              CSS_CLOSE : defStyle.CSS_CLOSE,
2777              /**
2778              * @property Style.CSS_CELL_TOP
2779              */
2780              CSS_CELL_TOP : defStyle.CSS_CELL_TOP,
2781              /**
2782              * @property Style.CSS_CELL_LEFT
2783              */
2784              CSS_CELL_LEFT : defStyle.CSS_CELL_LEFT,
2785              /**
2786              * @property Style.CSS_CELL_RIGHT
2787              */
2788              CSS_CELL_RIGHT : defStyle.CSS_CELL_RIGHT,
2789              /**
2790              * @property Style.CSS_CELL_BOTTOM
2791              */
2792              CSS_CELL_BOTTOM : defStyle.CSS_CELL_BOTTOM,
2793              /**
2794              * @property Style.CSS_CELL_HOVER
2795              */
2796              CSS_CELL_HOVER : defStyle.CSS_CELL_HOVER,
2797              /**
2798              * @property Style.CSS_CELL_HIGHLIGHT1
2799              */
2800              CSS_CELL_HIGHLIGHT1 : defStyle.CSS_CELL_HIGHLIGHT1,
2801              /**
2802              * @property Style.CSS_CELL_HIGHLIGHT2
2803              */
2804              CSS_CELL_HIGHLIGHT2 : defStyle.CSS_CELL_HIGHLIGHT2,
2805              /**
2806              * @property Style.CSS_CELL_HIGHLIGHT3
2807              */
2808              CSS_CELL_HIGHLIGHT3 : defStyle.CSS_CELL_HIGHLIGHT3,
2809              /**
2810              * @property Style.CSS_CELL_HIGHLIGHT4
2811              */
2812              CSS_CELL_HIGHLIGHT4 : defStyle.CSS_CELL_HIGHLIGHT4,
2813              /**
2814               * @property Style.CSS_WITH_TITLE
2815               */
2816              CSS_WITH_TITLE : defStyle.CSS_WITH_TITLE,
2817               /**
2818               * @property Style.CSS_FIXED_SIZE
2819               */
2820              CSS_FIXED_SIZE : defStyle.CSS_FIXED_SIZE,
2821               /**
2822               * @property Style.CSS_LINK_CLOSE
2823               */
2824              CSS_LINK_CLOSE : defStyle.CSS_LINK_CLOSE
2825          };
2826      },
2827  
2828      /**
2829      * Builds the date label that will be displayed in the calendar header or
2830      * footer, depending on configuration.
2831      * @method buildMonthLabel
2832      * @return {HTML} The formatted calendar month label
2833      */
2834      buildMonthLabel : function() {
2835          return this._buildMonthLabel(this.cfg.getProperty(DEF_CFG.PAGEDATE.key));
2836      },
2837  
2838      /**
2839       * Helper method, to format a Month Year string, given a JavaScript Date, based on the 
2840       * Calendar localization settings
2841       * 
2842       * @method _buildMonthLabel
2843       * @private
2844       * @param {Date} date
2845       * @return {HTML} Formated month, year string
2846       */
2847      _buildMonthLabel : function(date) {
2848          var monthLabel  = this.Locale.LOCALE_MONTHS[date.getMonth()] + this.Locale.MY_LABEL_MONTH_SUFFIX,
2849              yearLabel = (date.getFullYear() + this.Locale.YEAR_OFFSET) + this.Locale.MY_LABEL_YEAR_SUFFIX;
2850  
2851          if (this.Locale.MY_LABEL_MONTH_POSITION == 2 || this.Locale.MY_LABEL_YEAR_POSITION == 1) {
2852              return yearLabel + monthLabel;
2853          } else {
2854              return monthLabel + yearLabel;
2855          }
2856      },
2857  
2858      /**
2859      * Builds the date digit that will be displayed in calendar cells
2860      * @method buildDayLabel
2861      * @param {Date} workingDate The current working date
2862      * @return {Number} The day
2863      */
2864      buildDayLabel : function(workingDate) {
2865          return workingDate.getDate();
2866      },
2867  
2868      /**
2869       * Creates the title bar element and adds it to Calendar container DIV. NOTE: The title parameter passed into this method is added to the DOM as HTML and should be escaped by the implementor if coming from an external source.  
2870       * 
2871       * @method createTitleBar
2872       * @param {HTML} strTitle The title to display in the title bar
2873       * @return The title bar element
2874       */
2875      createTitleBar : function(strTitle) {
2876          var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || document.createElement("div");
2877          tDiv.className = YAHOO.widget.CalendarGroup.CSS_2UPTITLE;
2878          tDiv.innerHTML = strTitle;
2879          this.oDomContainer.insertBefore(tDiv, this.oDomContainer.firstChild);
2880      
2881          Dom.addClass(this.oDomContainer, this.Style.CSS_WITH_TITLE);
2882      
2883          return tDiv;
2884      },
2885      
2886      /**
2887       * Removes the title bar element from the DOM
2888       * 
2889       * @method removeTitleBar
2890       */
2891      removeTitleBar : function() {
2892          var tDiv = Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || null;
2893          if (tDiv) {
2894              Event.purgeElement(tDiv);
2895              this.oDomContainer.removeChild(tDiv);
2896          }
2897          Dom.removeClass(this.oDomContainer, this.Style.CSS_WITH_TITLE);
2898      },
2899  
2900      /**
2901       * Creates the close button HTML element and adds it to Calendar container DIV
2902       * 
2903       * @method createCloseButton
2904       * @return {HTMLElement} The close HTML element created
2905       */
2906      createCloseButton : function() {
2907          var cssClose = YAHOO.widget.CalendarGroup.CSS_2UPCLOSE,
2908              cssLinkClose = this.Style.CSS_LINK_CLOSE,
2909              DEPR_CLOSE_PATH = "us/my/bn/x_d.gif",
2910  
2911              lnk = Dom.getElementsByClassName(cssLinkClose, "a", this.oDomContainer)[0],
2912              strings = this.cfg.getProperty(DEF_CFG.STRINGS.key),
2913              closeStr = (strings && strings.close) ? strings.close : "";
2914  
2915          if (!lnk) {
2916              lnk = document.createElement("a");
2917              Event.addListener(lnk, "click", function(e, cal) {
2918                  cal.hide(); 
2919                  Event.preventDefault(e);
2920              }, this);
2921          }
2922  
2923          lnk.href = "#";
2924          lnk.className = cssLinkClose;
2925  
2926          if (Calendar.IMG_ROOT !== null) {
2927              var img = Dom.getElementsByClassName(cssClose, "img", lnk)[0] || document.createElement("img");
2928              img.src = Calendar.IMG_ROOT + DEPR_CLOSE_PATH;
2929              img.className = cssClose;
2930              lnk.appendChild(img);
2931          } else {
2932              lnk.innerHTML = '<span class="' + cssClose + ' ' + this.Style.CSS_CLOSE + '">' + closeStr + '</span>';
2933          }
2934          this.oDomContainer.appendChild(lnk);
2935  
2936          return lnk;
2937      },
2938      
2939      /**
2940       * Removes the close button HTML element from the DOM
2941       * 
2942       * @method removeCloseButton
2943       */
2944      removeCloseButton : function() {
2945          var btn = Dom.getElementsByClassName(this.Style.CSS_LINK_CLOSE, "a", this.oDomContainer)[0] || null;
2946          if (btn) {
2947              Event.purgeElement(btn);
2948              this.oDomContainer.removeChild(btn);
2949          }
2950      },
2951  
2952      /**
2953      * Renders the calendar header. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
2954      * @method renderHeader
2955      * @param {HTML[]} html The current working HTML array
2956      * @return {HTML[]} The current working HTML array
2957      */
2958      renderHeader : function(html) {
2959  
2960  
2961          var colSpan = 7,
2962              DEPR_NAV_LEFT = "us/tr/callt.gif",
2963              DEPR_NAV_RIGHT = "us/tr/calrt.gif",
2964              cfg = this.cfg,
2965              pageDate = cfg.getProperty(DEF_CFG.PAGEDATE.key),
2966              strings= cfg.getProperty(DEF_CFG.STRINGS.key),
2967              prevStr = (strings && strings.previousMonth) ?  strings.previousMonth : "",
2968              nextStr = (strings && strings.nextMonth) ? strings.nextMonth : "",
2969              monthLabel;
2970  
2971          if (cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) {
2972              colSpan += 1;
2973          }
2974      
2975          if (cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) {
2976              colSpan += 1;
2977          }
2978  
2979          html[html.length] = "<thead>";
2980          html[html.length] =  "<tr>";
2981          html[html.length] =   '<th colspan="' + colSpan + '" class="' + this.Style.CSS_HEADER_TEXT + '">';
2982          html[html.length] =    '<div class="' + this.Style.CSS_HEADER + '">';
2983  
2984          var renderLeft, renderRight = false;
2985  
2986          if (this.parent) {
2987              if (this.index === 0) {
2988                  renderLeft = true;
2989              }
2990              if (this.index == (this.parent.cfg.getProperty("pages") -1)) {
2991                  renderRight = true;
2992              }
2993          } else {
2994              renderLeft = true;
2995              renderRight = true;
2996          }
2997  
2998          if (renderLeft) {
2999              monthLabel  = this._buildMonthLabel(DateMath.subtract(pageDate, DateMath.MONTH, 1));
3000  
3001              var leftArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_LEFT.key);
3002              // Check for deprecated customization - If someone set IMG_ROOT, but didn't set NAV_ARROW_LEFT, then set NAV_ARROW_LEFT to the old deprecated value
3003              if (leftArrow === null && Calendar.IMG_ROOT !== null) {
3004                  leftArrow = Calendar.IMG_ROOT + DEPR_NAV_LEFT;
3005              }
3006              var leftStyle = (leftArrow === null) ? "" : ' style="background-image:url(' + leftArrow + ')"';
3007              html[html.length] = '<a class="' + this.Style.CSS_NAV_LEFT + '"' + leftStyle + ' href="#">' + prevStr + ' (' + monthLabel + ')' + '</a>';
3008          }
3009  
3010          var lbl = this.buildMonthLabel();
3011          var cal = this.parent || this;
3012          if (cal.cfg.getProperty("navigator")) {
3013              lbl = "<a class=\"" + this.Style.CSS_NAV + "\" href=\"#\">" + lbl + "</a>";
3014          }
3015          html[html.length] = lbl;
3016  
3017          if (renderRight) {
3018              monthLabel  = this._buildMonthLabel(DateMath.add(pageDate, DateMath.MONTH, 1));
3019  
3020              var rightArrow = cfg.getProperty(DEF_CFG.NAV_ARROW_RIGHT.key);
3021              if (rightArrow === null && Calendar.IMG_ROOT !== null) {
3022                  rightArrow = Calendar.IMG_ROOT + DEPR_NAV_RIGHT;
3023              }
3024              var rightStyle = (rightArrow === null) ? "" : ' style="background-image:url(' + rightArrow + ')"';
3025              html[html.length] = '<a class="' + this.Style.CSS_NAV_RIGHT + '"' + rightStyle + ' href="#">' + nextStr + ' (' + monthLabel + ')' + '</a>';
3026          }
3027  
3028          html[html.length] = '</div>\n</th>\n</tr>';
3029  
3030          if (cfg.getProperty(DEF_CFG.SHOW_WEEKDAYS.key)) {
3031              html = this.buildWeekdays(html);
3032          }
3033          
3034          html[html.length] = '</thead>';
3035      
3036          return html;
3037      },
3038  
3039      /**
3040      * Renders the Calendar's weekday headers. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
3041      * @method buildWeekdays
3042      * @param {HTML[]} html The current working HTML array
3043      * @return {HTML[]} The current working HTML array
3044      */
3045      buildWeekdays : function(html) {
3046  
3047          html[html.length] = '<tr class="' + this.Style.CSS_WEEKDAY_ROW + '">';
3048  
3049          if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key)) {
3050              html[html.length] = '<th>&#160;</th>';
3051          }
3052  
3053          for(var i=0;i < this.Locale.LOCALE_WEEKDAYS.length; ++i) {
3054              html[html.length] = '<th class="' + this.Style.CSS_WEEKDAY_CELL + '">' + this.Locale.LOCALE_WEEKDAYS[i] + '</th>';
3055          }
3056  
3057          if (this.cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key)) {
3058              html[html.length] = '<th>&#160;</th>';
3059          }
3060  
3061          html[html.length] = '</tr>';
3062  
3063          return html;
3064      },
3065      
3066      /**
3067      * Renders the calendar body. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
3068      * @method renderBody
3069      * @param {Date} workingDate The current working Date being used for the render process
3070      * @param {HTML[]} html The current working HTML array
3071      * @return {HTML[]} The current working HTML array
3072      */
3073      renderBody : function(workingDate, html) {
3074  
3075          var startDay = this.cfg.getProperty(DEF_CFG.START_WEEKDAY.key);
3076  
3077          this.preMonthDays = workingDate.getDay();
3078          if (startDay > 0) {
3079              this.preMonthDays -= startDay;
3080          }
3081          if (this.preMonthDays < 0) {
3082              this.preMonthDays += 7;
3083          }
3084  
3085          this.monthDays = DateMath.findMonthEnd(workingDate).getDate();
3086          this.postMonthDays = Calendar.DISPLAY_DAYS-this.preMonthDays-this.monthDays;
3087  
3088  
3089          workingDate = DateMath.subtract(workingDate, DateMath.DAY, this.preMonthDays);
3090      
3091          var weekNum,
3092              weekClass,
3093              weekPrefix = "w",
3094              cellPrefix = "_cell",
3095              workingDayPrefix = "wd",
3096              dayPrefix = "d",
3097              cellRenderers,
3098              renderer,
3099              t = this.today,
3100              cfg = this.cfg,
3101              oom,
3102              todayYear = t.getFullYear(),
3103              todayMonth = t.getMonth(),
3104              todayDate = t.getDate(),
3105              useDate = cfg.getProperty(DEF_CFG.PAGEDATE.key),
3106              hideBlankWeeks = cfg.getProperty(DEF_CFG.HIDE_BLANK_WEEKS.key),
3107              showWeekFooter = cfg.getProperty(DEF_CFG.SHOW_WEEK_FOOTER.key),
3108              showWeekHeader = cfg.getProperty(DEF_CFG.SHOW_WEEK_HEADER.key),
3109              oomSelect = cfg.getProperty(DEF_CFG.OOM_SELECT.key),
3110              mindate = cfg.getProperty(DEF_CFG.MINDATE.key),
3111              maxdate = cfg.getProperty(DEF_CFG.MAXDATE.key),
3112              yearOffset = this.Locale.YEAR_OFFSET;
3113  
3114          if (mindate) {
3115              mindate = DateMath.clearTime(mindate);
3116          }
3117          if (maxdate) {
3118              maxdate = DateMath.clearTime(maxdate);
3119          }
3120  
3121          html[html.length] = '<tbody class="m' + (useDate.getMonth()+1) + ' ' + this.Style.CSS_BODY + '">';
3122  
3123          var i = 0,
3124              tempDiv = document.createElement("div"),
3125              cell = document.createElement("td");
3126  
3127          tempDiv.appendChild(cell);
3128  
3129          var cal = this.parent || this;
3130  
3131          for (var r = 0; r < 6; r++) {
3132              weekNum = DateMath.getWeekNumber(workingDate, startDay);
3133              weekClass = weekPrefix + weekNum;
3134  
3135              // Local OOM check for performance, since we already have pagedate
3136              if (r !== 0 && hideBlankWeeks === true && workingDate.getMonth() != useDate.getMonth()) {
3137                  break;
3138              } else {
3139                  html[html.length] = '<tr class="' + weekClass + '">';
3140  
3141                  if (showWeekHeader) { html = this.renderRowHeader(weekNum, html); }
3142  
3143                  for (var d=0; d < 7; d++){ // Render actual days
3144  
3145                      cellRenderers = [];
3146  
3147                      this.clearElement(cell);
3148                      cell.className = this.Style.CSS_CELL;
3149                      cell.id = this.id + cellPrefix + i;
3150  
3151                      if (workingDate.getDate()  == todayDate && 
3152                          workingDate.getMonth()  == todayMonth &&
3153                          workingDate.getFullYear() == todayYear) {
3154                          cellRenderers[cellRenderers.length]=cal.renderCellStyleToday;
3155                      }
3156  
3157                      var workingArray = [workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()];
3158                      this.cellDates[this.cellDates.length] = workingArray; // Add this date to cellDates
3159  
3160                      // Local OOM check for performance, since we already have pagedate
3161                      oom = workingDate.getMonth() != useDate.getMonth(); 
3162                      if (oom && !oomSelect) {
3163                          cellRenderers[cellRenderers.length]=cal.renderCellNotThisMonth;
3164                      } else {
3165                          Dom.addClass(cell, workingDayPrefix + workingDate.getDay());
3166                          Dom.addClass(cell, dayPrefix + workingDate.getDate());
3167  
3168                          // Concat, so that we're not splicing from an array 
3169                          // which we're also iterating
3170                          var rs = this.renderStack.concat();
3171  
3172                          for (var s=0, l = rs.length; s < l; ++s) {
3173  
3174                              renderer = null;
3175  
3176                              var rArray = rs[s],
3177                                  type = rArray[0],
3178                                  month,
3179                                  day,
3180                                  year;
3181  
3182                              switch (type) {
3183                                  case Calendar.DATE:
3184                                      month = rArray[1][1];
3185                                      day = rArray[1][2];
3186                                      year = rArray[1][0];
3187  
3188                                      if (workingDate.getMonth()+1 == month && workingDate.getDate() == day && workingDate.getFullYear() == year) {
3189                                          renderer = rArray[2];
3190                                          this.renderStack.splice(s,1);
3191                                      }
3192  
3193                                      break;
3194                                  case Calendar.MONTH_DAY:
3195                                      month = rArray[1][0];
3196                                      day = rArray[1][1];
3197  
3198                                      if (workingDate.getMonth()+1 == month && workingDate.getDate() == day) {
3199                                          renderer = rArray[2];
3200                                          this.renderStack.splice(s,1);
3201                                      }
3202                                      break;
3203                                  case Calendar.RANGE:
3204                                      var date1 = rArray[1][0],
3205                                          date2 = rArray[1][1],
3206                                          d1month = date1[1],
3207                                          d1day = date1[2],
3208                                          d1year = date1[0],
3209                                          d1 = DateMath.getDate(d1year, d1month-1, d1day),
3210                                          d2month = date2[1],
3211                                          d2day = date2[2],
3212                                          d2year = date2[0],
3213                                          d2 = DateMath.getDate(d2year, d2month-1, d2day);
3214  
3215                                      if (workingDate.getTime() >= d1.getTime() && workingDate.getTime() <= d2.getTime()) {
3216                                          renderer = rArray[2];
3217  
3218                                          if (workingDate.getTime()==d2.getTime()) { 
3219                                              this.renderStack.splice(s,1);
3220                                          }
3221                                      }
3222                                      break;
3223                                  case Calendar.WEEKDAY:
3224                                      var weekday = rArray[1][0];
3225                                      if (workingDate.getDay()+1 == weekday) {
3226                                          renderer = rArray[2];
3227                                      }
3228                                      break;
3229                                  case Calendar.MONTH:
3230                                      month = rArray[1][0];
3231                                      if (workingDate.getMonth()+1 == month) {
3232                                          renderer = rArray[2];
3233                                      }
3234                                      break;
3235                              }
3236  
3237                              if (renderer) {
3238                                  cellRenderers[cellRenderers.length]=renderer;
3239                              }
3240                          }
3241  
3242                      }
3243  
3244                      if (this._indexOfSelectedFieldArray(workingArray) > -1) {
3245                          cellRenderers[cellRenderers.length]=cal.renderCellStyleSelected; 
3246                      }
3247  
3248                      if (oom) {
3249                          cellRenderers[cellRenderers.length] = cal.styleCellNotThisMonth; 
3250                      }
3251  
3252                      if ((mindate && (workingDate.getTime() < mindate.getTime())) || (maxdate && (workingDate.getTime() > maxdate.getTime()))) {
3253                          cellRenderers[cellRenderers.length] = cal.renderOutOfBoundsDate;
3254                      } else {
3255                          cellRenderers[cellRenderers.length] = cal.styleCellDefault;
3256                          cellRenderers[cellRenderers.length] = cal.renderCellDefault;
3257                      }
3258  
3259                      for (var x=0; x < cellRenderers.length; ++x) {
3260                          if (cellRenderers[x].call(cal, workingDate, cell) == Calendar.STOP_RENDER) {
3261                              break;
3262                          }
3263                      }
3264  
3265                      workingDate.setTime(workingDate.getTime() + DateMath.ONE_DAY_MS);
3266                      // Just in case we crossed DST/Summertime boundaries
3267                      workingDate = DateMath.clearTime(workingDate);
3268  
3269                      if (i >= 0 && i <= 6) {
3270                          Dom.addClass(cell, this.Style.CSS_CELL_TOP);
3271                      }
3272                      if ((i % 7) === 0) {
3273                          Dom.addClass(cell, this.Style.CSS_CELL_LEFT);
3274                      }
3275                      if (((i+1) % 7) === 0) {
3276                          Dom.addClass(cell, this.Style.CSS_CELL_RIGHT);
3277                      }
3278  
3279                      var postDays = this.postMonthDays; 
3280                      if (hideBlankWeeks && postDays >= 7) {
3281                          var blankWeeks = Math.floor(postDays/7);
3282                          for (var p=0;p<blankWeeks;++p) {
3283                              postDays -= 7;
3284                          }
3285                      }
3286                      
3287                      if (i >= ((this.preMonthDays+postDays+this.monthDays)-7)) {
3288                          Dom.addClass(cell, this.Style.CSS_CELL_BOTTOM);
3289                      }
3290      
3291                      html[html.length] = tempDiv.innerHTML;
3292                      i++;
3293                  }
3294      
3295                  if (showWeekFooter) { html = this.renderRowFooter(weekNum, html); }
3296      
3297                  html[html.length] = '</tr>';
3298              }
3299          }
3300      
3301          html[html.length] = '</tbody>';
3302      
3303          return html;
3304      },
3305      
3306      /**
3307      * Renders the calendar footer. In the default implementation, there is no footer. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
3308      * @method renderFooter
3309      * @param {HTML[]} html The current working HTML array
3310      * @return {HTML[]} The current working HTML array
3311      */
3312      renderFooter : function(html) { return html; },
3313      
3314      /**
3315      * Renders the calendar after it has been configured. The render() method has a specific call chain that will execute
3316      * when the method is called: renderHeader, renderBody, renderFooter.
3317      * Refer to the documentation for those methods for information on individual render tasks.
3318      * @method render
3319      */
3320      render : function() {
3321          this.beforeRenderEvent.fire();
3322  
3323          // Find starting day of the current month
3324          var workingDate = DateMath.findMonthStart(this.cfg.getProperty(DEF_CFG.PAGEDATE.key));
3325  
3326          this.resetRenderers();
3327          this.cellDates.length = 0;
3328  
3329          Event.purgeElement(this.oDomContainer, true);
3330  
3331          var html = [], 
3332              table;
3333  
3334          html[html.length] = '<table cellSpacing="0" class="' + this.Style.CSS_CALENDAR + ' y' + (workingDate.getFullYear() + this.Locale.YEAR_OFFSET) +'" id="' + this.id + '">';
3335          html = this.renderHeader(html);
3336          html = this.renderBody(workingDate, html);
3337          html = this.renderFooter(html);
3338          html[html.length] = '</table>';
3339  
3340          this.oDomContainer.innerHTML = html.join("\n");
3341  
3342          this.applyListeners();
3343  
3344          // Using oDomContainer.ownerDocument, to allow for cross-frame rendering
3345          table = ((this._oDoc) && this._oDoc.getElementById(this.id)) || (this.id);
3346  
3347          this.cells = Dom.getElementsByClassName(this.Style.CSS_CELL, "td", table);
3348  
3349          this.cfg.refireEvent(DEF_CFG.TITLE.key);
3350          this.cfg.refireEvent(DEF_CFG.CLOSE.key);
3351          this.cfg.refireEvent(DEF_CFG.IFRAME.key);
3352  
3353          this.renderEvent.fire();
3354      },
3355  
3356      /**
3357      * Applies the Calendar's DOM listeners to applicable elements.
3358      * @method applyListeners
3359      */
3360      applyListeners : function() {
3361          var root = this.oDomContainer,
3362              cal = this.parent || this,
3363              anchor = "a",
3364              click = "click";
3365  
3366          var linkLeft = Dom.getElementsByClassName(this.Style.CSS_NAV_LEFT, anchor, root),
3367              linkRight = Dom.getElementsByClassName(this.Style.CSS_NAV_RIGHT, anchor, root);
3368  
3369          if (linkLeft && linkLeft.length > 0) {
3370              this.linkLeft = linkLeft[0];
3371              Event.addListener(this.linkLeft, click, this.doPreviousMonthNav, cal, true);
3372          }
3373  
3374          if (linkRight && linkRight.length > 0) {
3375              this.linkRight = linkRight[0];
3376              Event.addListener(this.linkRight, click, this.doNextMonthNav, cal, true);
3377          }
3378  
3379          if (cal.cfg.getProperty("navigator") !== null) {
3380              this.applyNavListeners();
3381          }
3382  
3383          if (this.domEventMap) {
3384              var el,elements;
3385              for (var cls in this.domEventMap) { 
3386                  if (Lang.hasOwnProperty(this.domEventMap, cls)) {
3387                      var items = this.domEventMap[cls];
3388      
3389                      if (! (items instanceof Array)) {
3390                          items = [items];
3391                      }
3392      
3393                      for (var i=0;i<items.length;i++) {
3394                          var item = items[i];
3395                          elements = Dom.getElementsByClassName(cls, item.tag, this.oDomContainer);
3396      
3397                          for (var c=0;c<elements.length;c++) {
3398                              el = elements[c];
3399                               Event.addListener(el, item.event, item.handler, item.scope, item.correct );
3400                          }
3401                      }
3402                  }
3403              }
3404          }
3405  
3406          Event.addListener(this.oDomContainer, "click", this.doSelectCell, this);
3407          Event.addListener(this.oDomContainer, "mouseover", this.doCellMouseOver, this);
3408          Event.addListener(this.oDomContainer, "mouseout", this.doCellMouseOut, this);
3409      },
3410  
3411      /**
3412      * Applies the DOM listeners to activate the Calendar Navigator.
3413      * @method applyNavListeners
3414      */
3415      applyNavListeners : function() {
3416          var calParent = this.parent || this,
3417              cal = this,
3418              navBtns = Dom.getElementsByClassName(this.Style.CSS_NAV, "a", this.oDomContainer);
3419  
3420          if (navBtns.length > 0) {
3421  
3422              Event.addListener(navBtns, "click", function (e, obj) {
3423                  var target = Event.getTarget(e);
3424                  // this == navBtn
3425                  if (this === target || Dom.isAncestor(this, target)) {
3426                      Event.preventDefault(e);
3427                  }
3428                  var navigator = calParent.oNavigator;
3429                  if (navigator) {
3430                      var pgdate = cal.cfg.getProperty("pagedate");
3431                      navigator.setYear(pgdate.getFullYear() + cal.Locale.YEAR_OFFSET);
3432                      navigator.setMonth(pgdate.getMonth());
3433                      navigator.show();
3434                  }
3435              });
3436          }
3437      },
3438  
3439      /**
3440      * Retrieves the Date object for the specified Calendar cell
3441      * @method getDateByCellId
3442      * @param {String} id The id of the cell
3443      * @return {Date} The Date object for the specified Calendar cell
3444      */
3445      getDateByCellId : function(id) {
3446          var date = this.getDateFieldsByCellId(id);
3447          return (date) ? DateMath.getDate(date[0],date[1]-1,date[2]) : null;
3448      },
3449      
3450      /**
3451      * Retrieves the Date object for the specified Calendar cell
3452      * @method getDateFieldsByCellId
3453      * @param {String} id The id of the cell
3454      * @return {Array} The array of Date fields for the specified Calendar cell
3455      */
3456      getDateFieldsByCellId : function(id) {
3457          id = this.getIndexFromId(id);
3458          return (id > -1) ? this.cellDates[id] : null;
3459      },
3460  
3461      /**
3462       * Find the Calendar's cell index for a given date.
3463       * If the date is not found, the method returns -1.
3464       * <p>
3465       * The returned index can be used to lookup the cell HTMLElement  
3466       * using the Calendar's cells array or passed to selectCell to select 
3467       * cells by index. 
3468       * </p>
3469       *
3470       * See <a href="#cells">cells</a>, <a href="#selectCell">selectCell</a>.
3471       *
3472       * @method getCellIndex
3473       * @param {Date} date JavaScript Date object, for which to find a cell index.
3474       * @return {Number} The index of the date in Calendars cellDates/cells arrays, or -1 if the date 
3475       * is not on the curently rendered Calendar page.
3476       */
3477      getCellIndex : function(date) {
3478          var idx = -1;
3479          if (date) {
3480              var m = date.getMonth(),
3481                  y = date.getFullYear(),
3482                  d = date.getDate(),
3483                  dates = this.cellDates;
3484  
3485              for (var i = 0; i < dates.length; ++i) {
3486                  var cellDate = dates[i];
3487                  if (cellDate[0] === y && cellDate[1] === m+1 && cellDate[2] === d) {
3488                      idx = i;
3489                      break;
3490                  }
3491              }
3492          }
3493          return idx;
3494      },
3495  
3496      /**
3497       * Given the id used to mark each Calendar cell, this method
3498       * extracts the index number from the id.
3499       * 
3500       * @param {String} strId The cell id
3501       * @return {Number} The index of the cell, or -1 if id does not contain an index number
3502       */
3503      getIndexFromId : function(strId) {
3504          var idx = -1,
3505              li = strId.lastIndexOf("_cell");
3506  
3507          if (li > -1) {
3508              idx = parseInt(strId.substring(li + 5), 10);
3509          }
3510  
3511          return idx;
3512      },
3513      
3514      // BEGIN BUILT-IN TABLE CELL RENDERERS
3515      
3516      /**
3517      * Renders a cell that falls before the minimum date or after the maximum date.
3518      * @method renderOutOfBoundsDate
3519      * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3520      * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3521      * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3522      *   should not be terminated
3523      */
3524      renderOutOfBoundsDate : function(workingDate, cell) {
3525          Dom.addClass(cell, this.Style.CSS_CELL_OOB);
3526          cell.innerHTML = workingDate.getDate();
3527          return Calendar.STOP_RENDER;
3528      },
3529  
3530      /**
3531      * Renders the row header HTML for a week.
3532      *
3533      * @method renderRowHeader
3534      * @param {Number} weekNum The week number of the current row
3535      * @param {HTML[]} cell The current working HTML array
3536      */
3537      renderRowHeader : function(weekNum, html) {
3538          html[html.length] = '<th class="' + this.Style.CSS_ROW_HEADER + '">' + weekNum + '</th>';
3539          return html;
3540      },
3541  
3542      /**
3543      * Renders the row footer HTML for a week.
3544      *
3545      * @method renderRowFooter
3546      * @param {Number} weekNum The week number of the current row
3547      * @param {HTML[]} cell The current working HTML array
3548      */
3549      renderRowFooter : function(weekNum, html) {
3550          html[html.length] = '<th class="' + this.Style.CSS_ROW_FOOTER + '">' + weekNum + '</th>';
3551          return html;
3552      },
3553  
3554      /**
3555      * Renders a single standard calendar cell in the calendar widget table.
3556      *
3557      * All logic for determining how a standard default cell will be rendered is 
3558      * encapsulated in this method, and must be accounted for when extending the
3559      * widget class.
3560      *
3561      * @method renderCellDefault
3562      * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3563      * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3564      */
3565      renderCellDefault : function(workingDate, cell) {
3566          cell.innerHTML = '<a href="#" class="' + this.Style.CSS_CELL_SELECTOR + '">' + this.buildDayLabel(workingDate) + "</a>";
3567      },
3568      
3569      /**
3570      * Styles a selectable cell.
3571      * @method styleCellDefault
3572      * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3573      * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3574      */
3575      styleCellDefault : function(workingDate, cell) {
3576          Dom.addClass(cell, this.Style.CSS_CELL_SELECTABLE);
3577      },
3578      
3579      
3580      /**
3581      * Renders a single standard calendar cell using the CSS hightlight1 style
3582      * @method renderCellStyleHighlight1
3583      * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3584      * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3585      */
3586      renderCellStyleHighlight1 : function(workingDate, cell) {
3587          Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT1);
3588      },
3589      
3590      /**
3591      * Renders a single standard calendar cell using the CSS hightlight2 style
3592      * @method renderCellStyleHighlight2
3593      * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3594      * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3595      */
3596      renderCellStyleHighlight2 : function(workingDate, cell) {
3597          Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT2);
3598      },
3599      
3600      /**
3601      * Renders a single standard calendar cell using the CSS hightlight3 style
3602      * @method renderCellStyleHighlight3
3603      * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3604      * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3605      */
3606      renderCellStyleHighlight3 : function(workingDate, cell) {
3607          Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT3);
3608      },
3609      
3610      /**
3611      * Renders a single standard calendar cell using the CSS hightlight4 style
3612      * @method renderCellStyleHighlight4
3613      * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3614      * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3615      */
3616      renderCellStyleHighlight4 : function(workingDate, cell) {
3617          Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT4);
3618      },
3619      
3620      /**
3621      * Applies the default style used for rendering today's date to the current calendar cell
3622      * @method renderCellStyleToday
3623      * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3624      * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3625      */
3626      renderCellStyleToday : function(workingDate, cell) {
3627          Dom.addClass(cell, this.Style.CSS_CELL_TODAY);
3628      },
3629  
3630      /**
3631      * Applies the default style used for rendering selected dates to the current calendar cell
3632      * @method renderCellStyleSelected
3633      * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3634      * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3635      * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3636      *   should not be terminated
3637      */
3638      renderCellStyleSelected : function(workingDate, cell) {
3639          Dom.addClass(cell, this.Style.CSS_CELL_SELECTED);
3640      },
3641  
3642      /**
3643      * Applies the default style used for rendering dates that are not a part of the current
3644      * month (preceding or trailing the cells for the current month)
3645      *
3646      * @method renderCellNotThisMonth
3647      * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3648      * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3649      * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3650      *   should not be terminated
3651      */
3652      renderCellNotThisMonth : function(workingDate, cell) {
3653          this.styleCellNotThisMonth(workingDate, cell);
3654          cell.innerHTML=workingDate.getDate();
3655          return Calendar.STOP_RENDER;
3656      },
3657  
3658      /** Applies the style used for rendering out-of-month dates to the current calendar cell
3659      * @method styleCellNotThisMonth
3660      * @param {Date}                 workingDate     The current working Date object being used to generate the calendar
3661      * @param {HTMLTableCellElement} cell            The current working cell in the calendar
3662      */
3663      styleCellNotThisMonth : function(workingDate, cell) {
3664          YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOM);
3665      },
3666  
3667      /**
3668      * Renders the current calendar cell as a non-selectable "black-out" date using the default
3669      * restricted style.
3670      * @method renderBodyCellRestricted
3671      * @param {Date}     workingDate  The current working Date object being used to generate the calendar
3672      * @param {HTMLTableCellElement} cell   The current working cell in the calendar
3673      * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
3674      *   should not be terminated
3675      */
3676      renderBodyCellRestricted : function(workingDate, cell) {
3677          Dom.addClass(cell, this.Style.CSS_CELL);
3678          Dom.addClass(cell, this.Style.CSS_CELL_RESTRICTED);
3679          cell.innerHTML=workingDate.getDate();
3680          return Calendar.STOP_RENDER;
3681      },
3682      
3683      // END BUILT-IN TABLE CELL RENDERERS
3684      
3685      // BEGIN MONTH NAVIGATION METHODS
3686  
3687      /**
3688      * Adds the designated number of months to the current calendar month, and sets the current
3689      * calendar page date to the new month.
3690      * @method addMonths
3691      * @param {Number} count The number of months to add to the current calendar
3692      */
3693      addMonths : function(count) {
3694          var cfgPageDate = DEF_CFG.PAGEDATE.key,
3695  
3696          prevDate = this.cfg.getProperty(cfgPageDate),
3697          newDate = DateMath.add(prevDate, DateMath.MONTH, count);
3698  
3699          this.cfg.setProperty(cfgPageDate, newDate);
3700          this.resetRenderers();
3701          this.changePageEvent.fire(prevDate, newDate);
3702      },
3703  
3704      /**
3705      * Subtracts the designated number of months from the current calendar month, and sets the current
3706      * calendar page date to the new month.
3707      * @method subtractMonths
3708      * @param {Number} count The number of months to subtract from the current calendar
3709      */
3710      subtractMonths : function(count) {
3711          this.addMonths(-1*count);
3712      },
3713  
3714      /**
3715      * Adds the designated number of years to the current calendar, and sets the current
3716      * calendar page date to the new month.
3717      * @method addYears
3718      * @param {Number} count The number of years to add to the current calendar
3719      */
3720      addYears : function(count) {
3721          var cfgPageDate = DEF_CFG.PAGEDATE.key,
3722  
3723          prevDate = this.cfg.getProperty(cfgPageDate),
3724          newDate = DateMath.add(prevDate, DateMath.YEAR, count);
3725  
3726          this.cfg.setProperty(cfgPageDate, newDate);
3727          this.resetRenderers();
3728          this.changePageEvent.fire(prevDate, newDate);
3729      },
3730  
3731      /**
3732      * Subtcats the designated number of years from the current calendar, and sets the current
3733      * calendar page date to the new month.
3734      * @method subtractYears
3735      * @param {Number} count The number of years to subtract from the current calendar
3736      */
3737      subtractYears : function(count) {
3738          this.addYears(-1*count);
3739      },
3740  
3741      /**
3742      * Navigates to the next month page in the calendar widget.
3743      * @method nextMonth
3744      */
3745      nextMonth : function() {
3746          this.addMonths(1);
3747      },
3748      
3749      /**
3750      * Navigates to the previous month page in the calendar widget.
3751      * @method previousMonth
3752      */
3753      previousMonth : function() {
3754          this.addMonths(-1);
3755      },
3756      
3757      /**
3758      * Navigates to the next year in the currently selected month in the calendar widget.
3759      * @method nextYear
3760      */
3761      nextYear : function() {
3762          this.addYears(1);
3763      },
3764      
3765      /**
3766      * Navigates to the previous year in the currently selected month in the calendar widget.
3767      * @method previousYear
3768      */
3769      previousYear : function() {
3770          this.addYears(-1);
3771      },
3772  
3773      // END MONTH NAVIGATION METHODS
3774      
3775      // BEGIN SELECTION METHODS
3776      
3777      /**
3778      * Resets the calendar widget to the originally selected month and year, and 
3779      * sets the calendar to the initial selection(s).
3780      * @method reset
3781      */
3782      reset : function() {
3783          this.cfg.resetProperty(DEF_CFG.SELECTED.key);
3784          this.cfg.resetProperty(DEF_CFG.PAGEDATE.key);
3785          this.resetEvent.fire();
3786      },
3787      
3788      /**
3789      * Clears the selected dates in the current calendar widget and sets the calendar
3790      * to the current month and year.
3791      * @method clear
3792      */
3793      clear : function() {
3794          this.cfg.setProperty(DEF_CFG.SELECTED.key, []);
3795          this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.today.getTime()));
3796          this.clearEvent.fire();
3797      },
3798      
3799      /**
3800      * Selects a date or a collection of dates on the current calendar. This method, by default,
3801      * does not call the render method explicitly. Once selection has completed, render must be 
3802      * called for the changes to be reflected visually.
3803      *
3804      * Any dates which are OOB (out of bounds, not selectable) will not be selected and the array of 
3805      * selected dates passed to the selectEvent will not contain OOB dates.
3806      * 
3807      * If all dates are OOB, the no state change will occur; beforeSelect and select events will not be fired.
3808      *
3809      * @method select
3810      * @param {String/Date/Date[]} date The date string of dates to select in the current calendar. Valid formats are
3811      *        individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3812      *        Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3813      *        This method can also take a JavaScript Date object or an array of Date objects.
3814      * @return {Date[]}   Array of JavaScript Date objects representing all individual dates that are currently selected.
3815      */
3816      select : function(date) {
3817  
3818          var aToBeSelected = this._toFieldArray(date),
3819              validDates = [],
3820              selected = [],
3821              cfgSelected = DEF_CFG.SELECTED.key;
3822  
3823          
3824          for (var a=0; a < aToBeSelected.length; ++a) {
3825              var toSelect = aToBeSelected[a];
3826  
3827              if (!this.isDateOOB(this._toDate(toSelect))) {
3828  
3829                  if (validDates.length === 0) {
3830                      this.beforeSelectEvent.fire();
3831                      selected = this.cfg.getProperty(cfgSelected);
3832                  }
3833                  validDates.push(toSelect);
3834  
3835                  if (this._indexOfSelectedFieldArray(toSelect) == -1) { 
3836                      selected[selected.length] = toSelect;
3837                  }
3838              }
3839          }
3840  
3841  
3842          if (validDates.length > 0) {
3843              if (this.parent) {
3844                  this.parent.cfg.setProperty(cfgSelected, selected);
3845              } else {
3846                  this.cfg.setProperty(cfgSelected, selected);
3847              }
3848              this.selectEvent.fire(validDates);
3849          }
3850  
3851          return this.getSelectedDates();
3852      },
3853      
3854      /**
3855      * Selects a date on the current calendar by referencing the index of the cell that should be selected.
3856      * This method is used to easily select a single cell (usually with a mouse click) without having to do
3857      * a full render. The selected style is applied to the cell directly.
3858      *
3859      * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month 
3860      * or out of bounds cells), it will not be selected and in such a case beforeSelect and select events will not be fired.
3861      * 
3862      * @method selectCell
3863      * @param {Number} cellIndex The index of the cell to select in the current calendar. 
3864      * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3865      */
3866      selectCell : function(cellIndex) {
3867  
3868          var cell = this.cells[cellIndex],
3869              cellDate = this.cellDates[cellIndex],
3870              dCellDate = this._toDate(cellDate),
3871              selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
3872  
3873  
3874          if (selectable) {
3875      
3876              this.beforeSelectEvent.fire();
3877      
3878              var cfgSelected = DEF_CFG.SELECTED.key;
3879              var selected = this.cfg.getProperty(cfgSelected);
3880      
3881              var selectDate = cellDate.concat();
3882      
3883              if (this._indexOfSelectedFieldArray(selectDate) == -1) {
3884                  selected[selected.length] = selectDate;
3885              }
3886              if (this.parent) {
3887                  this.parent.cfg.setProperty(cfgSelected, selected);
3888              } else {
3889                  this.cfg.setProperty(cfgSelected, selected);
3890              }
3891              this.renderCellStyleSelected(dCellDate,cell);
3892              this.selectEvent.fire([selectDate]);
3893      
3894              this.doCellMouseOut.call(cell, null, this);  
3895          }
3896      
3897          return this.getSelectedDates();
3898      },
3899      
3900      /**
3901      * Deselects a date or a collection of dates on the current calendar. This method, by default,
3902      * does not call the render method explicitly. Once deselection has completed, render must be 
3903      * called for the changes to be reflected visually.
3904      * 
3905      * The method will not attempt to deselect any dates which are OOB (out of bounds, and hence not selectable) 
3906      * and the array of deselected dates passed to the deselectEvent will not contain any OOB dates.
3907      * 
3908      * If all dates are OOB, beforeDeselect and deselect events will not be fired.
3909      * 
3910      * @method deselect
3911      * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
3912      *        individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3913      *        Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3914      *        This method can also take a JavaScript Date object or an array of Date objects. 
3915      * @return {Date[]}   Array of JavaScript Date objects representing all individual dates that are currently selected.
3916      */
3917      deselect : function(date) {
3918  
3919          var aToBeDeselected = this._toFieldArray(date),
3920              validDates = [],
3921              selected = [],
3922              cfgSelected = DEF_CFG.SELECTED.key;
3923  
3924  
3925          for (var a=0; a < aToBeDeselected.length; ++a) {
3926              var toDeselect = aToBeDeselected[a];
3927      
3928              if (!this.isDateOOB(this._toDate(toDeselect))) {
3929      
3930                  if (validDates.length === 0) {
3931                      this.beforeDeselectEvent.fire();
3932                      selected = this.cfg.getProperty(cfgSelected);
3933                  }
3934      
3935                  validDates.push(toDeselect);
3936      
3937                  var index = this._indexOfSelectedFieldArray(toDeselect);
3938                  if (index != -1) { 
3939                      selected.splice(index,1);
3940                  }
3941              }
3942          }
3943      
3944      
3945          if (validDates.length > 0) {
3946              if (this.parent) {
3947                  this.parent.cfg.setProperty(cfgSelected, selected);
3948              } else {
3949                  this.cfg.setProperty(cfgSelected, selected);
3950              }
3951              this.deselectEvent.fire(validDates);
3952          }
3953      
3954          return this.getSelectedDates();
3955      },
3956      
3957      /**
3958      * Deselects a date on the current calendar by referencing the index of the cell that should be deselected.
3959      * This method is used to easily deselect a single cell (usually with a mouse click) without having to do
3960      * a full render. The selected style is removed from the cell directly.
3961      * 
3962      * If the cell is not marked with the CSS_CELL_SELECTABLE class (as is the case by default for out of month 
3963      * or out of bounds cells), the method will not attempt to deselect it and in such a case, beforeDeselect and 
3964      * deselect events will not be fired.
3965      * 
3966      * @method deselectCell
3967      * @param {Number} cellIndex The index of the cell to deselect in the current calendar. 
3968      * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3969      */
3970      deselectCell : function(cellIndex) {
3971          var cell = this.cells[cellIndex],
3972              cellDate = this.cellDates[cellIndex],
3973              cellDateIndex = this._indexOfSelectedFieldArray(cellDate);
3974  
3975          var selectable = Dom.hasClass(cell, this.Style.CSS_CELL_SELECTABLE);
3976  
3977          if (selectable) {
3978  
3979              this.beforeDeselectEvent.fire();
3980  
3981              var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key),
3982                  dCellDate = this._toDate(cellDate),
3983                  selectDate = cellDate.concat();
3984  
3985              if (cellDateIndex > -1) {
3986                  if ((this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth() == dCellDate.getMonth() &&
3987                      this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getFullYear() == dCellDate.getFullYear()) || this.cfg.getProperty(DEF_CFG.OOM_SELECT.key)) {
3988                      Dom.removeClass(cell, this.Style.CSS_CELL_SELECTED);
3989                  }
3990                  selected.splice(cellDateIndex, 1);
3991              }
3992  
3993              if (this.parent) {
3994                  this.parent.cfg.setProperty(DEF_CFG.SELECTED.key, selected);
3995              } else {
3996                  this.cfg.setProperty(DEF_CFG.SELECTED.key, selected);
3997              }
3998  
3999              this.deselectEvent.fire([selectDate]);
4000          }
4001  
4002          return this.getSelectedDates();
4003      },
4004  
4005      /**
4006      * Deselects all dates on the current calendar.
4007      * @method deselectAll
4008      * @return {Date[]}  Array of JavaScript Date objects representing all individual dates that are currently selected.
4009      *      Assuming that this function executes properly, the return value should be an empty array.
4010      *      However, the empty array is returned for the sake of being able to check the selection status
4011      *      of the calendar.
4012      */
4013      deselectAll : function() {
4014          this.beforeDeselectEvent.fire();
4015          
4016          var cfgSelected = DEF_CFG.SELECTED.key,
4017              selected = this.cfg.getProperty(cfgSelected),
4018              count = selected.length,
4019              sel = selected.concat();
4020  
4021          if (this.parent) {
4022              this.parent.cfg.setProperty(cfgSelected, []);
4023          } else {
4024              this.cfg.setProperty(cfgSelected, []);
4025          }
4026          
4027          if (count > 0) {
4028              this.deselectEvent.fire(sel);
4029          }
4030      
4031          return this.getSelectedDates();
4032      },
4033      
4034      // END SELECTION METHODS
4035      
4036      // BEGIN TYPE CONVERSION METHODS
4037      
4038      /**
4039      * Converts a date (either a JavaScript Date object, or a date string) to the internal data structure
4040      * used to represent dates: [[yyyy,mm,dd],[yyyy,mm,dd]].
4041      * @method _toFieldArray
4042      * @private
4043      * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
4044      *        individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
4045      *        Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
4046      *        This method can also take a JavaScript Date object or an array of Date objects. 
4047      * @return {Array[](Number[])} Array of date field arrays
4048      */
4049      _toFieldArray : function(date) {
4050          var returnDate = [];
4051      
4052          if (date instanceof Date) {
4053              returnDate = [[date.getFullYear(), date.getMonth()+1, date.getDate()]];
4054          } else if (Lang.isString(date)) {
4055              returnDate = this._parseDates(date);
4056          } else if (Lang.isArray(date)) {
4057              for (var i=0;i<date.length;++i) {
4058                  var d = date[i];
4059                  returnDate[returnDate.length] = [d.getFullYear(),d.getMonth()+1,d.getDate()];
4060              }
4061          }
4062          
4063          return returnDate;
4064      },
4065      
4066      /**
4067      * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object. The date field array
4068      * is the format in which dates are as provided as arguments to selectEvent and deselectEvent listeners.
4069      * 
4070      * @method toDate
4071      * @param {Number[]} dateFieldArray The date field array to convert to a JavaScript Date.
4072      * @return {Date} JavaScript Date object representing the date field array.
4073      */
4074      toDate : function(dateFieldArray) {
4075          return this._toDate(dateFieldArray);
4076      },
4077      
4078      /**
4079      * Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
4080      * @method _toDate
4081      * @private
4082      * @deprecated Made public, toDate 
4083      * @param {Number[]}  dateFieldArray The date field array to convert to a JavaScript Date.
4084      * @return {Date} JavaScript Date object representing the date field array
4085      */
4086      _toDate : function(dateFieldArray) {
4087          if (dateFieldArray instanceof Date) {
4088              return dateFieldArray;
4089          } else {
4090              return DateMath.getDate(dateFieldArray[0],dateFieldArray[1]-1,dateFieldArray[2]);
4091          }
4092      },
4093      
4094      // END TYPE CONVERSION METHODS 
4095      
4096      // BEGIN UTILITY METHODS
4097      
4098      /**
4099      * Determines if 2 field arrays are equal.
4100      * @method _fieldArraysAreEqual
4101      * @private
4102      * @param {Number[]} array1 The first date field array to compare
4103      * @param {Number[]} array2 The first date field array to compare
4104      * @return {Boolean} The boolean that represents the equality of the two arrays
4105      */
4106      _fieldArraysAreEqual : function(array1, array2) {
4107          var match = false;
4108      
4109          if (array1[0]==array2[0]&&array1[1]==array2[1]&&array1[2]==array2[2]) {
4110              match=true; 
4111          }
4112      
4113          return match;
4114      },
4115      
4116      /**
4117      * Gets the index of a date field array [yyyy,mm,dd] in the current list of selected dates.
4118      * @method _indexOfSelectedFieldArray
4119      * @private
4120      * @param {Number[]}  find The date field array to search for
4121      * @return {Number}   The index of the date field array within the collection of selected dates.
4122      *        -1 will be returned if the date is not found.
4123      */
4124      _indexOfSelectedFieldArray : function(find) {
4125          var selected = -1,
4126              seldates = this.cfg.getProperty(DEF_CFG.SELECTED.key);
4127      
4128          for (var s=0;s<seldates.length;++s) {
4129              var sArray = seldates[s];
4130              if (find[0]==sArray[0]&&find[1]==sArray[1]&&find[2]==sArray[2]) {
4131                  selected = s;
4132                  break;
4133              }
4134          }
4135      
4136          return selected;
4137      },
4138      
4139      /**
4140      * Determines whether a given date is OOM (out of month).
4141      * @method isDateOOM
4142      * @param {Date} date The JavaScript Date object for which to check the OOM status
4143      * @return {Boolean} true if the date is OOM
4144      */
4145      isDateOOM : function(date) {
4146          return (date.getMonth() != this.cfg.getProperty(DEF_CFG.PAGEDATE.key).getMonth());
4147      },
4148      
4149      /**
4150      * Determines whether a given date is OOB (out of bounds - less than the mindate or more than the maxdate).
4151      *
4152      * @method isDateOOB
4153      * @param {Date} date The JavaScript Date object for which to check the OOB status
4154      * @return {Boolean} true if the date is OOB
4155      */
4156      isDateOOB : function(date) {
4157          var minDate = this.cfg.getProperty(DEF_CFG.MINDATE.key),
4158              maxDate = this.cfg.getProperty(DEF_CFG.MAXDATE.key),
4159              dm = DateMath;
4160          
4161          if (minDate) {
4162              minDate = dm.clearTime(minDate);
4163          } 
4164          if (maxDate) {
4165              maxDate = dm.clearTime(maxDate);
4166          }
4167      
4168          var clearedDate = new Date(date.getTime());
4169          clearedDate = dm.clearTime(clearedDate);
4170      
4171          return ((minDate && clearedDate.getTime() < minDate.getTime()) || (maxDate && clearedDate.getTime() > maxDate.getTime()));
4172      },
4173      
4174      /**
4175       * Parses a pagedate configuration property value. The value can either be specified as a string of form "mm/yyyy" or a Date object 
4176       * and is parsed into a Date object normalized to the first day of the month. If no value is passed in, the month and year from today's date are used to create the Date object 
4177       * @method _parsePageDate
4178       * @private
4179       * @param {Date|String} date Pagedate value which needs to be parsed
4180       * @return {Date} The Date object representing the pagedate
4181       */
4182      _parsePageDate : function(date) {
4183          var parsedDate;
4184  
4185          if (date) {
4186              if (date instanceof Date) {
4187                  parsedDate = DateMath.findMonthStart(date);
4188              } else {
4189                  var month, year, aMonthYear;
4190                  aMonthYear = date.split(this.cfg.getProperty(DEF_CFG.DATE_FIELD_DELIMITER.key));
4191                  month = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_MONTH_POSITION.key)-1], 10)-1;
4192                  year = parseInt(aMonthYear[this.cfg.getProperty(DEF_CFG.MY_YEAR_POSITION.key)-1], 10) - this.Locale.YEAR_OFFSET;
4193  
4194                  parsedDate = DateMath.getDate(year, month, 1);
4195              }
4196          } else {
4197              parsedDate = DateMath.getDate(this.today.getFullYear(), this.today.getMonth(), 1);
4198          }
4199          return parsedDate;
4200      },
4201      
4202      // END UTILITY METHODS
4203      
4204      // BEGIN EVENT HANDLERS
4205      
4206      /**
4207      * Event executed before a date is selected in the calendar widget.
4208      * @deprecated Event handlers for this event should be susbcribed to beforeSelectEvent.
4209      */
4210      onBeforeSelect : function() {
4211          if (this.cfg.getProperty(DEF_CFG.MULTI_SELECT.key) === false) {
4212              if (this.parent) {
4213                  this.parent.callChildFunction("clearAllBodyCellStyles", this.Style.CSS_CELL_SELECTED);
4214                  this.parent.deselectAll();
4215              } else {
4216                  this.clearAllBodyCellStyles(this.Style.CSS_CELL_SELECTED);
4217                  this.deselectAll();
4218              }
4219          }
4220      },
4221      
4222      /**
4223      * Event executed when a date is selected in the calendar widget.
4224      * @param {Array} selected An array of date field arrays representing which date or dates were selected. Example: [ [2006,8,6],[2006,8,7],[2006,8,8] ]
4225      * @deprecated Event handlers for this event should be susbcribed to selectEvent.
4226      */
4227      onSelect : function(selected) { },
4228      
4229      /**
4230      * Event executed before a date is deselected in the calendar widget.
4231      * @deprecated Event handlers for this event should be susbcribed to beforeDeselectEvent.
4232      */
4233      onBeforeDeselect : function() { },
4234      
4235      /**
4236      * Event executed when a date is deselected in the calendar widget.
4237      * @param {Array} selected An array of date field arrays representing which date or dates were deselected. Example: [ [2006,8,6],[2006,8,7],[2006,8,8] ]
4238      * @deprecated Event handlers for this event should be susbcribed to deselectEvent.
4239      */
4240      onDeselect : function(deselected) { },
4241      
4242      /**
4243      * Event executed when the user navigates to a different calendar page.
4244      * @deprecated Event handlers for this event should be susbcribed to changePageEvent.
4245      */
4246      onChangePage : function() {
4247          this.render();
4248      },
4249  
4250      /**
4251      * Event executed when the calendar widget is rendered.
4252      * @deprecated Event handlers for this event should be susbcribed to renderEvent.
4253      */
4254      onRender : function() { },
4255  
4256      /**
4257      * Event executed when the calendar widget is reset to its original state.
4258      * @deprecated Event handlers for this event should be susbcribed to resetEvemt.
4259      */
4260      onReset : function() { this.render(); },
4261  
4262      /**
4263      * Event executed when the calendar widget is completely cleared to the current month with no selections.
4264      * @deprecated Event handlers for this event should be susbcribed to clearEvent.
4265      */
4266      onClear : function() { this.render(); },
4267      
4268      /**
4269      * Validates the calendar widget. This method has no default implementation
4270      * and must be extended by subclassing the widget.
4271      * @return Should return true if the widget validates, and false if
4272      * it doesn't.
4273      * @type Boolean
4274      */
4275      validate : function() { return true; },
4276      
4277      // END EVENT HANDLERS
4278      
4279      // BEGIN DATE PARSE METHODS
4280      
4281      /**
4282      * Converts a date string to a date field array
4283      * @private
4284      * @param {String} sDate   Date string. Valid formats are mm/dd and mm/dd/yyyy.
4285      * @return    A date field array representing the string passed to the method
4286      * @type Array[](Number[])
4287      */
4288      _parseDate : function(sDate) {
4289          var aDate = sDate.split(this.Locale.DATE_FIELD_DELIMITER),
4290              rArray;
4291  
4292          if (aDate.length == 2) {
4293              rArray = [aDate[this.Locale.MD_MONTH_POSITION-1],aDate[this.Locale.MD_DAY_POSITION-1]];
4294              rArray.type = Calendar.MONTH_DAY;
4295          } else {
4296              rArray = [aDate[this.Locale.MDY_YEAR_POSITION-1] - this.Locale.YEAR_OFFSET, aDate[this.Locale.MDY_MONTH_POSITION-1],aDate[this.Locale.MDY_DAY_POSITION-1]];
4297              rArray.type = Calendar.DATE;
4298          }
4299  
4300          for (var i=0;i<rArray.length;i++) {
4301              rArray[i] = parseInt(rArray[i], 10);
4302          }
4303      
4304          return rArray;
4305      },
4306      
4307      /**
4308      * Converts a multi or single-date string to an array of date field arrays
4309      * @private
4310      * @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
4311      * @return       An array of date field arrays
4312      * @type Array[](Number[])
4313      */
4314      _parseDates : function(sDates) {
4315          var aReturn = [],
4316              aDates = sDates.split(this.Locale.DATE_DELIMITER);
4317          
4318          for (var d=0;d<aDates.length;++d) {
4319              var sDate = aDates[d];
4320      
4321              if (sDate.indexOf(this.Locale.DATE_RANGE_DELIMITER) != -1) {
4322                  // This is a range
4323                  var aRange = sDate.split(this.Locale.DATE_RANGE_DELIMITER),
4324                      dateStart = this._parseDate(aRange[0]),
4325                      dateEnd = this._parseDate(aRange[1]),
4326                      fullRange = this._parseRange(dateStart, dateEnd);
4327  
4328                  aReturn = aReturn.concat(fullRange);
4329              } else {
4330                  // This is not a range
4331                  var aDate = this._parseDate(sDate);
4332                  aReturn.push(aDate);
4333              }
4334          }
4335          return aReturn;
4336      },
4337      
4338      /**
4339      * Converts a date range to the full list of included dates
4340      * @private
4341      * @param {Number[]} startDate Date field array representing the first date in the range
4342      * @param {Number[]} endDate  Date field array representing the last date in the range
4343      * @return       An array of date field arrays
4344      * @type Array[](Number[])
4345      */
4346      _parseRange : function(startDate, endDate) {
4347          var dCurrent = DateMath.add(DateMath.getDate(startDate[0],startDate[1]-1,startDate[2]),DateMath.DAY,1),
4348              dEnd     = DateMath.getDate(endDate[0],  endDate[1]-1,  endDate[2]),
4349              results = [];
4350  
4351          results.push(startDate);
4352          while (dCurrent.getTime() <= dEnd.getTime()) {
4353              results.push([dCurrent.getFullYear(),dCurrent.getMonth()+1,dCurrent.getDate()]);
4354              dCurrent = DateMath.add(dCurrent,DateMath.DAY,1);
4355          }
4356          return results;
4357      },
4358      
4359      // END DATE PARSE METHODS
4360      
4361      // BEGIN RENDERER METHODS
4362      
4363      /**
4364      * Resets the render stack of the current calendar to its original pre-render value.
4365      */
4366      resetRenderers : function() {
4367          this.renderStack = this._renderStack.concat();
4368      },
4369  
4370      /**
4371       * Removes all custom renderers added to the Calendar through the addRenderer, addMonthRenderer and 
4372       * addWeekdayRenderer methods. Calendar's render method needs to be called after removing renderers 
4373       * to re-render the Calendar without custom renderers applied.
4374       */
4375      removeRenderers : function() {
4376          this._renderStack = [];
4377          this.renderStack = [];
4378      },
4379  
4380      /**
4381      * Clears the inner HTML, CSS class and style information from the specified cell.
4382      * @method clearElement
4383      * @param {HTMLTableCellElement} cell The cell to clear
4384      */ 
4385      clearElement : function(cell) {
4386          cell.innerHTML = "&#160;";
4387          cell.className="";
4388      },
4389      
4390      /**
4391      * Adds a renderer to the render stack. The function reference passed to this method will be executed
4392      * when a date cell matches the conditions specified in the date string for this renderer.
4393      * 
4394      * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should 
4395      * escape markup used to set the cell contents, if coming from an external source.<p>
4396      * @method addRenderer
4397      * @param {String} sDates  A date string to associate with the specified renderer. Valid formats
4398      *         include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
4399      * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4400      */
4401      addRenderer : function(sDates, fnRender) {
4402          var aDates = this._parseDates(sDates);
4403          for (var i=0;i<aDates.length;++i) {
4404              var aDate = aDates[i];
4405          
4406              if (aDate.length == 2) { // this is either a range or a month/day combo
4407                  if (aDate[0] instanceof Array) { // this is a range
4408                      this._addRenderer(Calendar.RANGE,aDate,fnRender);
4409                  } else { // this is a month/day combo
4410                      this._addRenderer(Calendar.MONTH_DAY,aDate,fnRender);
4411                  }
4412              } else if (aDate.length == 3) {
4413                  this._addRenderer(Calendar.DATE,aDate,fnRender);
4414              }
4415          }
4416      },
4417      
4418      /**
4419      * The private method used for adding cell renderers to the local render stack.
4420      * This method is called by other methods that set the renderer type prior to the method call.
4421      * @method _addRenderer
4422      * @private
4423      * @param {String} type  The type string that indicates the type of date renderer being added.
4424      *         Values are YAHOO.widget.Calendar.DATE, YAHOO.widget.Calendar.MONTH_DAY, YAHOO.widget.Calendar.WEEKDAY,
4425      *         YAHOO.widget.Calendar.RANGE, YAHOO.widget.Calendar.MONTH
4426      * @param {Array}  aDates  An array of dates used to construct the renderer. The format varies based
4427      *         on the renderer type
4428      * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4429      */
4430      _addRenderer : function(type, aDates, fnRender) {
4431          var add = [type,aDates,fnRender];
4432          this.renderStack.unshift(add); 
4433          this._renderStack = this.renderStack.concat();
4434      },
4435  
4436      /**
4437      * Adds a month renderer to the render stack. The function reference passed to this method will be executed
4438      * when a date cell matches the month passed to this method
4439      * 
4440      * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should 
4441      * escape markup used to set the cell contents, if coming from an external source.<p>
4442      * @method addMonthRenderer
4443      * @param {Number} month  The month (1-12) to associate with this renderer
4444      * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4445      */
4446      addMonthRenderer : function(month, fnRender) {
4447          this._addRenderer(Calendar.MONTH,[month],fnRender);
4448      },
4449  
4450      /**
4451      * Adds a weekday renderer to the render stack. The function reference passed to this method will be executed
4452      * when a date cell matches the weekday passed to this method.
4453      *
4454      * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should 
4455      * escape HTML used to set the cell contents, if coming from an external source.<p>
4456      *
4457      * @method addWeekdayRenderer
4458      * @param {Number} weekday  The weekday (Sunday = 1, Monday = 2 ... Saturday = 7) to associate with this renderer
4459      * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
4460      */
4461      addWeekdayRenderer : function(weekday, fnRender) {
4462          this._addRenderer(Calendar.WEEKDAY,[weekday],fnRender);
4463      },
4464  
4465      // END RENDERER METHODS
4466      
4467      // BEGIN CSS METHODS
4468      
4469      /**
4470      * Removes all styles from all body cells in the current calendar table.
4471      * @method clearAllBodyCellStyles
4472      * @param {style} style The CSS class name to remove from all calendar body cells
4473      */
4474      clearAllBodyCellStyles : function(style) {
4475          for (var c=0;c<this.cells.length;++c) {
4476              Dom.removeClass(this.cells[c],style);
4477          }
4478      },
4479      
4480      // END CSS METHODS
4481      
4482      // BEGIN GETTER/SETTER METHODS
4483      /**
4484      * Sets the calendar's month explicitly
4485      * @method setMonth
4486      * @param {Number} month  The numeric month, from 0 (January) to 11 (December)
4487      */
4488      setMonth : function(month) {
4489          var cfgPageDate = DEF_CFG.PAGEDATE.key,
4490              current = this.cfg.getProperty(cfgPageDate);
4491          current.setMonth(parseInt(month, 10));
4492          this.cfg.setProperty(cfgPageDate, current);
4493      },
4494  
4495      /**
4496      * Sets the calendar's year explicitly.
4497      * @method setYear
4498      * @param {Number} year  The numeric 4-digit year
4499      */
4500      setYear : function(year) {
4501          var cfgPageDate = DEF_CFG.PAGEDATE.key,
4502              current = this.cfg.getProperty(cfgPageDate);
4503  
4504          current.setFullYear(parseInt(year, 10) - this.Locale.YEAR_OFFSET);
4505          this.cfg.setProperty(cfgPageDate, current);
4506      },
4507  
4508      /**
4509      * Gets the list of currently selected dates from the calendar.
4510      * @method getSelectedDates
4511      * @return {Date[]} An array of currently selected JavaScript Date objects.
4512      */
4513      getSelectedDates : function() {
4514          var returnDates = [],
4515              selected = this.cfg.getProperty(DEF_CFG.SELECTED.key);
4516  
4517          for (var d=0;d<selected.length;++d) {
4518              var dateArray = selected[d];
4519  
4520              var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]);
4521              returnDates.push(date);
4522          }
4523  
4524          returnDates.sort( function(a,b) { return a-b; } );
4525          return returnDates;
4526      },
4527  
4528      /// END GETTER/SETTER METHODS ///
4529      
4530      /**
4531      * Hides the Calendar's outer container from view.
4532      * @method hide
4533      */
4534      hide : function() {
4535          if (this.beforeHideEvent.fire()) {
4536              this.oDomContainer.style.display = "none";
4537              this.hideEvent.fire();
4538          }
4539      },
4540  
4541      /**
4542      * Shows the Calendar's outer container.
4543      * @method show
4544      */
4545      show : function() {
4546          if (this.beforeShowEvent.fire()) {
4547              this.oDomContainer.style.display = "block";
4548              this.showEvent.fire();
4549          }
4550      },
4551  
4552      /**
4553      * Returns a string representing the current browser.
4554      * @deprecated As of 2.3.0, environment information is available in YAHOO.env.ua
4555      * @see YAHOO.env.ua
4556      * @property browser
4557      * @type String
4558      */
4559      browser : (function() {
4560                  var ua = navigator.userAgent.toLowerCase();
4561                        if (ua.indexOf('opera')!=-1) { // Opera (check first in case of spoof)
4562                           return 'opera';
4563                        } else if (ua.indexOf('msie 7')!=-1) { // IE7
4564                           return 'ie7';
4565                        } else if (ua.indexOf('msie') !=-1) { // IE
4566                           return 'ie';
4567                        } else if (ua.indexOf('safari')!=-1) { // Safari (check before Gecko because it includes "like Gecko")
4568                           return 'safari';
4569                        } else if (ua.indexOf('gecko') != -1) { // Gecko
4570                           return 'gecko';
4571                        } else {
4572                           return false;
4573                        }
4574                  })(),
4575      /**
4576      * Returns a string representation of the object.
4577      * @method toString
4578      * @return {String} A string representation of the Calendar object.
4579      */
4580      toString : function() {
4581          return "Calendar " + this.id;
4582      },
4583  
4584      /**
4585       * Destroys the Calendar instance. The method will remove references
4586       * to HTML elements, remove any event listeners added by the Calendar,
4587       * and destroy the Config and CalendarNavigator instances it has created.
4588       *
4589       * @method destroy
4590       */
4591      destroy : function() {
4592  
4593          if (this.beforeDestroyEvent.fire()) {
4594              var cal = this;
4595  
4596              // Child objects
4597              if (cal.navigator) {
4598                  cal.navigator.destroy();
4599              }
4600  
4601              if (cal.cfg) {
4602                  cal.cfg.destroy();
4603              }
4604  
4605              // DOM event listeners
4606              Event.purgeElement(cal.oDomContainer, true);
4607  
4608              // Generated markup/DOM - Not removing the container DIV since we didn't create it.
4609              Dom.removeClass(cal.oDomContainer, cal.Style.CSS_WITH_TITLE);
4610              Dom.removeClass(cal.oDomContainer, cal.Style.CSS_CONTAINER);
4611              Dom.removeClass(cal.oDomContainer, cal.Style.CSS_SINGLE);
4612              cal.oDomContainer.innerHTML = "";
4613  
4614              // JS-to-DOM references
4615              cal.oDomContainer = null;
4616              cal.cells = null;
4617  
4618              this.destroyEvent.fire();
4619          }
4620      }
4621  };
4622  
4623  YAHOO.widget.Calendar = Calendar;
4624  
4625  /**
4626  * @namespace YAHOO.widget
4627  * @class Calendar_Core
4628  * @extends YAHOO.widget.Calendar
4629  * @deprecated The old Calendar_Core class is no longer necessary.
4630  */
4631  YAHOO.widget.Calendar_Core = YAHOO.widget.Calendar;
4632  
4633  YAHOO.widget.Cal_Core = YAHOO.widget.Calendar;
4634  
4635  })();
4636  (function() {
4637  
4638      var Dom = YAHOO.util.Dom,
4639          DateMath = YAHOO.widget.DateMath,
4640          Event = YAHOO.util.Event,
4641          Lang = YAHOO.lang,
4642          Calendar = YAHOO.widget.Calendar;
4643  
4644  /**
4645  * YAHOO.widget.CalendarGroup is a special container class for YAHOO.widget.Calendar. This class facilitates
4646  * the ability to have multi-page calendar views that share a single dataset and are
4647  * dependent on each other.
4648  *
4649  * The calendar group instance will refer to each of its elements using a 0-based index.
4650  * For example, to construct the placeholder for a calendar group widget with id "cal1" and
4651  * containerId of "cal1Container", the markup would be as follows:
4652  *   <xmp>
4653  *       <div id="cal1Container_0"></div>
4654  *       <div id="cal1Container_1"></div>
4655  *   </xmp>
4656  * The tables for the calendars ("cal1_0" and "cal1_1") will be inserted into those containers.
4657  *
4658  * <p>
4659  * <strong>NOTE: As of 2.4.0, the constructor's ID argument is optional.</strong>
4660  * The CalendarGroup can be constructed by simply providing a container ID string, 
4661  * or a reference to a container DIV HTMLElement (the element needs to exist 
4662  * in the document).
4663  * 
4664  * E.g.:
4665  *   <xmp>
4666  *       var c = new YAHOO.widget.CalendarGroup("calContainer", configOptions);
4667  *   </xmp>
4668  * or:
4669  *   <xmp>
4670  *       var containerDiv = YAHOO.util.Dom.get("calContainer");
4671  *       var c = new YAHOO.widget.CalendarGroup(containerDiv, configOptions);
4672  *   </xmp>
4673  * </p>
4674  * <p>
4675  * If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix.
4676  * For example if an ID is not provided, and the container's ID is "calContainer", the CalendarGroup's ID will be set to "calContainer_t".
4677  * </p>
4678  * 
4679  * @namespace YAHOO.widget
4680  * @class CalendarGroup
4681  * @constructor
4682  * @param {String} id optional The id of the table element that will represent the CalendarGroup widget. As of 2.4.0, this argument is optional.
4683  * @param {String | HTMLElement} container The id of the container div element that will wrap the CalendarGroup table, or a reference to a DIV element which exists in the document.
4684  * @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup.
4685  */
4686  function CalendarGroup(id, containerId, config) {
4687      if (arguments.length > 0) {
4688          this.init.apply(this, arguments);
4689      }
4690  }
4691  
4692  /**
4693  * The set of default Config property keys and values for the CalendarGroup.
4694  * 
4695  * <p>
4696  * NOTE: This property is made public in order to allow users to change 
4697  * the default values of configuration properties. Users should not 
4698  * modify the key string, unless they are overriding the Calendar implementation
4699  * </p>
4700  *
4701  * @property YAHOO.widget.CalendarGroup.DEFAULT_CONFIG
4702  * @static
4703  * @type Object An object with key/value pairs, the key being the 
4704  * uppercase configuration property name and the value being an objec 
4705  * literal with a key string property, and a value property, specifying the 
4706  * default value of the property 
4707  */
4708  
4709  /**
4710  * The set of default Config property keys and values for the CalendarGroup
4711  * @property YAHOO.widget.CalendarGroup._DEFAULT_CONFIG
4712  * @deprecated Made public. See the public DEFAULT_CONFIG property for details
4713  * @private
4714  * @static
4715  * @type Object
4716  */
4717  CalendarGroup.DEFAULT_CONFIG = CalendarGroup._DEFAULT_CONFIG = Calendar.DEFAULT_CONFIG;
4718  CalendarGroup.DEFAULT_CONFIG.PAGES = {key:"pages", value:2};
4719  
4720  var DEF_CFG = CalendarGroup.DEFAULT_CONFIG;
4721  
4722  CalendarGroup.prototype = {
4723  
4724      /**
4725      * Initializes the calendar group. All subclasses must call this method in order for the
4726      * group to be initialized properly.
4727      * @method init
4728      * @param {String} id optional The id of the table element that will represent the CalendarGroup widget. As of 2.4.0, this argument is optional.
4729      * @param {String | HTMLElement} container The id of the container div element that will wrap the CalendarGroup table, or a reference to a DIV element which exists in the document.
4730      * @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup.
4731      */
4732      init : function(id, container, config) {
4733  
4734          // Normalize 2.4.0, pre 2.4.0 args
4735          var nArgs = this._parseArgs(arguments);
4736  
4737          id = nArgs.id;
4738          container = nArgs.container;
4739          config = nArgs.config;
4740  
4741          this.oDomContainer = Dom.get(container);
4742  
4743          if (!this.oDomContainer.id) {
4744              this.oDomContainer.id = Dom.generateId();
4745          }
4746          if (!id) {
4747              id = this.oDomContainer.id + "_t";
4748          }
4749  
4750          /**
4751          * The unique id associated with the CalendarGroup
4752          * @property id
4753          * @type String
4754          */
4755          this.id = id;
4756  
4757          /**
4758          * The unique id associated with the CalendarGroup container
4759          * @property containerId
4760          * @type String
4761          */
4762          this.containerId = this.oDomContainer.id;
4763  
4764          this.initEvents();
4765          this.initStyles();
4766  
4767          /**
4768          * The collection of Calendar pages contained within the CalendarGroup
4769          * @property pages
4770          * @type YAHOO.widget.Calendar[]
4771          */
4772          this.pages = [];
4773  
4774          Dom.addClass(this.oDomContainer, CalendarGroup.CSS_CONTAINER);
4775          Dom.addClass(this.oDomContainer, CalendarGroup.CSS_MULTI_UP);
4776  
4777          /**
4778          * The Config object used to hold the configuration variables for the CalendarGroup
4779          * @property cfg
4780          * @type YAHOO.util.Config
4781          */
4782          this.cfg = new YAHOO.util.Config(this);
4783  
4784          /**
4785          * The local object which contains the CalendarGroup's options
4786          * @property Options
4787          * @type Object
4788          */
4789          this.Options = {};
4790  
4791          /**
4792          * The local object which contains the CalendarGroup's locale settings
4793          * @property Locale
4794          * @type Object
4795          */
4796          this.Locale = {};
4797  
4798          this.setupConfig();
4799  
4800          if (config) {
4801              this.cfg.applyConfig(config, true);
4802          }
4803  
4804          this.cfg.fireQueue();
4805  
4806      },
4807  
4808      setupConfig : function() {
4809  
4810          var cfg = this.cfg;
4811  
4812          /**
4813          * The number of pages to include in the CalendarGroup. This value can only be set once, in the CalendarGroup's constructor arguments.
4814          * @config pages
4815          * @type Number
4816          * @default 2
4817          */
4818          cfg.addProperty(DEF_CFG.PAGES.key, { value:DEF_CFG.PAGES.value, validator:cfg.checkNumber, handler:this.configPages } );
4819  
4820          /**
4821          * The positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to 
4822          * be used when displaying or parsing dates.  NOTE: All JS Date objects returned by methods, or expected as input by
4823          * methods will always represent the Gregorian year, in order to maintain date/month/week values.
4824          *
4825          * @config year_offset
4826          * @type Number
4827          * @default 0
4828          */
4829          cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, handler: this.delegateConfig, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, suppressEvent:true } );
4830  
4831          /**
4832          * The date to use to represent "Today".
4833          *
4834          * @config today
4835          * @type Date
4836          * @default Today's date
4837          */
4838          cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler: this.configToday, suppressEvent:false } );
4839  
4840          /**
4841          * The month/year representing the current visible Calendar date (mm/yyyy)
4842          * @config pagedate
4843          * @type String | Date
4844          * @default Today's date
4845          */
4846          cfg.addProperty(DEF_CFG.PAGEDATE.key, { value: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), handler:this.configPageDate } );
4847  
4848          /**
4849          * The date or range of dates representing the current Calendar selection
4850          *
4851          * @config selected
4852          * @type String
4853          * @default []
4854          */
4855          cfg.addProperty(DEF_CFG.SELECTED.key, { value:[], handler:this.configSelected } );
4856  
4857          /**
4858          * The title to display above the CalendarGroup's month header. The title is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
4859          * @config title
4860          * @type HTML
4861          * @default ""
4862          */
4863          cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } );
4864  
4865          /**
4866          * Whether or not a close button should be displayed for this CalendarGroup
4867          * @config close
4868          * @type Boolean
4869          * @default false
4870          */
4871          cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } );
4872  
4873          /**
4874          * Whether or not an iframe shim should be placed under the Calendar to prevent select boxes from bleeding through in Internet Explorer 6 and below.
4875          * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be 
4876          * enabled if required.
4877          * 
4878          * @config iframe
4879          * @type Boolean
4880          * @default true for IE6 and below, false for all other browsers
4881          */
4882          cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } );
4883  
4884          /**
4885          * The minimum selectable date in the current Calendar (mm/dd/yyyy)
4886          * @config mindate
4887          * @type String | Date
4888          * @default null
4889          */
4890          cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.delegateConfig } );
4891  
4892          /**
4893          * The maximum selectable date in the current Calendar (mm/dd/yyyy)
4894          * @config maxdate
4895          * @type String | Date
4896          * @default null
4897          */
4898          cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.delegateConfig  } );
4899  
4900          /**
4901          * True if the Calendar should allow multiple selections. False by default.
4902          * @config MULTI_SELECT
4903          * @type Boolean
4904          * @default false
4905          */
4906          cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4907  
4908          /**
4909          * True if the Calendar should allow selection of out-of-month dates. False by default.
4910          * @config OOM_SELECT
4911          * @type Boolean
4912          * @default false
4913          */
4914          cfg.addProperty(DEF_CFG.OOM_SELECT.key, { value:DEF_CFG.OOM_SELECT.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4915  
4916          /**
4917          * The weekday the week begins on. Default is 0 (Sunday).
4918          * @config START_WEEKDAY
4919          * @type number
4920          * @default 0
4921          */ 
4922          cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.delegateConfig, validator:cfg.checkNumber  } );
4923          
4924          /**
4925          * True if the Calendar should show weekday labels. True by default.
4926          * @config SHOW_WEEKDAYS
4927          * @type Boolean
4928          * @default true
4929          */ 
4930          cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4931          
4932          /**
4933          * True if the Calendar should show week row headers. False by default.
4934          * @config SHOW_WEEK_HEADER
4935          * @type Boolean
4936          * @default false
4937          */ 
4938          cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key,{ value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4939          
4940          /**
4941          * True if the Calendar should show week row footers. False by default.
4942          * @config SHOW_WEEK_FOOTER
4943          * @type Boolean
4944          * @default false
4945          */
4946          cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4947          
4948          /**
4949          * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
4950          * @config HIDE_BLANK_WEEKS
4951          * @type Boolean
4952          * @default false
4953          */  
4954          cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key,{ value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } );
4955  
4956          /**
4957          * The image URL that should be used for the left navigation arrow. The image URL is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
4958          * @config NAV_ARROW_LEFT
4959          * @type String
4960          * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"
4961          * @default null
4962          */  
4963          cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.delegateConfig } );
4964  
4965          /**
4966          * The image URL that should be used for the right navigation arrow. The image URL is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
4967          * @config NAV_ARROW_RIGHT
4968          * @type String
4969          * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
4970          * @default null
4971          */  
4972          cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.delegateConfig } );
4973      
4974          // Locale properties
4975          
4976          /**
4977          * The short month labels for the current locale. The month labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
4978          * @config MONTHS_SHORT
4979          * @type HTML[]
4980          * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
4981          */
4982          cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.delegateConfig } );
4983          
4984          /**
4985          * The long month labels for the current locale. The month labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
4986          * @config MONTHS_LONG
4987          * @type HTML[]
4988          * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
4989          */ 
4990          cfg.addProperty(DEF_CFG.MONTHS_LONG.key,  { value:DEF_CFG.MONTHS_LONG.value, handler:this.delegateConfig } );
4991          
4992          /**
4993          * The 1-character weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
4994          * @config WEEKDAYS_1CHAR
4995          * @type HTML[]
4996          * @default ["S", "M", "T", "W", "T", "F", "S"]
4997          */ 
4998          cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.delegateConfig } );
4999          
5000          /**
5001          * The short weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
5002          * @config WEEKDAYS_SHORT
5003          * @type HTML[]
5004          * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
5005          */ 
5006          cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.delegateConfig } );
5007          
5008          /**
5009          * The medium weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
5010          * @config WEEKDAYS_MEDIUM
5011          * @type HTML[]
5012          * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
5013          */ 
5014          cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.delegateConfig } );
5015          
5016          /**
5017          * The long weekday labels for the current locale. The weekday labels are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source.
5018          * @config WEEKDAYS_LONG
5019          * @type HTML[]
5020          * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
5021          */ 
5022          cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.delegateConfig } );
5023      
5024          /**
5025          * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
5026          * @config LOCALE_MONTHS
5027          * @type String
5028          * @default "long"
5029          */
5030          cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.delegateConfig } );
5031      
5032          /**
5033          * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
5034          * @config LOCALE_WEEKDAYS
5035          * @type String
5036          * @default "short"
5037          */ 
5038          cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.delegateConfig } );
5039      
5040          /**
5041          * The value used to delimit individual dates in a date string passed to various Calendar functions.
5042          * @config DATE_DELIMITER
5043          * @type String
5044          * @default ","
5045          */
5046          cfg.addProperty(DEF_CFG.DATE_DELIMITER.key,  { value:DEF_CFG.DATE_DELIMITER.value, handler:this.delegateConfig } );
5047      
5048          /**
5049          * The value used to delimit date fields in a date string passed to various Calendar functions.
5050          * @config DATE_FIELD_DELIMITER
5051          * @type String
5052          * @default "/"
5053          */ 
5054          cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key,{ value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.delegateConfig } );
5055      
5056          /**
5057          * The value used to delimit date ranges in a date string passed to various Calendar functions.
5058          * @config DATE_RANGE_DELIMITER
5059          * @type String
5060          * @default "-"
5061          */
5062          cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key,{ value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.delegateConfig } );
5063      
5064          /**
5065          * The position of the month in a month/year date string
5066          * @config MY_MONTH_POSITION
5067          * @type Number
5068          * @default 1
5069          */
5070          cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5071          
5072          /**
5073          * The position of the year in a month/year date string
5074          * @config MY_YEAR_POSITION
5075          * @type Number
5076          * @default 2
5077          */ 
5078          cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5079          
5080          /**
5081          * The position of the month in a month/day date string
5082          * @config MD_MONTH_POSITION
5083          * @type Number
5084          * @default 1
5085          */ 
5086          cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5087          
5088          /**
5089          * The position of the day in a month/year date string
5090          * @config MD_DAY_POSITION
5091          * @type Number
5092          * @default 2
5093          */ 
5094          cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key,  { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5095          
5096          /**
5097          * The position of the month in a month/day/year date string
5098          * @config MDY_MONTH_POSITION
5099          * @type Number
5100          * @default 1
5101          */ 
5102          cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5103          
5104          /**
5105          * The position of the day in a month/day/year date string
5106          * @config MDY_DAY_POSITION
5107          * @type Number
5108          * @default 2
5109          */ 
5110          cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5111          
5112          /**
5113          * The position of the year in a month/day/year date string
5114          * @config MDY_YEAR_POSITION
5115          * @type Number
5116          * @default 3
5117          */ 
5118          cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5119      
5120          /**
5121          * The position of the month in the month year label string used as the Calendar header
5122          * @config MY_LABEL_MONTH_POSITION
5123          * @type Number
5124          * @default 1
5125          */
5126          cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5127      
5128          /**
5129          * The position of the year in the month year label string used as the Calendar header
5130          * @config MY_LABEL_YEAR_POSITION
5131          * @type Number
5132          * @default 2
5133          */
5134          cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } );
5135  
5136          /**
5137          * The suffix used after the month when rendering the Calendar header
5138          * @config MY_LABEL_MONTH_SUFFIX
5139          * @type String
5140          * @default " "
5141          */
5142          cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.delegateConfig } );
5143          
5144          /**
5145          * The suffix used after the year when rendering the Calendar header
5146          * @config MY_LABEL_YEAR_SUFFIX
5147          * @type String
5148          * @default ""
5149          */
5150          cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.delegateConfig } );
5151  
5152          /**
5153          * Configuration for the Month/Year CalendarNavigator UI which allows the user to jump directly to a 
5154          * specific Month/Year without having to scroll sequentially through months.
5155          * <p>
5156          * Setting this property to null (default value) or false, will disable the CalendarNavigator UI.
5157          * </p>
5158          * <p>
5159          * Setting this property to true will enable the CalendarNavigatior UI with the default CalendarNavigator configuration values.
5160          * </p>
5161          * <p>
5162          * This property can also be set to an object literal containing configuration properties for the CalendarNavigator UI.
5163          * The configuration object expects the the following case-sensitive properties, with the "strings" property being a nested object.
5164          * Any properties which are not provided will use the default values (defined in the CalendarNavigator class).
5165          * </p>
5166          * <dl>
5167          * <dt>strings</dt>
5168          * <dd><em>Object</em> :  An object with the properties shown below, defining the string labels to use in the Navigator's UI. The strings are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 
5169          *     <dl>
5170          *         <dt>month</dt><dd><em>HTML</em> : The markup to use for the month label. Defaults to "Month".</dd>
5171          *         <dt>year</dt><dd><em>HTML</em> : The markup to use for the year label. Defaults to "Year".</dd>
5172          *         <dt>submit</dt><dd><em>HTML</em> : The markup to use for the submit button label. Defaults to "Okay".</dd>
5173          *         <dt>cancel</dt><dd><em>HTML</em> : The markup to use for the cancel button label. Defaults to "Cancel".</dd>
5174          *         <dt>invalidYear</dt><dd><em>HTML</em> : The markup to use for invalid year values. Defaults to "Year needs to be a number".</dd>
5175          *     </dl>
5176          * </dd>
5177          * <dt>monthFormat</dt><dd><em>String</em> : The month format to use. Either YAHOO.widget.Calendar.LONG, or YAHOO.widget.Calendar.SHORT. Defaults to YAHOO.widget.Calendar.LONG</dd>
5178          * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
5179          * </dl>
5180          * <p>E.g.</p>
5181          * <pre>
5182          * var navConfig = {
5183          *   strings: {
5184          *    month:"Calendar Month",
5185          *    year:"Calendar Year",
5186          *    submit: "Submit",
5187          *    cancel: "Cancel",
5188          *    invalidYear: "Please enter a valid year"
5189          *   },
5190          *   monthFormat: YAHOO.widget.Calendar.SHORT,
5191          *   initialFocus: "month"
5192          * }
5193          * </pre>
5194          * @config navigator
5195          * @type {Object|Boolean}
5196          * @default null
5197          */
5198          cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } );
5199  
5200          /**
5201           * The map of UI strings which the CalendarGroup UI uses.
5202           *
5203           * @config strings
5204           * @type {Object}
5205           * @default An object with the properties shown below:
5206           *     <dl>
5207           *         <dt>previousMonth</dt><dd><em>HTML</em> : The markup to use for the "Previous Month" navigation label. Defaults to "Previous Month". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd>
5208           *         <dt>nextMonth</dt><dd><em>HTML</em> : The markup to use for the "Next Month" navigation UI. Defaults to "Next Month". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd>
5209           *         <dt>close</dt><dd><em>HTML</em> : The markup to use for the close button label. Defaults to "Close". The string is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</dd>
5210           *     </dl>
5211           */
5212          cfg.addProperty(DEF_CFG.STRINGS.key, { 
5213              value:DEF_CFG.STRINGS.value, 
5214              handler:this.configStrings, 
5215              validator: function(val) {
5216                  return Lang.isObject(val);
5217              },
5218              supercedes: DEF_CFG.STRINGS.supercedes
5219          });
5220      },
5221  
5222      /**
5223      * Initializes CalendarGroup's built-in CustomEvents
5224      * @method initEvents
5225      */
5226      initEvents : function() {
5227  
5228          var me = this,
5229              strEvent = "Event",
5230              CE = YAHOO.util.CustomEvent;
5231  
5232          /**
5233          * Proxy subscriber to subscribe to the CalendarGroup's child Calendars' CustomEvents
5234          * @method sub
5235          * @private
5236          * @param {Function} fn The function to subscribe to this CustomEvent
5237          * @param {Object} obj The CustomEvent's scope object
5238          * @param {Boolean} bOverride Whether or not to apply scope correction
5239          */
5240          var sub = function(fn, obj, bOverride) {
5241              for (var p=0;p<me.pages.length;++p) {
5242                  var cal = me.pages[p];
5243                  cal[this.type + strEvent].subscribe(fn, obj, bOverride);
5244              }
5245          };
5246  
5247          /**
5248          * Proxy unsubscriber to unsubscribe from the CalendarGroup's child Calendars' CustomEvents
5249          * @method unsub
5250          * @private
5251          * @param {Function} fn The function to subscribe to this CustomEvent
5252          * @param {Object} obj The CustomEvent's scope object
5253          */
5254          var unsub = function(fn, obj) {
5255              for (var p=0;p<me.pages.length;++p) {
5256                  var cal = me.pages[p];
5257                  cal[this.type + strEvent].unsubscribe(fn, obj);
5258              }
5259          };
5260  
5261          var defEvents = Calendar._EVENT_TYPES;
5262  
5263          /**
5264          * Fired before a date selection is made
5265          * @event beforeSelectEvent
5266          */
5267          me.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT);
5268          me.beforeSelectEvent.subscribe = sub; me.beforeSelectEvent.unsubscribe = unsub;
5269  
5270          /**
5271          * Fired when a date selection is made
5272          * @event selectEvent
5273          * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
5274          */
5275          me.selectEvent = new CE(defEvents.SELECT); 
5276          me.selectEvent.subscribe = sub; me.selectEvent.unsubscribe = unsub;
5277  
5278          /**
5279          * Fired before a date or set of dates is deselected
5280          * @event beforeDeselectEvent
5281          */
5282          me.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT); 
5283          me.beforeDeselectEvent.subscribe = sub; me.beforeDeselectEvent.unsubscribe = unsub;
5284  
5285          /**
5286          * Fired when a date or set of dates has been deselected
5287          * @event deselectEvent
5288          * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
5289          */
5290          me.deselectEvent = new CE(defEvents.DESELECT); 
5291          me.deselectEvent.subscribe = sub; me.deselectEvent.unsubscribe = unsub;
5292          
5293          /**
5294          * Fired when the Calendar page is changed
5295          * @event changePageEvent
5296          */
5297          me.changePageEvent = new CE(defEvents.CHANGE_PAGE); 
5298          me.changePageEvent.subscribe = sub; me.changePageEvent.unsubscribe = unsub;
5299  
5300          /**
5301          * Fired before the Calendar is rendered
5302          * @event beforeRenderEvent
5303          */
5304          me.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER);
5305          me.beforeRenderEvent.subscribe = sub; me.beforeRenderEvent.unsubscribe = unsub;
5306      
5307          /**
5308          * Fired when the Calendar is rendered
5309          * @event renderEvent
5310          */
5311          me.renderEvent = new CE(defEvents.RENDER);
5312          me.renderEvent.subscribe = sub; me.renderEvent.unsubscribe = unsub;
5313      
5314          /**
5315          * Fired when the Calendar is reset
5316          * @event resetEvent
5317          */
5318          me.resetEvent = new CE(defEvents.RESET); 
5319          me.resetEvent.subscribe = sub; me.resetEvent.unsubscribe = unsub;
5320      
5321          /**
5322          * Fired when the Calendar is cleared
5323          * @event clearEvent
5324          */
5325          me.clearEvent = new CE(defEvents.CLEAR);
5326          me.clearEvent.subscribe = sub; me.clearEvent.unsubscribe = unsub;
5327  
5328          /**
5329          * Fired just before the CalendarGroup is to be shown
5330          * @event beforeShowEvent
5331          */
5332          me.beforeShowEvent = new CE(defEvents.BEFORE_SHOW);
5333      
5334          /**
5335          * Fired after the CalendarGroup is shown
5336          * @event showEvent
5337          */
5338          me.showEvent = new CE(defEvents.SHOW);
5339      
5340          /**
5341          * Fired just before the CalendarGroup is to be hidden
5342          * @event beforeHideEvent
5343          */
5344          me.beforeHideEvent = new CE(defEvents.BEFORE_HIDE);
5345      
5346          /**
5347          * Fired after the CalendarGroup is hidden
5348          * @event hideEvent
5349          */
5350          me.hideEvent = new CE(defEvents.HIDE);
5351  
5352          /**
5353          * Fired just before the CalendarNavigator is to be shown
5354          * @event beforeShowNavEvent
5355          */
5356          me.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV);
5357      
5358          /**
5359          * Fired after the CalendarNavigator is shown
5360          * @event showNavEvent
5361          */
5362          me.showNavEvent = new CE(defEvents.SHOW_NAV);
5363      
5364          /**
5365          * Fired just before the CalendarNavigator is to be hidden
5366          * @event beforeHideNavEvent
5367          */
5368          me.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV);
5369  
5370          /**
5371          * Fired after the CalendarNavigator is hidden
5372          * @event hideNavEvent
5373          */
5374          me.hideNavEvent = new CE(defEvents.HIDE_NAV);
5375  
5376          /**
5377          * Fired just before the CalendarNavigator is to be rendered
5378          * @event beforeRenderNavEvent
5379          */
5380          me.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV);
5381  
5382          /**
5383          * Fired after the CalendarNavigator is rendered
5384          * @event renderNavEvent
5385          */
5386          me.renderNavEvent = new CE(defEvents.RENDER_NAV);
5387  
5388          /**
5389          * Fired just before the CalendarGroup is to be destroyed
5390          * @event beforeDestroyEvent
5391          */
5392          me.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY);
5393  
5394          /**
5395          * Fired after the CalendarGroup is destroyed. This event should be used
5396          * for notification only. When this event is fired, important CalendarGroup instance
5397          * properties, dom references and event listeners have already been 
5398          * removed/dereferenced, and hence the CalendarGroup instance is not in a usable 
5399          * state.
5400          *
5401          * @event destroyEvent
5402          */
5403          me.destroyEvent = new CE(defEvents.DESTROY);
5404      },
5405      
5406      /**
5407      * The default Config handler for the "pages" property
5408      * @method configPages
5409      * @param {String} type The CustomEvent type (usually the property name)
5410      * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5411      * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5412      */
5413      configPages : function(type, args, obj) {
5414          var pageCount = args[0],
5415              cfgPageDate = DEF_CFG.PAGEDATE.key,
5416              sep = "_",
5417              caldate,
5418              firstPageDate = null,
5419              groupCalClass = "groupcal",
5420              firstClass = "first-of-type",
5421              lastClass = "last-of-type";
5422  
5423          for (var p=0;p<pageCount;++p) {
5424              var calId = this.id + sep + p,
5425                  calContainerId = this.containerId + sep + p,
5426                  childConfig = this.cfg.getConfig();
5427  
5428              childConfig.close = false;
5429              childConfig.title = false;
5430              childConfig.navigator = null;
5431  
5432              if (p > 0) {
5433                  caldate = new Date(firstPageDate);
5434                  this._setMonthOnDate(caldate, caldate.getMonth() + p);
5435                  childConfig.pageDate = caldate;
5436              }
5437  
5438              var cal = this.constructChild(calId, calContainerId, childConfig);
5439  
5440              Dom.removeClass(cal.oDomContainer, this.Style.CSS_SINGLE);
5441              Dom.addClass(cal.oDomContainer, groupCalClass);
5442  
5443              if (p===0) {
5444                  firstPageDate = cal.cfg.getProperty(cfgPageDate);
5445                  Dom.addClass(cal.oDomContainer, firstClass);
5446              }
5447      
5448              if (p==(pageCount-1)) {
5449                  Dom.addClass(cal.oDomContainer, lastClass);
5450              }
5451      
5452              cal.parent = this;
5453              cal.index = p; 
5454      
5455              this.pages[this.pages.length] = cal;
5456          }
5457      },
5458      
5459      /**
5460      * The default Config handler for the "pagedate" property
5461      * @method configPageDate
5462      * @param {String} type The CustomEvent type (usually the property name)
5463      * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5464      * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5465      */
5466      configPageDate : function(type, args, obj) {
5467          var val = args[0],
5468              firstPageDate;
5469  
5470          var cfgPageDate = DEF_CFG.PAGEDATE.key;
5471          
5472          for (var p=0;p<this.pages.length;++p) {
5473              var cal = this.pages[p];
5474              if (p === 0) {
5475                  firstPageDate = cal._parsePageDate(val);
5476                  cal.cfg.setProperty(cfgPageDate, firstPageDate);
5477              } else {
5478                  var pageDate = new Date(firstPageDate);
5479                  this._setMonthOnDate(pageDate, pageDate.getMonth() + p);
5480                  cal.cfg.setProperty(cfgPageDate, pageDate);
5481              }
5482          }
5483      },
5484      
5485      /**
5486      * The default Config handler for the CalendarGroup "selected" property
5487      * @method configSelected
5488      * @param {String} type The CustomEvent type (usually the property name)
5489      * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5490      * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5491      */
5492      configSelected : function(type, args, obj) {
5493          var cfgSelected = DEF_CFG.SELECTED.key;
5494          this.delegateConfig(type, args, obj);
5495          var selected = (this.pages.length > 0) ? this.pages[0].cfg.getProperty(cfgSelected) : []; 
5496          this.cfg.setProperty(cfgSelected, selected, true);
5497      },
5498  
5499      
5500      /**
5501      * Delegates a configuration property to the CustomEvents associated with the CalendarGroup's children
5502      * @method delegateConfig
5503      * @param {String} type The CustomEvent type (usually the property name)
5504      * @param {Object[]} args The CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
5505      * @param {Object} obj The scope object. For configuration handlers, this will usually equal the owner.
5506      */
5507      delegateConfig : function(type, args, obj) {
5508          var val = args[0];
5509          var cal;
5510      
5511          for (var p=0;p<this.pages.length;p++) {
5512              cal = this.pages[p];
5513              cal.cfg.setProperty(type, val);
5514          }
5515      },
5516  
5517      /**
5518      * Adds a function to all child Calendars within this CalendarGroup.
5519      * @method setChildFunction
5520      * @param {String}  fnName  The name of the function
5521      * @param {Function}  fn   The function to apply to each Calendar page object
5522      */
5523      setChildFunction : function(fnName, fn) {
5524          var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key);
5525      
5526          for (var p=0;p<pageCount;++p) {
5527              this.pages[p][fnName] = fn;
5528          }
5529      },
5530  
5531      /**
5532      * Calls a function within all child Calendars within this CalendarGroup.
5533      * @method callChildFunction
5534      * @param {String}  fnName  The name of the function
5535      * @param {Array}  args  The arguments to pass to the function
5536      */
5537      callChildFunction : function(fnName, args) {
5538          var pageCount = this.cfg.getProperty(DEF_CFG.PAGES.key);
5539  
5540          for (var p=0;p<pageCount;++p) {
5541              var page = this.pages[p];
5542              if (page[fnName]) {
5543                  var fn = page[fnName];
5544                  fn.call(page, args);
5545              }
5546          } 
5547      },
5548  
5549      /**
5550      * Constructs a child calendar. This method can be overridden if a subclassed version of the default
5551      * calendar is to be used.
5552      * @method constructChild
5553      * @param {String} id   The id of the table element that will represent the calendar widget
5554      * @param {String} containerId The id of the container div element that will wrap the calendar table
5555      * @param {Object} config  The configuration object containing the Calendar's arguments
5556      * @return {YAHOO.widget.Calendar} The YAHOO.widget.Calendar instance that is constructed
5557      */
5558      constructChild : function(id,containerId,config) {
5559          var container = document.getElementById(containerId);
5560          if (! container) {
5561              container = document.createElement("div");
5562              container.id = containerId;
5563              this.oDomContainer.appendChild(container);
5564          }
5565          return new Calendar(id,containerId,config);
5566      },
5567      
5568      /**
5569      * Sets the calendar group's month explicitly. This month will be set into the first
5570      * page of the multi-page calendar, and all other months will be iterated appropriately.
5571      * @method setMonth
5572      * @param {Number} month  The numeric month, from 0 (January) to 11 (December)
5573      */
5574      setMonth : function(month) {
5575          month = parseInt(month, 10);
5576          var currYear;
5577  
5578          var cfgPageDate = DEF_CFG.PAGEDATE.key;
5579  
5580          for (var p=0; p<this.pages.length; ++p) {
5581              var cal = this.pages[p];
5582              var pageDate = cal.cfg.getProperty(cfgPageDate);
5583              if (p === 0) {
5584                  currYear = pageDate.getFullYear();
5585              } else {
5586                  pageDate.setFullYear(currYear);
5587              }
5588              this._setMonthOnDate(pageDate, month+p); 
5589              cal.cfg.setProperty(cfgPageDate, pageDate);
5590          }
5591      },
5592  
5593      /**
5594      * Sets the calendar group's year explicitly. This year will be set into the first
5595      * page of the multi-page calendar, and all other months will be iterated appropriately.
5596      * @method setYear
5597      * @param {Number} year  The numeric 4-digit year
5598      */
5599      setYear : function(year) {
5600      
5601          var cfgPageDate = DEF_CFG.PAGEDATE.key;
5602      
5603          year = parseInt(year, 10);
5604          for (var p=0;p<this.pages.length;++p) {
5605              var cal = this.pages[p];
5606              var pageDate = cal.cfg.getProperty(cfgPageDate);
5607      
5608              if ((pageDate.getMonth()+1) == 1 && p>0) {
5609                  year+=1;
5610              }
5611              cal.setYear(year);
5612          }
5613      },
5614  
5615      /**
5616      * Calls the render function of all child calendars within the group.
5617      * @method render
5618      */
5619      render : function() {
5620          this.renderHeader();
5621          for (var p=0;p<this.pages.length;++p) {
5622              var cal = this.pages[p];
5623              cal.render();
5624          }
5625          this.renderFooter();
5626      },
5627  
5628      /**
5629      * Selects a date or a collection of dates on the current calendar. This method, by default,
5630      * does not call the render method explicitly. Once selection has completed, render must be 
5631      * called for the changes to be reflected visually.
5632      * @method select
5633      * @param    {String/Date/Date[]}    date    The date string of dates to select in the current calendar. Valid formats are
5634      *                               individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
5635      *                               Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
5636      *                               This method can also take a JavaScript Date object or an array of Date objects.
5637      * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5638      */
5639      select : function(date) {
5640          for (var p=0;p<this.pages.length;++p) {
5641              var cal = this.pages[p];
5642              cal.select(date);
5643          }
5644          return this.getSelectedDates();
5645      },
5646  
5647      /**
5648      * Selects dates in the CalendarGroup based on the cell index provided. This method is used to select cells without having to do a full render. The selected style is applied to the cells directly.
5649      * The value of the MULTI_SELECT Configuration attribute will determine the set of dates which get selected. 
5650      * <ul>
5651      *    <li>If MULTI_SELECT is false, selectCell will select the cell at the specified index for only the last displayed Calendar page.</li>
5652      *    <li>If MULTI_SELECT is true, selectCell will select the cell at the specified index, on each displayed Calendar page.</li>
5653      * </ul>
5654      * @method selectCell
5655      * @param {Number} cellIndex The index of the cell to be selected. 
5656      * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5657      */
5658      selectCell : function(cellIndex) {
5659          for (var p=0;p<this.pages.length;++p) {
5660              var cal = this.pages[p];
5661              cal.selectCell(cellIndex);
5662          }
5663          return this.getSelectedDates();
5664      },
5665      
5666      /**
5667      * Deselects a date or a collection of dates on the current calendar. This method, by default,
5668      * does not call the render method explicitly. Once deselection has completed, render must be 
5669      * called for the changes to be reflected visually.
5670      * @method deselect
5671      * @param {String/Date/Date[]} date The date string of dates to deselect in the current calendar. Valid formats are
5672      *        individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
5673      *        Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
5674      *        This method can also take a JavaScript Date object or an array of Date objects. 
5675      * @return {Date[]}   Array of JavaScript Date objects representing all individual dates that are currently selected.
5676      */
5677      deselect : function(date) {
5678          for (var p=0;p<this.pages.length;++p) {
5679              var cal = this.pages[p];
5680              cal.deselect(date);
5681          }
5682          return this.getSelectedDates();
5683      },
5684      
5685      /**
5686      * Deselects all dates on the current calendar.
5687      * @method deselectAll
5688      * @return {Date[]}  Array of JavaScript Date objects representing all individual dates that are currently selected.
5689      *      Assuming that this function executes properly, the return value should be an empty array.
5690      *      However, the empty array is returned for the sake of being able to check the selection status
5691      *      of the calendar.
5692      */
5693      deselectAll : function() {
5694          for (var p=0;p<this.pages.length;++p) {
5695              var cal = this.pages[p];
5696              cal.deselectAll();
5697          }
5698          return this.getSelectedDates();
5699      },
5700  
5701      /**
5702      * Deselects dates in the CalendarGroup based on the cell index provided. This method is used to select cells without having to do a full render. The selected style is applied to the cells directly.
5703      * deselectCell will deselect the cell at the specified index on each displayed Calendar page.
5704      *
5705      * @method deselectCell
5706      * @param {Number} cellIndex The index of the cell to deselect. 
5707      * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
5708      */
5709      deselectCell : function(cellIndex) {
5710          for (var p=0;p<this.pages.length;++p) {
5711              var cal = this.pages[p];
5712              cal.deselectCell(cellIndex);
5713          }
5714          return this.getSelectedDates();
5715      },
5716  
5717      /**
5718      * Resets the calendar widget to the originally selected month and year, and 
5719      * sets the calendar to the initial selection(s).
5720      * @method reset
5721      */
5722      reset : function() {
5723          for (var p=0;p<this.pages.length;++p) {
5724              var cal = this.pages[p];
5725              cal.reset();
5726          }
5727      },
5728  
5729      /**
5730      * Clears the selected dates in the current calendar widget and sets the calendar
5731      * to the current month and year.
5732      * @method clear
5733      */
5734      clear : function() {
5735          for (var p=0;p<this.pages.length;++p) {
5736              var cal = this.pages[p];
5737              cal.clear();
5738          }
5739  
5740          this.cfg.setProperty(DEF_CFG.SELECTED.key, []);
5741          this.cfg.setProperty(DEF_CFG.PAGEDATE.key, new Date(this.pages[0].today.getTime()));
5742          this.render();
5743      },
5744  
5745      /**
5746      * Navigates to the next month page in the calendar widget.
5747      * @method nextMonth
5748      */
5749      nextMonth : function() {
5750          for (var p=0;p<this.pages.length;++p) {
5751              var cal = this.pages[p];
5752              cal.nextMonth();
5753          }
5754      },
5755      
5756      /**
5757      * Navigates to the previous month page in the calendar widget.
5758      * @method previousMonth
5759      */
5760      previousMonth : function() {
5761          for (var p=this.pages.length-1;p>=0;--p) {
5762              var cal = this.pages[p];
5763              cal.previousMonth();
5764          }
5765      },
5766      
5767      /**
5768      * Navigates to the next year in the currently selected month in the calendar widget.
5769      * @method nextYear
5770      */
5771      nextYear : function() {
5772          for (var p=0;p<this.pages.length;++p) {
5773              var cal = this.pages[p];
5774              cal.nextYear();
5775          }
5776      },
5777  
5778      /**
5779      * Navigates to the previous year in the currently selected month in the calendar widget.
5780      * @method previousYear
5781      */
5782      previousYear : function() {
5783          for (var p=0;p<this.pages.length;++p) {
5784              var cal = this.pages[p];
5785              cal.previousYear();
5786          }
5787      },
5788  
5789      /**
5790      * Gets the list of currently selected dates from the calendar.
5791      * @return   An array of currently selected JavaScript Date objects.
5792      * @type Date[]
5793      */
5794      getSelectedDates : function() { 
5795          var returnDates = [];
5796          var selected = this.cfg.getProperty(DEF_CFG.SELECTED.key);
5797          for (var d=0;d<selected.length;++d) {
5798              var dateArray = selected[d];
5799  
5800              var date = DateMath.getDate(dateArray[0],dateArray[1]-1,dateArray[2]);
5801              returnDates.push(date);
5802          }
5803  
5804          returnDates.sort( function(a,b) { return a-b; } );
5805          return returnDates;
5806      },
5807  
5808      /**
5809      * Adds a renderer to the render stack. The function reference passed to this method will be executed
5810      * when a date cell matches the conditions specified in the date string for this renderer.
5811      * 
5812      * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should 
5813      * escape markup used to set the cell contents, if coming from an external source.<p>
5814      * @method addRenderer
5815      * @param {String} sDates  A date string to associate with the specified renderer. Valid formats
5816      *         include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
5817      * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5818      */
5819      addRenderer : function(sDates, fnRender) {
5820          for (var p=0;p<this.pages.length;++p) {
5821              var cal = this.pages[p];
5822              cal.addRenderer(sDates, fnRender);
5823          }
5824      },
5825  
5826      /**
5827      * Adds a month renderer to the render stack. The function reference passed to this method will be executed
5828      * when a date cell matches the month passed to this method
5829      * 
5830      * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should 
5831      * escape markup used to set the cell contents, if coming from an external source.<p>
5832      * @method addMonthRenderer
5833      * @param {Number} month  The month (1-12) to associate with this renderer
5834      * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5835      */
5836      addMonthRenderer : function(month, fnRender) {
5837          for (var p=0;p<this.pages.length;++p) {
5838              var cal = this.pages[p];
5839              cal.addMonthRenderer(month, fnRender);
5840          }
5841      },
5842  
5843      /**
5844      * Adds a weekday renderer to the render stack. The function reference passed to this method will be executed
5845      * when a date cell matches the weekday passed to this method.
5846      *
5847      * <p>NOTE: The contents of the cell set by the renderer will be added to the DOM as HTML. The custom renderer implementation should 
5848      * escape HTML used to set the cell contents, if coming from an external source.<p>
5849      *
5850      * @method addWeekdayRenderer
5851      * @param {Number} weekday  The weekday (Sunday = 1, Monday = 2 ... Saturday = 7) to associate with this renderer
5852      * @param {Function} fnRender The function executed to render cells that match the render rules for this renderer.
5853      */
5854      addWeekdayRenderer : function(weekday, fnRender) {
5855          for (var p=0;p<this.pages.length;++p) {
5856              var cal = this.pages[p];
5857              cal.addWeekdayRenderer(weekday, fnRender);
5858          }
5859      },
5860  
5861      /**
5862       * Removes all custom renderers added to the CalendarGroup through the addRenderer, addMonthRenderer and 
5863       * addWeekRenderer methods. CalendarGroup's render method needs to be called to after removing renderers 
5864       * to see the changes applied.
5865       * 
5866       * @method removeRenderers
5867       */
5868      removeRenderers : function() {
5869          this.callChildFunction("removeRenderers");
5870      },
5871  
5872      /**
5873      * Renders the header for the CalendarGroup.
5874      * @method renderHeader
5875      */
5876      renderHeader : function() {
5877          // EMPTY DEFAULT IMPL
5878      },
5879  
5880      /**
5881      * Renders a footer for the 2-up calendar container. By default, this method is
5882      * unimplemented.
5883      * @method renderFooter
5884      */
5885      renderFooter : function() {
5886          // EMPTY DEFAULT IMPL
5887      },
5888  
5889      /**
5890      * Adds the designated number of months to the current calendar month, and sets the current
5891      * calendar page date to the new month.
5892      * @method addMonths
5893      * @param {Number} count The number of months to add to the current calendar
5894      */
5895      addMonths : function(count) {
5896          this.callChildFunction("addMonths", count);
5897      },
5898      
5899      /**
5900      * Subtracts the designated number of months from the current calendar month, and sets the current
5901      * calendar page date to the new month.
5902      * @method subtractMonths
5903      * @param {Number} count The number of months to subtract from the current calendar
5904      */
5905      subtractMonths : function(count) {
5906          this.callChildFunction("subtractMonths", count);
5907      },
5908  
5909      /**
5910      * Adds the designated number of years to the current calendar, and sets the current
5911      * calendar page date to the new month.
5912      * @method addYears
5913      * @param {Number} count The number of years to add to the current calendar
5914      */
5915      addYears : function(count) {
5916          this.callChildFunction("addYears", count);
5917      },
5918  
5919      /**
5920      * Subtcats the designated number of years from the current calendar, and sets the current
5921      * calendar page date to the new month.
5922      * @method subtractYears
5923      * @param {Number} count The number of years to subtract from the current calendar
5924      */
5925      subtractYears : function(count) {
5926          this.callChildFunction("subtractYears", count);
5927      },
5928  
5929      /**
5930       * Returns the Calendar page instance which has a pagedate (month/year) matching the given date. 
5931       * Returns null if no match is found.
5932       * 
5933       * @method getCalendarPage
5934       * @param {Date} date The JavaScript Date object for which a Calendar page is to be found.
5935       * @return {Calendar} The Calendar page instance representing the month to which the date 
5936       * belongs.
5937       */
5938      getCalendarPage : function(date) {
5939          var cal = null;
5940          if (date) {
5941              var y = date.getFullYear(),
5942                  m = date.getMonth();
5943  
5944              var pages = this.pages;
5945              for (var i = 0; i < pages.length; ++i) {
5946                  var pageDate = pages[i].cfg.getProperty("pagedate");
5947                  if (pageDate.getFullYear() === y && pageDate.getMonth() === m) {
5948                      cal = pages[i];
5949                      break;
5950                  }
5951              }
5952          }
5953          return cal;
5954      },
5955  
5956      /**
5957      * Sets the month on a Date object, taking into account year rollover if the month is less than 0 or greater than 11.
5958      * The Date object passed in is modified. It should be cloned before passing it into this method if the original value needs to be maintained
5959      * @method _setMonthOnDate
5960      * @private
5961      * @param {Date} date The Date object on which to set the month index
5962      * @param {Number} iMonth The month index to set
5963      */
5964      _setMonthOnDate : function(date, iMonth) {
5965          // Bug in Safari 1.3, 2.0 (WebKit build < 420), Date.setMonth does not work consistently if iMonth is not 0-11
5966          if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420 && (iMonth < 0 || iMonth > 11)) {
5967              var newDate = DateMath.add(date, DateMath.MONTH, iMonth-date.getMonth());
5968              date.setTime(newDate.getTime());
5969          } else {
5970              date.setMonth(iMonth);
5971          }
5972      },
5973      
5974      /**
5975       * Fixes the width of the CalendarGroup container element, to account for miswrapped floats
5976       * @method _fixWidth
5977       * @private
5978       */
5979      _fixWidth : function() {
5980          var w = 0;
5981          for (var p=0;p<this.pages.length;++p) {
5982              var cal = this.pages[p];
5983              w += cal.oDomContainer.offsetWidth;
5984          }
5985          if (w > 0) {
5986              this.oDomContainer.style.width = w + "px";
5987          }
5988      },
5989      
5990      /**
5991      * Returns a string representation of the object.
5992      * @method toString
5993      * @return {String} A string representation of the CalendarGroup object.
5994      */
5995      toString : function() {
5996          return "CalendarGroup " + this.id;
5997      },
5998  
5999      /**
6000       * Destroys the CalendarGroup instance. The method will remove references
6001       * to HTML elements, remove any event listeners added by the CalendarGroup.
6002       * 
6003       * It will also destroy the Config and CalendarNavigator instances created by the 
6004       * CalendarGroup and the individual Calendar instances created for each page.
6005       *
6006       * @method destroy
6007       */
6008      destroy : function() {
6009  
6010          if (this.beforeDestroyEvent.fire()) {
6011  
6012              var cal = this;
6013      
6014              // Child objects
6015              if (cal.navigator) {
6016                  cal.navigator.destroy();
6017              }
6018      
6019              if (cal.cfg) {
6020                  cal.cfg.destroy();
6021              }
6022      
6023              // DOM event listeners
6024              Event.purgeElement(cal.oDomContainer, true);
6025      
6026              // Generated markup/DOM - Not removing the container DIV since we didn't create it.
6027              Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_CONTAINER);
6028              Dom.removeClass(cal.oDomContainer, CalendarGroup.CSS_MULTI_UP);
6029              
6030              for (var i = 0, l = cal.pages.length; i < l; i++) {
6031                  cal.pages[i].destroy();
6032                  cal.pages[i] = null;
6033              }
6034      
6035              cal.oDomContainer.innerHTML = "";
6036      
6037              // JS-to-DOM references
6038              cal.oDomContainer = null;
6039      
6040              this.destroyEvent.fire();
6041          }
6042      }
6043  };
6044  
6045  /**
6046  * CSS class representing the container for the calendar
6047  * @property YAHOO.widget.CalendarGroup.CSS_CONTAINER
6048  * @static
6049  * @final
6050  * @type String
6051  */
6052  CalendarGroup.CSS_CONTAINER = "yui-calcontainer";
6053  
6054  /**
6055  * CSS class representing the container for the calendar
6056  * @property YAHOO.widget.CalendarGroup.CSS_MULTI_UP
6057  * @static
6058  * @final
6059  * @type String
6060  */
6061  CalendarGroup.CSS_MULTI_UP = "multi";
6062  
6063  /**
6064  * CSS class representing the title for the 2-up calendar
6065  * @property YAHOO.widget.CalendarGroup.CSS_2UPTITLE
6066  * @static
6067  * @final
6068  * @type String
6069  */
6070  CalendarGroup.CSS_2UPTITLE = "title";
6071  
6072  /**
6073  * CSS class representing the close icon for the 2-up calendar
6074  * @property YAHOO.widget.CalendarGroup.CSS_2UPCLOSE
6075  * @static
6076  * @final
6077  * @deprecated Along with Calendar.IMG_ROOT and NAV_ARROW_LEFT, NAV_ARROW_RIGHT configuration properties.
6078  *     Calendar's <a href="YAHOO.widget.Calendar.html#Style.CSS_CLOSE">Style.CSS_CLOSE</a> property now represents the CSS class used to render the close icon
6079  * @type String
6080  */
6081  CalendarGroup.CSS_2UPCLOSE = "close-icon";
6082  
6083  YAHOO.lang.augmentProto(CalendarGroup, Calendar, "buildDayLabel",
6084                                                   "buildMonthLabel",
6085                                                   "renderOutOfBoundsDate",
6086                                                   "renderRowHeader",
6087                                                   "renderRowFooter",
6088                                                   "renderCellDefault",
6089                                                   "styleCellDefault",
6090                                                   "renderCellStyleHighlight1",
6091                                                   "renderCellStyleHighlight2",
6092                                                   "renderCellStyleHighlight3",
6093                                                   "renderCellStyleHighlight4",
6094                                                   "renderCellStyleToday",
6095                                                   "renderCellStyleSelected",
6096                                                   "renderCellNotThisMonth",
6097                                                   "styleCellNotThisMonth",
6098                                                   "renderBodyCellRestricted",
6099                                                   "initStyles",
6100                                                   "configTitle",
6101                                                   "configClose",
6102                                                   "configIframe",
6103                                                   "configStrings",
6104                                                   "configToday",
6105                                                   "configNavigator",
6106                                                   "createTitleBar",
6107                                                   "createCloseButton",
6108                                                   "removeTitleBar",
6109                                                   "removeCloseButton",
6110                                                   "hide",
6111                                                   "show",
6112                                                   "toDate",
6113                                                   "_toDate",
6114                                                   "_parseArgs",
6115                                                   "browser");
6116  
6117  YAHOO.widget.CalGrp = CalendarGroup;
6118  YAHOO.widget.CalendarGroup = CalendarGroup;
6119  
6120  /**
6121  * @class YAHOO.widget.Calendar2up
6122  * @extends YAHOO.widget.CalendarGroup
6123  * @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
6124  */
6125  YAHOO.widget.Calendar2up = function(id, containerId, config) {
6126      this.init(id, containerId, config);
6127  };
6128  
6129  YAHOO.extend(YAHOO.widget.Calendar2up, CalendarGroup);
6130  
6131  /**
6132  * @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
6133  */
6134  YAHOO.widget.Cal2up = YAHOO.widget.Calendar2up;
6135  
6136  })();
6137  /**
6138   * The CalendarNavigator is used along with a Calendar/CalendarGroup to 
6139   * provide a Month/Year popup navigation control, allowing the user to navigate 
6140   * to a specific month/year in the Calendar/CalendarGroup without having to 
6141   * scroll through months sequentially
6142   *
6143   * @namespace YAHOO.widget
6144   * @class CalendarNavigator
6145   * @constructor
6146   * @param {Calendar|CalendarGroup} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached.
6147   */
6148  YAHOO.widget.CalendarNavigator = function(cal) {
6149      this.init(cal);
6150  };
6151  
6152  (function() {
6153      // Setup static properties (inside anon fn, so that we can use shortcuts)
6154      var CN = YAHOO.widget.CalendarNavigator;
6155  
6156      /**
6157       * YAHOO.widget.CalendarNavigator.CLASSES contains constants
6158       * for the class values applied to the CalendarNaviatgator's 
6159       * DOM elements
6160       * @property YAHOO.widget.CalendarNavigator.CLASSES
6161       * @type Object
6162       * @static
6163       */
6164      CN.CLASSES = {
6165          /**
6166           * Class applied to the Calendar Navigator's bounding box
6167           * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV
6168           * @type String
6169           * @static
6170           */
6171          NAV :"yui-cal-nav",
6172          /**
6173           * Class applied to the Calendar/CalendarGroup's bounding box to indicate
6174           * the Navigator is currently visible
6175           * @property YAHOO.widget.CalendarNavigator.CLASSES.NAV_VISIBLE
6176           * @type String
6177           * @static
6178           */
6179          NAV_VISIBLE: "yui-cal-nav-visible",
6180          /**
6181           * Class applied to the Navigator mask's bounding box
6182           * @property YAHOO.widget.CalendarNavigator.CLASSES.MASK
6183           * @type String
6184           * @static
6185           */
6186          MASK : "yui-cal-nav-mask",
6187          /**
6188           * Class applied to the year label/control bounding box
6189           * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR
6190           * @type String
6191           * @static
6192           */
6193          YEAR : "yui-cal-nav-y",
6194          /**
6195           * Class applied to the month label/control bounding box
6196           * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH
6197           * @type String
6198           * @static
6199           */
6200          MONTH : "yui-cal-nav-m",
6201          /**
6202           * Class applied to the submit/cancel button's bounding box
6203           * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTONS
6204           * @type String
6205           * @static
6206           */
6207          BUTTONS : "yui-cal-nav-b",
6208          /**
6209           * Class applied to buttons wrapping element
6210           * @property YAHOO.widget.CalendarNavigator.CLASSES.BUTTON
6211           * @type String
6212           * @static
6213           */
6214          BUTTON : "yui-cal-nav-btn",
6215          /**
6216           * Class applied to the validation error area's bounding box
6217           * @property YAHOO.widget.CalendarNavigator.CLASSES.ERROR
6218           * @type String
6219           * @static
6220           */
6221          ERROR : "yui-cal-nav-e",
6222          /**
6223           * Class applied to the year input control
6224           * @property YAHOO.widget.CalendarNavigator.CLASSES.YEAR_CTRL
6225           * @type String
6226           * @static
6227           */
6228          YEAR_CTRL : "yui-cal-nav-yc",
6229          /**
6230           * Class applied to the month input control
6231           * @property YAHOO.widget.CalendarNavigator.CLASSES.MONTH_CTRL
6232           * @type String
6233           * @static
6234           */
6235          MONTH_CTRL : "yui-cal-nav-mc",
6236          /**
6237           * Class applied to controls with invalid data (e.g. a year input field with invalid an year)
6238           * @property YAHOO.widget.CalendarNavigator.CLASSES.INVALID
6239           * @type String
6240           * @static
6241           */
6242          INVALID : "yui-invalid",
6243          /**
6244           * Class applied to default controls
6245           * @property YAHOO.widget.CalendarNavigator.CLASSES.DEFAULT
6246           * @type String
6247           * @static
6248           */
6249          DEFAULT : "yui-default"
6250      };
6251  
6252      /**
6253       * Object literal containing the default configuration values for the CalendarNavigator
6254       * The configuration object is expected to follow the format below, with the properties being
6255       * case sensitive.
6256       * <dl>
6257       * <dt>strings</dt>
6258       * <dd><em>Object</em> :  An object with the properties shown below, defining the string labels to use in the Navigator's UI
6259       *     <dl>
6260       *         <dt>month</dt><dd><em>HTML</em> : The markup to use for the month label. Defaults to "Month".</dd>
6261       *         <dt>year</dt><dd><em>HTML</em> : The markup to use for the year label. Defaults to "Year".</dd>
6262       *         <dt>submit</dt><dd><em>HTML</em> : The markup to use for the submit button label. Defaults to "Okay".</dd>
6263       *         <dt>cancel</dt><dd><em>HTML</em> : The markup to use for the cancel button label. Defaults to "Cancel".</dd>
6264       *         <dt>invalidYear</dt><dd><em>HTML</em> : The markup to use for invalid year values. Defaults to "Year needs to be a number".</dd>
6265       *     </dl>
6266       * </dd>
6267       * <dt>monthFormat</dt><dd><em>String</em> : The month format to use. Either YAHOO.widget.Calendar.LONG, or YAHOO.widget.Calendar.SHORT. Defaults to YAHOO.widget.Calendar.LONG</dd>
6268       * <dt>initialFocus</dt><dd><em>String</em> : Either "year" or "month" specifying which input control should get initial focus. Defaults to "year"</dd>
6269       * </dl>
6270       * @property DEFAULT_CONFIG
6271       * @type Object
6272       * @static
6273       */
6274      CN.DEFAULT_CONFIG = {
6275          strings : {
6276              month: "Month",
6277              year: "Year",
6278              submit: "Okay",
6279              cancel: "Cancel",
6280              invalidYear : "Year needs to be a number"
6281          },
6282          monthFormat: YAHOO.widget.Calendar.LONG,
6283          initialFocus: "year"
6284      };
6285  
6286      /**
6287       * Object literal containing the default configuration values for the CalendarNavigator
6288       * @property _DEFAULT_CFG
6289       * @protected
6290       * @deprecated Made public. See the public DEFAULT_CONFIG property
6291       * @type Object
6292       * @static
6293       */
6294      CN._DEFAULT_CFG = CN.DEFAULT_CONFIG;
6295  
6296  
6297      /**
6298       * The suffix added to the Calendar/CalendarGroup's ID, to generate
6299       * a unique ID for the Navigator and it's bounding box.
6300       * @property YAHOO.widget.CalendarNavigator.ID_SUFFIX
6301       * @static
6302       * @type String
6303       * @final
6304       */
6305      CN.ID_SUFFIX = "_nav";
6306      /**
6307       * The suffix added to the Navigator's ID, to generate
6308       * a unique ID for the month control.
6309       * @property YAHOO.widget.CalendarNavigator.MONTH_SUFFIX
6310       * @static
6311       * @type String 
6312       * @final
6313       */
6314      CN.MONTH_SUFFIX = "_month";
6315      /**
6316       * The suffix added to the Navigator's ID, to generate
6317       * a unique ID for the year control.
6318       * @property YAHOO.widget.CalendarNavigator.YEAR_SUFFIX
6319       * @static
6320       * @type String
6321       * @final
6322       */
6323      CN.YEAR_SUFFIX = "_year";
6324      /**
6325       * The suffix added to the Navigator's ID, to generate
6326       * a unique ID for the error bounding box.
6327       * @property YAHOO.widget.CalendarNavigator.ERROR_SUFFIX
6328       * @static
6329       * @type String
6330       * @final
6331       */
6332      CN.ERROR_SUFFIX = "_error";
6333      /**
6334       * The suffix added to the Navigator's ID, to generate
6335       * a unique ID for the "Cancel" button.
6336       * @property YAHOO.widget.CalendarNavigator.CANCEL_SUFFIX
6337       * @static
6338       * @type String
6339       * @final
6340       */
6341      CN.CANCEL_SUFFIX = "_cancel";
6342      /**
6343       * The suffix added to the Navigator's ID, to generate
6344       * a unique ID for the "Submit" button.
6345       * @property YAHOO.widget.CalendarNavigator.SUBMIT_SUFFIX
6346       * @static
6347       * @type String
6348       * @final
6349       */
6350      CN.SUBMIT_SUFFIX = "_submit";
6351  
6352      /**
6353       * The number of digits to which the year input control is to be limited.
6354       * @property YAHOO.widget.CalendarNavigator.YR_MAX_DIGITS
6355       * @static
6356       * @type Number
6357       */
6358      CN.YR_MAX_DIGITS = 4;
6359  
6360      /**
6361       * The amount by which to increment the current year value,
6362       * when the arrow up/down key is pressed on the year control
6363       * @property YAHOO.widget.CalendarNavigator.YR_MINOR_INC
6364       * @static
6365       * @type Number
6366       */
6367      CN.YR_MINOR_INC = 1;
6368  
6369      /**
6370       * The amount by which to increment the current year value,
6371       * when the page up/down key is pressed on the year control
6372       * @property YAHOO.widget.CalendarNavigator.YR_MAJOR_INC
6373       * @static
6374       * @type Number
6375       */
6376      CN.YR_MAJOR_INC = 10;
6377  
6378      /**
6379       * Artificial delay (in ms) between the time the Navigator is hidden
6380       * and the Calendar/CalendarGroup state is updated. Allows the user
6381       * the see the Calendar/CalendarGroup page changing. If set to 0
6382       * the Calendar/CalendarGroup page will be updated instantly
6383       * @property YAHOO.widget.CalendarNavigator.UPDATE_DELAY
6384       * @static
6385       * @type Number
6386       */
6387      CN.UPDATE_DELAY = 50;
6388  
6389      /**
6390       * Regular expression used to validate the year input
6391       * @property YAHOO.widget.CalendarNavigator.YR_PATTERN
6392       * @static
6393       * @type RegExp
6394       */
6395      CN.YR_PATTERN = /^\d+$/;
6396      /**
6397       * Regular expression used to trim strings
6398       * @property YAHOO.widget.CalendarNavigator.TRIM
6399       * @static
6400       * @type RegExp
6401       */
6402      CN.TRIM = /^\s*(.*?)\s*$/;
6403  })();
6404  
6405  YAHOO.widget.CalendarNavigator.prototype = {
6406  
6407      /**
6408       * The unique ID for this CalendarNavigator instance
6409       * @property id
6410       * @type String
6411       */
6412      id : null,
6413  
6414      /**
6415       * The Calendar/CalendarGroup instance to which the navigator belongs
6416       * @property cal
6417       * @type {Calendar|CalendarGroup}
6418       */
6419      cal : null,
6420  
6421      /**
6422       * Reference to the HTMLElement used to render the navigator's bounding box
6423       * @property navEl
6424       * @type HTMLElement
6425       */
6426      navEl : null,
6427  
6428      /**
6429       * Reference to the HTMLElement used to render the navigator's mask
6430       * @property maskEl
6431       * @type HTMLElement
6432       */
6433      maskEl : null,
6434  
6435      /**
6436       * Reference to the HTMLElement used to input the year
6437       * @property yearEl
6438       * @type HTMLElement
6439       */
6440      yearEl : null,
6441  
6442      /**
6443       * Reference to the HTMLElement used to input the month
6444       * @property monthEl
6445       * @type HTMLElement
6446       */
6447      monthEl : null,
6448  
6449      /**
6450       * Reference to the HTMLElement used to display validation errors
6451       * @property errorEl
6452       * @type HTMLElement
6453       */
6454      errorEl : null,
6455  
6456      /**
6457       * Reference to the HTMLElement used to update the Calendar/Calendar group
6458       * with the month/year values
6459       * @property submitEl
6460       * @type HTMLElement
6461       */
6462      submitEl : null,
6463      
6464      /**
6465       * Reference to the HTMLElement used to hide the navigator without updating the 
6466       * Calendar/Calendar group
6467       * @property cancelEl
6468       * @type HTMLElement
6469       */
6470      cancelEl : null,
6471  
6472      /** 
6473       * Reference to the first focusable control in the navigator (by default monthEl)
6474       * @property firstCtrl
6475       * @type HTMLElement
6476       */
6477      firstCtrl : null,
6478      
6479      /** 
6480       * Reference to the last focusable control in the navigator (by default cancelEl)
6481       * @property lastCtrl
6482       * @type HTMLElement
6483       */
6484      lastCtrl : null,
6485  
6486      /**
6487       * The document containing the Calendar/Calendar group instance
6488       * @protected
6489       * @property _doc
6490       * @type HTMLDocument
6491       */
6492      _doc : null,
6493  
6494      /**
6495       * Internal state property for the current year displayed in the navigator
6496       * @protected
6497       * @property _year
6498       * @type Number
6499       */
6500      _year: null,
6501      
6502      /**
6503       * Internal state property for the current month index displayed in the navigator
6504       * @protected
6505       * @property _month
6506       * @type Number
6507       */
6508      _month: 0,
6509  
6510      /**
6511       * Private internal state property which indicates whether or not the 
6512       * Navigator has been rendered.
6513       * @private
6514       * @property __rendered
6515       * @type Boolean
6516       */
6517      __rendered: false,
6518  
6519      /**
6520       * Init lifecycle method called as part of construction
6521       * 
6522       * @method init
6523       * @param {Calendar} cal The instance of the Calendar or CalendarGroup to which this CalendarNavigator should be attached
6524       */
6525      init : function(cal) {
6526          var calBox = cal.oDomContainer;
6527  
6528          this.cal = cal;
6529          this.id = calBox.id + YAHOO.widget.CalendarNavigator.ID_SUFFIX;
6530          this._doc = calBox.ownerDocument;
6531  
6532          /**
6533           * Private flag, to identify IE Quirks
6534           * @private
6535           * @property __isIEQuirks
6536           */
6537          var ie = YAHOO.env.ua.ie;
6538          this.__isIEQuirks = (ie && ((ie <= 6) || (this._doc.compatMode == "BackCompat")));
6539      },
6540  
6541      /**
6542       * Displays the navigator and mask, updating the input controls to reflect the 
6543       * currently set month and year. The show method will invoke the render method
6544       * if the navigator has not been renderered already, allowing for lazy rendering
6545       * of the control.
6546       * 
6547       * The show method will fire the Calendar/CalendarGroup's beforeShowNav and showNav events
6548       * 
6549       * @method show
6550       */
6551      show : function() {
6552          var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES;
6553  
6554          if (this.cal.beforeShowNavEvent.fire()) {
6555              if (!this.__rendered) {
6556                  this.render();
6557              }
6558              this.clearErrors();
6559  
6560              this._updateMonthUI();
6561              this._updateYearUI();
6562              this._show(this.navEl, true);
6563  
6564              this.setInitialFocus();
6565              this.showMask();
6566  
6567              YAHOO.util.Dom.addClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE);
6568              this.cal.showNavEvent.fire();
6569          }
6570      },
6571  
6572      /**
6573       * Hides the navigator and mask
6574       * 
6575       * The show method will fire the Calendar/CalendarGroup's beforeHideNav event and hideNav events
6576       * @method hide
6577       */
6578      hide : function() {
6579          var CLASSES = YAHOO.widget.CalendarNavigator.CLASSES;
6580  
6581          if (this.cal.beforeHideNavEvent.fire()) {
6582              this._show(this.navEl, false);
6583              this.hideMask();
6584              YAHOO.util.Dom.removeClass(this.cal.oDomContainer, CLASSES.NAV_VISIBLE);
6585              this.cal.hideNavEvent.fire();
6586          }
6587      },
6588      
6589  
6590      /**
6591       * Displays the navigator's mask element
6592       * 
6593       * @method showMask
6594       */
6595      showMask : function() {
6596          this._show(this.maskEl, true);
6597          if (this.__isIEQuirks) {
6598              this._syncMask();
6599          }
6600      },
6601  
6602      /**
6603       * Hides the navigator's mask element
6604       * 
6605       * @method hideMask
6606       */
6607      hideMask : function() {
6608          this._show(this.maskEl, false);
6609      },
6610  
6611      /**
6612       * Returns the current month set on the navigator
6613       * 
6614       * Note: This may not be the month set in the UI, if 
6615       * the UI contains an invalid value.
6616       * 
6617       * @method getMonth
6618       * @return {Number} The Navigator's current month index
6619       */
6620      getMonth: function() {
6621          return this._month;
6622      },
6623  
6624      /**
6625       * Returns the current year set on the navigator
6626       * 
6627       * Note: This may not be the year set in the UI, if 
6628       * the UI contains an invalid value.
6629       * 
6630       * @method getYear
6631       * @return {Number} The Navigator's current year value
6632       */
6633      getYear: function() {
6634          return this._year;
6635      },
6636  
6637      /**
6638       * Sets the current month on the Navigator, and updates the UI
6639       * 
6640       * @method setMonth
6641       * @param {Number} nMonth The month index, from 0 (Jan) through 11 (Dec).
6642       */
6643      setMonth : function(nMonth) {
6644          if (nMonth >= 0 && nMonth < 12) {
6645              this._month = nMonth;
6646          }
6647          this._updateMonthUI();
6648      },
6649  
6650      /**
6651       * Sets the current year on the Navigator, and updates the UI. If the 
6652       * provided year is invalid, it will not be set.
6653       * 
6654       * @method setYear
6655       * @param {Number} nYear The full year value to set the Navigator to.
6656       */
6657      setYear : function(nYear) {
6658          var yrPattern = YAHOO.widget.CalendarNavigator.YR_PATTERN;
6659          if (YAHOO.lang.isNumber(nYear) && yrPattern.test(nYear+"")) {
6660              this._year = nYear;
6661          }
6662          this._updateYearUI();
6663      },
6664  
6665      /**
6666       * Renders the HTML for the navigator, adding it to the 
6667       * document and attaches event listeners if it has not 
6668       * already been rendered.
6669       * 
6670       * @method render
6671       */
6672      render: function() {
6673          this.cal.beforeRenderNavEvent.fire();
6674          if (!this.__rendered) {
6675              this.createNav();
6676              this.createMask();
6677              this.applyListeners();
6678              this.__rendered = true;
6679          }
6680          this.cal.renderNavEvent.fire();
6681      },
6682  
6683      /**
6684       * Creates the navigator's containing HTMLElement, it's contents, and appends 
6685       * the containg element to the Calendar/CalendarGroup's container.
6686       * 
6687       * @method createNav
6688       */
6689      createNav : function() {
6690          var NAV = YAHOO.widget.CalendarNavigator;
6691          var doc = this._doc;
6692  
6693          var d = doc.createElement("div");
6694          d.className = NAV.CLASSES.NAV;
6695  
6696          var htmlBuf = this.renderNavContents([]);
6697  
6698          d.innerHTML = htmlBuf.join('');
6699          this.cal.oDomContainer.appendChild(d);
6700  
6701          this.navEl = d;
6702  
6703          this.yearEl = doc.getElementById(this.id + NAV.YEAR_SUFFIX);
6704          this.monthEl = doc.getElementById(this.id + NAV.MONTH_SUFFIX);
6705          this.errorEl = doc.getElementById(this.id + NAV.ERROR_SUFFIX);
6706          this.submitEl = doc.getElementById(this.id + NAV.SUBMIT_SUFFIX);
6707          this.cancelEl = doc.getElementById(this.id + NAV.CANCEL_SUFFIX);
6708  
6709          if (YAHOO.env.ua.gecko && this.yearEl && this.yearEl.type == "text") {
6710              // Avoid XUL error on focus, select [ https://bugzilla.mozilla.org/show_bug.cgi?id=236791, 
6711              // supposedly fixed in 1.8.1, but there are reports of it still being around for methods other than blur ]
6712              this.yearEl.setAttribute("autocomplete", "off");
6713          }
6714  
6715          this._setFirstLastElements();
6716      },
6717  
6718      /**
6719       * Creates the Mask HTMLElement and appends it to the Calendar/CalendarGroups
6720       * container.
6721       * 
6722       * @method createMask
6723       */
6724      createMask : function() {
6725          var C = YAHOO.widget.CalendarNavigator.CLASSES;
6726  
6727          var d = this._doc.createElement("div");
6728          d.className = C.MASK;
6729  
6730          this.cal.oDomContainer.appendChild(d);
6731          this.maskEl = d;
6732      },
6733  
6734      /**
6735       * Used to set the width/height of the mask in pixels to match the Calendar Container.
6736       * Currently only used for IE6 or IE in quirks mode. The other A-Grade browser are handled using CSS (width/height 100%).
6737       * <p>
6738       * The method is also registered as an HTMLElement resize listener on the Calendars container element.
6739       * </p>
6740       * @protected
6741       * @method _syncMask
6742       */
6743      _syncMask : function() {
6744          var c = this.cal.oDomContainer;
6745          if (c && this.maskEl) {
6746              var r = YAHOO.util.Dom.getRegion(c);
6747              YAHOO.util.Dom.setStyle(this.maskEl, "width", r.right - r.left + "px");
6748              YAHOO.util.Dom.setStyle(this.maskEl, "height", r.bottom - r.top + "px");
6749          }
6750      },
6751  
6752      /**
6753       * Renders the contents of the navigator. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
6754       * 
6755       * @method renderNavContents
6756       * 
6757       * @param {HTML[]} html The HTML buffer to append the HTML to.
6758       * @return {HTML[]} A reference to the buffer passed in.
6759       */
6760      renderNavContents : function(html) {
6761          var NAV = YAHOO.widget.CalendarNavigator,
6762              C = NAV.CLASSES,
6763              h = html; // just to use a shorter name
6764  
6765          h[h.length] = '<div class="' + C.MONTH + '">';
6766          this.renderMonth(h);
6767          h[h.length] = '</div>';
6768          h[h.length] = '<div class="' + C.YEAR + '">';
6769          this.renderYear(h);
6770          h[h.length] = '</div>';
6771          h[h.length] = '<div class="' + C.BUTTONS + '">';
6772          this.renderButtons(h);
6773          h[h.length] = '</div>';
6774          h[h.length] = '<div class="' + C.ERROR + '" id="' + this.id + NAV.ERROR_SUFFIX + '"></div>';
6775  
6776          return h;
6777      },
6778  
6779      /**
6780       * Renders the month label and control for the navigator. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
6781       * 
6782       * @method renderNavContents
6783       * @param {HTML[]} html The HTML buffer to append the HTML to.
6784       * @return {HTML[]} A reference to the buffer passed in.
6785       */
6786      renderMonth : function(html) {
6787          var NAV = YAHOO.widget.CalendarNavigator,
6788              C = NAV.CLASSES;
6789  
6790          var id = this.id + NAV.MONTH_SUFFIX,
6791              mf = this.__getCfg("monthFormat"),
6792              months = this.cal.cfg.getProperty((mf == YAHOO.widget.Calendar.SHORT) ? "MONTHS_SHORT" : "MONTHS_LONG"),
6793              h = html;
6794  
6795          if (months && months.length > 0) {
6796              h[h.length] = '<label for="' + id + '">';
6797              h[h.length] = this.__getCfg("month", true);
6798              h[h.length] = '</label>';
6799              h[h.length] = '<select name="' + id + '" id="' + id + '" class="' + C.MONTH_CTRL + '">';
6800              for (var i = 0; i < months.length; i++) {
6801                  h[h.length] = '<option value="' + i + '">';
6802                  h[h.length] = months[i];
6803                  h[h.length] = '</option>';
6804              }
6805              h[h.length] = '</select>';
6806          }
6807          return h;
6808      },
6809  
6810      /**
6811       * Renders the year label and control for the navigator. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source. 
6812       * 
6813       * @method renderYear
6814       * @param {Array} html The HTML buffer to append the HTML to.
6815       * @return {Array} A reference to the buffer passed in.
6816       */
6817      renderYear : function(html) {
6818          var NAV = YAHOO.widget.CalendarNavigator,
6819              C = NAV.CLASSES;
6820  
6821          var id = this.id + NAV.YEAR_SUFFIX,
6822              size = NAV.YR_MAX_DIGITS,
6823              h = html;
6824  
6825          h[h.length] = '<label for="' + id + '">';
6826          h[h.length] = this.__getCfg("year", true);
6827          h[h.length] = '</label>';
6828          h[h.length] = '<input type="text" name="' + id + '" id="' + id + '" class="' + C.YEAR_CTRL + '" maxlength="' + size + '"/>';
6829          return h;
6830      },
6831  
6832      /**
6833       * Renders the submit/cancel buttons for the navigator. NOTE: The contents of the array passed into this method are added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.
6834       * 
6835       * @method renderButtons
6836       * @param {Array} html The HTML buffer to append the HTML to.
6837       * @return {Array} A reference to the buffer passed in.
6838       */
6839      renderButtons : function(html) {
6840          var C = YAHOO.widget.CalendarNavigator.CLASSES;
6841          var h = html;
6842  
6843          h[h.length] = '<span class="' + C.BUTTON + ' ' + C.DEFAULT + '">';
6844          h[h.length] = '<button type="button" id="' + this.id + '_submit' + '">';
6845          h[h.length] = this.__getCfg("submit", true);
6846          h[h.length] = '</button>';
6847          h[h.length] = '</span>';
6848          h[h.length] = '<span class="' + C.BUTTON +'">';
6849          h[h.length] = '<button type="button" id="' + this.id + '_cancel' + '">';
6850          h[h.length] = this.__getCfg("cancel", true);
6851          h[h.length] = '</button>';
6852          h[h.length] = '</span>';
6853  
6854          return h;
6855      },
6856  
6857      /**
6858       * Attaches DOM event listeners to the rendered elements
6859       * <p>
6860       * The method will call applyKeyListeners, to setup keyboard specific 
6861       * listeners
6862       * </p>
6863       * @method applyListeners
6864       */
6865      applyListeners : function() {
6866          var E = YAHOO.util.Event;
6867  
6868          function yearUpdateHandler() {
6869              if (this.validate()) {
6870                  this.setYear(this._getYearFromUI());
6871              }
6872          }
6873  
6874          function monthUpdateHandler() {
6875              this.setMonth(this._getMonthFromUI());
6876          }
6877  
6878          E.on(this.submitEl, "click", this.submit, this, true);
6879          E.on(this.cancelEl, "click", this.cancel, this, true);
6880          E.on(this.yearEl, "blur", yearUpdateHandler, this, true);
6881          E.on(this.monthEl, "change", monthUpdateHandler, this, true);
6882  
6883          if (this.__isIEQuirks) {
6884              YAHOO.util.Event.on(this.cal.oDomContainer, "resize", this._syncMask, this, true);
6885          }
6886  
6887          this.applyKeyListeners();
6888      },
6889  
6890      /**
6891       * Removes/purges DOM event listeners from the rendered elements
6892       * 
6893       * @method purgeListeners
6894       */
6895      purgeListeners : function() {
6896          var E = YAHOO.util.Event;
6897          E.removeListener(this.submitEl, "click", this.submit);
6898          E.removeListener(this.cancelEl, "click", this.cancel);
6899          E.removeListener(this.yearEl, "blur");
6900          E.removeListener(this.monthEl, "change");
6901          if (this.__isIEQuirks) {
6902              E.removeListener(this.cal.oDomContainer, "resize", this._syncMask);
6903          }
6904  
6905          this.purgeKeyListeners();
6906      },
6907  
6908      /**
6909       * Attaches DOM listeners for keyboard support. 
6910       * Tab/Shift-Tab looping, Enter Key Submit on Year element,
6911       * Up/Down/PgUp/PgDown year increment on Year element
6912       * <p>
6913       * NOTE: MacOSX Safari 2.x doesn't let you tab to buttons and 
6914       * MacOSX Gecko does not let you tab to buttons or select controls,
6915       * so for these browsers, Tab/Shift-Tab looping is limited to the 
6916       * elements which can be reached using the tab key.
6917       * </p>
6918       * @method applyKeyListeners
6919       */
6920      applyKeyListeners : function() {
6921          var E = YAHOO.util.Event,
6922              ua = YAHOO.env.ua;
6923  
6924          // IE/Safari 3.1 doesn't fire keypress for arrow/pg keys (non-char keys)
6925          var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress";
6926  
6927          // - IE/Safari 3.1 doesn't fire keypress for non-char keys
6928          // - Opera doesn't allow us to cancel keydown or keypress for tab, but 
6929          //   changes focus successfully on keydown (keypress is too late to change focus - opera's already moved on).
6930          var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress";
6931  
6932          // Everyone likes keypress for Enter (char keys) - whoo hoo!
6933          E.on(this.yearEl, "keypress", this._handleEnterKey, this, true);
6934  
6935          E.on(this.yearEl, arrowEvt, this._handleDirectionKeys, this, true);
6936          E.on(this.lastCtrl, tabEvt, this._handleTabKey, this, true);
6937          E.on(this.firstCtrl, tabEvt, this._handleShiftTabKey, this, true);
6938      },
6939  
6940      /**
6941       * Removes/purges DOM listeners for keyboard support
6942       *
6943       * @method purgeKeyListeners
6944       */
6945      purgeKeyListeners : function() {
6946          var E = YAHOO.util.Event,
6947              ua = YAHOO.env.ua;
6948  
6949          var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress";
6950          var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress";
6951  
6952          E.removeListener(this.yearEl, "keypress", this._handleEnterKey);
6953          E.removeListener(this.yearEl, arrowEvt, this._handleDirectionKeys);
6954          E.removeListener(this.lastCtrl, tabEvt, this._handleTabKey);
6955          E.removeListener(this.firstCtrl, tabEvt, this._handleShiftTabKey);
6956      },
6957  
6958      /**
6959       * Updates the Calendar/CalendarGroup's pagedate with the currently set month and year if valid.
6960       * <p>
6961       * If the currently set month/year is invalid, a validation error will be displayed and the 
6962       * Calendar/CalendarGroup's pagedate will not be updated.
6963       * </p>
6964       * @method submit
6965       */
6966      submit : function() {
6967          if (this.validate()) {
6968              this.hide();
6969  
6970              this.setMonth(this._getMonthFromUI());
6971              this.setYear(this._getYearFromUI());
6972  
6973              var cal = this.cal;
6974  
6975              // Artificial delay, just to help the user see something changed
6976              var delay = YAHOO.widget.CalendarNavigator.UPDATE_DELAY;
6977              if (delay > 0) {
6978                  var nav = this;
6979                  window.setTimeout(function(){ nav._update(cal); }, delay);
6980              } else {
6981                  this._update(cal);
6982              }
6983          }
6984      },
6985  
6986      /**
6987       * Updates the Calendar rendered state, based on the state of the CalendarNavigator
6988       * @method _update
6989       * @param cal The Calendar instance to update
6990       * @protected
6991       */
6992      _update : function(cal) {
6993          var date = YAHOO.widget.DateMath.getDate(this.getYear() - cal.cfg.getProperty("YEAR_OFFSET"), this.getMonth(), 1);
6994          cal.cfg.setProperty("pagedate", date);
6995          cal.render();
6996      },
6997  
6998      /**
6999       * Hides the navigator and mask, without updating the Calendar/CalendarGroup's state
7000       * 
7001       * @method cancel
7002       */
7003      cancel : function() {
7004          this.hide();
7005      },
7006  
7007      /**
7008       * Validates the current state of the UI controls
7009       * 
7010       * @method validate
7011       * @return {Boolean} true, if the current UI state contains valid values, false if not
7012       */
7013      validate : function() {
7014          if (this._getYearFromUI() !== null) {
7015              this.clearErrors();
7016              return true;
7017          } else {
7018              this.setYearError();
7019              this.setError(this.__getCfg("invalidYear", true));
7020              return false;
7021          }
7022      },
7023  
7024      /**
7025       * Displays an error message in the Navigator's error panel.
7026       * 
7027       * @method setError
7028       * @param {HTML} msg The markup for the error message to display. NOTE: The msg passed into this method is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source. 
7029       */
7030      setError : function(msg) {
7031          if (this.errorEl) {
7032              this.errorEl.innerHTML = msg;
7033              this._show(this.errorEl, true);
7034          }
7035      },
7036  
7037      /**
7038       * Clears the navigator's error message and hides the error panel
7039       * @method clearError 
7040       */
7041      clearError : function() {
7042          if (this.errorEl) {
7043              this.errorEl.innerHTML = "";
7044              this._show(this.errorEl, false);
7045          }
7046      },
7047  
7048      /**
7049       * Displays the validation error UI for the year control
7050       * @method setYearError
7051       */
7052      setYearError : function() {
7053          YAHOO.util.Dom.addClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID);
7054      },
7055  
7056      /**
7057       * Removes the validation error UI for the year control
7058       * @method clearYearError
7059       */
7060      clearYearError : function() {
7061          YAHOO.util.Dom.removeClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID);
7062      },
7063  
7064      /**
7065       * Clears all validation and error messages in the UI
7066       * @method clearErrors
7067       */
7068      clearErrors : function() {
7069          this.clearError();
7070          this.clearYearError();
7071      },
7072  
7073      /**
7074       * Sets the initial focus, based on the configured value
7075       * @method setInitialFocus
7076       */
7077      setInitialFocus : function() {
7078          var el = this.submitEl,
7079              f = this.__getCfg("initialFocus");
7080  
7081          if (f && f.toLowerCase) {
7082              f = f.toLowerCase();
7083              if (f == "year") {
7084                  el = this.yearEl;
7085                  try {
7086                      this.yearEl.select();
7087                  } catch (selErr) {
7088                      // Ignore;
7089                  }
7090              } else if (f == "month") {
7091                  el = this.monthEl;
7092              }
7093          }
7094  
7095          if (el && YAHOO.lang.isFunction(el.focus)) {
7096              try {
7097                  el.focus();
7098              } catch (focusErr) {
7099                  // TODO: Fall back if focus fails?
7100              }
7101          }
7102      },
7103  
7104      /**
7105       * Removes all renderered HTML elements for the Navigator from
7106       * the DOM, purges event listeners and clears (nulls) any property
7107       * references to HTML references
7108       * @method erase
7109       */
7110      erase : function() {
7111          if (this.__rendered) {
7112              this.purgeListeners();
7113  
7114              // Clear out innerHTML references
7115              this.yearEl = null;
7116              this.monthEl = null;
7117              this.errorEl = null;
7118              this.submitEl = null;
7119              this.cancelEl = null;
7120              this.firstCtrl = null;
7121              this.lastCtrl = null;
7122              if (this.navEl) {
7123                  this.navEl.innerHTML = "";
7124              }
7125  
7126              var p = this.navEl.parentNode;
7127              if (p) {
7128                  p.removeChild(this.navEl);
7129              }
7130              this.navEl = null;
7131  
7132              var pm = this.maskEl.parentNode;
7133              if (pm) {
7134                  pm.removeChild(this.maskEl);
7135              }
7136              this.maskEl = null;
7137              this.__rendered = false;
7138          }
7139      },
7140  
7141      /**
7142       * Destroys the Navigator object and any HTML references
7143       * @method destroy
7144       */
7145      destroy : function() {
7146          this.erase();
7147          this._doc = null;
7148          this.cal = null;
7149          this.id = null;
7150      },
7151  
7152      /**
7153       * Protected implementation to handle how UI elements are 
7154       * hidden/shown.
7155       *
7156       * @method _show
7157       * @protected
7158       */
7159      _show : function(el, bShow) {
7160          if (el) {
7161              YAHOO.util.Dom.setStyle(el, "display", (bShow) ? "block" : "none");
7162          }
7163      },
7164  
7165      /**
7166       * Returns the month value (index), from the month UI element
7167       * @protected
7168       * @method _getMonthFromUI
7169       * @return {Number} The month index, or 0 if a UI element for the month
7170       * is not found
7171       */
7172      _getMonthFromUI : function() {
7173          if (this.monthEl) {
7174              return this.monthEl.selectedIndex;
7175          } else {
7176              return 0; // Default to Jan
7177          }
7178      },
7179  
7180      /**
7181       * Returns the year value, from the Navitator's year UI element
7182       * @protected
7183       * @method _getYearFromUI
7184       * @return {Number} The year value set in the UI, if valid. null is returned if 
7185       * the UI does not contain a valid year value.
7186       */
7187      _getYearFromUI : function() {
7188          var NAV = YAHOO.widget.CalendarNavigator;
7189  
7190          var yr = null;
7191          if (this.yearEl) {
7192              var value = this.yearEl.value;
7193              value = value.replace(NAV.TRIM, "$1");
7194  
7195              if (NAV.YR_PATTERN.test(value)) {
7196                  yr = parseInt(value, 10);
7197              }
7198          }
7199          return yr;
7200      },
7201  
7202      /**
7203       * Updates the Navigator's year UI, based on the year value set on the Navigator object
7204       * @protected
7205       * @method _updateYearUI
7206       */
7207      _updateYearUI : function() {
7208          if (this.yearEl && this._year !== null) {
7209              this.yearEl.value = this._year;
7210          }
7211      },
7212  
7213      /**
7214       * Updates the Navigator's month UI, based on the month value set on the Navigator object
7215       * @protected
7216       * @method _updateMonthUI
7217       */
7218      _updateMonthUI : function() {
7219          if (this.monthEl) {
7220              this.monthEl.selectedIndex = this._month;
7221          }
7222      },
7223  
7224      /**
7225       * Sets up references to the first and last focusable element in the Navigator's UI
7226       * in terms of tab order (Naviagator's firstEl and lastEl properties). The references
7227       * are used to control modality by looping around from the first to the last control
7228       * and visa versa for tab/shift-tab navigation.
7229       * <p>
7230       * See <a href="#applyKeyListeners">applyKeyListeners</a>
7231       * </p>
7232       * @protected
7233       * @method _setFirstLastElements
7234       */
7235      _setFirstLastElements : function() {
7236          this.firstCtrl = this.monthEl;
7237          this.lastCtrl = this.cancelEl;
7238  
7239          // Special handling for MacOSX.
7240          // - Safari 2.x can't focus on buttons
7241          // - Gecko can't focus on select boxes or buttons
7242          if (this.__isMac) {
7243              if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420){
7244                  this.firstCtrl = this.monthEl;
7245                  this.lastCtrl = this.yearEl;
7246              }
7247              if (YAHOO.env.ua.gecko) {
7248                  this.firstCtrl = this.yearEl;
7249                  this.lastCtrl = this.yearEl;
7250              }
7251          }
7252      },
7253  
7254      /**
7255       * Default Keyboard event handler to capture Enter 
7256       * on the Navigator's year control (yearEl)
7257       * 
7258       * @method _handleEnterKey
7259       * @protected
7260       * @param {Event} e The DOM event being handled
7261       */
7262      _handleEnterKey : function(e) {
7263          var KEYS = YAHOO.util.KeyListener.KEY;
7264  
7265          if (YAHOO.util.Event.getCharCode(e) == KEYS.ENTER) {
7266              YAHOO.util.Event.preventDefault(e);
7267              this.submit();
7268          }
7269      },
7270  
7271      /**
7272       * Default Keyboard event handler to capture up/down/pgup/pgdown
7273       * on the Navigator's year control (yearEl).
7274       * 
7275       * @method _handleDirectionKeys
7276       * @protected
7277       * @param {Event} e The DOM event being handled
7278       */
7279      _handleDirectionKeys : function(e) {
7280          var E = YAHOO.util.Event,
7281              KEYS = YAHOO.util.KeyListener.KEY,
7282              NAV = YAHOO.widget.CalendarNavigator;
7283  
7284          var value = (this.yearEl.value) ? parseInt(this.yearEl.value, 10) : null;
7285          if (isFinite(value)) {
7286              var dir = false;
7287              switch(E.getCharCode(e)) {
7288                  case KEYS.UP:
7289                      this.yearEl.value = value + NAV.YR_MINOR_INC;
7290                      dir = true;
7291                      break;
7292                  case KEYS.DOWN:
7293                      this.yearEl.value = Math.max(value - NAV.YR_MINOR_INC, 0);
7294                      dir = true;
7295                      break;
7296                  case KEYS.PAGE_UP:
7297                      this.yearEl.value = value + NAV.YR_MAJOR_INC;
7298                      dir = true;
7299                      break;
7300                  case KEYS.PAGE_DOWN:
7301                      this.yearEl.value = Math.max(value - NAV.YR_MAJOR_INC, 0);
7302                      dir = true;
7303                      break;
7304                  default:
7305                      break;
7306              }
7307              if (dir) {
7308                  E.preventDefault(e);
7309                  try {
7310                      this.yearEl.select();
7311                  } catch(err) {
7312                      // Ignore
7313                  }
7314              }
7315          }
7316      },
7317  
7318      /**
7319       * Default Keyboard event handler to capture Tab 
7320       * on the last control (lastCtrl) in the Navigator.
7321       * 
7322       * @method _handleTabKey
7323       * @protected
7324       * @param {Event} e The DOM event being handled
7325       */
7326      _handleTabKey : function(e) {
7327          var E = YAHOO.util.Event,
7328              KEYS = YAHOO.util.KeyListener.KEY;
7329  
7330          if (E.getCharCode(e) == KEYS.TAB && !e.shiftKey) {
7331              try {
7332                  E.preventDefault(e);
7333                  this.firstCtrl.focus();
7334              } catch (err) {
7335                  // Ignore - mainly for focus edge cases
7336              }
7337          }
7338      },
7339  
7340      /**
7341       * Default Keyboard event handler to capture Shift-Tab 
7342       * on the first control (firstCtrl) in the Navigator.
7343       * 
7344       * @method _handleShiftTabKey
7345       * @protected
7346       * @param {Event} e The DOM event being handled
7347       */
7348      _handleShiftTabKey : function(e) {
7349          var E = YAHOO.util.Event,
7350              KEYS = YAHOO.util.KeyListener.KEY;
7351  
7352          if (e.shiftKey && E.getCharCode(e) == KEYS.TAB) {
7353              try {
7354                  E.preventDefault(e);
7355                  this.lastCtrl.focus();
7356              } catch (err) {
7357                  // Ignore - mainly for focus edge cases
7358              }
7359          }
7360      },
7361  
7362      /**
7363       * Retrieve Navigator configuration values from 
7364       * the parent Calendar/CalendarGroup's config value.
7365       * <p>
7366       * If it has not been set in the user provided configuration, the method will 
7367       * return the default value of the configuration property, as set in DEFAULT_CONFIG
7368       * </p>
7369       * @private
7370       * @method __getCfg
7371       * @param {String} Case sensitive property name.
7372       * @param {Boolean} true, if the property is a string property, false if not.
7373       * @return The value of the configuration property
7374       */
7375      __getCfg : function(prop, bIsStr) {
7376          var DEF_CFG = YAHOO.widget.CalendarNavigator.DEFAULT_CONFIG;
7377          var cfg = this.cal.cfg.getProperty("navigator");
7378  
7379          if (bIsStr) {
7380              return (cfg !== true && cfg.strings && cfg.strings[prop]) ? cfg.strings[prop] : DEF_CFG.strings[prop];
7381          } else {
7382              return (cfg !== true && cfg[prop]) ? cfg[prop] : DEF_CFG[prop];
7383          }
7384      },
7385  
7386      /**
7387       * Private flag, to identify MacOS
7388       * @private
7389       * @property __isMac
7390       */
7391      __isMac : (navigator.userAgent.toLowerCase().indexOf("macintosh") != -1)
7392  
7393  };
7394  YAHOO.register("calendar", YAHOO.widget.Calendar, {version: "2.9.0", build: "2800"});
7395  
7396  }, '2.9.0' ,{"requires": ["yui2-yahoo", "yui2-dom", "yui2-event", "yui2-skin-sam-calendar"], "supersedes": ["yui2-datemath"]});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1