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