[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 YUI.add('yui2-containercore', function(Y) { Y.use('yui2-container'); }, '3.3.0' ,{"requires": ["yui2-yahoo", "yui2-dom", "yui2-event"]}); 2 YUI.add('yui2-container', 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 (function () { 694 695 /** 696 * The Container family of components is designed to enable developers to 697 * create different kinds of content-containing modules on the web. Module 698 * and Overlay are the most basic containers, and they can be used directly 699 * or extended to build custom containers. Also part of the Container family 700 * are four UI controls that extend Module and Overlay: Tooltip, Panel, 701 * Dialog, and SimpleDialog. 702 * @module container 703 * @title Container 704 * @requires yahoo, dom, event 705 * @optional dragdrop, animation, button 706 */ 707 708 /** 709 * Module is a JavaScript representation of the Standard Module Format. 710 * Standard Module Format is a simple standard for markup containers where 711 * child nodes representing the header, body, and footer of the content are 712 * denoted using the CSS classes "hd", "bd", and "ft" respectively. 713 * Module is the base class for all other classes in the YUI 714 * Container package. 715 * @namespace YAHOO.widget 716 * @class Module 717 * @constructor 718 * @param {String} el The element ID representing the Module <em>OR</em> 719 * @param {HTMLElement} el The element representing the Module 720 * @param {Object} userConfig The configuration Object literal containing 721 * the configuration that should be set for this module. See configuration 722 * documentation for more details. 723 */ 724 YAHOO.widget.Module = function (el, userConfig) { 725 if (el) { 726 this.init(el, userConfig); 727 } else { 728 YAHOO.log("No element or element ID specified" + 729 " for Module instantiation", "error"); 730 } 731 }; 732 733 var Dom = YAHOO.util.Dom, 734 Config = YAHOO.util.Config, 735 Event = YAHOO.util.Event, 736 CustomEvent = YAHOO.util.CustomEvent, 737 Module = YAHOO.widget.Module, 738 UA = YAHOO.env.ua, 739 740 m_oModuleTemplate, 741 m_oHeaderTemplate, 742 m_oBodyTemplate, 743 m_oFooterTemplate, 744 745 /** 746 * Constant representing the name of the Module's events 747 * @property EVENT_TYPES 748 * @private 749 * @final 750 * @type Object 751 */ 752 EVENT_TYPES = { 753 "BEFORE_INIT": "beforeInit", 754 "INIT": "init", 755 "APPEND": "append", 756 "BEFORE_RENDER": "beforeRender", 757 "RENDER": "render", 758 "CHANGE_HEADER": "changeHeader", 759 "CHANGE_BODY": "changeBody", 760 "CHANGE_FOOTER": "changeFooter", 761 "CHANGE_CONTENT": "changeContent", 762 "DESTROY": "destroy", 763 "BEFORE_SHOW": "beforeShow", 764 "SHOW": "show", 765 "BEFORE_HIDE": "beforeHide", 766 "HIDE": "hide" 767 }, 768 769 /** 770 * Constant representing the Module's configuration properties 771 * @property DEFAULT_CONFIG 772 * @private 773 * @final 774 * @type Object 775 */ 776 DEFAULT_CONFIG = { 777 778 "VISIBLE": { 779 key: "visible", 780 value: true, 781 validator: YAHOO.lang.isBoolean 782 }, 783 784 "EFFECT": { 785 key: "effect", 786 suppressEvent: true, 787 supercedes: ["visible"] 788 }, 789 790 "MONITOR_RESIZE": { 791 key: "monitorresize", 792 value: true 793 }, 794 795 "APPEND_TO_DOCUMENT_BODY": { 796 key: "appendtodocumentbody", 797 value: false 798 } 799 }; 800 801 /** 802 * Constant representing the prefix path to use for non-secure images 803 * @property YAHOO.widget.Module.IMG_ROOT 804 * @static 805 * @final 806 * @type String 807 */ 808 Module.IMG_ROOT = null; 809 810 /** 811 * Constant representing the prefix path to use for securely served images 812 * @property YAHOO.widget.Module.IMG_ROOT_SSL 813 * @static 814 * @final 815 * @type String 816 */ 817 Module.IMG_ROOT_SSL = null; 818 819 /** 820 * Constant for the default CSS class name that represents a Module 821 * @property YAHOO.widget.Module.CSS_MODULE 822 * @static 823 * @final 824 * @type String 825 */ 826 Module.CSS_MODULE = "yui-module"; 827 828 /** 829 * CSS classname representing the module header. NOTE: The classname is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 830 * @property YAHOO.widget.Module.CSS_HEADER 831 * @static 832 * @final 833 * @type String 834 */ 835 Module.CSS_HEADER = "hd"; 836 837 /** 838 * CSS classname representing the module body. NOTE: The classname is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 839 * @property YAHOO.widget.Module.CSS_BODY 840 * @static 841 * @final 842 * @type String 843 */ 844 Module.CSS_BODY = "bd"; 845 846 /** 847 * CSS classname representing the module footer. NOTE: The classname is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 848 * @property YAHOO.widget.Module.CSS_FOOTER 849 * @static 850 * @final 851 * @type String 852 */ 853 Module.CSS_FOOTER = "ft"; 854 855 /** 856 * Constant representing the url for the "src" attribute of the iframe 857 * used to monitor changes to the browser's base font size 858 * @property YAHOO.widget.Module.RESIZE_MONITOR_SECURE_URL 859 * @static 860 * @final 861 * @type String 862 */ 863 Module.RESIZE_MONITOR_SECURE_URL = "javascript:false;"; 864 865 /** 866 * Constant representing the buffer amount (in pixels) to use when positioning 867 * the text resize monitor offscreen. The resize monitor is positioned 868 * offscreen by an amount eqaul to its offsetHeight + the buffer value. 869 * 870 * @property YAHOO.widget.Module.RESIZE_MONITOR_BUFFER 871 * @static 872 * @type Number 873 */ 874 // Set to 1, to work around pixel offset in IE8, which increases when zoom is used 875 Module.RESIZE_MONITOR_BUFFER = 1; 876 877 /** 878 * Singleton CustomEvent fired when the font size is changed in the browser. 879 * Opera's "zoom" functionality currently does not support text 880 * size detection. 881 * @event YAHOO.widget.Module.textResizeEvent 882 */ 883 Module.textResizeEvent = new CustomEvent("textResize"); 884 885 /** 886 * Helper utility method, which forces a document level 887 * redraw for Opera, which can help remove repaint 888 * irregularities after applying DOM changes. 889 * 890 * @method YAHOO.widget.Module.forceDocumentRedraw 891 * @static 892 */ 893 Module.forceDocumentRedraw = function() { 894 var docEl = document.documentElement; 895 if (docEl) { 896 docEl.className += " "; 897 docEl.className = YAHOO.lang.trim(docEl.className); 898 } 899 }; 900 901 function createModuleTemplate() { 902 903 if (!m_oModuleTemplate) { 904 m_oModuleTemplate = document.createElement("div"); 905 906 m_oModuleTemplate.innerHTML = ("<div class=\"" + 907 Module.CSS_HEADER + "\"></div>" + "<div class=\"" + 908 Module.CSS_BODY + "\"></div><div class=\"" + 909 Module.CSS_FOOTER + "\"></div>"); 910 911 m_oHeaderTemplate = m_oModuleTemplate.firstChild; 912 m_oBodyTemplate = m_oHeaderTemplate.nextSibling; 913 m_oFooterTemplate = m_oBodyTemplate.nextSibling; 914 } 915 916 return m_oModuleTemplate; 917 } 918 919 function createHeader() { 920 if (!m_oHeaderTemplate) { 921 createModuleTemplate(); 922 } 923 return (m_oHeaderTemplate.cloneNode(false)); 924 } 925 926 function createBody() { 927 if (!m_oBodyTemplate) { 928 createModuleTemplate(); 929 } 930 return (m_oBodyTemplate.cloneNode(false)); 931 } 932 933 function createFooter() { 934 if (!m_oFooterTemplate) { 935 createModuleTemplate(); 936 } 937 return (m_oFooterTemplate.cloneNode(false)); 938 } 939 940 Module.prototype = { 941 942 /** 943 * The class's constructor function 944 * @property contructor 945 * @type Function 946 */ 947 constructor: Module, 948 949 /** 950 * The main module element that contains the header, body, and footer 951 * @property element 952 * @type HTMLElement 953 */ 954 element: null, 955 956 /** 957 * The header element, denoted with CSS class "hd" 958 * @property header 959 * @type HTMLElement 960 */ 961 header: null, 962 963 /** 964 * The body element, denoted with CSS class "bd" 965 * @property body 966 * @type HTMLElement 967 */ 968 body: null, 969 970 /** 971 * The footer element, denoted with CSS class "ft" 972 * @property footer 973 * @type HTMLElement 974 */ 975 footer: null, 976 977 /** 978 * The id of the element 979 * @property id 980 * @type String 981 */ 982 id: null, 983 984 /** 985 * A string representing the root path for all images created by 986 * a Module instance. 987 * @deprecated It is recommend that any images for a Module be applied 988 * via CSS using the "background-image" property. 989 * @property imageRoot 990 * @type String 991 */ 992 imageRoot: Module.IMG_ROOT, 993 994 /** 995 * Initializes the custom events for Module which are fired 996 * automatically at appropriate times by the Module class. 997 * @method initEvents 998 */ 999 initEvents: function () { 1000 1001 var SIGNATURE = CustomEvent.LIST; 1002 1003 /** 1004 * CustomEvent fired prior to class initalization. 1005 * @event beforeInitEvent 1006 * @param {class} classRef class reference of the initializing 1007 * class, such as this.beforeInitEvent.fire(Module) 1008 */ 1009 this.beforeInitEvent = this.createEvent(EVENT_TYPES.BEFORE_INIT); 1010 this.beforeInitEvent.signature = SIGNATURE; 1011 1012 /** 1013 * CustomEvent fired after class initalization. 1014 * @event initEvent 1015 * @param {class} classRef class reference of the initializing 1016 * class, such as this.beforeInitEvent.fire(Module) 1017 */ 1018 this.initEvent = this.createEvent(EVENT_TYPES.INIT); 1019 this.initEvent.signature = SIGNATURE; 1020 1021 /** 1022 * CustomEvent fired when the Module is appended to the DOM 1023 * @event appendEvent 1024 */ 1025 this.appendEvent = this.createEvent(EVENT_TYPES.APPEND); 1026 this.appendEvent.signature = SIGNATURE; 1027 1028 /** 1029 * CustomEvent fired before the Module is rendered 1030 * @event beforeRenderEvent 1031 */ 1032 this.beforeRenderEvent = this.createEvent(EVENT_TYPES.BEFORE_RENDER); 1033 this.beforeRenderEvent.signature = SIGNATURE; 1034 1035 /** 1036 * CustomEvent fired after the Module is rendered 1037 * @event renderEvent 1038 */ 1039 this.renderEvent = this.createEvent(EVENT_TYPES.RENDER); 1040 this.renderEvent.signature = SIGNATURE; 1041 1042 /** 1043 * CustomEvent fired when the header content of the Module 1044 * is modified 1045 * @event changeHeaderEvent 1046 * @param {String/HTMLElement} content String/element representing 1047 * the new header content 1048 */ 1049 this.changeHeaderEvent = this.createEvent(EVENT_TYPES.CHANGE_HEADER); 1050 this.changeHeaderEvent.signature = SIGNATURE; 1051 1052 /** 1053 * CustomEvent fired when the body content of the Module is modified 1054 * @event changeBodyEvent 1055 * @param {String/HTMLElement} content String/element representing 1056 * the new body content 1057 */ 1058 this.changeBodyEvent = this.createEvent(EVENT_TYPES.CHANGE_BODY); 1059 this.changeBodyEvent.signature = SIGNATURE; 1060 1061 /** 1062 * CustomEvent fired when the footer content of the Module 1063 * is modified 1064 * @event changeFooterEvent 1065 * @param {String/HTMLElement} content String/element representing 1066 * the new footer content 1067 */ 1068 this.changeFooterEvent = this.createEvent(EVENT_TYPES.CHANGE_FOOTER); 1069 this.changeFooterEvent.signature = SIGNATURE; 1070 1071 /** 1072 * CustomEvent fired when the content of the Module is modified 1073 * @event changeContentEvent 1074 */ 1075 this.changeContentEvent = this.createEvent(EVENT_TYPES.CHANGE_CONTENT); 1076 this.changeContentEvent.signature = SIGNATURE; 1077 1078 /** 1079 * CustomEvent fired when the Module is destroyed 1080 * @event destroyEvent 1081 */ 1082 this.destroyEvent = this.createEvent(EVENT_TYPES.DESTROY); 1083 this.destroyEvent.signature = SIGNATURE; 1084 1085 /** 1086 * CustomEvent fired before the Module is shown 1087 * @event beforeShowEvent 1088 */ 1089 this.beforeShowEvent = this.createEvent(EVENT_TYPES.BEFORE_SHOW); 1090 this.beforeShowEvent.signature = SIGNATURE; 1091 1092 /** 1093 * CustomEvent fired after the Module is shown 1094 * @event showEvent 1095 */ 1096 this.showEvent = this.createEvent(EVENT_TYPES.SHOW); 1097 this.showEvent.signature = SIGNATURE; 1098 1099 /** 1100 * CustomEvent fired before the Module is hidden 1101 * @event beforeHideEvent 1102 */ 1103 this.beforeHideEvent = this.createEvent(EVENT_TYPES.BEFORE_HIDE); 1104 this.beforeHideEvent.signature = SIGNATURE; 1105 1106 /** 1107 * CustomEvent fired after the Module is hidden 1108 * @event hideEvent 1109 */ 1110 this.hideEvent = this.createEvent(EVENT_TYPES.HIDE); 1111 this.hideEvent.signature = SIGNATURE; 1112 }, 1113 1114 /** 1115 * String identifying whether the current platform is windows or mac. This property 1116 * currently only identifies these 2 platforms, and returns false otherwise. 1117 * @property platform 1118 * @deprecated Use YAHOO.env.ua 1119 * @type {String|Boolean} 1120 */ 1121 platform: function () { 1122 var ua = navigator.userAgent.toLowerCase(); 1123 1124 if (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1) { 1125 return "windows"; 1126 } else if (ua.indexOf("macintosh") != -1) { 1127 return "mac"; 1128 } else { 1129 return false; 1130 } 1131 }(), 1132 1133 /** 1134 * String representing the user-agent of the browser 1135 * @deprecated Use YAHOO.env.ua 1136 * @property browser 1137 * @type {String|Boolean} 1138 */ 1139 browser: function () { 1140 var ua = navigator.userAgent.toLowerCase(); 1141 /* 1142 Check Opera first in case of spoof and check Safari before 1143 Gecko since Safari's user agent string includes "like Gecko" 1144 */ 1145 if (ua.indexOf('opera') != -1) { 1146 return 'opera'; 1147 } else if (ua.indexOf('msie 7') != -1) { 1148 return 'ie7'; 1149 } else if (ua.indexOf('msie') != -1) { 1150 return 'ie'; 1151 } else if (ua.indexOf('safari') != -1) { 1152 return 'safari'; 1153 } else if (ua.indexOf('gecko') != -1) { 1154 return 'gecko'; 1155 } else { 1156 return false; 1157 } 1158 }(), 1159 1160 /** 1161 * Boolean representing whether or not the current browsing context is 1162 * secure (https) 1163 * @property isSecure 1164 * @type Boolean 1165 */ 1166 isSecure: function () { 1167 if (window.location.href.toLowerCase().indexOf("https") === 0) { 1168 return true; 1169 } else { 1170 return false; 1171 } 1172 }(), 1173 1174 /** 1175 * Initializes the custom events for Module which are fired 1176 * automatically at appropriate times by the Module class. 1177 */ 1178 initDefaultConfig: function () { 1179 // Add properties // 1180 /** 1181 * Specifies whether the Module is visible on the page. 1182 * @config visible 1183 * @type Boolean 1184 * @default true 1185 */ 1186 this.cfg.addProperty(DEFAULT_CONFIG.VISIBLE.key, { 1187 handler: this.configVisible, 1188 value: DEFAULT_CONFIG.VISIBLE.value, 1189 validator: DEFAULT_CONFIG.VISIBLE.validator 1190 }); 1191 1192 /** 1193 * <p> 1194 * Object or array of objects representing the ContainerEffect 1195 * classes that are active for animating the container. 1196 * </p> 1197 * <p> 1198 * <strong>NOTE:</strong> Although this configuration 1199 * property is introduced at the Module level, an out of the box 1200 * implementation is not shipped for the Module class so setting 1201 * the proroperty on the Module class has no effect. The Overlay 1202 * class is the first class to provide out of the box ContainerEffect 1203 * support. 1204 * </p> 1205 * @config effect 1206 * @type Object 1207 * @default null 1208 */ 1209 this.cfg.addProperty(DEFAULT_CONFIG.EFFECT.key, { 1210 handler: this.configEffect, 1211 suppressEvent: DEFAULT_CONFIG.EFFECT.suppressEvent, 1212 supercedes: DEFAULT_CONFIG.EFFECT.supercedes 1213 }); 1214 1215 /** 1216 * Specifies whether to create a special proxy iframe to monitor 1217 * for user font resizing in the document 1218 * @config monitorresize 1219 * @type Boolean 1220 * @default true 1221 */ 1222 this.cfg.addProperty(DEFAULT_CONFIG.MONITOR_RESIZE.key, { 1223 handler: this.configMonitorResize, 1224 value: DEFAULT_CONFIG.MONITOR_RESIZE.value 1225 }); 1226 1227 /** 1228 * Specifies if the module should be rendered as the first child 1229 * of document.body or appended as the last child when render is called 1230 * with document.body as the "appendToNode". 1231 * <p> 1232 * Appending to the body while the DOM is still being constructed can 1233 * lead to Operation Aborted errors in IE hence this flag is set to 1234 * false by default. 1235 * </p> 1236 * 1237 * @config appendtodocumentbody 1238 * @type Boolean 1239 * @default false 1240 */ 1241 this.cfg.addProperty(DEFAULT_CONFIG.APPEND_TO_DOCUMENT_BODY.key, { 1242 value: DEFAULT_CONFIG.APPEND_TO_DOCUMENT_BODY.value 1243 }); 1244 }, 1245 1246 /** 1247 * The Module class's initialization method, which is executed for 1248 * Module and all of its subclasses. This method is automatically 1249 * called by the constructor, and sets up all DOM references for 1250 * pre-existing markup, and creates required markup if it is not 1251 * already present. 1252 * <p> 1253 * If the element passed in does not have an id, one will be generated 1254 * for it. 1255 * </p> 1256 * @method init 1257 * @param {String} el The element ID representing the Module <em>OR</em> 1258 * @param {HTMLElement} el The element representing the Module 1259 * @param {Object} userConfig The configuration Object literal 1260 * containing the configuration that should be set for this module. 1261 * See configuration documentation for more details. 1262 */ 1263 init: function (el, userConfig) { 1264 1265 var elId, child; 1266 1267 this.initEvents(); 1268 this.beforeInitEvent.fire(Module); 1269 1270 /** 1271 * The Module's Config object used for monitoring 1272 * configuration properties. 1273 * @property cfg 1274 * @type YAHOO.util.Config 1275 */ 1276 this.cfg = new Config(this); 1277 1278 if (this.isSecure) { 1279 this.imageRoot = Module.IMG_ROOT_SSL; 1280 } 1281 1282 if (typeof el == "string") { 1283 elId = el; 1284 el = document.getElementById(el); 1285 if (! el) { 1286 el = (createModuleTemplate()).cloneNode(false); 1287 el.id = elId; 1288 } 1289 } 1290 1291 this.id = Dom.generateId(el); 1292 this.element = el; 1293 1294 child = this.element.firstChild; 1295 1296 if (child) { 1297 var fndHd = false, fndBd = false, fndFt = false; 1298 do { 1299 // We're looking for elements 1300 if (1 == child.nodeType) { 1301 if (!fndHd && Dom.hasClass(child, Module.CSS_HEADER)) { 1302 this.header = child; 1303 fndHd = true; 1304 } else if (!fndBd && Dom.hasClass(child, Module.CSS_BODY)) { 1305 this.body = child; 1306 fndBd = true; 1307 } else if (!fndFt && Dom.hasClass(child, Module.CSS_FOOTER)){ 1308 this.footer = child; 1309 fndFt = true; 1310 } 1311 } 1312 } while ((child = child.nextSibling)); 1313 } 1314 1315 this.initDefaultConfig(); 1316 1317 Dom.addClass(this.element, Module.CSS_MODULE); 1318 1319 if (userConfig) { 1320 this.cfg.applyConfig(userConfig, true); 1321 } 1322 1323 /* 1324 Subscribe to the fireQueue() method of Config so that any 1325 queued configuration changes are excecuted upon render of 1326 the Module 1327 */ 1328 1329 if (!Config.alreadySubscribed(this.renderEvent, this.cfg.fireQueue, this.cfg)) { 1330 this.renderEvent.subscribe(this.cfg.fireQueue, this.cfg, true); 1331 } 1332 1333 this.initEvent.fire(Module); 1334 }, 1335 1336 /** 1337 * Initialize an empty IFRAME that is placed out of the visible area 1338 * that can be used to detect text resize. 1339 * @method initResizeMonitor 1340 */ 1341 initResizeMonitor: function () { 1342 1343 var isGeckoWin = (UA.gecko && this.platform == "windows"); 1344 if (isGeckoWin) { 1345 // Help prevent spinning loading icon which 1346 // started with FireFox 2.0.0.8/Win 1347 var self = this; 1348 setTimeout(function(){self._initResizeMonitor();}, 0); 1349 } else { 1350 this._initResizeMonitor(); 1351 } 1352 }, 1353 1354 /** 1355 * Create and initialize the text resize monitoring iframe. 1356 * 1357 * @protected 1358 * @method _initResizeMonitor 1359 */ 1360 _initResizeMonitor : function() { 1361 1362 var oDoc, 1363 oIFrame, 1364 sHTML; 1365 1366 function fireTextResize() { 1367 Module.textResizeEvent.fire(); 1368 } 1369 1370 if (!UA.opera) { 1371 oIFrame = Dom.get("_yuiResizeMonitor"); 1372 1373 var supportsCWResize = this._supportsCWResize(); 1374 1375 if (!oIFrame) { 1376 oIFrame = document.createElement("iframe"); 1377 1378 if (this.isSecure && Module.RESIZE_MONITOR_SECURE_URL && UA.ie) { 1379 oIFrame.src = Module.RESIZE_MONITOR_SECURE_URL; 1380 } 1381 1382 if (!supportsCWResize) { 1383 // Can't monitor on contentWindow, so fire from inside iframe 1384 sHTML = ["<html><head><script ", 1385 "type=\"text/javascript\">", 1386 "window.onresize=function(){window.parent.", 1387 "YAHOO.widget.Module.textResizeEvent.", 1388 "fire();};<", 1389 "\/script></head>", 1390 "<body></body></html>"].join(''); 1391 1392 oIFrame.src = "data:text/html;charset=utf-8," + encodeURIComponent(sHTML); 1393 } 1394 1395 oIFrame.id = "_yuiResizeMonitor"; 1396 oIFrame.title = "Text Resize Monitor"; 1397 oIFrame.tabIndex = -1; 1398 oIFrame.setAttribute("role", "presentation"); 1399 1400 /* 1401 Need to set "position" property before inserting the 1402 iframe into the document or Safari's status bar will 1403 forever indicate the iframe is loading 1404 (See YUILibrary bug #1723064) 1405 */ 1406 oIFrame.style.position = "absolute"; 1407 oIFrame.style.visibility = "hidden"; 1408 1409 var db = document.body, 1410 fc = db.firstChild; 1411 if (fc) { 1412 db.insertBefore(oIFrame, fc); 1413 } else { 1414 db.appendChild(oIFrame); 1415 } 1416 1417 // Setting the background color fixes an issue with IE6/IE7, where 1418 // elements in the DOM, with -ve margin-top which positioned them 1419 // offscreen (so they would be overlapped by the iframe and its -ve top 1420 // setting), would have their -ve margin-top ignored, when the iframe 1421 // was added. 1422 oIFrame.style.backgroundColor = "transparent"; 1423 1424 oIFrame.style.borderWidth = "0"; 1425 oIFrame.style.width = "2em"; 1426 oIFrame.style.height = "2em"; 1427 oIFrame.style.left = "0"; 1428 oIFrame.style.top = (-1 * (oIFrame.offsetHeight + Module.RESIZE_MONITOR_BUFFER)) + "px"; 1429 oIFrame.style.visibility = "visible"; 1430 1431 /* 1432 Don't open/close the document for Gecko like we used to, since it 1433 leads to duplicate cookies. (See YUILibrary bug #1721755) 1434 */ 1435 if (UA.webkit) { 1436 oDoc = oIFrame.contentWindow.document; 1437 oDoc.open(); 1438 oDoc.close(); 1439 } 1440 } 1441 1442 if (oIFrame && oIFrame.contentWindow) { 1443 Module.textResizeEvent.subscribe(this.onDomResize, this, true); 1444 1445 if (!Module.textResizeInitialized) { 1446 if (supportsCWResize) { 1447 if (!Event.on(oIFrame.contentWindow, "resize", fireTextResize)) { 1448 /* 1449 This will fail in IE if document.domain has 1450 changed, so we must change the listener to 1451 use the oIFrame element instead 1452 */ 1453 Event.on(oIFrame, "resize", fireTextResize); 1454 } 1455 } 1456 Module.textResizeInitialized = true; 1457 } 1458 this.resizeMonitor = oIFrame; 1459 } 1460 } 1461 }, 1462 1463 /** 1464 * Text resize monitor helper method. 1465 * Determines if the browser supports resize events on iframe content windows. 1466 * 1467 * @private 1468 * @method _supportsCWResize 1469 */ 1470 _supportsCWResize : function() { 1471 /* 1472 Gecko 1.8.0 (FF1.5), 1.8.1.0-5 (FF2) won't fire resize on contentWindow. 1473 Gecko 1.8.1.6+ (FF2.0.0.6+) and all other browsers will fire resize on contentWindow. 1474 1475 We don't want to start sniffing for patch versions, so fire textResize the same 1476 way on all FF2 flavors 1477 */ 1478 var bSupported = true; 1479 if (UA.gecko && UA.gecko <= 1.8) { 1480 bSupported = false; 1481 } 1482 return bSupported; 1483 }, 1484 1485 /** 1486 * Event handler fired when the resize monitor element is resized. 1487 * @method onDomResize 1488 * @param {DOMEvent} e The DOM resize event 1489 * @param {Object} obj The scope object passed to the handler 1490 */ 1491 onDomResize: function (e, obj) { 1492 1493 var nTop = -1 * (this.resizeMonitor.offsetHeight + Module.RESIZE_MONITOR_BUFFER); 1494 1495 this.resizeMonitor.style.top = nTop + "px"; 1496 this.resizeMonitor.style.left = "0"; 1497 }, 1498 1499 /** 1500 * Sets the Module's header content to the markup specified, or appends 1501 * the passed element to the header. 1502 * 1503 * If no header is present, one will 1504 * be automatically created. An empty string can be passed to the method 1505 * to clear the contents of the header. 1506 * 1507 * @method setHeader 1508 * @param {HTML} headerContent The markup used to set the header content. 1509 * As a convenience, non HTMLElement objects can also be passed into 1510 * the method, and will be treated as strings, with the header innerHTML 1511 * set to their default toString implementations. 1512 * 1513 * <p>NOTE: Markup passed into this method is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</p> 1514 * 1515 * <em>OR</em> 1516 * @param {HTMLElement} headerContent The HTMLElement to append to 1517 * <em>OR</em> 1518 * @param {DocumentFragment} headerContent The document fragment 1519 * containing elements which are to be added to the header 1520 */ 1521 setHeader: function (headerContent) { 1522 var oHeader = this.header || (this.header = createHeader()); 1523 1524 if (headerContent.nodeName) { 1525 oHeader.innerHTML = ""; 1526 oHeader.appendChild(headerContent); 1527 } else { 1528 oHeader.innerHTML = headerContent; 1529 } 1530 1531 if (this._rendered) { 1532 this._renderHeader(); 1533 } 1534 1535 this.changeHeaderEvent.fire(headerContent); 1536 this.changeContentEvent.fire(); 1537 1538 }, 1539 1540 /** 1541 * Appends the passed element to the header. If no header is present, 1542 * one will be automatically created. 1543 * @method appendToHeader 1544 * @param {HTMLElement | DocumentFragment} element The element to 1545 * append to the header. In the case of a document fragment, the 1546 * children of the fragment will be appended to the header. 1547 */ 1548 appendToHeader: function (element) { 1549 var oHeader = this.header || (this.header = createHeader()); 1550 1551 oHeader.appendChild(element); 1552 1553 this.changeHeaderEvent.fire(element); 1554 this.changeContentEvent.fire(); 1555 1556 }, 1557 1558 /** 1559 * Sets the Module's body content to the HTML specified. 1560 * 1561 * If no body is present, one will be automatically created. 1562 * 1563 * An empty string can be passed to the method to clear the contents of the body. 1564 * @method setBody 1565 * @param {HTML} bodyContent The HTML used to set the body content 1566 * As a convenience, non HTMLElement objects can also be passed into 1567 * the method, and will be treated as strings, with the body innerHTML 1568 * set to their default toString implementations. 1569 * 1570 * <p>NOTE: Markup passed into this method is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</p> 1571 * 1572 * <em>OR</em> 1573 * @param {HTMLElement} bodyContent The HTMLElement to add as the first and only 1574 * child of the body element. 1575 * <em>OR</em> 1576 * @param {DocumentFragment} bodyContent The document fragment 1577 * containing elements which are to be added to the body 1578 */ 1579 setBody: function (bodyContent) { 1580 var oBody = this.body || (this.body = createBody()); 1581 1582 if (bodyContent.nodeName) { 1583 oBody.innerHTML = ""; 1584 oBody.appendChild(bodyContent); 1585 } else { 1586 oBody.innerHTML = bodyContent; 1587 } 1588 1589 if (this._rendered) { 1590 this._renderBody(); 1591 } 1592 1593 this.changeBodyEvent.fire(bodyContent); 1594 this.changeContentEvent.fire(); 1595 }, 1596 1597 /** 1598 * Appends the passed element to the body. If no body is present, one 1599 * will be automatically created. 1600 * @method appendToBody 1601 * @param {HTMLElement | DocumentFragment} element The element to 1602 * append to the body. In the case of a document fragment, the 1603 * children of the fragment will be appended to the body. 1604 * 1605 */ 1606 appendToBody: function (element) { 1607 var oBody = this.body || (this.body = createBody()); 1608 1609 oBody.appendChild(element); 1610 1611 this.changeBodyEvent.fire(element); 1612 this.changeContentEvent.fire(); 1613 1614 }, 1615 1616 /** 1617 * Sets the Module's footer content to the HTML specified, or appends 1618 * the passed element to the footer. If no footer is present, one will 1619 * be automatically created. An empty string can be passed to the method 1620 * to clear the contents of the footer. 1621 * @method setFooter 1622 * @param {HTML} footerContent The HTML used to set the footer 1623 * As a convenience, non HTMLElement objects can also be passed into 1624 * the method, and will be treated as strings, with the footer innerHTML 1625 * set to their default toString implementations. 1626 * 1627 * <p>NOTE: Markup passed into this method is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</p> 1628 * 1629 * <em>OR</em> 1630 * @param {HTMLElement} footerContent The HTMLElement to append to 1631 * the footer 1632 * <em>OR</em> 1633 * @param {DocumentFragment} footerContent The document fragment containing 1634 * elements which are to be added to the footer 1635 */ 1636 setFooter: function (footerContent) { 1637 1638 var oFooter = this.footer || (this.footer = createFooter()); 1639 1640 if (footerContent.nodeName) { 1641 oFooter.innerHTML = ""; 1642 oFooter.appendChild(footerContent); 1643 } else { 1644 oFooter.innerHTML = footerContent; 1645 } 1646 1647 if (this._rendered) { 1648 this._renderFooter(); 1649 } 1650 1651 this.changeFooterEvent.fire(footerContent); 1652 this.changeContentEvent.fire(); 1653 }, 1654 1655 /** 1656 * Appends the passed element to the footer. If no footer is present, 1657 * one will be automatically created. 1658 * @method appendToFooter 1659 * @param {HTMLElement | DocumentFragment} element The element to 1660 * append to the footer. In the case of a document fragment, the 1661 * children of the fragment will be appended to the footer 1662 */ 1663 appendToFooter: function (element) { 1664 1665 var oFooter = this.footer || (this.footer = createFooter()); 1666 1667 oFooter.appendChild(element); 1668 1669 this.changeFooterEvent.fire(element); 1670 this.changeContentEvent.fire(); 1671 1672 }, 1673 1674 /** 1675 * Renders the Module by inserting the elements that are not already 1676 * in the main Module into their correct places. Optionally appends 1677 * the Module to the specified node prior to the render's execution. 1678 * <p> 1679 * For Modules without existing markup, the appendToNode argument 1680 * is REQUIRED. If this argument is ommitted and the current element is 1681 * not present in the document, the function will return false, 1682 * indicating that the render was a failure. 1683 * </p> 1684 * <p> 1685 * NOTE: As of 2.3.1, if the appendToNode is the document's body element 1686 * then the module is rendered as the first child of the body element, 1687 * and not appended to it, to avoid Operation Aborted errors in IE when 1688 * rendering the module before window's load event is fired. You can 1689 * use the appendtodocumentbody configuration property to change this 1690 * to append to document.body if required. 1691 * </p> 1692 * @method render 1693 * @param {String} appendToNode The element id to which the Module 1694 * should be appended to prior to rendering <em>OR</em> 1695 * @param {HTMLElement} appendToNode The element to which the Module 1696 * should be appended to prior to rendering 1697 * @param {HTMLElement} moduleElement OPTIONAL. The element that 1698 * represents the actual Standard Module container. 1699 * @return {Boolean} Success or failure of the render 1700 */ 1701 render: function (appendToNode, moduleElement) { 1702 1703 var me = this; 1704 1705 function appendTo(parentNode) { 1706 if (typeof parentNode == "string") { 1707 parentNode = document.getElementById(parentNode); 1708 } 1709 1710 if (parentNode) { 1711 me._addToParent(parentNode, me.element); 1712 me.appendEvent.fire(); 1713 } 1714 } 1715 1716 this.beforeRenderEvent.fire(); 1717 1718 if (! moduleElement) { 1719 moduleElement = this.element; 1720 } 1721 1722 if (appendToNode) { 1723 appendTo(appendToNode); 1724 } else { 1725 // No node was passed in. If the element is not already in the Dom, this fails 1726 if (! Dom.inDocument(this.element)) { 1727 YAHOO.log("Render failed. Must specify appendTo node if " + " Module isn't already in the DOM.", "error"); 1728 return false; 1729 } 1730 } 1731 1732 this._renderHeader(moduleElement); 1733 this._renderBody(moduleElement); 1734 this._renderFooter(moduleElement); 1735 1736 this._rendered = true; 1737 1738 this.renderEvent.fire(); 1739 return true; 1740 }, 1741 1742 /** 1743 * Renders the currently set header into it's proper position under the 1744 * module element. If the module element is not provided, "this.element" 1745 * is used. 1746 * 1747 * @method _renderHeader 1748 * @protected 1749 * @param {HTMLElement} moduleElement Optional. A reference to the module element 1750 */ 1751 _renderHeader: function(moduleElement){ 1752 moduleElement = moduleElement || this.element; 1753 1754 // Need to get everything into the DOM if it isn't already 1755 if (this.header && !Dom.inDocument(this.header)) { 1756 // There is a header, but it's not in the DOM yet. Need to add it. 1757 var firstChild = moduleElement.firstChild; 1758 if (firstChild) { 1759 moduleElement.insertBefore(this.header, firstChild); 1760 } else { 1761 moduleElement.appendChild(this.header); 1762 } 1763 } 1764 }, 1765 1766 /** 1767 * Renders the currently set body into it's proper position under the 1768 * module element. If the module element is not provided, "this.element" 1769 * is used. 1770 * 1771 * @method _renderBody 1772 * @protected 1773 * @param {HTMLElement} moduleElement Optional. A reference to the module element. 1774 */ 1775 _renderBody: function(moduleElement){ 1776 moduleElement = moduleElement || this.element; 1777 1778 if (this.body && !Dom.inDocument(this.body)) { 1779 // There is a body, but it's not in the DOM yet. Need to add it. 1780 if (this.footer && Dom.isAncestor(moduleElement, this.footer)) { 1781 moduleElement.insertBefore(this.body, this.footer); 1782 } else { 1783 moduleElement.appendChild(this.body); 1784 } 1785 } 1786 }, 1787 1788 /** 1789 * Renders the currently set footer into it's proper position under the 1790 * module element. If the module element is not provided, "this.element" 1791 * is used. 1792 * 1793 * @method _renderFooter 1794 * @protected 1795 * @param {HTMLElement} moduleElement Optional. A reference to the module element 1796 */ 1797 _renderFooter: function(moduleElement){ 1798 moduleElement = moduleElement || this.element; 1799 1800 if (this.footer && !Dom.inDocument(this.footer)) { 1801 // There is a footer, but it's not in the DOM yet. Need to add it. 1802 moduleElement.appendChild(this.footer); 1803 } 1804 }, 1805 1806 /** 1807 * Removes the Module element from the DOM, sets all child elements to null, and purges the bounding element of event listeners. 1808 * @method destroy 1809 * @param {boolean} shallowPurge If true, only the parent element's DOM event listeners are purged. If false, or not provided, all children are also purged of DOM event listeners. 1810 * NOTE: The flag is a "shallowPurge" flag, as opposed to what may be a more intuitive "purgeChildren" flag to maintain backwards compatibility with behavior prior to 2.9.0. 1811 */ 1812 destroy: function (shallowPurge) { 1813 1814 var parent, 1815 purgeChildren = !(shallowPurge); 1816 1817 if (this.element) { 1818 Event.purgeElement(this.element, purgeChildren); 1819 parent = this.element.parentNode; 1820 } 1821 1822 if (parent) { 1823 parent.removeChild(this.element); 1824 } 1825 1826 this.element = null; 1827 this.header = null; 1828 this.body = null; 1829 this.footer = null; 1830 1831 Module.textResizeEvent.unsubscribe(this.onDomResize, this); 1832 1833 this.cfg.destroy(); 1834 this.cfg = null; 1835 1836 this.destroyEvent.fire(); 1837 }, 1838 1839 /** 1840 * Shows the Module element by setting the visible configuration 1841 * property to true. Also fires two events: beforeShowEvent prior to 1842 * the visibility change, and showEvent after. 1843 * @method show 1844 */ 1845 show: function () { 1846 this.cfg.setProperty("visible", true); 1847 }, 1848 1849 /** 1850 * Hides the Module element by setting the visible configuration 1851 * property to false. Also fires two events: beforeHideEvent prior to 1852 * the visibility change, and hideEvent after. 1853 * @method hide 1854 */ 1855 hide: function () { 1856 this.cfg.setProperty("visible", false); 1857 }, 1858 1859 // BUILT-IN EVENT HANDLERS FOR MODULE // 1860 /** 1861 * Default event handler for changing the visibility property of a 1862 * Module. By default, this is achieved by switching the "display" style 1863 * between "block" and "none". 1864 * This method is responsible for firing showEvent and hideEvent. 1865 * @param {String} type The CustomEvent type (usually the property name) 1866 * @param {Object[]} args The CustomEvent arguments. For configuration 1867 * handlers, args[0] will equal the newly applied value for the property. 1868 * @param {Object} obj The scope object. For configuration handlers, 1869 * this will usually equal the owner. 1870 * @method configVisible 1871 */ 1872 configVisible: function (type, args, obj) { 1873 var visible = args[0]; 1874 if (visible) { 1875 if(this.beforeShowEvent.fire()) { 1876 Dom.setStyle(this.element, "display", "block"); 1877 this.showEvent.fire(); 1878 } 1879 } else { 1880 if (this.beforeHideEvent.fire()) { 1881 Dom.setStyle(this.element, "display", "none"); 1882 this.hideEvent.fire(); 1883 } 1884 } 1885 }, 1886 1887 /** 1888 * Default event handler for the "effect" configuration property 1889 * @param {String} type The CustomEvent type (usually the property name) 1890 * @param {Object[]} args The CustomEvent arguments. For configuration 1891 * handlers, args[0] will equal the newly applied value for the property. 1892 * @param {Object} obj The scope object. For configuration handlers, 1893 * this will usually equal the owner. 1894 * @method configEffect 1895 */ 1896 configEffect: function (type, args, obj) { 1897 this._cachedEffects = (this.cacheEffects) ? this._createEffects(args[0]) : null; 1898 }, 1899 1900 /** 1901 * If true, ContainerEffects (and Anim instances) are cached when "effect" is set, and reused. 1902 * If false, new instances are created each time the container is hidden or shown, as was the 1903 * behavior prior to 2.9.0. 1904 * 1905 * @property cacheEffects 1906 * @since 2.9.0 1907 * @default true 1908 * @type boolean 1909 */ 1910 cacheEffects : true, 1911 1912 /** 1913 * Creates an array of ContainerEffect instances from the provided configs 1914 * 1915 * @method _createEffects 1916 * @param {Array|Object} effectCfg An effect configuration or array of effect configurations 1917 * @return {Array} An array of ContainerEffect instances. 1918 * @protected 1919 */ 1920 _createEffects: function(effectCfg) { 1921 var effectInstances = null, 1922 n, 1923 i, 1924 eff; 1925 1926 if (effectCfg) { 1927 if (effectCfg instanceof Array) { 1928 effectInstances = []; 1929 n = effectCfg.length; 1930 for (i = 0; i < n; i++) { 1931 eff = effectCfg[i]; 1932 if (eff.effect) { 1933 effectInstances[effectInstances.length] = eff.effect(this, eff.duration); 1934 } 1935 } 1936 } else if (effectCfg.effect) { 1937 effectInstances = [effectCfg.effect(this, effectCfg.duration)]; 1938 } 1939 } 1940 1941 return effectInstances; 1942 }, 1943 1944 /** 1945 * Default event handler for the "monitorresize" configuration property 1946 * @param {String} type The CustomEvent type (usually the property name) 1947 * @param {Object[]} args The CustomEvent arguments. For configuration 1948 * handlers, args[0] will equal the newly applied value for the property. 1949 * @param {Object} obj The scope object. For configuration handlers, 1950 * this will usually equal the owner. 1951 * @method configMonitorResize 1952 */ 1953 configMonitorResize: function (type, args, obj) { 1954 var monitor = args[0]; 1955 if (monitor) { 1956 this.initResizeMonitor(); 1957 } else { 1958 Module.textResizeEvent.unsubscribe(this.onDomResize, this, true); 1959 this.resizeMonitor = null; 1960 } 1961 }, 1962 1963 /** 1964 * This method is a protected helper, used when constructing the DOM structure for the module 1965 * to account for situations which may cause Operation Aborted errors in IE. It should not 1966 * be used for general DOM construction. 1967 * <p> 1968 * If the parentNode is not document.body, the element is appended as the last element. 1969 * </p> 1970 * <p> 1971 * If the parentNode is document.body the element is added as the first child to help 1972 * prevent Operation Aborted errors in IE. 1973 * </p> 1974 * 1975 * @param {parentNode} The HTML element to which the element will be added 1976 * @param {element} The HTML element to be added to parentNode's children 1977 * @method _addToParent 1978 * @protected 1979 */ 1980 _addToParent: function(parentNode, element) { 1981 if (!this.cfg.getProperty("appendtodocumentbody") && parentNode === document.body && parentNode.firstChild) { 1982 parentNode.insertBefore(element, parentNode.firstChild); 1983 } else { 1984 parentNode.appendChild(element); 1985 } 1986 }, 1987 1988 /** 1989 * Returns a String representation of the Object. 1990 * @method toString 1991 * @return {String} The string representation of the Module 1992 */ 1993 toString: function () { 1994 return "Module " + this.id; 1995 } 1996 }; 1997 1998 YAHOO.lang.augmentProto(Module, YAHOO.util.EventProvider); 1999 2000 }()); 2001 (function () { 2002 2003 /** 2004 * Overlay is a Module that is absolutely positioned above the page flow. It 2005 * has convenience methods for positioning and sizing, as well as options for 2006 * controlling zIndex and constraining the Overlay's position to the current 2007 * visible viewport. Overlay also contains a dynamicly generated IFRAME which 2008 * is placed beneath it for Internet Explorer 6 and 5.x so that it will be 2009 * properly rendered above SELECT elements. 2010 * @namespace YAHOO.widget 2011 * @class Overlay 2012 * @extends YAHOO.widget.Module 2013 * @param {String} el The element ID representing the Overlay <em>OR</em> 2014 * @param {HTMLElement} el The element representing the Overlay 2015 * @param {Object} userConfig The configuration object literal containing 2016 * the configuration that should be set for this Overlay. See configuration 2017 * documentation for more details. 2018 * @constructor 2019 */ 2020 YAHOO.widget.Overlay = function (el, userConfig) { 2021 YAHOO.widget.Overlay.superclass.constructor.call(this, el, userConfig); 2022 }; 2023 2024 var Lang = YAHOO.lang, 2025 CustomEvent = YAHOO.util.CustomEvent, 2026 Module = YAHOO.widget.Module, 2027 Event = YAHOO.util.Event, 2028 Dom = YAHOO.util.Dom, 2029 Config = YAHOO.util.Config, 2030 UA = YAHOO.env.ua, 2031 Overlay = YAHOO.widget.Overlay, 2032 2033 _SUBSCRIBE = "subscribe", 2034 _UNSUBSCRIBE = "unsubscribe", 2035 _CONTAINED = "contained", 2036 2037 m_oIFrameTemplate, 2038 2039 /** 2040 * Constant representing the name of the Overlay's events 2041 * @property EVENT_TYPES 2042 * @private 2043 * @final 2044 * @type Object 2045 */ 2046 EVENT_TYPES = { 2047 "BEFORE_MOVE": "beforeMove", 2048 "MOVE": "move" 2049 }, 2050 2051 /** 2052 * Constant representing the Overlay's configuration properties 2053 * @property DEFAULT_CONFIG 2054 * @private 2055 * @final 2056 * @type Object 2057 */ 2058 DEFAULT_CONFIG = { 2059 2060 "X": { 2061 key: "x", 2062 validator: Lang.isNumber, 2063 suppressEvent: true, 2064 supercedes: ["iframe"] 2065 }, 2066 2067 "Y": { 2068 key: "y", 2069 validator: Lang.isNumber, 2070 suppressEvent: true, 2071 supercedes: ["iframe"] 2072 }, 2073 2074 "XY": { 2075 key: "xy", 2076 suppressEvent: true, 2077 supercedes: ["iframe"] 2078 }, 2079 2080 "CONTEXT": { 2081 key: "context", 2082 suppressEvent: true, 2083 supercedes: ["iframe"] 2084 }, 2085 2086 "FIXED_CENTER": { 2087 key: "fixedcenter", 2088 value: false, 2089 supercedes: ["iframe", "visible"] 2090 }, 2091 2092 "WIDTH": { 2093 key: "width", 2094 suppressEvent: true, 2095 supercedes: ["context", "fixedcenter", "iframe"] 2096 }, 2097 2098 "HEIGHT": { 2099 key: "height", 2100 suppressEvent: true, 2101 supercedes: ["context", "fixedcenter", "iframe"] 2102 }, 2103 2104 "AUTO_FILL_HEIGHT" : { 2105 key: "autofillheight", 2106 supercedes: ["height"], 2107 value:"body" 2108 }, 2109 2110 "ZINDEX": { 2111 key: "zindex", 2112 value: null 2113 }, 2114 2115 "CONSTRAIN_TO_VIEWPORT": { 2116 key: "constraintoviewport", 2117 value: false, 2118 validator: Lang.isBoolean, 2119 supercedes: ["iframe", "x", "y", "xy"] 2120 }, 2121 2122 "IFRAME": { 2123 key: "iframe", 2124 value: (UA.ie == 6 ? true : false), 2125 validator: Lang.isBoolean, 2126 supercedes: ["zindex"] 2127 }, 2128 2129 "PREVENT_CONTEXT_OVERLAP": { 2130 key: "preventcontextoverlap", 2131 value: false, 2132 validator: Lang.isBoolean, 2133 supercedes: ["constraintoviewport"] 2134 } 2135 2136 }; 2137 2138 /** 2139 * The URL that will be placed in the iframe 2140 * @property YAHOO.widget.Overlay.IFRAME_SRC 2141 * @static 2142 * @final 2143 * @type String 2144 */ 2145 Overlay.IFRAME_SRC = "javascript:false;"; 2146 2147 /** 2148 * Number representing how much the iframe shim should be offset from each 2149 * side of an Overlay instance, in pixels. 2150 * @property YAHOO.widget.Overlay.IFRAME_SRC 2151 * @default 3 2152 * @static 2153 * @final 2154 * @type Number 2155 */ 2156 Overlay.IFRAME_OFFSET = 3; 2157 2158 /** 2159 * Number representing the minimum distance an Overlay instance should be 2160 * positioned relative to the boundaries of the browser's viewport, in pixels. 2161 * @property YAHOO.widget.Overlay.VIEWPORT_OFFSET 2162 * @default 10 2163 * @static 2164 * @final 2165 * @type Number 2166 */ 2167 Overlay.VIEWPORT_OFFSET = 10; 2168 2169 /** 2170 * Constant representing the top left corner of an element, used for 2171 * configuring the context element alignment 2172 * @property YAHOO.widget.Overlay.TOP_LEFT 2173 * @static 2174 * @final 2175 * @type String 2176 */ 2177 Overlay.TOP_LEFT = "tl"; 2178 2179 /** 2180 * Constant representing the top right corner of an element, used for 2181 * configuring the context element alignment 2182 * @property YAHOO.widget.Overlay.TOP_RIGHT 2183 * @static 2184 * @final 2185 * @type String 2186 */ 2187 Overlay.TOP_RIGHT = "tr"; 2188 2189 /** 2190 * Constant representing the top bottom left corner of an element, used for 2191 * configuring the context element alignment 2192 * @property YAHOO.widget.Overlay.BOTTOM_LEFT 2193 * @static 2194 * @final 2195 * @type String 2196 */ 2197 Overlay.BOTTOM_LEFT = "bl"; 2198 2199 /** 2200 * Constant representing the bottom right corner of an element, used for 2201 * configuring the context element alignment 2202 * @property YAHOO.widget.Overlay.BOTTOM_RIGHT 2203 * @static 2204 * @final 2205 * @type String 2206 */ 2207 Overlay.BOTTOM_RIGHT = "br"; 2208 2209 Overlay.PREVENT_OVERLAP_X = { 2210 "tltr": true, 2211 "blbr": true, 2212 "brbl": true, 2213 "trtl": true 2214 }; 2215 2216 Overlay.PREVENT_OVERLAP_Y = { 2217 "trbr": true, 2218 "tlbl": true, 2219 "bltl": true, 2220 "brtr": true 2221 }; 2222 2223 /** 2224 * Constant representing the default CSS class used for an Overlay 2225 * @property YAHOO.widget.Overlay.CSS_OVERLAY 2226 * @static 2227 * @final 2228 * @type String 2229 */ 2230 Overlay.CSS_OVERLAY = "yui-overlay"; 2231 2232 /** 2233 * Constant representing the default hidden CSS class used for an Overlay. This class is 2234 * applied to the overlay's outer DIV whenever it's hidden. 2235 * 2236 * @property YAHOO.widget.Overlay.CSS_HIDDEN 2237 * @static 2238 * @final 2239 * @type String 2240 */ 2241 Overlay.CSS_HIDDEN = "yui-overlay-hidden"; 2242 2243 /** 2244 * Constant representing the default CSS class used for an Overlay iframe shim. 2245 * 2246 * @property YAHOO.widget.Overlay.CSS_IFRAME 2247 * @static 2248 * @final 2249 * @type String 2250 */ 2251 Overlay.CSS_IFRAME = "yui-overlay-iframe"; 2252 2253 /** 2254 * Constant representing the names of the standard module elements 2255 * used in the overlay. 2256 * @property YAHOO.widget.Overlay.STD_MOD_RE 2257 * @static 2258 * @final 2259 * @type RegExp 2260 */ 2261 Overlay.STD_MOD_RE = /^\s*?(body|footer|header)\s*?$/i; 2262 2263 /** 2264 * A singleton CustomEvent used for reacting to the DOM event for 2265 * window scroll 2266 * @event YAHOO.widget.Overlay.windowScrollEvent 2267 */ 2268 Overlay.windowScrollEvent = new CustomEvent("windowScroll"); 2269 2270 /** 2271 * A singleton CustomEvent used for reacting to the DOM event for 2272 * window resize 2273 * @event YAHOO.widget.Overlay.windowResizeEvent 2274 */ 2275 Overlay.windowResizeEvent = new CustomEvent("windowResize"); 2276 2277 /** 2278 * The DOM event handler used to fire the CustomEvent for window scroll 2279 * @method YAHOO.widget.Overlay.windowScrollHandler 2280 * @static 2281 * @param {DOMEvent} e The DOM scroll event 2282 */ 2283 Overlay.windowScrollHandler = function (e) { 2284 var t = Event.getTarget(e); 2285 2286 // - Webkit (Safari 2/3) and Opera 9.2x bubble scroll events from elements to window 2287 // - FF2/3 and IE6/7, Opera 9.5x don't bubble scroll events from elements to window 2288 // - IE doesn't recognize scroll registered on the document. 2289 // 2290 // Also, when document view is scrolled, IE doesn't provide a target, 2291 // rest of the browsers set target to window.document, apart from opera 2292 // which sets target to window. 2293 if (!t || t === window || t === window.document) { 2294 if (UA.ie) { 2295 2296 if (! window.scrollEnd) { 2297 window.scrollEnd = -1; 2298 } 2299 2300 clearTimeout(window.scrollEnd); 2301 2302 window.scrollEnd = setTimeout(function () { 2303 Overlay.windowScrollEvent.fire(); 2304 }, 1); 2305 2306 } else { 2307 Overlay.windowScrollEvent.fire(); 2308 } 2309 } 2310 }; 2311 2312 /** 2313 * The DOM event handler used to fire the CustomEvent for window resize 2314 * @method YAHOO.widget.Overlay.windowResizeHandler 2315 * @static 2316 * @param {DOMEvent} e The DOM resize event 2317 */ 2318 Overlay.windowResizeHandler = function (e) { 2319 2320 if (UA.ie) { 2321 if (! window.resizeEnd) { 2322 window.resizeEnd = -1; 2323 } 2324 2325 clearTimeout(window.resizeEnd); 2326 2327 window.resizeEnd = setTimeout(function () { 2328 Overlay.windowResizeEvent.fire(); 2329 }, 100); 2330 } else { 2331 Overlay.windowResizeEvent.fire(); 2332 } 2333 }; 2334 2335 /** 2336 * A boolean that indicated whether the window resize and scroll events have 2337 * already been subscribed to. 2338 * @property YAHOO.widget.Overlay._initialized 2339 * @private 2340 * @type Boolean 2341 */ 2342 Overlay._initialized = null; 2343 2344 if (Overlay._initialized === null) { 2345 Event.on(window, "scroll", Overlay.windowScrollHandler); 2346 Event.on(window, "resize", Overlay.windowResizeHandler); 2347 Overlay._initialized = true; 2348 } 2349 2350 /** 2351 * Internal map of special event types, which are provided 2352 * by the instance. It maps the event type to the custom event 2353 * instance. Contains entries for the "windowScroll", "windowResize" and 2354 * "textResize" static container events. 2355 * 2356 * @property YAHOO.widget.Overlay._TRIGGER_MAP 2357 * @type Object 2358 * @static 2359 * @private 2360 */ 2361 Overlay._TRIGGER_MAP = { 2362 "windowScroll" : Overlay.windowScrollEvent, 2363 "windowResize" : Overlay.windowResizeEvent, 2364 "textResize" : Module.textResizeEvent 2365 }; 2366 2367 YAHOO.extend(Overlay, Module, { 2368 2369 /** 2370 * <p> 2371 * Array of default event types which will trigger 2372 * context alignment for the Overlay class. 2373 * </p> 2374 * <p>The array is empty by default for Overlay, 2375 * but maybe populated in future releases, so classes extending 2376 * Overlay which need to define their own set of CONTEXT_TRIGGERS 2377 * should concatenate their super class's prototype.CONTEXT_TRIGGERS 2378 * value with their own array of values. 2379 * </p> 2380 * <p> 2381 * E.g.: 2382 * <code>CustomOverlay.prototype.CONTEXT_TRIGGERS = YAHOO.widget.Overlay.prototype.CONTEXT_TRIGGERS.concat(["windowScroll"]);</code> 2383 * </p> 2384 * 2385 * @property CONTEXT_TRIGGERS 2386 * @type Array 2387 * @final 2388 */ 2389 CONTEXT_TRIGGERS : [], 2390 2391 /** 2392 * The Overlay initialization method, which is executed for Overlay and 2393 * all of its subclasses. This method is automatically called by the 2394 * constructor, and sets up all DOM references for pre-existing markup, 2395 * and creates required markup if it is not already present. 2396 * @method init 2397 * @param {String} el The element ID representing the Overlay <em>OR</em> 2398 * @param {HTMLElement} el The element representing the Overlay 2399 * @param {Object} userConfig The configuration object literal 2400 * containing the configuration that should be set for this Overlay. 2401 * See configuration documentation for more details. 2402 */ 2403 init: function (el, userConfig) { 2404 2405 /* 2406 Note that we don't pass the user config in here yet because we 2407 only want it executed once, at the lowest subclass level 2408 */ 2409 2410 Overlay.superclass.init.call(this, el/*, userConfig*/); 2411 2412 this.beforeInitEvent.fire(Overlay); 2413 2414 Dom.addClass(this.element, Overlay.CSS_OVERLAY); 2415 2416 if (userConfig) { 2417 this.cfg.applyConfig(userConfig, true); 2418 } 2419 2420 if (this.platform == "mac" && UA.gecko) { 2421 2422 if (! Config.alreadySubscribed(this.showEvent, 2423 this.showMacGeckoScrollbars, this)) { 2424 2425 this.showEvent.subscribe(this.showMacGeckoScrollbars, 2426 this, true); 2427 2428 } 2429 2430 if (! Config.alreadySubscribed(this.hideEvent, 2431 this.hideMacGeckoScrollbars, this)) { 2432 2433 this.hideEvent.subscribe(this.hideMacGeckoScrollbars, 2434 this, true); 2435 2436 } 2437 } 2438 2439 this.initEvent.fire(Overlay); 2440 }, 2441 2442 /** 2443 * Initializes the custom events for Overlay which are fired 2444 * automatically at appropriate times by the Overlay class. 2445 * @method initEvents 2446 */ 2447 initEvents: function () { 2448 2449 Overlay.superclass.initEvents.call(this); 2450 2451 var SIGNATURE = CustomEvent.LIST; 2452 2453 /** 2454 * CustomEvent fired before the Overlay is moved. 2455 * @event beforeMoveEvent 2456 * @param {Number} x x coordinate 2457 * @param {Number} y y coordinate 2458 */ 2459 this.beforeMoveEvent = this.createEvent(EVENT_TYPES.BEFORE_MOVE); 2460 this.beforeMoveEvent.signature = SIGNATURE; 2461 2462 /** 2463 * CustomEvent fired after the Overlay is moved. 2464 * @event moveEvent 2465 * @param {Number} x x coordinate 2466 * @param {Number} y y coordinate 2467 */ 2468 this.moveEvent = this.createEvent(EVENT_TYPES.MOVE); 2469 this.moveEvent.signature = SIGNATURE; 2470 2471 }, 2472 2473 /** 2474 * Initializes the class's configurable properties which can be changed 2475 * using the Overlay's Config object (cfg). 2476 * @method initDefaultConfig 2477 */ 2478 initDefaultConfig: function () { 2479 2480 Overlay.superclass.initDefaultConfig.call(this); 2481 2482 var cfg = this.cfg; 2483 2484 // Add overlay config properties // 2485 2486 /** 2487 * The absolute x-coordinate position of the Overlay 2488 * @config x 2489 * @type Number 2490 * @default null 2491 */ 2492 cfg.addProperty(DEFAULT_CONFIG.X.key, { 2493 2494 handler: this.configX, 2495 validator: DEFAULT_CONFIG.X.validator, 2496 suppressEvent: DEFAULT_CONFIG.X.suppressEvent, 2497 supercedes: DEFAULT_CONFIG.X.supercedes 2498 2499 }); 2500 2501 /** 2502 * The absolute y-coordinate position of the Overlay 2503 * @config y 2504 * @type Number 2505 * @default null 2506 */ 2507 cfg.addProperty(DEFAULT_CONFIG.Y.key, { 2508 2509 handler: this.configY, 2510 validator: DEFAULT_CONFIG.Y.validator, 2511 suppressEvent: DEFAULT_CONFIG.Y.suppressEvent, 2512 supercedes: DEFAULT_CONFIG.Y.supercedes 2513 2514 }); 2515 2516 /** 2517 * An array with the absolute x and y positions of the Overlay 2518 * @config xy 2519 * @type Number[] 2520 * @default null 2521 */ 2522 cfg.addProperty(DEFAULT_CONFIG.XY.key, { 2523 handler: this.configXY, 2524 suppressEvent: DEFAULT_CONFIG.XY.suppressEvent, 2525 supercedes: DEFAULT_CONFIG.XY.supercedes 2526 }); 2527 2528 /** 2529 * <p> 2530 * The array of context arguments for context-sensitive positioning. 2531 * </p> 2532 * 2533 * <p> 2534 * The format of the array is: <code>[contextElementOrId, overlayCorner, contextCorner, arrayOfTriggerEvents (optional), xyOffset (optional)]</code>, the 2535 * the 5 array elements described in detail below: 2536 * </p> 2537 * 2538 * <dl> 2539 * <dt>contextElementOrId <String|HTMLElement></dt> 2540 * <dd>A reference to the context element to which the overlay should be aligned (or it's id).</dd> 2541 * <dt>overlayCorner <String></dt> 2542 * <dd>The corner of the overlay which is to be used for alignment. This corner will be aligned to the 2543 * corner of the context element defined by the "contextCorner" entry which follows. Supported string values are: 2544 * "tr" (top right), "tl" (top left), "br" (bottom right), or "bl" (bottom left).</dd> 2545 * <dt>contextCorner <String></dt> 2546 * <dd>The corner of the context element which is to be used for alignment. Supported string values are the same ones listed for the "overlayCorner" entry above.</dd> 2547 * <dt>arrayOfTriggerEvents (optional) <Array[String|CustomEvent]></dt> 2548 * <dd> 2549 * <p> 2550 * By default, context alignment is a one time operation, aligning the Overlay to the context element when context configuration property is set, or when the <a href="#method_align">align</a> 2551 * method is invoked. However, you can use the optional "arrayOfTriggerEvents" entry to define the list of events which should force the overlay to re-align itself with the context element. 2552 * This is useful in situations where the layout of the document may change, resulting in the context element's position being modified. 2553 * </p> 2554 * <p> 2555 * The array can contain either event type strings for events the instance publishes (e.g. "beforeShow") or CustomEvent instances. Additionally the following 2556 * 3 static container event types are also currently supported : <code>"windowResize", "windowScroll", "textResize"</code> (defined in <a href="#property__TRIGGER_MAP">_TRIGGER_MAP</a> private property). 2557 * </p> 2558 * </dd> 2559 * <dt>xyOffset <Number[]></dt> 2560 * <dd> 2561 * A 2 element Array specifying the X and Y pixel amounts by which the Overlay should be offset from the aligned corner. e.g. [5,0] offsets the Overlay 5 pixels to the left, <em>after</em> aligning the given context corners. 2562 * NOTE: If using this property and no triggers need to be defined, the arrayOfTriggerEvents property should be set to null to maintain correct array positions for the arguments. 2563 * </dd> 2564 * </dl> 2565 * 2566 * <p> 2567 * For example, setting this property to <code>["img1", "tl", "bl"]</code> will 2568 * align the Overlay's top left corner to the bottom left corner of the 2569 * context element with id "img1". 2570 * </p> 2571 * <p> 2572 * Setting this property to <code>["img1", "tl", "bl", null, [0,5]</code> will 2573 * align the Overlay's top left corner to the bottom left corner of the 2574 * context element with id "img1", and then offset it by 5 pixels on the Y axis (providing a 5 pixel gap between the bottom of the context element and top of the overlay). 2575 * </p> 2576 * <p> 2577 * Adding the optional trigger values: <code>["img1", "tl", "bl", ["beforeShow", "windowResize"], [0,5]]</code>, 2578 * will re-align the overlay position, whenever the "beforeShow" or "windowResize" events are fired. 2579 * </p> 2580 * 2581 * @config context 2582 * @type Array 2583 * @default null 2584 */ 2585 cfg.addProperty(DEFAULT_CONFIG.CONTEXT.key, { 2586 handler: this.configContext, 2587 suppressEvent: DEFAULT_CONFIG.CONTEXT.suppressEvent, 2588 supercedes: DEFAULT_CONFIG.CONTEXT.supercedes 2589 }); 2590 2591 /** 2592 * Determines whether or not the Overlay should be anchored 2593 * to the center of the viewport. 2594 * 2595 * <p>This property can be set to:</p> 2596 * 2597 * <dl> 2598 * <dt>true</dt> 2599 * <dd> 2600 * To enable fixed center positioning 2601 * <p> 2602 * When enabled, the overlay will 2603 * be positioned in the center of viewport when initially displayed, and 2604 * will remain in the center of the viewport whenever the window is 2605 * scrolled or resized. 2606 * </p> 2607 * <p> 2608 * If the overlay is too big for the viewport, 2609 * it's top left corner will be aligned with the top left corner of the viewport. 2610 * </p> 2611 * </dd> 2612 * <dt>false</dt> 2613 * <dd> 2614 * To disable fixed center positioning. 2615 * <p>In this case the overlay can still be 2616 * centered as a one-off operation, by invoking the <code>center()</code> method, 2617 * however it will not remain centered when the window is scrolled/resized. 2618 * </dd> 2619 * <dt>"contained"<dt> 2620 * <dd>To enable fixed center positioning, as with the <code>true</code> option. 2621 * <p>However, unlike setting the property to <code>true</code>, 2622 * when the property is set to <code>"contained"</code>, if the overlay is 2623 * too big for the viewport, it will not get automatically centered when the 2624 * user scrolls or resizes the window (until the window is large enough to contain the 2625 * overlay). This is useful in cases where the Overlay has both header and footer 2626 * UI controls which the user may need to access. 2627 * </p> 2628 * </dd> 2629 * </dl> 2630 * 2631 * @config fixedcenter 2632 * @type Boolean | String 2633 * @default false 2634 */ 2635 cfg.addProperty(DEFAULT_CONFIG.FIXED_CENTER.key, { 2636 handler: this.configFixedCenter, 2637 value: DEFAULT_CONFIG.FIXED_CENTER.value, 2638 validator: DEFAULT_CONFIG.FIXED_CENTER.validator, 2639 supercedes: DEFAULT_CONFIG.FIXED_CENTER.supercedes 2640 }); 2641 2642 /** 2643 * CSS width of the Overlay. 2644 * @config width 2645 * @type String 2646 * @default null 2647 */ 2648 cfg.addProperty(DEFAULT_CONFIG.WIDTH.key, { 2649 handler: this.configWidth, 2650 suppressEvent: DEFAULT_CONFIG.WIDTH.suppressEvent, 2651 supercedes: DEFAULT_CONFIG.WIDTH.supercedes 2652 }); 2653 2654 /** 2655 * CSS height of the Overlay. 2656 * @config height 2657 * @type String 2658 * @default null 2659 */ 2660 cfg.addProperty(DEFAULT_CONFIG.HEIGHT.key, { 2661 handler: this.configHeight, 2662 suppressEvent: DEFAULT_CONFIG.HEIGHT.suppressEvent, 2663 supercedes: DEFAULT_CONFIG.HEIGHT.supercedes 2664 }); 2665 2666 /** 2667 * Standard module element which should auto fill out the height of the Overlay if the height config property is set. 2668 * Supported values are "header", "body", "footer". 2669 * 2670 * @config autofillheight 2671 * @type String 2672 * @default null 2673 */ 2674 cfg.addProperty(DEFAULT_CONFIG.AUTO_FILL_HEIGHT.key, { 2675 handler: this.configAutoFillHeight, 2676 value : DEFAULT_CONFIG.AUTO_FILL_HEIGHT.value, 2677 validator : this._validateAutoFill, 2678 supercedes: DEFAULT_CONFIG.AUTO_FILL_HEIGHT.supercedes 2679 }); 2680 2681 /** 2682 * CSS z-index of the Overlay. 2683 * @config zIndex 2684 * @type Number 2685 * @default null 2686 */ 2687 cfg.addProperty(DEFAULT_CONFIG.ZINDEX.key, { 2688 handler: this.configzIndex, 2689 value: DEFAULT_CONFIG.ZINDEX.value 2690 }); 2691 2692 /** 2693 * True if the Overlay should be prevented from being positioned 2694 * out of the viewport. 2695 * @config constraintoviewport 2696 * @type Boolean 2697 * @default false 2698 */ 2699 cfg.addProperty(DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.key, { 2700 2701 handler: this.configConstrainToViewport, 2702 value: DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.value, 2703 validator: DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.validator, 2704 supercedes: DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.supercedes 2705 2706 }); 2707 2708 /** 2709 * @config iframe 2710 * @description Boolean indicating whether or not the Overlay should 2711 * have an IFRAME shim; used to prevent SELECT elements from 2712 * poking through an Overlay instance in IE6. When set to "true", 2713 * the iframe shim is created when the Overlay instance is intially 2714 * made visible. 2715 * @type Boolean 2716 * @default true for IE6 and below, false for all other browsers. 2717 */ 2718 cfg.addProperty(DEFAULT_CONFIG.IFRAME.key, { 2719 2720 handler: this.configIframe, 2721 value: DEFAULT_CONFIG.IFRAME.value, 2722 validator: DEFAULT_CONFIG.IFRAME.validator, 2723 supercedes: DEFAULT_CONFIG.IFRAME.supercedes 2724 2725 }); 2726 2727 /** 2728 * @config preventcontextoverlap 2729 * @description Boolean indicating whether or not the Overlay should overlap its 2730 * context element (defined using the "context" configuration property) when the 2731 * "constraintoviewport" configuration property is set to "true". 2732 * @type Boolean 2733 * @default false 2734 */ 2735 cfg.addProperty(DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.key, { 2736 value: DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.value, 2737 validator: DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.validator, 2738 supercedes: DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.supercedes 2739 }); 2740 }, 2741 2742 /** 2743 * Moves the Overlay to the specified position. This function is 2744 * identical to calling this.cfg.setProperty("xy", [x,y]); 2745 * @method moveTo 2746 * @param {Number} x The Overlay's new x position 2747 * @param {Number} y The Overlay's new y position 2748 */ 2749 moveTo: function (x, y) { 2750 this.cfg.setProperty("xy", [x, y]); 2751 }, 2752 2753 /** 2754 * Adds a CSS class ("hide-scrollbars") and removes a CSS class 2755 * ("show-scrollbars") to the Overlay to fix a bug in Gecko on Mac OS X 2756 * (https://bugzilla.mozilla.org/show_bug.cgi?id=187435) 2757 * @method hideMacGeckoScrollbars 2758 */ 2759 hideMacGeckoScrollbars: function () { 2760 Dom.replaceClass(this.element, "show-scrollbars", "hide-scrollbars"); 2761 }, 2762 2763 /** 2764 * Adds a CSS class ("show-scrollbars") and removes a CSS class 2765 * ("hide-scrollbars") to the Overlay to fix a bug in Gecko on Mac OS X 2766 * (https://bugzilla.mozilla.org/show_bug.cgi?id=187435) 2767 * @method showMacGeckoScrollbars 2768 */ 2769 showMacGeckoScrollbars: function () { 2770 Dom.replaceClass(this.element, "hide-scrollbars", "show-scrollbars"); 2771 }, 2772 2773 /** 2774 * Internal implementation to set the visibility of the overlay in the DOM. 2775 * 2776 * @method _setDomVisibility 2777 * @param {boolean} visible Whether to show or hide the Overlay's outer element 2778 * @protected 2779 */ 2780 _setDomVisibility : function(show) { 2781 Dom.setStyle(this.element, "visibility", (show) ? "visible" : "hidden"); 2782 var hiddenClass = Overlay.CSS_HIDDEN; 2783 2784 if (show) { 2785 Dom.removeClass(this.element, hiddenClass); 2786 } else { 2787 Dom.addClass(this.element, hiddenClass); 2788 } 2789 }, 2790 2791 // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // 2792 /** 2793 * The default event handler fired when the "visible" property is 2794 * changed. This method is responsible for firing showEvent 2795 * and hideEvent. 2796 * @method configVisible 2797 * @param {String} type The CustomEvent type (usually the property name) 2798 * @param {Object[]} args The CustomEvent arguments. For configuration 2799 * handlers, args[0] will equal the newly applied value for the property. 2800 * @param {Object} obj The scope object. For configuration handlers, 2801 * this will usually equal the owner. 2802 */ 2803 configVisible: function (type, args, obj) { 2804 2805 var visible = args[0], 2806 currentVis = Dom.getStyle(this.element, "visibility"), 2807 effects = this._cachedEffects || this._createEffects(this.cfg.getProperty("effect")), 2808 isMacGecko = (this.platform == "mac" && UA.gecko), 2809 alreadySubscribed = Config.alreadySubscribed, 2810 ei, e, j, k, h, 2811 nEffectInstances; 2812 2813 if (currentVis == "inherit") { 2814 e = this.element.parentNode; 2815 2816 while (e.nodeType != 9 && e.nodeType != 11) { 2817 currentVis = Dom.getStyle(e, "visibility"); 2818 2819 if (currentVis != "inherit") { 2820 break; 2821 } 2822 2823 e = e.parentNode; 2824 } 2825 2826 if (currentVis == "inherit") { 2827 currentVis = "visible"; 2828 } 2829 } 2830 2831 if (visible) { // Show 2832 2833 if (isMacGecko) { 2834 this.showMacGeckoScrollbars(); 2835 } 2836 2837 if (effects) { // Animate in 2838 if (visible) { // Animate in if not showing 2839 2840 // Fading out is a bit of a hack, but didn't want to risk doing 2841 // something broader (e.g a generic this._animatingOut) for 2.9.0 2842 2843 if (currentVis != "visible" || currentVis === "" || this._fadingOut) { 2844 if (this.beforeShowEvent.fire()) { 2845 2846 nEffectInstances = effects.length; 2847 2848 for (j = 0; j < nEffectInstances; j++) { 2849 ei = effects[j]; 2850 if (j === 0 && !alreadySubscribed(ei.animateInCompleteEvent, this.showEvent.fire, this.showEvent)) { 2851 ei.animateInCompleteEvent.subscribe(this.showEvent.fire, this.showEvent, true); 2852 } 2853 ei.animateIn(); 2854 } 2855 } 2856 } 2857 } 2858 } else { // Show 2859 if (currentVis != "visible" || currentVis === "") { 2860 if (this.beforeShowEvent.fire()) { 2861 this._setDomVisibility(true); 2862 this.cfg.refireEvent("iframe"); 2863 this.showEvent.fire(); 2864 } 2865 } else { 2866 this._setDomVisibility(true); 2867 } 2868 } 2869 } else { // Hide 2870 2871 if (isMacGecko) { 2872 this.hideMacGeckoScrollbars(); 2873 } 2874 2875 if (effects) { // Animate out if showing 2876 if (currentVis == "visible" || this._fadingIn) { 2877 if (this.beforeHideEvent.fire()) { 2878 nEffectInstances = effects.length; 2879 for (k = 0; k < nEffectInstances; k++) { 2880 h = effects[k]; 2881 2882 if (k === 0 && !alreadySubscribed(h.animateOutCompleteEvent, this.hideEvent.fire, this.hideEvent)) { 2883 h.animateOutCompleteEvent.subscribe(this.hideEvent.fire, this.hideEvent, true); 2884 } 2885 h.animateOut(); 2886 } 2887 } 2888 2889 } else if (currentVis === "") { 2890 this._setDomVisibility(false); 2891 } 2892 2893 } else { // Simple hide 2894 2895 if (currentVis == "visible" || currentVis === "") { 2896 if (this.beforeHideEvent.fire()) { 2897 this._setDomVisibility(false); 2898 this.hideEvent.fire(); 2899 } 2900 } else { 2901 this._setDomVisibility(false); 2902 } 2903 } 2904 } 2905 }, 2906 2907 /** 2908 * Fixed center event handler used for centering on scroll/resize, but only if 2909 * the overlay is visible and, if "fixedcenter" is set to "contained", only if 2910 * the overlay fits within the viewport. 2911 * 2912 * @method doCenterOnDOMEvent 2913 */ 2914 doCenterOnDOMEvent: function () { 2915 var cfg = this.cfg, 2916 fc = cfg.getProperty("fixedcenter"); 2917 2918 if (cfg.getProperty("visible")) { 2919 if (fc && (fc !== _CONTAINED || this.fitsInViewport())) { 2920 this.center(); 2921 } 2922 } 2923 }, 2924 2925 /** 2926 * Determines if the Overlay (including the offset value defined by Overlay.VIEWPORT_OFFSET) 2927 * will fit entirely inside the viewport, in both dimensions - width and height. 2928 * 2929 * @method fitsInViewport 2930 * @return boolean true if the Overlay will fit, false if not 2931 */ 2932 fitsInViewport : function() { 2933 var nViewportOffset = Overlay.VIEWPORT_OFFSET, 2934 element = this.element, 2935 elementWidth = element.offsetWidth, 2936 elementHeight = element.offsetHeight, 2937 viewportWidth = Dom.getViewportWidth(), 2938 viewportHeight = Dom.getViewportHeight(); 2939 2940 return ((elementWidth + nViewportOffset < viewportWidth) && (elementHeight + nViewportOffset < viewportHeight)); 2941 }, 2942 2943 /** 2944 * The default event handler fired when the "fixedcenter" property 2945 * is changed. 2946 * @method configFixedCenter 2947 * @param {String} type The CustomEvent type (usually the property name) 2948 * @param {Object[]} args The CustomEvent arguments. For configuration 2949 * handlers, args[0] will equal the newly applied value for the property. 2950 * @param {Object} obj The scope object. For configuration handlers, 2951 * this will usually equal the owner. 2952 */ 2953 configFixedCenter: function (type, args, obj) { 2954 2955 var val = args[0], 2956 alreadySubscribed = Config.alreadySubscribed, 2957 windowResizeEvent = Overlay.windowResizeEvent, 2958 windowScrollEvent = Overlay.windowScrollEvent; 2959 2960 if (val) { 2961 this.center(); 2962 2963 if (!alreadySubscribed(this.beforeShowEvent, this.center)) { 2964 this.beforeShowEvent.subscribe(this.center); 2965 } 2966 2967 if (!alreadySubscribed(windowResizeEvent, this.doCenterOnDOMEvent, this)) { 2968 windowResizeEvent.subscribe(this.doCenterOnDOMEvent, this, true); 2969 } 2970 2971 if (!alreadySubscribed(windowScrollEvent, this.doCenterOnDOMEvent, this)) { 2972 windowScrollEvent.subscribe(this.doCenterOnDOMEvent, this, true); 2973 } 2974 2975 } else { 2976 this.beforeShowEvent.unsubscribe(this.center); 2977 2978 windowResizeEvent.unsubscribe(this.doCenterOnDOMEvent, this); 2979 windowScrollEvent.unsubscribe(this.doCenterOnDOMEvent, this); 2980 } 2981 }, 2982 2983 /** 2984 * The default event handler fired when the "height" property is changed. 2985 * @method configHeight 2986 * @param {String} type The CustomEvent type (usually the property name) 2987 * @param {Object[]} args The CustomEvent arguments. For configuration 2988 * handlers, args[0] will equal the newly applied value for the property. 2989 * @param {Object} obj The scope object. For configuration handlers, 2990 * this will usually equal the owner. 2991 */ 2992 configHeight: function (type, args, obj) { 2993 2994 var height = args[0], 2995 el = this.element; 2996 2997 Dom.setStyle(el, "height", height); 2998 this.cfg.refireEvent("iframe"); 2999 }, 3000 3001 /** 3002 * The default event handler fired when the "autofillheight" property is changed. 3003 * @method configAutoFillHeight 3004 * 3005 * @param {String} type The CustomEvent type (usually the property name) 3006 * @param {Object[]} args The CustomEvent arguments. For configuration 3007 * handlers, args[0] will equal the newly applied value for the property. 3008 * @param {Object} obj The scope object. For configuration handlers, 3009 * this will usually equal the owner. 3010 */ 3011 configAutoFillHeight: function (type, args, obj) { 3012 var fillEl = args[0], 3013 cfg = this.cfg, 3014 autoFillHeight = "autofillheight", 3015 height = "height", 3016 currEl = cfg.getProperty(autoFillHeight), 3017 autoFill = this._autoFillOnHeightChange; 3018 3019 cfg.unsubscribeFromConfigEvent(height, autoFill); 3020 Module.textResizeEvent.unsubscribe(autoFill); 3021 this.changeContentEvent.unsubscribe(autoFill); 3022 3023 if (currEl && fillEl !== currEl && this[currEl]) { 3024 Dom.setStyle(this[currEl], height, ""); 3025 } 3026 3027 if (fillEl) { 3028 fillEl = Lang.trim(fillEl.toLowerCase()); 3029 3030 cfg.subscribeToConfigEvent(height, autoFill, this[fillEl], this); 3031 Module.textResizeEvent.subscribe(autoFill, this[fillEl], this); 3032 this.changeContentEvent.subscribe(autoFill, this[fillEl], this); 3033 3034 cfg.setProperty(autoFillHeight, fillEl, true); 3035 } 3036 }, 3037 3038 /** 3039 * The default event handler fired when the "width" property is changed. 3040 * @method configWidth 3041 * @param {String} type The CustomEvent type (usually the property name) 3042 * @param {Object[]} args The CustomEvent arguments. For configuration 3043 * handlers, args[0] will equal the newly applied value for the property. 3044 * @param {Object} obj The scope object. For configuration handlers, 3045 * this will usually equal the owner. 3046 */ 3047 configWidth: function (type, args, obj) { 3048 3049 var width = args[0], 3050 el = this.element; 3051 3052 Dom.setStyle(el, "width", width); 3053 this.cfg.refireEvent("iframe"); 3054 }, 3055 3056 /** 3057 * The default event handler fired when the "zIndex" property is changed. 3058 * @method configzIndex 3059 * @param {String} type The CustomEvent type (usually the property name) 3060 * @param {Object[]} args The CustomEvent arguments. For configuration 3061 * handlers, args[0] will equal the newly applied value for the property. 3062 * @param {Object} obj The scope object. For configuration handlers, 3063 * this will usually equal the owner. 3064 */ 3065 configzIndex: function (type, args, obj) { 3066 3067 var zIndex = args[0], 3068 el = this.element; 3069 3070 if (! zIndex) { 3071 zIndex = Dom.getStyle(el, "zIndex"); 3072 if (! zIndex || isNaN(zIndex)) { 3073 zIndex = 0; 3074 } 3075 } 3076 3077 if (this.iframe || this.cfg.getProperty("iframe") === true) { 3078 if (zIndex <= 0) { 3079 zIndex = 1; 3080 } 3081 } 3082 3083 Dom.setStyle(el, "zIndex", zIndex); 3084 this.cfg.setProperty("zIndex", zIndex, true); 3085 3086 if (this.iframe) { 3087 this.stackIframe(); 3088 } 3089 }, 3090 3091 /** 3092 * The default event handler fired when the "xy" property is changed. 3093 * @method configXY 3094 * @param {String} type The CustomEvent type (usually the property name) 3095 * @param {Object[]} args The CustomEvent arguments. For configuration 3096 * handlers, args[0] will equal the newly applied value for the property. 3097 * @param {Object} obj The scope object. For configuration handlers, 3098 * this will usually equal the owner. 3099 */ 3100 configXY: function (type, args, obj) { 3101 3102 var pos = args[0], 3103 x = pos[0], 3104 y = pos[1]; 3105 3106 this.cfg.setProperty("x", x); 3107 this.cfg.setProperty("y", y); 3108 3109 this.beforeMoveEvent.fire([x, y]); 3110 3111 x = this.cfg.getProperty("x"); 3112 y = this.cfg.getProperty("y"); 3113 3114 YAHOO.log(("xy: " + [x, y]), "iframe"); 3115 3116 this.cfg.refireEvent("iframe"); 3117 this.moveEvent.fire([x, y]); 3118 }, 3119 3120 /** 3121 * The default event handler fired when the "x" property is changed. 3122 * @method configX 3123 * @param {String} type The CustomEvent type (usually the property name) 3124 * @param {Object[]} args The CustomEvent arguments. For configuration 3125 * handlers, args[0] will equal the newly applied value for the property. 3126 * @param {Object} obj The scope object. For configuration handlers, 3127 * this will usually equal the owner. 3128 */ 3129 configX: function (type, args, obj) { 3130 3131 var x = args[0], 3132 y = this.cfg.getProperty("y"); 3133 3134 this.cfg.setProperty("x", x, true); 3135 this.cfg.setProperty("y", y, true); 3136 3137 this.beforeMoveEvent.fire([x, y]); 3138 3139 x = this.cfg.getProperty("x"); 3140 y = this.cfg.getProperty("y"); 3141 3142 Dom.setX(this.element, x, true); 3143 3144 this.cfg.setProperty("xy", [x, y], true); 3145 3146 this.cfg.refireEvent("iframe"); 3147 this.moveEvent.fire([x, y]); 3148 }, 3149 3150 /** 3151 * The default event handler fired when the "y" property is changed. 3152 * @method configY 3153 * @param {String} type The CustomEvent type (usually the property name) 3154 * @param {Object[]} args The CustomEvent arguments. For configuration 3155 * handlers, args[0] will equal the newly applied value for the property. 3156 * @param {Object} obj The scope object. For configuration handlers, 3157 * this will usually equal the owner. 3158 */ 3159 configY: function (type, args, obj) { 3160 3161 var x = this.cfg.getProperty("x"), 3162 y = args[0]; 3163 3164 this.cfg.setProperty("x", x, true); 3165 this.cfg.setProperty("y", y, true); 3166 3167 this.beforeMoveEvent.fire([x, y]); 3168 3169 x = this.cfg.getProperty("x"); 3170 y = this.cfg.getProperty("y"); 3171 3172 Dom.setY(this.element, y, true); 3173 3174 this.cfg.setProperty("xy", [x, y], true); 3175 3176 this.cfg.refireEvent("iframe"); 3177 this.moveEvent.fire([x, y]); 3178 }, 3179 3180 /** 3181 * Shows the iframe shim, if it has been enabled. 3182 * @method showIframe 3183 */ 3184 showIframe: function () { 3185 3186 var oIFrame = this.iframe, 3187 oParentNode; 3188 3189 if (oIFrame) { 3190 oParentNode = this.element.parentNode; 3191 3192 if (oParentNode != oIFrame.parentNode) { 3193 this._addToParent(oParentNode, oIFrame); 3194 } 3195 oIFrame.style.display = "block"; 3196 } 3197 }, 3198 3199 /** 3200 * Hides the iframe shim, if it has been enabled. 3201 * @method hideIframe 3202 */ 3203 hideIframe: function () { 3204 if (this.iframe) { 3205 this.iframe.style.display = "none"; 3206 } 3207 }, 3208 3209 /** 3210 * Syncronizes the size and position of iframe shim to that of its 3211 * corresponding Overlay instance. 3212 * @method syncIframe 3213 */ 3214 syncIframe: function () { 3215 3216 var oIFrame = this.iframe, 3217 oElement = this.element, 3218 nOffset = Overlay.IFRAME_OFFSET, 3219 nDimensionOffset = (nOffset * 2), 3220 aXY; 3221 3222 if (oIFrame) { 3223 // Size <iframe> 3224 oIFrame.style.width = (oElement.offsetWidth + nDimensionOffset + "px"); 3225 oIFrame.style.height = (oElement.offsetHeight + nDimensionOffset + "px"); 3226 3227 // Position <iframe> 3228 aXY = this.cfg.getProperty("xy"); 3229 3230 if (!Lang.isArray(aXY) || (isNaN(aXY[0]) || isNaN(aXY[1]))) { 3231 this.syncPosition(); 3232 aXY = this.cfg.getProperty("xy"); 3233 } 3234 Dom.setXY(oIFrame, [(aXY[0] - nOffset), (aXY[1] - nOffset)]); 3235 } 3236 }, 3237 3238 /** 3239 * Sets the zindex of the iframe shim, if it exists, based on the zindex of 3240 * the Overlay element. The zindex of the iframe is set to be one less 3241 * than the Overlay element's zindex. 3242 * 3243 * <p>NOTE: This method will not bump up the zindex of the Overlay element 3244 * to ensure that the iframe shim has a non-negative zindex. 3245 * If you require the iframe zindex to be 0 or higher, the zindex of 3246 * the Overlay element should be set to a value greater than 0, before 3247 * this method is called. 3248 * </p> 3249 * @method stackIframe 3250 */ 3251 stackIframe: function () { 3252 if (this.iframe) { 3253 var overlayZ = Dom.getStyle(this.element, "zIndex"); 3254 if (!YAHOO.lang.isUndefined(overlayZ) && !isNaN(overlayZ)) { 3255 Dom.setStyle(this.iframe, "zIndex", (overlayZ - 1)); 3256 } 3257 } 3258 }, 3259 3260 /** 3261 * The default event handler fired when the "iframe" property is changed. 3262 * @method configIframe 3263 * @param {String} type The CustomEvent type (usually the property name) 3264 * @param {Object[]} args The CustomEvent arguments. For configuration 3265 * handlers, args[0] will equal the newly applied value for the property. 3266 * @param {Object} obj The scope object. For configuration handlers, 3267 * this will usually equal the owner. 3268 */ 3269 configIframe: function (type, args, obj) { 3270 3271 var bIFrame = args[0]; 3272 3273 function createIFrame() { 3274 3275 var oIFrame = this.iframe, 3276 oElement = this.element, 3277 oParent; 3278 3279 if (!oIFrame) { 3280 if (!m_oIFrameTemplate) { 3281 m_oIFrameTemplate = document.createElement("iframe"); 3282 3283 if (this.isSecure) { 3284 m_oIFrameTemplate.src = Overlay.IFRAME_SRC; 3285 } 3286 3287 /* 3288 Set the opacity of the <iframe> to 0 so that it 3289 doesn't modify the opacity of any transparent 3290 elements that may be on top of it (like a shadow). 3291 */ 3292 if (UA.ie) { 3293 m_oIFrameTemplate.style.filter = "alpha(opacity=0)"; 3294 /* 3295 Need to set the "frameBorder" property to 0 3296 supress the default <iframe> border in IE. 3297 Setting the CSS "border" property alone 3298 doesn't supress it. 3299 */ 3300 m_oIFrameTemplate.frameBorder = 0; 3301 } 3302 else { 3303 m_oIFrameTemplate.style.opacity = "0"; 3304 } 3305 3306 m_oIFrameTemplate.style.position = "absolute"; 3307 m_oIFrameTemplate.style.border = "none"; 3308 m_oIFrameTemplate.style.margin = "0"; 3309 m_oIFrameTemplate.style.padding = "0"; 3310 m_oIFrameTemplate.style.display = "none"; 3311 m_oIFrameTemplate.tabIndex = -1; 3312 m_oIFrameTemplate.className = Overlay.CSS_IFRAME; 3313 } 3314 3315 oIFrame = m_oIFrameTemplate.cloneNode(false); 3316 oIFrame.id = this.id + "_f"; 3317 oParent = oElement.parentNode; 3318 3319 var parentNode = oParent || document.body; 3320 3321 this._addToParent(parentNode, oIFrame); 3322 this.iframe = oIFrame; 3323 } 3324 3325 /* 3326 Show the <iframe> before positioning it since the "setXY" 3327 method of DOM requires the element be in the document 3328 and visible. 3329 */ 3330 this.showIframe(); 3331 3332 /* 3333 Syncronize the size and position of the <iframe> to that 3334 of the Overlay. 3335 */ 3336 this.syncIframe(); 3337 this.stackIframe(); 3338 3339 // Add event listeners to update the <iframe> when necessary 3340 if (!this._hasIframeEventListeners) { 3341 this.showEvent.subscribe(this.showIframe); 3342 this.hideEvent.subscribe(this.hideIframe); 3343 this.changeContentEvent.subscribe(this.syncIframe); 3344 3345 this._hasIframeEventListeners = true; 3346 } 3347 } 3348 3349 function onBeforeShow() { 3350 createIFrame.call(this); 3351 this.beforeShowEvent.unsubscribe(onBeforeShow); 3352 this._iframeDeferred = false; 3353 } 3354 3355 if (bIFrame) { // <iframe> shim is enabled 3356 3357 if (this.cfg.getProperty("visible")) { 3358 createIFrame.call(this); 3359 } else { 3360 if (!this._iframeDeferred) { 3361 this.beforeShowEvent.subscribe(onBeforeShow); 3362 this._iframeDeferred = true; 3363 } 3364 } 3365 3366 } else { // <iframe> shim is disabled 3367 this.hideIframe(); 3368 3369 if (this._hasIframeEventListeners) { 3370 this.showEvent.unsubscribe(this.showIframe); 3371 this.hideEvent.unsubscribe(this.hideIframe); 3372 this.changeContentEvent.unsubscribe(this.syncIframe); 3373 3374 this._hasIframeEventListeners = false; 3375 } 3376 } 3377 }, 3378 3379 /** 3380 * Set's the container's XY value from DOM if not already set. 3381 * 3382 * Differs from syncPosition, in that the XY value is only sync'd with DOM if 3383 * not already set. The method also refire's the XY config property event, so any 3384 * beforeMove, Move event listeners are invoked. 3385 * 3386 * @method _primeXYFromDOM 3387 * @protected 3388 */ 3389 _primeXYFromDOM : function() { 3390 if (YAHOO.lang.isUndefined(this.cfg.getProperty("xy"))) { 3391 // Set CFG XY based on DOM XY 3392 this.syncPosition(); 3393 // Account for XY being set silently in syncPosition (no moveTo fired/called) 3394 this.cfg.refireEvent("xy"); 3395 this.beforeShowEvent.unsubscribe(this._primeXYFromDOM); 3396 } 3397 }, 3398 3399 /** 3400 * The default event handler fired when the "constraintoviewport" 3401 * property is changed. 3402 * @method configConstrainToViewport 3403 * @param {String} type The CustomEvent type (usually the property name) 3404 * @param {Object[]} args The CustomEvent arguments. For configuration 3405 * handlers, args[0] will equal the newly applied value for 3406 * the property. 3407 * @param {Object} obj The scope object. For configuration handlers, 3408 * this will usually equal the owner. 3409 */ 3410 configConstrainToViewport: function (type, args, obj) { 3411 var val = args[0]; 3412 3413 if (val) { 3414 if (! Config.alreadySubscribed(this.beforeMoveEvent, this.enforceConstraints, this)) { 3415 this.beforeMoveEvent.subscribe(this.enforceConstraints, this, true); 3416 } 3417 if (! Config.alreadySubscribed(this.beforeShowEvent, this._primeXYFromDOM)) { 3418 this.beforeShowEvent.subscribe(this._primeXYFromDOM); 3419 } 3420 } else { 3421 this.beforeShowEvent.unsubscribe(this._primeXYFromDOM); 3422 this.beforeMoveEvent.unsubscribe(this.enforceConstraints, this); 3423 } 3424 }, 3425 3426 /** 3427 * The default event handler fired when the "context" property 3428 * is changed. 3429 * 3430 * @method configContext 3431 * @param {String} type The CustomEvent type (usually the property name) 3432 * @param {Object[]} args The CustomEvent arguments. For configuration 3433 * handlers, args[0] will equal the newly applied value for the property. 3434 * @param {Object} obj The scope object. For configuration handlers, 3435 * this will usually equal the owner. 3436 */ 3437 configContext: function (type, args, obj) { 3438 3439 var contextArgs = args[0], 3440 contextEl, 3441 elementMagnetCorner, 3442 contextMagnetCorner, 3443 triggers, 3444 offset, 3445 defTriggers = this.CONTEXT_TRIGGERS; 3446 3447 if (contextArgs) { 3448 3449 contextEl = contextArgs[0]; 3450 elementMagnetCorner = contextArgs[1]; 3451 contextMagnetCorner = contextArgs[2]; 3452 triggers = contextArgs[3]; 3453 offset = contextArgs[4]; 3454 3455 if (defTriggers && defTriggers.length > 0) { 3456 triggers = (triggers || []).concat(defTriggers); 3457 } 3458 3459 if (contextEl) { 3460 if (typeof contextEl == "string") { 3461 this.cfg.setProperty("context", [ 3462 document.getElementById(contextEl), 3463 elementMagnetCorner, 3464 contextMagnetCorner, 3465 triggers, 3466 offset], 3467 true); 3468 } 3469 3470 if (elementMagnetCorner && contextMagnetCorner) { 3471 this.align(elementMagnetCorner, contextMagnetCorner, offset); 3472 } 3473 3474 if (this._contextTriggers) { 3475 // Unsubscribe Old Set 3476 this._processTriggers(this._contextTriggers, _UNSUBSCRIBE, this._alignOnTrigger); 3477 } 3478 3479 if (triggers) { 3480 // Subscribe New Set 3481 this._processTriggers(triggers, _SUBSCRIBE, this._alignOnTrigger); 3482 this._contextTriggers = triggers; 3483 } 3484 } 3485 } 3486 }, 3487 3488 /** 3489 * Custom Event handler for context alignment triggers. Invokes the align method 3490 * 3491 * @method _alignOnTrigger 3492 * @protected 3493 * 3494 * @param {String} type The event type (not used by the default implementation) 3495 * @param {Any[]} args The array of arguments for the trigger event (not used by the default implementation) 3496 */ 3497 _alignOnTrigger: function(type, args) { 3498 this.align(); 3499 }, 3500 3501 /** 3502 * Helper method to locate the custom event instance for the event name string 3503 * passed in. As a convenience measure, any custom events passed in are returned. 3504 * 3505 * @method _findTriggerCE 3506 * @private 3507 * 3508 * @param {String|CustomEvent} t Either a CustomEvent, or event type (e.g. "windowScroll") for which a 3509 * custom event instance needs to be looked up from the Overlay._TRIGGER_MAP. 3510 */ 3511 _findTriggerCE : function(t) { 3512 var tce = null; 3513 if (t instanceof CustomEvent) { 3514 tce = t; 3515 } else if (Overlay._TRIGGER_MAP[t]) { 3516 tce = Overlay._TRIGGER_MAP[t]; 3517 } 3518 return tce; 3519 }, 3520 3521 /** 3522 * Utility method that subscribes or unsubscribes the given 3523 * function from the list of trigger events provided. 3524 * 3525 * @method _processTriggers 3526 * @protected 3527 * 3528 * @param {Array[String|CustomEvent]} triggers An array of either CustomEvents, event type strings 3529 * (e.g. "beforeShow", "windowScroll") to/from which the provided function should be 3530 * subscribed/unsubscribed respectively. 3531 * 3532 * @param {String} mode Either "subscribe" or "unsubscribe", specifying whether or not 3533 * we are subscribing or unsubscribing trigger listeners 3534 * 3535 * @param {Function} fn The function to be subscribed/unsubscribed to/from the trigger event. 3536 * Context is always set to the overlay instance, and no additional object argument 3537 * get passed to the subscribed function. 3538 */ 3539 _processTriggers : function(triggers, mode, fn) { 3540 var t, tce; 3541 3542 for (var i = 0, l = triggers.length; i < l; ++i) { 3543 t = triggers[i]; 3544 tce = this._findTriggerCE(t); 3545 if (tce) { 3546 tce[mode](fn, this, true); 3547 } else { 3548 this[mode](t, fn); 3549 } 3550 } 3551 }, 3552 3553 // END BUILT-IN PROPERTY EVENT HANDLERS // 3554 /** 3555 * Aligns the Overlay to its context element using the specified corner 3556 * points (represented by the constants TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, 3557 * and BOTTOM_RIGHT. 3558 * @method align 3559 * @param {String} elementAlign The String representing the corner of 3560 * the Overlay that should be aligned to the context element 3561 * @param {String} contextAlign The corner of the context element 3562 * that the elementAlign corner should stick to. 3563 * @param {Number[]} xyOffset Optional. A 2 element array specifying the x and y pixel offsets which should be applied 3564 * after aligning the element and context corners. For example, passing in [5, -10] for this value, would offset the 3565 * Overlay by 5 pixels along the X axis (horizontally) and -10 pixels along the Y axis (vertically) after aligning the specified corners. 3566 */ 3567 align: function (elementAlign, contextAlign, xyOffset) { 3568 3569 var contextArgs = this.cfg.getProperty("context"), 3570 me = this, 3571 context, 3572 element, 3573 contextRegion; 3574 3575 function doAlign(v, h) { 3576 3577 var alignX = null, alignY = null; 3578 3579 switch (elementAlign) { 3580 3581 case Overlay.TOP_LEFT: 3582 alignX = h; 3583 alignY = v; 3584 break; 3585 3586 case Overlay.TOP_RIGHT: 3587 alignX = h - element.offsetWidth; 3588 alignY = v; 3589 break; 3590 3591 case Overlay.BOTTOM_LEFT: 3592 alignX = h; 3593 alignY = v - element.offsetHeight; 3594 break; 3595 3596 case Overlay.BOTTOM_RIGHT: 3597 alignX = h - element.offsetWidth; 3598 alignY = v - element.offsetHeight; 3599 break; 3600 } 3601 3602 if (alignX !== null && alignY !== null) { 3603 if (xyOffset) { 3604 alignX += xyOffset[0]; 3605 alignY += xyOffset[1]; 3606 } 3607 me.moveTo(alignX, alignY); 3608 } 3609 } 3610 3611 if (contextArgs) { 3612 context = contextArgs[0]; 3613 element = this.element; 3614 me = this; 3615 3616 if (! elementAlign) { 3617 elementAlign = contextArgs[1]; 3618 } 3619 3620 if (! contextAlign) { 3621 contextAlign = contextArgs[2]; 3622 } 3623 3624 if (!xyOffset && contextArgs[4]) { 3625 xyOffset = contextArgs[4]; 3626 } 3627 3628 if (element && context) { 3629 contextRegion = Dom.getRegion(context); 3630 3631 switch (contextAlign) { 3632 3633 case Overlay.TOP_LEFT: 3634 doAlign(contextRegion.top, contextRegion.left); 3635 break; 3636 3637 case Overlay.TOP_RIGHT: 3638 doAlign(contextRegion.top, contextRegion.right); 3639 break; 3640 3641 case Overlay.BOTTOM_LEFT: 3642 doAlign(contextRegion.bottom, contextRegion.left); 3643 break; 3644 3645 case Overlay.BOTTOM_RIGHT: 3646 doAlign(contextRegion.bottom, contextRegion.right); 3647 break; 3648 } 3649 } 3650 } 3651 }, 3652 3653 /** 3654 * The default event handler executed when the moveEvent is fired, if the 3655 * "constraintoviewport" is set to true. 3656 * @method enforceConstraints 3657 * @param {String} type The CustomEvent type (usually the property name) 3658 * @param {Object[]} args The CustomEvent arguments. For configuration 3659 * handlers, args[0] will equal the newly applied value for the property. 3660 * @param {Object} obj The scope object. For configuration handlers, 3661 * this will usually equal the owner. 3662 */ 3663 enforceConstraints: function (type, args, obj) { 3664 var pos = args[0]; 3665 3666 var cXY = this.getConstrainedXY(pos[0], pos[1]); 3667 this.cfg.setProperty("x", cXY[0], true); 3668 this.cfg.setProperty("y", cXY[1], true); 3669 this.cfg.setProperty("xy", cXY, true); 3670 }, 3671 3672 /** 3673 * Shared implementation method for getConstrainedX and getConstrainedY. 3674 * 3675 * <p> 3676 * Given a coordinate value, returns the calculated coordinate required to 3677 * position the Overlay if it is to be constrained to the viewport, based on the 3678 * current element size, viewport dimensions, scroll values and preventoverlap 3679 * settings 3680 * </p> 3681 * 3682 * @method _getConstrainedPos 3683 * @protected 3684 * @param {String} pos The coordinate which needs to be constrained, either "x" or "y" 3685 * @param {Number} The coordinate value which needs to be constrained 3686 * @return {Number} The constrained coordinate value 3687 */ 3688 _getConstrainedPos: function(pos, val) { 3689 3690 var overlayEl = this.element, 3691 3692 buffer = Overlay.VIEWPORT_OFFSET, 3693 3694 x = (pos == "x"), 3695 3696 overlaySize = (x) ? overlayEl.offsetWidth : overlayEl.offsetHeight, 3697 viewportSize = (x) ? Dom.getViewportWidth() : Dom.getViewportHeight(), 3698 docScroll = (x) ? Dom.getDocumentScrollLeft() : Dom.getDocumentScrollTop(), 3699 overlapPositions = (x) ? Overlay.PREVENT_OVERLAP_X : Overlay.PREVENT_OVERLAP_Y, 3700 3701 context = this.cfg.getProperty("context"), 3702 3703 bOverlayFitsInViewport = (overlaySize + buffer < viewportSize), 3704 bPreventContextOverlap = this.cfg.getProperty("preventcontextoverlap") && context && overlapPositions[(context[1] + context[2])], 3705 3706 minConstraint = docScroll + buffer, 3707 maxConstraint = docScroll + viewportSize - overlaySize - buffer, 3708 3709 constrainedVal = val; 3710 3711 if (val < minConstraint || val > maxConstraint) { 3712 if (bPreventContextOverlap) { 3713 constrainedVal = this._preventOverlap(pos, context[0], overlaySize, viewportSize, docScroll); 3714 } else { 3715 if (bOverlayFitsInViewport) { 3716 if (val < minConstraint) { 3717 constrainedVal = minConstraint; 3718 } else if (val > maxConstraint) { 3719 constrainedVal = maxConstraint; 3720 } 3721 } else { 3722 constrainedVal = minConstraint; 3723 } 3724 } 3725 } 3726 3727 return constrainedVal; 3728 }, 3729 3730 /** 3731 * Helper method, used to position the Overlap to prevent overlap with the 3732 * context element (used when preventcontextoverlap is enabled) 3733 * 3734 * @method _preventOverlap 3735 * @protected 3736 * @param {String} pos The coordinate to prevent overlap for, either "x" or "y". 3737 * @param {HTMLElement} contextEl The context element 3738 * @param {Number} overlaySize The related overlay dimension value (for "x", the width, for "y", the height) 3739 * @param {Number} viewportSize The related viewport dimension value (for "x", the width, for "y", the height) 3740 * @param {Object} docScroll The related document scroll value (for "x", the scrollLeft, for "y", the scrollTop) 3741 * 3742 * @return {Number} The new coordinate value which was set to prevent overlap 3743 */ 3744 _preventOverlap : function(pos, contextEl, overlaySize, viewportSize, docScroll) { 3745 3746 var x = (pos == "x"), 3747 3748 buffer = Overlay.VIEWPORT_OFFSET, 3749 3750 overlay = this, 3751 3752 contextElPos = ((x) ? Dom.getX(contextEl) : Dom.getY(contextEl)) - docScroll, 3753 contextElSize = (x) ? contextEl.offsetWidth : contextEl.offsetHeight, 3754 3755 minRegionSize = contextElPos - buffer, 3756 maxRegionSize = (viewportSize - (contextElPos + contextElSize)) - buffer, 3757 3758 bFlipped = false, 3759 3760 flip = function () { 3761 var flippedVal; 3762 3763 if ((overlay.cfg.getProperty(pos) - docScroll) > contextElPos) { 3764 flippedVal = (contextElPos - overlaySize); 3765 } else { 3766 flippedVal = (contextElPos + contextElSize); 3767 } 3768 3769 overlay.cfg.setProperty(pos, (flippedVal + docScroll), true); 3770 3771 return flippedVal; 3772 }, 3773 3774 setPosition = function () { 3775 3776 var displayRegionSize = ((overlay.cfg.getProperty(pos) - docScroll) > contextElPos) ? maxRegionSize : minRegionSize, 3777 position; 3778 3779 if (overlaySize > displayRegionSize) { 3780 if (bFlipped) { 3781 /* 3782 All possible positions and values have been 3783 tried, but none were successful, so fall back 3784 to the original size and position. 3785 */ 3786 flip(); 3787 } else { 3788 flip(); 3789 bFlipped = true; 3790 position = setPosition(); 3791 } 3792 } 3793 3794 return position; 3795 }; 3796 3797 setPosition(); 3798 3799 return this.cfg.getProperty(pos); 3800 }, 3801 3802 /** 3803 * Given x coordinate value, returns the calculated x coordinate required to 3804 * position the Overlay if it is to be constrained to the viewport, based on the 3805 * current element size, viewport dimensions and scroll values. 3806 * 3807 * @param {Number} x The X coordinate value to be constrained 3808 * @return {Number} The constrained x coordinate 3809 */ 3810 getConstrainedX: function (x) { 3811 return this._getConstrainedPos("x", x); 3812 }, 3813 3814 /** 3815 * Given y coordinate value, returns the calculated y coordinate required to 3816 * position the Overlay if it is to be constrained to the viewport, based on the 3817 * current element size, viewport dimensions and scroll values. 3818 * 3819 * @param {Number} y The Y coordinate value to be constrained 3820 * @return {Number} The constrained y coordinate 3821 */ 3822 getConstrainedY : function (y) { 3823 return this._getConstrainedPos("y", y); 3824 }, 3825 3826 /** 3827 * Given x, y coordinate values, returns the calculated coordinates required to 3828 * position the Overlay if it is to be constrained to the viewport, based on the 3829 * current element size, viewport dimensions and scroll values. 3830 * 3831 * @param {Number} x The X coordinate value to be constrained 3832 * @param {Number} y The Y coordinate value to be constrained 3833 * @return {Array} The constrained x and y coordinates at index 0 and 1 respectively; 3834 */ 3835 getConstrainedXY: function(x, y) { 3836 return [this.getConstrainedX(x), this.getConstrainedY(y)]; 3837 }, 3838 3839 /** 3840 * Centers the container in the viewport. 3841 * @method center 3842 */ 3843 center: function () { 3844 3845 var nViewportOffset = Overlay.VIEWPORT_OFFSET, 3846 elementWidth = this.element.offsetWidth, 3847 elementHeight = this.element.offsetHeight, 3848 viewPortWidth = Dom.getViewportWidth(), 3849 viewPortHeight = Dom.getViewportHeight(), 3850 x, 3851 y; 3852 3853 if (elementWidth < viewPortWidth) { 3854 x = (viewPortWidth / 2) - (elementWidth / 2) + Dom.getDocumentScrollLeft(); 3855 } else { 3856 x = nViewportOffset + Dom.getDocumentScrollLeft(); 3857 } 3858 3859 if (elementHeight < viewPortHeight) { 3860 y = (viewPortHeight / 2) - (elementHeight / 2) + Dom.getDocumentScrollTop(); 3861 } else { 3862 y = nViewportOffset + Dom.getDocumentScrollTop(); 3863 } 3864 3865 this.cfg.setProperty("xy", [parseInt(x, 10), parseInt(y, 10)]); 3866 this.cfg.refireEvent("iframe"); 3867 3868 if (UA.webkit) { 3869 this.forceContainerRedraw(); 3870 } 3871 }, 3872 3873 /** 3874 * Synchronizes the Panel's "xy", "x", and "y" properties with the 3875 * Panel's position in the DOM. This is primarily used to update 3876 * position information during drag & drop. 3877 * @method syncPosition 3878 */ 3879 syncPosition: function () { 3880 3881 var pos = Dom.getXY(this.element); 3882 3883 this.cfg.setProperty("x", pos[0], true); 3884 this.cfg.setProperty("y", pos[1], true); 3885 this.cfg.setProperty("xy", pos, true); 3886 3887 }, 3888 3889 /** 3890 * Event handler fired when the resize monitor element is resized. 3891 * @method onDomResize 3892 * @param {DOMEvent} e The resize DOM event 3893 * @param {Object} obj The scope object 3894 */ 3895 onDomResize: function (e, obj) { 3896 3897 var me = this; 3898 3899 Overlay.superclass.onDomResize.call(this, e, obj); 3900 3901 setTimeout(function () { 3902 me.syncPosition(); 3903 me.cfg.refireEvent("iframe"); 3904 me.cfg.refireEvent("context"); 3905 }, 0); 3906 }, 3907 3908 /** 3909 * Determines the content box height of the given element (height of the element, without padding or borders) in pixels. 3910 * 3911 * @method _getComputedHeight 3912 * @private 3913 * @param {HTMLElement} el The element for which the content height needs to be determined 3914 * @return {Number} The content box height of the given element, or null if it could not be determined. 3915 */ 3916 _getComputedHeight : (function() { 3917 3918 if (document.defaultView && document.defaultView.getComputedStyle) { 3919 return function(el) { 3920 var height = null; 3921 if (el.ownerDocument && el.ownerDocument.defaultView) { 3922 var computed = el.ownerDocument.defaultView.getComputedStyle(el, ''); 3923 if (computed) { 3924 height = parseInt(computed.height, 10); 3925 } 3926 } 3927 return (Lang.isNumber(height)) ? height : null; 3928 }; 3929 } else { 3930 return function(el) { 3931 var height = null; 3932 if (el.style.pixelHeight) { 3933 height = el.style.pixelHeight; 3934 } 3935 return (Lang.isNumber(height)) ? height : null; 3936 }; 3937 } 3938 })(), 3939 3940 /** 3941 * autofillheight validator. Verifies that the autofill value is either null 3942 * or one of the strings : "body", "header" or "footer". 3943 * 3944 * @method _validateAutoFillHeight 3945 * @protected 3946 * @param {String} val 3947 * @return true, if valid, false otherwise 3948 */ 3949 _validateAutoFillHeight : function(val) { 3950 return (!val) || (Lang.isString(val) && Overlay.STD_MOD_RE.test(val)); 3951 }, 3952 3953 /** 3954 * The default custom event handler executed when the overlay's height is changed, 3955 * if the autofillheight property has been set. 3956 * 3957 * @method _autoFillOnHeightChange 3958 * @protected 3959 * @param {String} type The event type 3960 * @param {Array} args The array of arguments passed to event subscribers 3961 * @param {HTMLElement} el The header, body or footer element which is to be resized to fill 3962 * out the containers height 3963 */ 3964 _autoFillOnHeightChange : function(type, args, el) { 3965 var height = this.cfg.getProperty("height"); 3966 if ((height && height !== "auto") || (height === 0)) { 3967 this.fillHeight(el); 3968 } 3969 }, 3970 3971 /** 3972 * Returns the sub-pixel height of the el, using getBoundingClientRect, if available, 3973 * otherwise returns the offsetHeight 3974 * @method _getPreciseHeight 3975 * @private 3976 * @param {HTMLElement} el 3977 * @return {Float} The sub-pixel height if supported by the browser, else the rounded height. 3978 */ 3979 _getPreciseHeight : function(el) { 3980 var height = el.offsetHeight; 3981 3982 if (el.getBoundingClientRect) { 3983 var rect = el.getBoundingClientRect(); 3984 height = rect.bottom - rect.top; 3985 } 3986 3987 return height; 3988 }, 3989 3990 /** 3991 * <p> 3992 * Sets the height on the provided header, body or footer element to 3993 * fill out the height of the container. It determines the height of the 3994 * containers content box, based on it's configured height value, and 3995 * sets the height of the autofillheight element to fill out any 3996 * space remaining after the other standard module element heights 3997 * have been accounted for. 3998 * </p> 3999 * <p><strong>NOTE:</strong> This method is not designed to work if an explicit 4000 * height has not been set on the container, since for an "auto" height container, 4001 * the heights of the header/body/footer will drive the height of the container.</p> 4002 * 4003 * @method fillHeight 4004 * @param {HTMLElement} el The element which should be resized to fill out the height 4005 * of the container element. 4006 */ 4007 fillHeight : function(el) { 4008 if (el) { 4009 var container = this.innerElement || this.element, 4010 containerEls = [this.header, this.body, this.footer], 4011 containerEl, 4012 total = 0, 4013 filled = 0, 4014 remaining = 0, 4015 validEl = false; 4016 4017 for (var i = 0, l = containerEls.length; i < l; i++) { 4018 containerEl = containerEls[i]; 4019 if (containerEl) { 4020 if (el !== containerEl) { 4021 filled += this._getPreciseHeight(containerEl); 4022 } else { 4023 validEl = true; 4024 } 4025 } 4026 } 4027 4028 if (validEl) { 4029 4030 if (UA.ie || UA.opera) { 4031 // Need to set height to 0, to allow height to be reduced 4032 Dom.setStyle(el, 'height', 0 + 'px'); 4033 } 4034 4035 total = this._getComputedHeight(container); 4036 4037 // Fallback, if we can't get computed value for content height 4038 if (total === null) { 4039 Dom.addClass(container, "yui-override-padding"); 4040 total = container.clientHeight; // Content, No Border, 0 Padding (set by yui-override-padding) 4041 Dom.removeClass(container, "yui-override-padding"); 4042 } 4043 4044 remaining = Math.max(total - filled, 0); 4045 4046 Dom.setStyle(el, "height", remaining + "px"); 4047 4048 // Re-adjust height if required, to account for el padding and border 4049 if (el.offsetHeight != remaining) { 4050 remaining = Math.max(remaining - (el.offsetHeight - remaining), 0); 4051 } 4052 Dom.setStyle(el, "height", remaining + "px"); 4053 } 4054 } 4055 }, 4056 4057 /** 4058 * Places the Overlay on top of all other instances of 4059 * YAHOO.widget.Overlay. 4060 * @method bringToTop 4061 */ 4062 bringToTop: function () { 4063 4064 var aOverlays = [], 4065 oElement = this.element; 4066 4067 function compareZIndexDesc(p_oOverlay1, p_oOverlay2) { 4068 4069 var sZIndex1 = Dom.getStyle(p_oOverlay1, "zIndex"), 4070 sZIndex2 = Dom.getStyle(p_oOverlay2, "zIndex"), 4071 4072 nZIndex1 = (!sZIndex1 || isNaN(sZIndex1)) ? 0 : parseInt(sZIndex1, 10), 4073 nZIndex2 = (!sZIndex2 || isNaN(sZIndex2)) ? 0 : parseInt(sZIndex2, 10); 4074 4075 if (nZIndex1 > nZIndex2) { 4076 return -1; 4077 } else if (nZIndex1 < nZIndex2) { 4078 return 1; 4079 } else { 4080 return 0; 4081 } 4082 } 4083 4084 function isOverlayElement(p_oElement) { 4085 4086 var isOverlay = Dom.hasClass(p_oElement, Overlay.CSS_OVERLAY), 4087 Panel = YAHOO.widget.Panel; 4088 4089 if (isOverlay && !Dom.isAncestor(oElement, p_oElement)) { 4090 if (Panel && Dom.hasClass(p_oElement, Panel.CSS_PANEL)) { 4091 aOverlays[aOverlays.length] = p_oElement.parentNode; 4092 } else { 4093 aOverlays[aOverlays.length] = p_oElement; 4094 } 4095 } 4096 } 4097 4098 Dom.getElementsBy(isOverlayElement, "div", document.body); 4099 4100 aOverlays.sort(compareZIndexDesc); 4101 4102 var oTopOverlay = aOverlays[0], 4103 nTopZIndex; 4104 4105 if (oTopOverlay) { 4106 nTopZIndex = Dom.getStyle(oTopOverlay, "zIndex"); 4107 4108 if (!isNaN(nTopZIndex)) { 4109 var bRequiresBump = false; 4110 4111 if (oTopOverlay != oElement) { 4112 bRequiresBump = true; 4113 } else if (aOverlays.length > 1) { 4114 var nNextZIndex = Dom.getStyle(aOverlays[1], "zIndex"); 4115 // Don't rely on DOM order to stack if 2 overlays are at the same zindex. 4116 if (!isNaN(nNextZIndex) && (nTopZIndex == nNextZIndex)) { 4117 bRequiresBump = true; 4118 } 4119 } 4120 if (bRequiresBump) { 4121 this.cfg.setProperty("zindex", (parseInt(nTopZIndex, 10) + 2)); 4122 } 4123 } 4124 } 4125 }, 4126 4127 /** 4128 * Removes the Overlay element from the DOM and sets all child 4129 * elements to null. 4130 * @method destroy 4131 * @param {boolean} shallowPurge If true, only the parent element's DOM event listeners are purged. If false, or not provided, all children are also purged of DOM event listeners. 4132 * NOTE: The flag is a "shallowPurge" flag, as opposed to what may be a more intuitive "purgeChildren" flag to maintain backwards compatibility with behavior prior to 2.9.0. 4133 */ 4134 destroy: function (shallowPurge) { 4135 4136 if (this.iframe) { 4137 this.iframe.parentNode.removeChild(this.iframe); 4138 } 4139 4140 this.iframe = null; 4141 4142 Overlay.windowResizeEvent.unsubscribe( 4143 this.doCenterOnDOMEvent, this); 4144 4145 Overlay.windowScrollEvent.unsubscribe( 4146 this.doCenterOnDOMEvent, this); 4147 4148 Module.textResizeEvent.unsubscribe(this._autoFillOnHeightChange); 4149 4150 if (this._contextTriggers) { 4151 // Unsubscribe context triggers - to cover context triggers which listen for global 4152 // events such as windowResize and windowScroll. Easier just to unsubscribe all 4153 this._processTriggers(this._contextTriggers, _UNSUBSCRIBE, this._alignOnTrigger); 4154 } 4155 4156 Overlay.superclass.destroy.call(this, shallowPurge); 4157 }, 4158 4159 /** 4160 * Can be used to force the container to repaint/redraw it's contents. 4161 * <p> 4162 * By default applies and then removes a 1px bottom margin through the 4163 * application/removal of a "yui-force-redraw" class. 4164 * </p> 4165 * <p> 4166 * It is currently used by Overlay to force a repaint for webkit 4167 * browsers, when centering. 4168 * </p> 4169 * @method forceContainerRedraw 4170 */ 4171 forceContainerRedraw : function() { 4172 var c = this; 4173 Dom.addClass(c.element, "yui-force-redraw"); 4174 setTimeout(function() { 4175 Dom.removeClass(c.element, "yui-force-redraw"); 4176 }, 0); 4177 }, 4178 4179 /** 4180 * Returns a String representation of the object. 4181 * @method toString 4182 * @return {String} The string representation of the Overlay. 4183 */ 4184 toString: function () { 4185 return "Overlay " + this.id; 4186 } 4187 4188 }); 4189 }()); 4190 (function () { 4191 4192 /** 4193 * OverlayManager is used for maintaining the focus status of 4194 * multiple Overlays. 4195 * @namespace YAHOO.widget 4196 * @namespace YAHOO.widget 4197 * @class OverlayManager 4198 * @constructor 4199 * @param {Array} overlays Optional. A collection of Overlays to register 4200 * with the manager. 4201 * @param {Object} userConfig The object literal representing the user 4202 * configuration of the OverlayManager 4203 */ 4204 YAHOO.widget.OverlayManager = function (userConfig) { 4205 this.init(userConfig); 4206 }; 4207 4208 var Overlay = YAHOO.widget.Overlay, 4209 Event = YAHOO.util.Event, 4210 Dom = YAHOO.util.Dom, 4211 Config = YAHOO.util.Config, 4212 CustomEvent = YAHOO.util.CustomEvent, 4213 OverlayManager = YAHOO.widget.OverlayManager; 4214 4215 /** 4216 * The CSS class representing a focused Overlay 4217 * @property OverlayManager.CSS_FOCUSED 4218 * @static 4219 * @final 4220 * @type String 4221 */ 4222 OverlayManager.CSS_FOCUSED = "focused"; 4223 4224 OverlayManager.prototype = { 4225 4226 /** 4227 * The class's constructor function 4228 * @property contructor 4229 * @type Function 4230 */ 4231 constructor: OverlayManager, 4232 4233 /** 4234 * The array of Overlays that are currently registered 4235 * @property overlays 4236 * @type YAHOO.widget.Overlay[] 4237 */ 4238 overlays: null, 4239 4240 /** 4241 * Initializes the default configuration of the OverlayManager 4242 * @method initDefaultConfig 4243 */ 4244 initDefaultConfig: function () { 4245 /** 4246 * The collection of registered Overlays in use by 4247 * the OverlayManager 4248 * @config overlays 4249 * @type YAHOO.widget.Overlay[] 4250 * @default null 4251 */ 4252 this.cfg.addProperty("overlays", { suppressEvent: true } ); 4253 4254 /** 4255 * The default DOM event that should be used to focus an Overlay 4256 * @config focusevent 4257 * @type String 4258 * @default "mousedown" 4259 */ 4260 this.cfg.addProperty("focusevent", { value: "mousedown" } ); 4261 }, 4262 4263 /** 4264 * Initializes the OverlayManager 4265 * @method init 4266 * @param {Overlay[]} overlays Optional. A collection of Overlays to 4267 * register with the manager. 4268 * @param {Object} userConfig The object literal representing the user 4269 * configuration of the OverlayManager 4270 */ 4271 init: function (userConfig) { 4272 4273 /** 4274 * The OverlayManager's Config object used for monitoring 4275 * configuration properties. 4276 * @property cfg 4277 * @type Config 4278 */ 4279 this.cfg = new Config(this); 4280 4281 this.initDefaultConfig(); 4282 4283 if (userConfig) { 4284 this.cfg.applyConfig(userConfig, true); 4285 } 4286 this.cfg.fireQueue(); 4287 4288 /** 4289 * The currently activated Overlay 4290 * @property activeOverlay 4291 * @private 4292 * @type YAHOO.widget.Overlay 4293 */ 4294 var activeOverlay = null; 4295 4296 /** 4297 * Returns the currently focused Overlay 4298 * @method getActive 4299 * @return {Overlay} The currently focused Overlay 4300 */ 4301 this.getActive = function () { 4302 return activeOverlay; 4303 }; 4304 4305 /** 4306 * Focuses the specified Overlay 4307 * @method focus 4308 * @param {Overlay} overlay The Overlay to focus 4309 * @param {String} overlay The id of the Overlay to focus 4310 */ 4311 this.focus = function (overlay) { 4312 var o = this.find(overlay); 4313 if (o) { 4314 o.focus(); 4315 } 4316 }; 4317 4318 /** 4319 * Removes the specified Overlay from the manager 4320 * @method remove 4321 * @param {Overlay} overlay The Overlay to remove 4322 * @param {String} overlay The id of the Overlay to remove 4323 */ 4324 this.remove = function (overlay) { 4325 4326 var o = this.find(overlay), 4327 originalZ; 4328 4329 if (o) { 4330 if (activeOverlay == o) { 4331 activeOverlay = null; 4332 } 4333 4334 var bDestroyed = (o.element === null && o.cfg === null) ? true : false; 4335 4336 if (!bDestroyed) { 4337 // Set it's zindex so that it's sorted to the end. 4338 originalZ = Dom.getStyle(o.element, "zIndex"); 4339 o.cfg.setProperty("zIndex", -1000, true); 4340 } 4341 4342 this.overlays.sort(this.compareZIndexDesc); 4343 this.overlays = this.overlays.slice(0, (this.overlays.length - 1)); 4344 4345 o.hideEvent.unsubscribe(o.blur); 4346 o.destroyEvent.unsubscribe(this._onOverlayDestroy, o); 4347 o.focusEvent.unsubscribe(this._onOverlayFocusHandler, o); 4348 o.blurEvent.unsubscribe(this._onOverlayBlurHandler, o); 4349 4350 if (!bDestroyed) { 4351 Event.removeListener(o.element, this.cfg.getProperty("focusevent"), this._onOverlayElementFocus); 4352 o.cfg.setProperty("zIndex", originalZ, true); 4353 o.cfg.setProperty("manager", null); 4354 } 4355 4356 /* _managed Flag for custom or existing. Don't want to remove existing */ 4357 if (o.focusEvent._managed) { o.focusEvent = null; } 4358 if (o.blurEvent._managed) { o.blurEvent = null; } 4359 4360 if (o.focus._managed) { o.focus = null; } 4361 if (o.blur._managed) { o.blur = null; } 4362 } 4363 }; 4364 4365 /** 4366 * Removes focus from all registered Overlays in the manager 4367 * @method blurAll 4368 */ 4369 this.blurAll = function () { 4370 4371 var nOverlays = this.overlays.length, 4372 i; 4373 4374 if (nOverlays > 0) { 4375 i = nOverlays - 1; 4376 do { 4377 this.overlays[i].blur(); 4378 } 4379 while(i--); 4380 } 4381 }; 4382 4383 /** 4384 * Updates the state of the OverlayManager and overlay, as a result of the overlay 4385 * being blurred. 4386 * 4387 * @method _manageBlur 4388 * @param {Overlay} overlay The overlay instance which got blurred. 4389 * @protected 4390 */ 4391 this._manageBlur = function (overlay) { 4392 var changed = false; 4393 if (activeOverlay == overlay) { 4394 Dom.removeClass(activeOverlay.element, OverlayManager.CSS_FOCUSED); 4395 activeOverlay = null; 4396 changed = true; 4397 } 4398 return changed; 4399 }; 4400 4401 /** 4402 * Updates the state of the OverlayManager and overlay, as a result of the overlay 4403 * receiving focus. 4404 * 4405 * @method _manageFocus 4406 * @param {Overlay} overlay The overlay instance which got focus. 4407 * @protected 4408 */ 4409 this._manageFocus = function(overlay) { 4410 var changed = false; 4411 if (activeOverlay != overlay) { 4412 if (activeOverlay) { 4413 activeOverlay.blur(); 4414 } 4415 activeOverlay = overlay; 4416 this.bringToTop(activeOverlay); 4417 Dom.addClass(activeOverlay.element, OverlayManager.CSS_FOCUSED); 4418 changed = true; 4419 } 4420 return changed; 4421 }; 4422 4423 var overlays = this.cfg.getProperty("overlays"); 4424 4425 if (! this.overlays) { 4426 this.overlays = []; 4427 } 4428 4429 if (overlays) { 4430 this.register(overlays); 4431 this.overlays.sort(this.compareZIndexDesc); 4432 } 4433 }, 4434 4435 /** 4436 * @method _onOverlayElementFocus 4437 * @description Event handler for the DOM event that is used to focus 4438 * the Overlay instance as specified by the "focusevent" 4439 * configuration property. 4440 * @private 4441 * @param {Event} p_oEvent Object representing the DOM event 4442 * object passed back by the event utility (Event). 4443 */ 4444 _onOverlayElementFocus: function (p_oEvent) { 4445 4446 var oTarget = Event.getTarget(p_oEvent), 4447 oClose = this.close; 4448 4449 if (oClose && (oTarget == oClose || Dom.isAncestor(oClose, oTarget))) { 4450 this.blur(); 4451 } else { 4452 this.focus(); 4453 } 4454 }, 4455 4456 /** 4457 * @method _onOverlayDestroy 4458 * @description "destroy" event handler for the Overlay. 4459 * @private 4460 * @param {String} p_sType String representing the name of the event 4461 * that was fired. 4462 * @param {Array} p_aArgs Array of arguments sent when the event 4463 * was fired. 4464 * @param {Overlay} p_oOverlay Object representing the overlay that 4465 * fired the event. 4466 */ 4467 _onOverlayDestroy: function (p_sType, p_aArgs, p_oOverlay) { 4468 this.remove(p_oOverlay); 4469 }, 4470 4471 /** 4472 * @method _onOverlayFocusHandler 4473 * 4474 * @description focusEvent Handler, used to delegate to _manageFocus with the correct arguments. 4475 * 4476 * @private 4477 * @param {String} p_sType String representing the name of the event 4478 * that was fired. 4479 * @param {Array} p_aArgs Array of arguments sent when the event 4480 * was fired. 4481 * @param {Overlay} p_oOverlay Object representing the overlay that 4482 * fired the event. 4483 */ 4484 _onOverlayFocusHandler: function(p_sType, p_aArgs, p_oOverlay) { 4485 this._manageFocus(p_oOverlay); 4486 }, 4487 4488 /** 4489 * @method _onOverlayBlurHandler 4490 * @description blurEvent Handler, used to delegate to _manageBlur with the correct arguments. 4491 * 4492 * @private 4493 * @param {String} p_sType String representing the name of the event 4494 * that was fired. 4495 * @param {Array} p_aArgs Array of arguments sent when the event 4496 * was fired. 4497 * @param {Overlay} p_oOverlay Object representing the overlay that 4498 * fired the event. 4499 */ 4500 _onOverlayBlurHandler: function(p_sType, p_aArgs, p_oOverlay) { 4501 this._manageBlur(p_oOverlay); 4502 }, 4503 4504 /** 4505 * Subscribes to the Overlay based instance focusEvent, to allow the OverlayManager to 4506 * monitor focus state. 4507 * 4508 * If the instance already has a focusEvent (e.g. Menu), OverlayManager will subscribe 4509 * to the existing focusEvent, however if a focusEvent or focus method does not exist 4510 * on the instance, the _bindFocus method will add them, and the focus method will 4511 * update the OverlayManager's state directly. 4512 * 4513 * @method _bindFocus 4514 * @param {Overlay} overlay The overlay for which focus needs to be managed 4515 * @protected 4516 */ 4517 _bindFocus : function(overlay) { 4518 var mgr = this; 4519 4520 if (!overlay.focusEvent) { 4521 overlay.focusEvent = overlay.createEvent("focus"); 4522 overlay.focusEvent.signature = CustomEvent.LIST; 4523 overlay.focusEvent._managed = true; 4524 } else { 4525 overlay.focusEvent.subscribe(mgr._onOverlayFocusHandler, overlay, mgr); 4526 } 4527 4528 if (!overlay.focus) { 4529 Event.on(overlay.element, mgr.cfg.getProperty("focusevent"), mgr._onOverlayElementFocus, null, overlay); 4530 overlay.focus = function () { 4531 if (mgr._manageFocus(this)) { 4532 // For Panel/Dialog 4533 if (this.cfg.getProperty("visible") && this.focusFirst) { 4534 this.focusFirst(); 4535 } 4536 this.focusEvent.fire(); 4537 } 4538 }; 4539 overlay.focus._managed = true; 4540 } 4541 }, 4542 4543 /** 4544 * Subscribes to the Overlay based instance's blurEvent to allow the OverlayManager to 4545 * monitor blur state. 4546 * 4547 * If the instance already has a blurEvent (e.g. Menu), OverlayManager will subscribe 4548 * to the existing blurEvent, however if a blurEvent or blur method does not exist 4549 * on the instance, the _bindBlur method will add them, and the blur method 4550 * update the OverlayManager's state directly. 4551 * 4552 * @method _bindBlur 4553 * @param {Overlay} overlay The overlay for which blur needs to be managed 4554 * @protected 4555 */ 4556 _bindBlur : function(overlay) { 4557 var mgr = this; 4558 4559 if (!overlay.blurEvent) { 4560 overlay.blurEvent = overlay.createEvent("blur"); 4561 overlay.blurEvent.signature = CustomEvent.LIST; 4562 overlay.focusEvent._managed = true; 4563 } else { 4564 overlay.blurEvent.subscribe(mgr._onOverlayBlurHandler, overlay, mgr); 4565 } 4566 4567 if (!overlay.blur) { 4568 overlay.blur = function () { 4569 if (mgr._manageBlur(this)) { 4570 this.blurEvent.fire(); 4571 } 4572 }; 4573 overlay.blur._managed = true; 4574 } 4575 4576 overlay.hideEvent.subscribe(overlay.blur); 4577 }, 4578 4579 /** 4580 * Subscribes to the Overlay based instance's destroyEvent, to allow the Overlay 4581 * to be removed for the OverlayManager when destroyed. 4582 * 4583 * @method _bindDestroy 4584 * @param {Overlay} overlay The overlay instance being managed 4585 * @protected 4586 */ 4587 _bindDestroy : function(overlay) { 4588 var mgr = this; 4589 overlay.destroyEvent.subscribe(mgr._onOverlayDestroy, overlay, mgr); 4590 }, 4591 4592 /** 4593 * Ensures the zIndex configuration property on the managed overlay based instance 4594 * is set to the computed zIndex value from the DOM (with "auto" translating to 0). 4595 * 4596 * @method _syncZIndex 4597 * @param {Overlay} overlay The overlay instance being managed 4598 * @protected 4599 */ 4600 _syncZIndex : function(overlay) { 4601 var zIndex = Dom.getStyle(overlay.element, "zIndex"); 4602 if (!isNaN(zIndex)) { 4603 overlay.cfg.setProperty("zIndex", parseInt(zIndex, 10)); 4604 } else { 4605 overlay.cfg.setProperty("zIndex", 0); 4606 } 4607 }, 4608 4609 /** 4610 * Registers an Overlay or an array of Overlays with the manager. Upon 4611 * registration, the Overlay receives functions for focus and blur, 4612 * along with CustomEvents for each. 4613 * 4614 * @method register 4615 * @param {Overlay} overlay An Overlay to register with the manager. 4616 * @param {Overlay[]} overlay An array of Overlays to register with 4617 * the manager. 4618 * @return {boolean} true if any Overlays are registered. 4619 */ 4620 register: function (overlay) { 4621 4622 var registered = false, 4623 i, 4624 n; 4625 4626 if (overlay instanceof Overlay) { 4627 4628 overlay.cfg.addProperty("manager", { value: this } ); 4629 4630 this._bindFocus(overlay); 4631 this._bindBlur(overlay); 4632 this._bindDestroy(overlay); 4633 this._syncZIndex(overlay); 4634 4635 this.overlays.push(overlay); 4636 this.bringToTop(overlay); 4637 4638 registered = true; 4639 4640 } else if (overlay instanceof Array) { 4641 4642 for (i = 0, n = overlay.length; i < n; i++) { 4643 registered = this.register(overlay[i]) || registered; 4644 } 4645 4646 } 4647 4648 return registered; 4649 }, 4650 4651 /** 4652 * Places the specified Overlay instance on top of all other 4653 * Overlay instances. 4654 * @method bringToTop 4655 * @param {YAHOO.widget.Overlay} p_oOverlay Object representing an 4656 * Overlay instance. 4657 * @param {String} p_oOverlay String representing the id of an 4658 * Overlay instance. 4659 */ 4660 bringToTop: function (p_oOverlay) { 4661 4662 var oOverlay = this.find(p_oOverlay), 4663 nTopZIndex, 4664 oTopOverlay, 4665 aOverlays; 4666 4667 if (oOverlay) { 4668 4669 aOverlays = this.overlays; 4670 aOverlays.sort(this.compareZIndexDesc); 4671 4672 oTopOverlay = aOverlays[0]; 4673 4674 if (oTopOverlay) { 4675 nTopZIndex = Dom.getStyle(oTopOverlay.element, "zIndex"); 4676 4677 if (!isNaN(nTopZIndex)) { 4678 4679 var bRequiresBump = false; 4680 4681 if (oTopOverlay !== oOverlay) { 4682 bRequiresBump = true; 4683 } else if (aOverlays.length > 1) { 4684 var nNextZIndex = Dom.getStyle(aOverlays[1].element, "zIndex"); 4685 // Don't rely on DOM order to stack if 2 overlays are at the same zindex. 4686 if (!isNaN(nNextZIndex) && (nTopZIndex == nNextZIndex)) { 4687 bRequiresBump = true; 4688 } 4689 } 4690 4691 if (bRequiresBump) { 4692 oOverlay.cfg.setProperty("zindex", (parseInt(nTopZIndex, 10) + 2)); 4693 } 4694 } 4695 aOverlays.sort(this.compareZIndexDesc); 4696 } 4697 } 4698 }, 4699 4700 /** 4701 * Attempts to locate an Overlay by instance or ID. 4702 * @method find 4703 * @param {Overlay} overlay An Overlay to locate within the manager 4704 * @param {String} overlay An Overlay id to locate within the manager 4705 * @return {Overlay} The requested Overlay, if found, or null if it 4706 * cannot be located. 4707 */ 4708 find: function (overlay) { 4709 4710 var isInstance = overlay instanceof Overlay, 4711 overlays = this.overlays, 4712 n = overlays.length, 4713 found = null, 4714 o, 4715 i; 4716 4717 if (isInstance || typeof overlay == "string") { 4718 for (i = n-1; i >= 0; i--) { 4719 o = overlays[i]; 4720 if ((isInstance && (o === overlay)) || (o.id == overlay)) { 4721 found = o; 4722 break; 4723 } 4724 } 4725 } 4726 4727 return found; 4728 }, 4729 4730 /** 4731 * Used for sorting the manager's Overlays by z-index. 4732 * @method compareZIndexDesc 4733 * @private 4734 * @return {Number} 0, 1, or -1, depending on where the Overlay should 4735 * fall in the stacking order. 4736 */ 4737 compareZIndexDesc: function (o1, o2) { 4738 4739 var zIndex1 = (o1.cfg) ? o1.cfg.getProperty("zIndex") : null, // Sort invalid (destroyed) 4740 zIndex2 = (o2.cfg) ? o2.cfg.getProperty("zIndex") : null; // objects at bottom. 4741 4742 if (zIndex1 === null && zIndex2 === null) { 4743 return 0; 4744 } else if (zIndex1 === null){ 4745 return 1; 4746 } else if (zIndex2 === null) { 4747 return -1; 4748 } else if (zIndex1 > zIndex2) { 4749 return -1; 4750 } else if (zIndex1 < zIndex2) { 4751 return 1; 4752 } else { 4753 return 0; 4754 } 4755 }, 4756 4757 /** 4758 * Shows all Overlays in the manager. 4759 * @method showAll 4760 */ 4761 showAll: function () { 4762 var overlays = this.overlays, 4763 n = overlays.length, 4764 i; 4765 4766 for (i = n - 1; i >= 0; i--) { 4767 overlays[i].show(); 4768 } 4769 }, 4770 4771 /** 4772 * Hides all Overlays in the manager. 4773 * @method hideAll 4774 */ 4775 hideAll: function () { 4776 var overlays = this.overlays, 4777 n = overlays.length, 4778 i; 4779 4780 for (i = n - 1; i >= 0; i--) { 4781 overlays[i].hide(); 4782 } 4783 }, 4784 4785 /** 4786 * Returns a string representation of the object. 4787 * @method toString 4788 * @return {String} The string representation of the OverlayManager 4789 */ 4790 toString: function () { 4791 return "OverlayManager"; 4792 } 4793 }; 4794 }()); 4795 (function () { 4796 4797 /** 4798 * Tooltip is an implementation of Overlay that behaves like an OS tooltip, 4799 * displaying when the user mouses over a particular element, and 4800 * disappearing on mouse out. 4801 * @namespace YAHOO.widget 4802 * @class Tooltip 4803 * @extends YAHOO.widget.Overlay 4804 * @constructor 4805 * @param {String} el The element ID representing the Tooltip <em>OR</em> 4806 * @param {HTMLElement} el The element representing the Tooltip 4807 * @param {Object} userConfig The configuration object literal containing 4808 * the configuration that should be set for this Overlay. See configuration 4809 * documentation for more details. 4810 */ 4811 YAHOO.widget.Tooltip = function (el, userConfig) { 4812 YAHOO.widget.Tooltip.superclass.constructor.call(this, el, userConfig); 4813 }; 4814 4815 var Lang = YAHOO.lang, 4816 Event = YAHOO.util.Event, 4817 CustomEvent = YAHOO.util.CustomEvent, 4818 Dom = YAHOO.util.Dom, 4819 Tooltip = YAHOO.widget.Tooltip, 4820 UA = YAHOO.env.ua, 4821 bIEQuirks = (UA.ie && (UA.ie <= 6 || document.compatMode == "BackCompat")), 4822 4823 m_oShadowTemplate, 4824 4825 /** 4826 * Constant representing the Tooltip's configuration properties 4827 * @property DEFAULT_CONFIG 4828 * @private 4829 * @final 4830 * @type Object 4831 */ 4832 DEFAULT_CONFIG = { 4833 4834 "PREVENT_OVERLAP": { 4835 key: "preventoverlap", 4836 value: true, 4837 validator: Lang.isBoolean, 4838 supercedes: ["x", "y", "xy"] 4839 }, 4840 4841 "SHOW_DELAY": { 4842 key: "showdelay", 4843 value: 200, 4844 validator: Lang.isNumber 4845 }, 4846 4847 "AUTO_DISMISS_DELAY": { 4848 key: "autodismissdelay", 4849 value: 5000, 4850 validator: Lang.isNumber 4851 }, 4852 4853 "HIDE_DELAY": { 4854 key: "hidedelay", 4855 value: 250, 4856 validator: Lang.isNumber 4857 }, 4858 4859 "TEXT": { 4860 key: "text", 4861 suppressEvent: true 4862 }, 4863 4864 "CONTAINER": { 4865 key: "container" 4866 }, 4867 4868 "DISABLED": { 4869 key: "disabled", 4870 value: false, 4871 suppressEvent: true 4872 }, 4873 4874 "XY_OFFSET": { 4875 key: "xyoffset", 4876 value: [0, 25], 4877 suppressEvent: true 4878 } 4879 }, 4880 4881 /** 4882 * Constant representing the name of the Tooltip's events 4883 * @property EVENT_TYPES 4884 * @private 4885 * @final 4886 * @type Object 4887 */ 4888 EVENT_TYPES = { 4889 "CONTEXT_MOUSE_OVER": "contextMouseOver", 4890 "CONTEXT_MOUSE_OUT": "contextMouseOut", 4891 "CONTEXT_TRIGGER": "contextTrigger" 4892 }; 4893 4894 /** 4895 * Constant representing the Tooltip CSS class 4896 * @property YAHOO.widget.Tooltip.CSS_TOOLTIP 4897 * @static 4898 * @final 4899 * @type String 4900 */ 4901 Tooltip.CSS_TOOLTIP = "yui-tt"; 4902 4903 function restoreOriginalWidth(sOriginalWidth, sForcedWidth) { 4904 4905 var oConfig = this.cfg, 4906 sCurrentWidth = oConfig.getProperty("width"); 4907 4908 if (sCurrentWidth == sForcedWidth) { 4909 oConfig.setProperty("width", sOriginalWidth); 4910 } 4911 } 4912 4913 /* 4914 changeContent event handler that sets a Tooltip instance's "width" 4915 configuration property to the value of its root HTML 4916 elements's offsetWidth if a specific width has not been set. 4917 */ 4918 4919 function setWidthToOffsetWidth(p_sType, p_aArgs) { 4920 4921 if ("_originalWidth" in this) { 4922 restoreOriginalWidth.call(this, this._originalWidth, this._forcedWidth); 4923 } 4924 4925 var oBody = document.body, 4926 oConfig = this.cfg, 4927 sOriginalWidth = oConfig.getProperty("width"), 4928 sNewWidth, 4929 oClone; 4930 4931 if ((!sOriginalWidth || sOriginalWidth == "auto") && 4932 (oConfig.getProperty("container") != oBody || 4933 oConfig.getProperty("x") >= Dom.getViewportWidth() || 4934 oConfig.getProperty("y") >= Dom.getViewportHeight())) { 4935 4936 oClone = this.element.cloneNode(true); 4937 oClone.style.visibility = "hidden"; 4938 oClone.style.top = "0px"; 4939 oClone.style.left = "0px"; 4940 4941 oBody.appendChild(oClone); 4942 4943 sNewWidth = (oClone.offsetWidth + "px"); 4944 4945 oBody.removeChild(oClone); 4946 oClone = null; 4947 4948 oConfig.setProperty("width", sNewWidth); 4949 oConfig.refireEvent("xy"); 4950 4951 this._originalWidth = sOriginalWidth || ""; 4952 this._forcedWidth = sNewWidth; 4953 } 4954 } 4955 4956 // "onDOMReady" that renders the ToolTip 4957 4958 function onDOMReady(p_sType, p_aArgs, p_oObject) { 4959 this.render(p_oObject); 4960 } 4961 4962 // "init" event handler that automatically renders the Tooltip 4963 4964 function onInit() { 4965 Event.onDOMReady(onDOMReady, this.cfg.getProperty("container"), this); 4966 } 4967 4968 YAHOO.extend(Tooltip, YAHOO.widget.Overlay, { 4969 4970 /** 4971 * The Tooltip initialization method. This method is automatically 4972 * called by the constructor. A Tooltip is automatically rendered by 4973 * the init method, and it also is set to be invisible by default, 4974 * and constrained to viewport by default as well. 4975 * @method init 4976 * @param {String} el The element ID representing the Tooltip <em>OR</em> 4977 * @param {HTMLElement} el The element representing the Tooltip 4978 * @param {Object} userConfig The configuration object literal 4979 * containing the configuration that should be set for this Tooltip. 4980 * See configuration documentation for more details. 4981 */ 4982 init: function (el, userConfig) { 4983 4984 this.logger = new YAHOO.widget.LogWriter(this.toString()); 4985 4986 Tooltip.superclass.init.call(this, el); 4987 4988 this.beforeInitEvent.fire(Tooltip); 4989 4990 Dom.addClass(this.element, Tooltip.CSS_TOOLTIP); 4991 4992 if (userConfig) { 4993 this.cfg.applyConfig(userConfig, true); 4994 } 4995 4996 this.cfg.queueProperty("visible", false); 4997 this.cfg.queueProperty("constraintoviewport", true); 4998 4999 this.setBody(""); 5000 5001 this.subscribe("changeContent", setWidthToOffsetWidth); 5002 this.subscribe("init", onInit); 5003 this.subscribe("render", this.onRender); 5004 5005 this.initEvent.fire(Tooltip); 5006 }, 5007 5008 /** 5009 * Initializes the custom events for Tooltip 5010 * @method initEvents 5011 */ 5012 initEvents: function () { 5013 5014 Tooltip.superclass.initEvents.call(this); 5015 var SIGNATURE = CustomEvent.LIST; 5016 5017 /** 5018 * CustomEvent fired when user mouses over a context element. Returning false from 5019 * a subscriber to this event will prevent the tooltip from being displayed for 5020 * the current context element. 5021 * 5022 * @event contextMouseOverEvent 5023 * @param {HTMLElement} context The context element which the user just moused over 5024 * @param {DOMEvent} e The DOM event object, associated with the mouse over 5025 */ 5026 this.contextMouseOverEvent = this.createEvent(EVENT_TYPES.CONTEXT_MOUSE_OVER); 5027 this.contextMouseOverEvent.signature = SIGNATURE; 5028 5029 /** 5030 * CustomEvent fired when the user mouses out of a context element. 5031 * 5032 * @event contextMouseOutEvent 5033 * @param {HTMLElement} context The context element which the user just moused out of 5034 * @param {DOMEvent} e The DOM event object, associated with the mouse out 5035 */ 5036 this.contextMouseOutEvent = this.createEvent(EVENT_TYPES.CONTEXT_MOUSE_OUT); 5037 this.contextMouseOutEvent.signature = SIGNATURE; 5038 5039 /** 5040 * CustomEvent fired just before the tooltip is displayed for the current context. 5041 * <p> 5042 * You can subscribe to this event if you need to set up the text for the 5043 * tooltip based on the context element for which it is about to be displayed. 5044 * </p> 5045 * <p>This event differs from the beforeShow event in following respects:</p> 5046 * <ol> 5047 * <li> 5048 * When moving from one context element to another, if the tooltip is not 5049 * hidden (the <code>hidedelay</code> is not reached), the beforeShow and Show events will not 5050 * be fired when the tooltip is displayed for the new context since it is already visible. 5051 * However the contextTrigger event is always fired before displaying the tooltip for 5052 * a new context. 5053 * </li> 5054 * <li> 5055 * The trigger event provides access to the context element, allowing you to 5056 * set the text of the tooltip based on context element for which the tooltip is 5057 * triggered. 5058 * </li> 5059 * </ol> 5060 * <p> 5061 * It is not possible to prevent the tooltip from being displayed 5062 * using this event. You can use the contextMouseOverEvent if you need to prevent 5063 * the tooltip from being displayed. 5064 * </p> 5065 * @event contextTriggerEvent 5066 * @param {HTMLElement} context The context element for which the tooltip is triggered 5067 */ 5068 this.contextTriggerEvent = this.createEvent(EVENT_TYPES.CONTEXT_TRIGGER); 5069 this.contextTriggerEvent.signature = SIGNATURE; 5070 }, 5071 5072 /** 5073 * Initializes the class's configurable properties which can be 5074 * changed using the Overlay's Config object (cfg). 5075 * @method initDefaultConfig 5076 */ 5077 initDefaultConfig: function () { 5078 5079 Tooltip.superclass.initDefaultConfig.call(this); 5080 5081 /** 5082 * Specifies whether the Tooltip should be kept from overlapping 5083 * its context element. 5084 * @config preventoverlap 5085 * @type Boolean 5086 * @default true 5087 */ 5088 this.cfg.addProperty(DEFAULT_CONFIG.PREVENT_OVERLAP.key, { 5089 value: DEFAULT_CONFIG.PREVENT_OVERLAP.value, 5090 validator: DEFAULT_CONFIG.PREVENT_OVERLAP.validator, 5091 supercedes: DEFAULT_CONFIG.PREVENT_OVERLAP.supercedes 5092 }); 5093 5094 /** 5095 * The number of milliseconds to wait before showing a Tooltip 5096 * on mouseover. 5097 * @config showdelay 5098 * @type Number 5099 * @default 200 5100 */ 5101 this.cfg.addProperty(DEFAULT_CONFIG.SHOW_DELAY.key, { 5102 handler: this.configShowDelay, 5103 value: 200, 5104 validator: DEFAULT_CONFIG.SHOW_DELAY.validator 5105 }); 5106 5107 /** 5108 * The number of milliseconds to wait before automatically 5109 * dismissing a Tooltip after the mouse has been resting on the 5110 * context element. 5111 * @config autodismissdelay 5112 * @type Number 5113 * @default 5000 5114 */ 5115 this.cfg.addProperty(DEFAULT_CONFIG.AUTO_DISMISS_DELAY.key, { 5116 handler: this.configAutoDismissDelay, 5117 value: DEFAULT_CONFIG.AUTO_DISMISS_DELAY.value, 5118 validator: DEFAULT_CONFIG.AUTO_DISMISS_DELAY.validator 5119 }); 5120 5121 /** 5122 * The number of milliseconds to wait before hiding a Tooltip 5123 * after mouseout. 5124 * @config hidedelay 5125 * @type Number 5126 * @default 250 5127 */ 5128 this.cfg.addProperty(DEFAULT_CONFIG.HIDE_DELAY.key, { 5129 handler: this.configHideDelay, 5130 value: DEFAULT_CONFIG.HIDE_DELAY.value, 5131 validator: DEFAULT_CONFIG.HIDE_DELAY.validator 5132 }); 5133 5134 /** 5135 * Specifies the Tooltip's text. The text is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 5136 * @config text 5137 * @type HTML 5138 * @default null 5139 */ 5140 this.cfg.addProperty(DEFAULT_CONFIG.TEXT.key, { 5141 handler: this.configText, 5142 suppressEvent: DEFAULT_CONFIG.TEXT.suppressEvent 5143 }); 5144 5145 /** 5146 * Specifies the container element that the Tooltip's markup 5147 * should be rendered into. 5148 * @config container 5149 * @type HTMLElement/String 5150 * @default document.body 5151 */ 5152 this.cfg.addProperty(DEFAULT_CONFIG.CONTAINER.key, { 5153 handler: this.configContainer, 5154 value: document.body 5155 }); 5156 5157 /** 5158 * Specifies whether or not the tooltip is disabled. Disabled tooltips 5159 * will not be displayed. If the tooltip is driven by the title attribute 5160 * of the context element, the title attribute will still be removed for 5161 * disabled tooltips, to prevent default tooltip behavior. 5162 * 5163 * @config disabled 5164 * @type Boolean 5165 * @default false 5166 */ 5167 this.cfg.addProperty(DEFAULT_CONFIG.DISABLED.key, { 5168 handler: this.configContainer, 5169 value: DEFAULT_CONFIG.DISABLED.value, 5170 supressEvent: DEFAULT_CONFIG.DISABLED.suppressEvent 5171 }); 5172 5173 /** 5174 * Specifies the XY offset from the mouse position, where the tooltip should be displayed, specified 5175 * as a 2 element array (e.g. [10, 20]); 5176 * 5177 * @config xyoffset 5178 * @type Array 5179 * @default [0, 25] 5180 */ 5181 this.cfg.addProperty(DEFAULT_CONFIG.XY_OFFSET.key, { 5182 value: DEFAULT_CONFIG.XY_OFFSET.value.concat(), 5183 supressEvent: DEFAULT_CONFIG.XY_OFFSET.suppressEvent 5184 }); 5185 5186 /** 5187 * Specifies the element or elements that the Tooltip should be 5188 * anchored to on mouseover. 5189 * @config context 5190 * @type HTMLElement[]/String[] 5191 * @default null 5192 */ 5193 5194 /** 5195 * String representing the width of the Tooltip. <em>Please note: 5196 * </em> As of version 2.3 if either no value or a value of "auto" 5197 * is specified, and the Toolip's "container" configuration property 5198 * is set to something other than <code>document.body</code> or 5199 * its "context" element resides outside the immediately visible 5200 * portion of the document, the width of the Tooltip will be 5201 * calculated based on the offsetWidth of its root HTML and set just 5202 * before it is made visible. The original value will be 5203 * restored when the Tooltip is hidden. This ensures the Tooltip is 5204 * rendered at a usable width. For more information see 5205 * YUILibrary bug #1685496 and YUILibrary 5206 * bug #1735423. 5207 * @config width 5208 * @type String 5209 * @default null 5210 */ 5211 5212 }, 5213 5214 // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // 5215 5216 /** 5217 * The default event handler fired when the "text" property is changed. 5218 * @method configText 5219 * @param {String} type The CustomEvent type (usually the property name) 5220 * @param {Object[]} args The CustomEvent arguments. For configuration 5221 * handlers, args[0] will equal the newly applied value for the property. 5222 * @param {Object} obj The scope object. For configuration handlers, 5223 * this will usually equal the owner. 5224 */ 5225 configText: function (type, args, obj) { 5226 var text = args[0]; 5227 if (text) { 5228 this.setBody(text); 5229 } 5230 }, 5231 5232 /** 5233 * The default event handler fired when the "container" property 5234 * is changed. 5235 * @method configContainer 5236 * @param {String} type The CustomEvent type (usually the property name) 5237 * @param {Object[]} args The CustomEvent arguments. For 5238 * configuration handlers, args[0] will equal the newly applied value 5239 * for the property. 5240 * @param {Object} obj The scope object. For configuration handlers, 5241 * this will usually equal the owner. 5242 */ 5243 configContainer: function (type, args, obj) { 5244 var container = args[0]; 5245 5246 if (typeof container == 'string') { 5247 this.cfg.setProperty("container", document.getElementById(container), true); 5248 } 5249 }, 5250 5251 /** 5252 * @method _removeEventListeners 5253 * @description Removes all of the DOM event handlers from the HTML 5254 * element(s) that trigger the display of the tooltip. 5255 * @protected 5256 */ 5257 _removeEventListeners: function () { 5258 5259 var aElements = this._context, 5260 nElements, 5261 oElement, 5262 i; 5263 5264 if (aElements) { 5265 nElements = aElements.length; 5266 if (nElements > 0) { 5267 i = nElements - 1; 5268 do { 5269 oElement = aElements[i]; 5270 Event.removeListener(oElement, "mouseover", this.onContextMouseOver); 5271 Event.removeListener(oElement, "mousemove", this.onContextMouseMove); 5272 Event.removeListener(oElement, "mouseout", this.onContextMouseOut); 5273 } 5274 while (i--); 5275 } 5276 } 5277 }, 5278 5279 /** 5280 * The default event handler fired when the "context" property 5281 * is changed. 5282 * @method configContext 5283 * @param {String} type The CustomEvent type (usually the property name) 5284 * @param {Object[]} args The CustomEvent arguments. For configuration 5285 * handlers, args[0] will equal the newly applied value for the property. 5286 * @param {Object} obj The scope object. For configuration handlers, 5287 * this will usually equal the owner. 5288 */ 5289 configContext: function (type, args, obj) { 5290 5291 var context = args[0], 5292 aElements, 5293 nElements, 5294 oElement, 5295 i; 5296 5297 if (context) { 5298 5299 // Normalize parameter into an array 5300 if (! (context instanceof Array)) { 5301 if (typeof context == "string") { 5302 this.cfg.setProperty("context", [document.getElementById(context)], true); 5303 } else { // Assuming this is an element 5304 this.cfg.setProperty("context", [context], true); 5305 } 5306 context = this.cfg.getProperty("context"); 5307 } 5308 5309 // Remove any existing mouseover/mouseout listeners 5310 this._removeEventListeners(); 5311 5312 // Add mouseover/mouseout listeners to context elements 5313 this._context = context; 5314 5315 aElements = this._context; 5316 5317 if (aElements) { 5318 nElements = aElements.length; 5319 if (nElements > 0) { 5320 i = nElements - 1; 5321 do { 5322 oElement = aElements[i]; 5323 Event.on(oElement, "mouseover", this.onContextMouseOver, this); 5324 Event.on(oElement, "mousemove", this.onContextMouseMove, this); 5325 Event.on(oElement, "mouseout", this.onContextMouseOut, this); 5326 } 5327 while (i--); 5328 } 5329 } 5330 } 5331 }, 5332 5333 // END BUILT-IN PROPERTY EVENT HANDLERS // 5334 5335 // BEGIN BUILT-IN DOM EVENT HANDLERS // 5336 5337 /** 5338 * The default event handler fired when the user moves the mouse while 5339 * over the context element. 5340 * @method onContextMouseMove 5341 * @param {DOMEvent} e The current DOM event 5342 * @param {Object} obj The object argument 5343 */ 5344 onContextMouseMove: function (e, obj) { 5345 obj.pageX = Event.getPageX(e); 5346 obj.pageY = Event.getPageY(e); 5347 }, 5348 5349 /** 5350 * The default event handler fired when the user mouses over the 5351 * context element. 5352 * @method onContextMouseOver 5353 * @param {DOMEvent} e The current DOM event 5354 * @param {Object} obj The object argument 5355 */ 5356 onContextMouseOver: function (e, obj) { 5357 var context = this; 5358 5359 if (context.title) { 5360 obj._tempTitle = context.title; 5361 context.title = ""; 5362 } 5363 5364 // Fire first, to honor disabled set in the listner 5365 if (obj.fireEvent("contextMouseOver", context, e) !== false && !obj.cfg.getProperty("disabled")) { 5366 5367 // Stop the tooltip from being hidden (set on last mouseout) 5368 if (obj.hideProcId) { 5369 clearTimeout(obj.hideProcId); 5370 obj.logger.log("Clearing hide timer: " + obj.hideProcId, "time"); 5371 obj.hideProcId = null; 5372 } 5373 5374 Event.on(context, "mousemove", obj.onContextMouseMove, obj); 5375 5376 /** 5377 * The unique process ID associated with the thread responsible 5378 * for showing the Tooltip. 5379 * @type int 5380 */ 5381 obj.showProcId = obj.doShow(e, context); 5382 obj.logger.log("Setting show tooltip timeout: " + obj.showProcId, "time"); 5383 } 5384 }, 5385 5386 /** 5387 * The default event handler fired when the user mouses out of 5388 * the context element. 5389 * @method onContextMouseOut 5390 * @param {DOMEvent} e The current DOM event 5391 * @param {Object} obj The object argument 5392 */ 5393 onContextMouseOut: function (e, obj) { 5394 var el = this; 5395 5396 if (obj._tempTitle) { 5397 el.title = obj._tempTitle; 5398 obj._tempTitle = null; 5399 } 5400 5401 if (obj.showProcId) { 5402 clearTimeout(obj.showProcId); 5403 obj.logger.log("Clearing show timer: " + obj.showProcId, "time"); 5404 obj.showProcId = null; 5405 } 5406 5407 if (obj.hideProcId) { 5408 clearTimeout(obj.hideProcId); 5409 obj.logger.log("Clearing hide timer: " + obj.hideProcId, "time"); 5410 obj.hideProcId = null; 5411 } 5412 5413 obj.fireEvent("contextMouseOut", el, e); 5414 5415 obj.hideProcId = setTimeout(function () { 5416 obj.hide(); 5417 }, obj.cfg.getProperty("hidedelay")); 5418 }, 5419 5420 // END BUILT-IN DOM EVENT HANDLERS // 5421 5422 /** 5423 * Processes the showing of the Tooltip by setting the timeout delay 5424 * and offset of the Tooltip. 5425 * @method doShow 5426 * @param {DOMEvent} e The current DOM event 5427 * @param {HTMLElement} context The current context element 5428 * @return {Number} The process ID of the timeout function associated 5429 * with doShow 5430 */ 5431 doShow: function (e, context) { 5432 5433 var offset = this.cfg.getProperty("xyoffset"), 5434 xOffset = offset[0], 5435 yOffset = offset[1], 5436 me = this; 5437 5438 if (UA.opera && context.tagName && 5439 context.tagName.toUpperCase() == "A") { 5440 yOffset += 12; 5441 } 5442 5443 return setTimeout(function () { 5444 5445 var txt = me.cfg.getProperty("text"); 5446 5447 // title does not over-ride text 5448 if (me._tempTitle && (txt === "" || YAHOO.lang.isUndefined(txt) || YAHOO.lang.isNull(txt))) { 5449 me.setBody(me._tempTitle); 5450 } else { 5451 me.cfg.refireEvent("text"); 5452 } 5453 5454 me.logger.log("Show tooltip", "time"); 5455 me.moveTo(me.pageX + xOffset, me.pageY + yOffset); 5456 5457 if (me.cfg.getProperty("preventoverlap")) { 5458 me.preventOverlap(me.pageX, me.pageY); 5459 } 5460 5461 Event.removeListener(context, "mousemove", me.onContextMouseMove); 5462 5463 me.contextTriggerEvent.fire(context); 5464 5465 me.show(); 5466 5467 me.hideProcId = me.doHide(); 5468 me.logger.log("Hide tooltip time active: " + me.hideProcId, "time"); 5469 5470 }, this.cfg.getProperty("showdelay")); 5471 }, 5472 5473 /** 5474 * Sets the timeout for the auto-dismiss delay, which by default is 5 5475 * seconds, meaning that a tooltip will automatically dismiss itself 5476 * after 5 seconds of being displayed. 5477 * @method doHide 5478 */ 5479 doHide: function () { 5480 5481 var me = this; 5482 5483 me.logger.log("Setting hide tooltip timeout", "time"); 5484 5485 return setTimeout(function () { 5486 5487 me.logger.log("Hide tooltip", "time"); 5488 me.hide(); 5489 5490 }, this.cfg.getProperty("autodismissdelay")); 5491 5492 }, 5493 5494 /** 5495 * Fired when the Tooltip is moved, this event handler is used to 5496 * prevent the Tooltip from overlapping with its context element. 5497 * @method preventOverlay 5498 * @param {Number} pageX The x coordinate position of the mouse pointer 5499 * @param {Number} pageY The y coordinate position of the mouse pointer 5500 */ 5501 preventOverlap: function (pageX, pageY) { 5502 5503 var height = this.element.offsetHeight, 5504 mousePoint = new YAHOO.util.Point(pageX, pageY), 5505 elementRegion = Dom.getRegion(this.element); 5506 5507 elementRegion.top -= 5; 5508 elementRegion.left -= 5; 5509 elementRegion.right += 5; 5510 elementRegion.bottom += 5; 5511 5512 this.logger.log("context " + elementRegion, "ttip"); 5513 this.logger.log("mouse " + mousePoint, "ttip"); 5514 5515 if (elementRegion.contains(mousePoint)) { 5516 this.logger.log("OVERLAP", "warn"); 5517 this.cfg.setProperty("y", (pageY - height - 5)); 5518 } 5519 }, 5520 5521 5522 /** 5523 * @method onRender 5524 * @description "render" event handler for the Tooltip. 5525 * @param {String} p_sType String representing the name of the event 5526 * that was fired. 5527 * @param {Array} p_aArgs Array of arguments sent when the event 5528 * was fired. 5529 */ 5530 onRender: function (p_sType, p_aArgs) { 5531 5532 function sizeShadow() { 5533 5534 var oElement = this.element, 5535 oShadow = this.underlay; 5536 5537 if (oShadow) { 5538 oShadow.style.width = (oElement.offsetWidth + 6) + "px"; 5539 oShadow.style.height = (oElement.offsetHeight + 1) + "px"; 5540 } 5541 5542 } 5543 5544 function addShadowVisibleClass() { 5545 Dom.addClass(this.underlay, "yui-tt-shadow-visible"); 5546 5547 if (UA.ie) { 5548 this.forceUnderlayRedraw(); 5549 } 5550 } 5551 5552 function removeShadowVisibleClass() { 5553 Dom.removeClass(this.underlay, "yui-tt-shadow-visible"); 5554 } 5555 5556 function createShadow() { 5557 5558 var oShadow = this.underlay, 5559 oElement, 5560 Module, 5561 nIE, 5562 me; 5563 5564 if (!oShadow) { 5565 5566 oElement = this.element; 5567 Module = YAHOO.widget.Module; 5568 nIE = UA.ie; 5569 me = this; 5570 5571 if (!m_oShadowTemplate) { 5572 m_oShadowTemplate = document.createElement("div"); 5573 m_oShadowTemplate.className = "yui-tt-shadow"; 5574 } 5575 5576 oShadow = m_oShadowTemplate.cloneNode(false); 5577 5578 oElement.appendChild(oShadow); 5579 5580 this.underlay = oShadow; 5581 5582 // Backward compatibility, even though it's probably 5583 // intended to be "private", it isn't marked as such in the api docs 5584 this._shadow = this.underlay; 5585 5586 addShadowVisibleClass.call(this); 5587 5588 this.subscribe("beforeShow", addShadowVisibleClass); 5589 this.subscribe("hide", removeShadowVisibleClass); 5590 5591 if (bIEQuirks) { 5592 window.setTimeout(function () { 5593 sizeShadow.call(me); 5594 }, 0); 5595 5596 this.cfg.subscribeToConfigEvent("width", sizeShadow); 5597 this.cfg.subscribeToConfigEvent("height", sizeShadow); 5598 this.subscribe("changeContent", sizeShadow); 5599 5600 Module.textResizeEvent.subscribe(sizeShadow, this, true); 5601 this.subscribe("destroy", function () { 5602 Module.textResizeEvent.unsubscribe(sizeShadow, this); 5603 }); 5604 } 5605 } 5606 } 5607 5608 function onBeforeShow() { 5609 createShadow.call(this); 5610 this.unsubscribe("beforeShow", onBeforeShow); 5611 } 5612 5613 if (this.cfg.getProperty("visible")) { 5614 createShadow.call(this); 5615 } else { 5616 this.subscribe("beforeShow", onBeforeShow); 5617 } 5618 5619 }, 5620 5621 /** 5622 * Forces the underlay element to be repainted, through the application/removal 5623 * of a yui-force-redraw class to the underlay element. 5624 * 5625 * @method forceUnderlayRedraw 5626 */ 5627 forceUnderlayRedraw : function() { 5628 var tt = this; 5629 Dom.addClass(tt.underlay, "yui-force-redraw"); 5630 setTimeout(function() {Dom.removeClass(tt.underlay, "yui-force-redraw");}, 0); 5631 }, 5632 5633 /** 5634 * Removes the Tooltip element from the DOM and sets all child 5635 * elements to null. 5636 * @method destroy 5637 */ 5638 destroy: function () { 5639 5640 // Remove any existing mouseover/mouseout listeners 5641 this._removeEventListeners(); 5642 5643 Tooltip.superclass.destroy.call(this); 5644 5645 }, 5646 5647 /** 5648 * Returns a string representation of the object. 5649 * @method toString 5650 * @return {String} The string representation of the Tooltip 5651 */ 5652 toString: function () { 5653 return "Tooltip " + this.id; 5654 } 5655 5656 }); 5657 5658 }()); 5659 (function () { 5660 5661 /** 5662 * Panel is an implementation of Overlay that behaves like an OS window, 5663 * with a draggable header and an optional close icon at the top right. 5664 * @namespace YAHOO.widget 5665 * @class Panel 5666 * @extends YAHOO.widget.Overlay 5667 * @constructor 5668 * @param {String} el The element ID representing the Panel <em>OR</em> 5669 * @param {HTMLElement} el The element representing the Panel 5670 * @param {Object} userConfig The configuration object literal containing 5671 * the configuration that should be set for this Panel. See configuration 5672 * documentation for more details. 5673 */ 5674 YAHOO.widget.Panel = function (el, userConfig) { 5675 YAHOO.widget.Panel.superclass.constructor.call(this, el, userConfig); 5676 }; 5677 5678 var _currentModal = null; 5679 5680 var Lang = YAHOO.lang, 5681 Util = YAHOO.util, 5682 Dom = Util.Dom, 5683 Event = Util.Event, 5684 CustomEvent = Util.CustomEvent, 5685 KeyListener = YAHOO.util.KeyListener, 5686 Config = Util.Config, 5687 Overlay = YAHOO.widget.Overlay, 5688 Panel = YAHOO.widget.Panel, 5689 UA = YAHOO.env.ua, 5690 5691 bIEQuirks = (UA.ie && (UA.ie <= 6 || document.compatMode == "BackCompat")), 5692 5693 m_oMaskTemplate, 5694 m_oUnderlayTemplate, 5695 m_oCloseIconTemplate, 5696 5697 /** 5698 * Constant representing the name of the Panel's events 5699 * @property EVENT_TYPES 5700 * @private 5701 * @final 5702 * @type Object 5703 */ 5704 EVENT_TYPES = { 5705 "BEFORE_SHOW_MASK" : "beforeShowMask", 5706 "BEFORE_HIDE_MASK" : "beforeHideMask", 5707 "SHOW_MASK": "showMask", 5708 "HIDE_MASK": "hideMask", 5709 "DRAG": "drag" 5710 }, 5711 5712 /** 5713 * Constant representing the Panel's configuration properties 5714 * @property DEFAULT_CONFIG 5715 * @private 5716 * @final 5717 * @type Object 5718 */ 5719 DEFAULT_CONFIG = { 5720 5721 "CLOSE": { 5722 key: "close", 5723 value: true, 5724 validator: Lang.isBoolean, 5725 supercedes: ["visible"] 5726 }, 5727 5728 "DRAGGABLE": { 5729 key: "draggable", 5730 value: (Util.DD ? true : false), 5731 validator: Lang.isBoolean, 5732 supercedes: ["visible"] 5733 }, 5734 5735 "DRAG_ONLY" : { 5736 key: "dragonly", 5737 value: false, 5738 validator: Lang.isBoolean, 5739 supercedes: ["draggable"] 5740 }, 5741 5742 "UNDERLAY": { 5743 key: "underlay", 5744 value: "shadow", 5745 supercedes: ["visible"] 5746 }, 5747 5748 "MODAL": { 5749 key: "modal", 5750 value: false, 5751 validator: Lang.isBoolean, 5752 supercedes: ["visible", "zindex"] 5753 }, 5754 5755 "KEY_LISTENERS": { 5756 key: "keylisteners", 5757 suppressEvent: true, 5758 supercedes: ["visible"] 5759 }, 5760 5761 "STRINGS" : { 5762 key: "strings", 5763 supercedes: ["close"], 5764 validator: Lang.isObject, 5765 value: { 5766 close: "Close" 5767 } 5768 } 5769 }; 5770 5771 /** 5772 * Constant representing the default CSS class used for a Panel 5773 * @property YAHOO.widget.Panel.CSS_PANEL 5774 * @static 5775 * @final 5776 * @type String 5777 */ 5778 Panel.CSS_PANEL = "yui-panel"; 5779 5780 /** 5781 * Constant representing the default CSS class used for a Panel's 5782 * wrapping container 5783 * @property YAHOO.widget.Panel.CSS_PANEL_CONTAINER 5784 * @static 5785 * @final 5786 * @type String 5787 */ 5788 Panel.CSS_PANEL_CONTAINER = "yui-panel-container"; 5789 5790 /** 5791 * Constant representing the default set of focusable elements 5792 * on the pagewhich Modal Panels will prevent access to, when 5793 * the modal mask is displayed 5794 * 5795 * @property YAHOO.widget.Panel.FOCUSABLE 5796 * @static 5797 * @type Array 5798 */ 5799 Panel.FOCUSABLE = [ 5800 "a", 5801 "button", 5802 "select", 5803 "textarea", 5804 "input", 5805 "iframe" 5806 ]; 5807 5808 // Private CustomEvent listeners 5809 5810 /* 5811 "beforeRender" event handler that creates an empty header for a Panel 5812 instance if its "draggable" configuration property is set to "true" 5813 and no header has been created. 5814 */ 5815 5816 function createHeader(p_sType, p_aArgs) { 5817 if (!this.header && this.cfg.getProperty("draggable")) { 5818 this.setHeader(" "); 5819 } 5820 } 5821 5822 /* 5823 "hide" event handler that sets a Panel instance's "width" 5824 configuration property back to its original value before 5825 "setWidthToOffsetWidth" was called. 5826 */ 5827 5828 function restoreOriginalWidth(p_sType, p_aArgs, p_oObject) { 5829 5830 var sOriginalWidth = p_oObject[0], 5831 sNewWidth = p_oObject[1], 5832 oConfig = this.cfg, 5833 sCurrentWidth = oConfig.getProperty("width"); 5834 5835 if (sCurrentWidth == sNewWidth) { 5836 oConfig.setProperty("width", sOriginalWidth); 5837 } 5838 5839 this.unsubscribe("hide", restoreOriginalWidth, p_oObject); 5840 } 5841 5842 /* 5843 "beforeShow" event handler that sets a Panel instance's "width" 5844 configuration property to the value of its root HTML 5845 elements's offsetWidth 5846 */ 5847 5848 function setWidthToOffsetWidth(p_sType, p_aArgs) { 5849 5850 var oConfig, 5851 sOriginalWidth, 5852 sNewWidth; 5853 5854 if (bIEQuirks) { 5855 5856 oConfig = this.cfg; 5857 sOriginalWidth = oConfig.getProperty("width"); 5858 5859 if (!sOriginalWidth || sOriginalWidth == "auto") { 5860 5861 sNewWidth = (this.element.offsetWidth + "px"); 5862 5863 oConfig.setProperty("width", sNewWidth); 5864 5865 this.subscribe("hide", restoreOriginalWidth, 5866 [(sOriginalWidth || ""), sNewWidth]); 5867 5868 } 5869 } 5870 } 5871 5872 YAHOO.extend(Panel, Overlay, { 5873 5874 /** 5875 * The Overlay initialization method, which is executed for Overlay and 5876 * all of its subclasses. This method is automatically called by the 5877 * constructor, and sets up all DOM references for pre-existing markup, 5878 * and creates required markup if it is not already present. 5879 * @method init 5880 * @param {String} el The element ID representing the Overlay <em>OR</em> 5881 * @param {HTMLElement} el The element representing the Overlay 5882 * @param {Object} userConfig The configuration object literal 5883 * containing the configuration that should be set for this Overlay. 5884 * See configuration documentation for more details. 5885 */ 5886 init: function (el, userConfig) { 5887 /* 5888 Note that we don't pass the user config in here yet because 5889 we only want it executed once, at the lowest subclass level 5890 */ 5891 5892 Panel.superclass.init.call(this, el/*, userConfig*/); 5893 5894 this.beforeInitEvent.fire(Panel); 5895 5896 Dom.addClass(this.element, Panel.CSS_PANEL); 5897 5898 this.buildWrapper(); 5899 5900 if (userConfig) { 5901 this.cfg.applyConfig(userConfig, true); 5902 } 5903 5904 this.subscribe("showMask", this._addFocusHandlers); 5905 this.subscribe("hideMask", this._removeFocusHandlers); 5906 this.subscribe("beforeRender", createHeader); 5907 5908 this.subscribe("render", function() { 5909 this.setFirstLastFocusable(); 5910 this.subscribe("changeContent", this.setFirstLastFocusable); 5911 }); 5912 5913 this.subscribe("show", this._focusOnShow); 5914 5915 this.initEvent.fire(Panel); 5916 }, 5917 5918 /** 5919 * @method _onElementFocus 5920 * @private 5921 * 5922 * "focus" event handler for a focuable element. Used to automatically 5923 * blur the element when it receives focus to ensure that a Panel 5924 * instance's modality is not compromised. 5925 * 5926 * @param {Event} e The DOM event object 5927 */ 5928 _onElementFocus : function(e){ 5929 5930 if(_currentModal === this) { 5931 5932 var target = Event.getTarget(e), 5933 doc = document.documentElement, 5934 insideDoc = (target !== doc && target !== window); 5935 5936 // mask and documentElement checks added for IE, which focuses on the mask when it's clicked on, and focuses on 5937 // the documentElement, when the document scrollbars are clicked on 5938 if (insideDoc && target !== this.element && target !== this.mask && !Dom.isAncestor(this.element, target)) { 5939 try { 5940 this._focusFirstModal(); 5941 } catch(err){ 5942 // Just in case we fail to focus 5943 try { 5944 if (insideDoc && target !== document.body) { 5945 target.blur(); 5946 } 5947 } catch(err2) { } 5948 } 5949 } 5950 } 5951 }, 5952 5953 /** 5954 * Focuses on the first element if present, otherwise falls back to the focus mechanisms used for 5955 * modality. This method does not try/catch focus failures. The caller is responsible for catching exceptions, 5956 * and taking remedial measures. 5957 * 5958 * @method _focusFirstModal 5959 */ 5960 _focusFirstModal : function() { 5961 var el = this.firstElement; 5962 if (el) { 5963 el.focus(); 5964 } else { 5965 if (this._modalFocus) { 5966 this._modalFocus.focus(); 5967 } else { 5968 this.innerElement.focus(); 5969 } 5970 } 5971 }, 5972 5973 /** 5974 * @method _addFocusHandlers 5975 * @protected 5976 * 5977 * "showMask" event handler that adds a "focus" event handler to all 5978 * focusable elements in the document to enforce a Panel instance's 5979 * modality from being compromised. 5980 * 5981 * @param p_sType {String} Custom event type 5982 * @param p_aArgs {Array} Custom event arguments 5983 */ 5984 _addFocusHandlers: function(p_sType, p_aArgs) { 5985 if (!this.firstElement) { 5986 if (UA.webkit || UA.opera) { 5987 if (!this._modalFocus) { 5988 this._createHiddenFocusElement(); 5989 } 5990 } else { 5991 this.innerElement.tabIndex = 0; 5992 } 5993 } 5994 this._setTabLoop(this.firstElement, this.lastElement); 5995 Event.onFocus(document.documentElement, this._onElementFocus, this, true); 5996 _currentModal = this; 5997 }, 5998 5999 /** 6000 * Creates a hidden focusable element, used to focus on, 6001 * to enforce modality for browsers in which focus cannot 6002 * be applied to the container box. 6003 * 6004 * @method _createHiddenFocusElement 6005 * @private 6006 */ 6007 _createHiddenFocusElement : function() { 6008 var e = document.createElement("button"); 6009 e.style.height = "1px"; 6010 e.style.width = "1px"; 6011 e.style.position = "absolute"; 6012 e.style.left = "-10000em"; 6013 e.style.opacity = 0; 6014 e.tabIndex = -1; 6015 this.innerElement.appendChild(e); 6016 this._modalFocus = e; 6017 }, 6018 6019 /** 6020 * @method _removeFocusHandlers 6021 * @protected 6022 * 6023 * "hideMask" event handler that removes all "focus" event handlers added 6024 * by the "addFocusEventHandlers" method. 6025 * 6026 * @param p_sType {String} Event type 6027 * @param p_aArgs {Array} Event Arguments 6028 */ 6029 _removeFocusHandlers: function(p_sType, p_aArgs) { 6030 Event.removeFocusListener(document.documentElement, this._onElementFocus, this); 6031 6032 if (_currentModal == this) { 6033 _currentModal = null; 6034 } 6035 }, 6036 6037 /** 6038 * Focus handler for the show event 6039 * 6040 * @method _focusOnShow 6041 * @param {String} type Event Type 6042 * @param {Array} args Event arguments 6043 * @param {Object} obj Additional data 6044 */ 6045 _focusOnShow : function(type, args, obj) { 6046 6047 if (args && args[1]) { 6048 Event.stopEvent(args[1]); 6049 } 6050 6051 if (!this.focusFirst(type, args, obj)) { 6052 if (this.cfg.getProperty("modal")) { 6053 this._focusFirstModal(); 6054 } 6055 } 6056 }, 6057 6058 /** 6059 * Sets focus to the first element in the Panel. 6060 * 6061 * @method focusFirst 6062 * @return {Boolean} true, if successfully focused, false otherwise 6063 */ 6064 focusFirst: function (type, args, obj) { 6065 var el = this.firstElement, focused = false; 6066 6067 if (args && args[1]) { 6068 Event.stopEvent(args[1]); 6069 } 6070 6071 if (el) { 6072 try { 6073 el.focus(); 6074 focused = true; 6075 } catch(err) { 6076 // Ignore 6077 } 6078 } 6079 6080 return focused; 6081 }, 6082 6083 /** 6084 * Sets focus to the last element in the Panel. 6085 * 6086 * @method focusLast 6087 * @return {Boolean} true, if successfully focused, false otherwise 6088 */ 6089 focusLast: function (type, args, obj) { 6090 var el = this.lastElement, focused = false; 6091 6092 if (args && args[1]) { 6093 Event.stopEvent(args[1]); 6094 } 6095 6096 if (el) { 6097 try { 6098 el.focus(); 6099 focused = true; 6100 } catch(err) { 6101 // Ignore 6102 } 6103 } 6104 6105 return focused; 6106 }, 6107 6108 /** 6109 * Protected internal method for setTabLoop, which can be used by 6110 * subclasses to jump in and modify the arguments passed in if required. 6111 * 6112 * @method _setTabLoop 6113 * @param {HTMLElement} firstElement 6114 * @param {HTMLElement} lastElement 6115 * @protected 6116 * 6117 */ 6118 _setTabLoop : function(firstElement, lastElement) { 6119 this.setTabLoop(firstElement, lastElement); 6120 }, 6121 6122 /** 6123 * Sets up a tab, shift-tab loop between the first and last elements 6124 * provided. NOTE: Sets up the preventBackTab and preventTabOut KeyListener 6125 * instance properties, which are reset everytime this method is invoked. 6126 * 6127 * @method setTabLoop 6128 * @param {HTMLElement} firstElement 6129 * @param {HTMLElement} lastElement 6130 * 6131 */ 6132 setTabLoop : function(firstElement, lastElement) { 6133 6134 var backTab = this.preventBackTab, tab = this.preventTabOut, 6135 showEvent = this.showEvent, hideEvent = this.hideEvent; 6136 6137 if (backTab) { 6138 backTab.disable(); 6139 showEvent.unsubscribe(backTab.enable, backTab); 6140 hideEvent.unsubscribe(backTab.disable, backTab); 6141 backTab = this.preventBackTab = null; 6142 } 6143 6144 if (tab) { 6145 tab.disable(); 6146 showEvent.unsubscribe(tab.enable, tab); 6147 hideEvent.unsubscribe(tab.disable,tab); 6148 tab = this.preventTabOut = null; 6149 } 6150 6151 if (firstElement) { 6152 this.preventBackTab = new KeyListener(firstElement, 6153 {shift:true, keys:9}, 6154 {fn:this.focusLast, scope:this, correctScope:true} 6155 ); 6156 backTab = this.preventBackTab; 6157 6158 showEvent.subscribe(backTab.enable, backTab, true); 6159 hideEvent.subscribe(backTab.disable,backTab, true); 6160 } 6161 6162 if (lastElement) { 6163 this.preventTabOut = new KeyListener(lastElement, 6164 {shift:false, keys:9}, 6165 {fn:this.focusFirst, scope:this, correctScope:true} 6166 ); 6167 tab = this.preventTabOut; 6168 6169 showEvent.subscribe(tab.enable, tab, true); 6170 hideEvent.subscribe(tab.disable,tab, true); 6171 } 6172 }, 6173 6174 /** 6175 * Returns an array of the currently focusable items which reside within 6176 * Panel. The set of focusable elements the method looks for are defined 6177 * in the Panel.FOCUSABLE static property 6178 * 6179 * @method getFocusableElements 6180 * @param {HTMLElement} root element to start from. 6181 */ 6182 getFocusableElements : function(root) { 6183 6184 root = root || this.innerElement; 6185 6186 var focusable = {}, panel = this; 6187 for (var i = 0; i < Panel.FOCUSABLE.length; i++) { 6188 focusable[Panel.FOCUSABLE[i]] = true; 6189 } 6190 6191 // Not looking by Tag, since we want elements in DOM order 6192 6193 return Dom.getElementsBy(function(el) { return panel._testIfFocusable(el, focusable); }, null, root); 6194 }, 6195 6196 /** 6197 * This is the test method used by getFocusableElements, to determine which elements to 6198 * include in the focusable elements list. Users may override this to customize behavior. 6199 * 6200 * @method _testIfFocusable 6201 * @param {Object} el The element being tested 6202 * @param {Object} focusable The hash of known focusable elements, created by an array-to-map operation on Panel.FOCUSABLE 6203 * @protected 6204 */ 6205 _testIfFocusable: function(el, focusable) { 6206 if (el.focus && el.type !== "hidden" && !el.disabled && focusable[el.tagName.toLowerCase()]) { 6207 return true; 6208 } 6209 return false; 6210 }, 6211 6212 /** 6213 * Sets the firstElement and lastElement instance properties 6214 * to the first and last focusable elements in the Panel. 6215 * 6216 * @method setFirstLastFocusable 6217 */ 6218 setFirstLastFocusable : function() { 6219 6220 this.firstElement = null; 6221 this.lastElement = null; 6222 6223 var elements = this.getFocusableElements(); 6224 this.focusableElements = elements; 6225 6226 if (elements.length > 0) { 6227 this.firstElement = elements[0]; 6228 this.lastElement = elements[elements.length - 1]; 6229 } 6230 6231 if (this.cfg.getProperty("modal")) { 6232 this._setTabLoop(this.firstElement, this.lastElement); 6233 } 6234 }, 6235 6236 /** 6237 * Initializes the custom events for Module which are fired 6238 * automatically at appropriate times by the Module class. 6239 */ 6240 initEvents: function () { 6241 Panel.superclass.initEvents.call(this); 6242 6243 var SIGNATURE = CustomEvent.LIST; 6244 6245 /** 6246 * CustomEvent fired after the modality mask is shown 6247 * @event showMaskEvent 6248 */ 6249 this.showMaskEvent = this.createEvent(EVENT_TYPES.SHOW_MASK); 6250 this.showMaskEvent.signature = SIGNATURE; 6251 6252 /** 6253 * CustomEvent fired before the modality mask is shown. Subscribers can return false to prevent the 6254 * mask from being shown 6255 * @event beforeShowMaskEvent 6256 */ 6257 this.beforeShowMaskEvent = this.createEvent(EVENT_TYPES.BEFORE_SHOW_MASK); 6258 this.beforeShowMaskEvent.signature = SIGNATURE; 6259 6260 /** 6261 * CustomEvent fired after the modality mask is hidden 6262 * @event hideMaskEvent 6263 */ 6264 this.hideMaskEvent = this.createEvent(EVENT_TYPES.HIDE_MASK); 6265 this.hideMaskEvent.signature = SIGNATURE; 6266 6267 /** 6268 * CustomEvent fired before the modality mask is hidden. Subscribers can return false to prevent the 6269 * mask from being hidden 6270 * @event beforeHideMaskEvent 6271 */ 6272 this.beforeHideMaskEvent = this.createEvent(EVENT_TYPES.BEFORE_HIDE_MASK); 6273 this.beforeHideMaskEvent.signature = SIGNATURE; 6274 6275 /** 6276 * CustomEvent when the Panel is dragged 6277 * @event dragEvent 6278 */ 6279 this.dragEvent = this.createEvent(EVENT_TYPES.DRAG); 6280 this.dragEvent.signature = SIGNATURE; 6281 }, 6282 6283 /** 6284 * Initializes the class's configurable properties which can be changed 6285 * using the Panel's Config object (cfg). 6286 * @method initDefaultConfig 6287 */ 6288 initDefaultConfig: function () { 6289 Panel.superclass.initDefaultConfig.call(this); 6290 6291 // Add panel config properties // 6292 6293 /** 6294 * True if the Panel should display a "close" button 6295 * @config close 6296 * @type Boolean 6297 * @default true 6298 */ 6299 this.cfg.addProperty(DEFAULT_CONFIG.CLOSE.key, { 6300 handler: this.configClose, 6301 value: DEFAULT_CONFIG.CLOSE.value, 6302 validator: DEFAULT_CONFIG.CLOSE.validator, 6303 supercedes: DEFAULT_CONFIG.CLOSE.supercedes 6304 }); 6305 6306 /** 6307 * Boolean specifying if the Panel should be draggable. The default 6308 * value is "true" if the Drag and Drop utility is included, 6309 * otherwise it is "false." <strong>PLEASE NOTE:</strong> There is a 6310 * known issue in IE 6 (Strict Mode and Quirks Mode) and IE 7 6311 * (Quirks Mode) where Panels that either don't have a value set for 6312 * their "width" configuration property, or their "width" 6313 * configuration property is set to "auto" will only be draggable by 6314 * placing the mouse on the text of the Panel's header element. 6315 * To fix this bug, draggable Panels missing a value for their 6316 * "width" configuration property, or whose "width" configuration 6317 * property is set to "auto" will have it set to the value of 6318 * their root HTML element's offsetWidth before they are made 6319 * visible. The calculated width is then removed when the Panel is 6320 * hidden. <em>This fix is only applied to draggable Panels in IE 6 6321 * (Strict Mode and Quirks Mode) and IE 7 (Quirks Mode)</em>. For 6322 * more information on this issue see: 6323 * YUILibrary bugs #1726972 and #1589210. 6324 * @config draggable 6325 * @type Boolean 6326 * @default true 6327 */ 6328 this.cfg.addProperty(DEFAULT_CONFIG.DRAGGABLE.key, { 6329 handler: this.configDraggable, 6330 value: (Util.DD) ? true : false, 6331 validator: DEFAULT_CONFIG.DRAGGABLE.validator, 6332 supercedes: DEFAULT_CONFIG.DRAGGABLE.supercedes 6333 }); 6334 6335 /** 6336 * Boolean specifying if the draggable Panel should be drag only, not interacting with drop 6337 * targets on the page. 6338 * <p> 6339 * When set to true, draggable Panels will not check to see if they are over drop targets, 6340 * or fire the DragDrop events required to support drop target interaction (onDragEnter, 6341 * onDragOver, onDragOut, onDragDrop etc.). 6342 * If the Panel is not designed to be dropped on any target elements on the page, then this 6343 * flag can be set to true to improve performance. 6344 * </p> 6345 * <p> 6346 * When set to false, all drop target related events will be fired. 6347 * </p> 6348 * <p> 6349 * The property is set to false by default to maintain backwards compatibility but should be 6350 * set to true if drop target interaction is not required for the Panel, to improve performance.</p> 6351 * 6352 * @config dragOnly 6353 * @type Boolean 6354 * @default false 6355 */ 6356 this.cfg.addProperty(DEFAULT_CONFIG.DRAG_ONLY.key, { 6357 value: DEFAULT_CONFIG.DRAG_ONLY.value, 6358 validator: DEFAULT_CONFIG.DRAG_ONLY.validator, 6359 supercedes: DEFAULT_CONFIG.DRAG_ONLY.supercedes 6360 }); 6361 6362 /** 6363 * Sets the type of underlay to display for the Panel. Valid values 6364 * are "shadow," "matte," and "none". <strong>PLEASE NOTE:</strong> 6365 * The creation of the underlay element is deferred until the Panel 6366 * is initially made visible. For Gecko-based browsers on Mac 6367 * OS X the underlay elment is always created as it is used as a 6368 * shim to prevent Aqua scrollbars below a Panel instance from poking 6369 * through it (See YUILibrary bug #1723530). 6370 * @config underlay 6371 * @type String 6372 * @default shadow 6373 */ 6374 this.cfg.addProperty(DEFAULT_CONFIG.UNDERLAY.key, { 6375 handler: this.configUnderlay, 6376 value: DEFAULT_CONFIG.UNDERLAY.value, 6377 supercedes: DEFAULT_CONFIG.UNDERLAY.supercedes 6378 }); 6379 6380 /** 6381 * True if the Panel should be displayed in a modal fashion, 6382 * automatically creating a transparent mask over the document that 6383 * will not be removed until the Panel is dismissed. 6384 * @config modal 6385 * @type Boolean 6386 * @default false 6387 */ 6388 this.cfg.addProperty(DEFAULT_CONFIG.MODAL.key, { 6389 handler: this.configModal, 6390 value: DEFAULT_CONFIG.MODAL.value, 6391 validator: DEFAULT_CONFIG.MODAL.validator, 6392 supercedes: DEFAULT_CONFIG.MODAL.supercedes 6393 }); 6394 6395 /** 6396 * A KeyListener (or array of KeyListeners) that will be enabled 6397 * when the Panel is shown, and disabled when the Panel is hidden. 6398 * @config keylisteners 6399 * @type YAHOO.util.KeyListener[] 6400 * @default null 6401 */ 6402 this.cfg.addProperty(DEFAULT_CONFIG.KEY_LISTENERS.key, { 6403 handler: this.configKeyListeners, 6404 suppressEvent: DEFAULT_CONFIG.KEY_LISTENERS.suppressEvent, 6405 supercedes: DEFAULT_CONFIG.KEY_LISTENERS.supercedes 6406 }); 6407 6408 /** 6409 * UI Strings used by the Panel. The strings are inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 6410 * 6411 * @config strings 6412 * @type Object 6413 * @default An object literal with the properties shown below: 6414 * <dl> 6415 * <dt>close</dt><dd><em>HTML</em> : The markup to use as the label for the close icon. Defaults to "Close".</dd> 6416 * </dl> 6417 */ 6418 this.cfg.addProperty(DEFAULT_CONFIG.STRINGS.key, { 6419 value:DEFAULT_CONFIG.STRINGS.value, 6420 handler:this.configStrings, 6421 validator:DEFAULT_CONFIG.STRINGS.validator, 6422 supercedes:DEFAULT_CONFIG.STRINGS.supercedes 6423 }); 6424 }, 6425 6426 // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // 6427 6428 /** 6429 * The default event handler fired when the "close" property is changed. 6430 * The method controls the appending or hiding of the close icon at the 6431 * top right of the Panel. 6432 * @method configClose 6433 * @param {String} type The CustomEvent type (usually the property name) 6434 * @param {Object[]} args The CustomEvent arguments. For configuration 6435 * handlers, args[0] will equal the newly applied value for the property. 6436 * @param {Object} obj The scope object. For configuration handlers, 6437 * this will usually equal the owner. 6438 */ 6439 configClose: function (type, args, obj) { 6440 6441 var val = args[0], 6442 oClose = this.close, 6443 strings = this.cfg.getProperty("strings"), 6444 fc; 6445 6446 if (val) { 6447 if (!oClose) { 6448 6449 if (!m_oCloseIconTemplate) { 6450 m_oCloseIconTemplate = document.createElement("a"); 6451 m_oCloseIconTemplate.className = "container-close"; 6452 m_oCloseIconTemplate.href = "#"; 6453 } 6454 6455 oClose = m_oCloseIconTemplate.cloneNode(true); 6456 6457 fc = this.innerElement.firstChild; 6458 6459 if (fc) { 6460 this.innerElement.insertBefore(oClose, fc); 6461 } else { 6462 this.innerElement.appendChild(oClose); 6463 } 6464 6465 oClose.innerHTML = (strings && strings.close) ? strings.close : " "; 6466 6467 Event.on(oClose, "click", this._doClose, this, true); 6468 6469 this.close = oClose; 6470 6471 } else { 6472 oClose.style.display = "block"; 6473 } 6474 6475 } else { 6476 if (oClose) { 6477 oClose.style.display = "none"; 6478 } 6479 } 6480 6481 }, 6482 6483 /** 6484 * Event handler for the close icon 6485 * 6486 * @method _doClose 6487 * @protected 6488 * 6489 * @param {DOMEvent} e 6490 */ 6491 _doClose : function (e) { 6492 Event.preventDefault(e); 6493 this.hide(); 6494 }, 6495 6496 /** 6497 * The default event handler fired when the "draggable" property 6498 * is changed. 6499 * @method configDraggable 6500 * @param {String} type The CustomEvent type (usually the property name) 6501 * @param {Object[]} args The CustomEvent arguments. For configuration 6502 * handlers, args[0] will equal the newly applied value for the property. 6503 * @param {Object} obj The scope object. For configuration handlers, 6504 * this will usually equal the owner. 6505 */ 6506 configDraggable: function (type, args, obj) { 6507 var val = args[0]; 6508 6509 if (val) { 6510 if (!Util.DD) { 6511 YAHOO.log("DD dependency not met.", "error"); 6512 this.cfg.setProperty("draggable", false); 6513 return; 6514 } 6515 6516 if (this.header) { 6517 Dom.setStyle(this.header, "cursor", "move"); 6518 this.registerDragDrop(); 6519 } 6520 6521 this.subscribe("beforeShow", setWidthToOffsetWidth); 6522 6523 } else { 6524 6525 if (this.dd) { 6526 this.dd.unreg(); 6527 } 6528 6529 if (this.header) { 6530 Dom.setStyle(this.header,"cursor","auto"); 6531 } 6532 6533 this.unsubscribe("beforeShow", setWidthToOffsetWidth); 6534 } 6535 }, 6536 6537 /** 6538 * The default event handler fired when the "underlay" property 6539 * is changed. 6540 * @method configUnderlay 6541 * @param {String} type The CustomEvent type (usually the property name) 6542 * @param {Object[]} args The CustomEvent arguments. For configuration 6543 * handlers, args[0] will equal the newly applied value for the property. 6544 * @param {Object} obj The scope object. For configuration handlers, 6545 * this will usually equal the owner. 6546 */ 6547 configUnderlay: function (type, args, obj) { 6548 6549 var bMacGecko = (this.platform == "mac" && UA.gecko), 6550 sUnderlay = args[0].toLowerCase(), 6551 oUnderlay = this.underlay, 6552 oElement = this.element; 6553 6554 function createUnderlay() { 6555 var bNew = false; 6556 if (!oUnderlay) { // create if not already in DOM 6557 6558 if (!m_oUnderlayTemplate) { 6559 m_oUnderlayTemplate = document.createElement("div"); 6560 m_oUnderlayTemplate.className = "underlay"; 6561 } 6562 6563 oUnderlay = m_oUnderlayTemplate.cloneNode(false); 6564 this.element.appendChild(oUnderlay); 6565 6566 this.underlay = oUnderlay; 6567 6568 if (bIEQuirks) { 6569 this.sizeUnderlay(); 6570 this.cfg.subscribeToConfigEvent("width", this.sizeUnderlay); 6571 this.cfg.subscribeToConfigEvent("height", this.sizeUnderlay); 6572 6573 this.changeContentEvent.subscribe(this.sizeUnderlay); 6574 YAHOO.widget.Module.textResizeEvent.subscribe(this.sizeUnderlay, this, true); 6575 } 6576 6577 if (UA.webkit && UA.webkit < 420) { 6578 this.changeContentEvent.subscribe(this.forceUnderlayRedraw); 6579 } 6580 6581 bNew = true; 6582 } 6583 } 6584 6585 function onBeforeShow() { 6586 var bNew = createUnderlay.call(this); 6587 if (!bNew && bIEQuirks) { 6588 this.sizeUnderlay(); 6589 } 6590 this._underlayDeferred = false; 6591 this.beforeShowEvent.unsubscribe(onBeforeShow); 6592 } 6593 6594 function destroyUnderlay() { 6595 if (this._underlayDeferred) { 6596 this.beforeShowEvent.unsubscribe(onBeforeShow); 6597 this._underlayDeferred = false; 6598 } 6599 6600 if (oUnderlay) { 6601 this.cfg.unsubscribeFromConfigEvent("width", this.sizeUnderlay); 6602 this.cfg.unsubscribeFromConfigEvent("height",this.sizeUnderlay); 6603 this.changeContentEvent.unsubscribe(this.sizeUnderlay); 6604 this.changeContentEvent.unsubscribe(this.forceUnderlayRedraw); 6605 YAHOO.widget.Module.textResizeEvent.unsubscribe(this.sizeUnderlay, this, true); 6606 6607 this.element.removeChild(oUnderlay); 6608 6609 this.underlay = null; 6610 } 6611 } 6612 6613 switch (sUnderlay) { 6614 case "shadow": 6615 Dom.removeClass(oElement, "matte"); 6616 Dom.addClass(oElement, "shadow"); 6617 break; 6618 case "matte": 6619 if (!bMacGecko) { 6620 destroyUnderlay.call(this); 6621 } 6622 Dom.removeClass(oElement, "shadow"); 6623 Dom.addClass(oElement, "matte"); 6624 break; 6625 default: 6626 if (!bMacGecko) { 6627 destroyUnderlay.call(this); 6628 } 6629 Dom.removeClass(oElement, "shadow"); 6630 Dom.removeClass(oElement, "matte"); 6631 break; 6632 } 6633 6634 if ((sUnderlay == "shadow") || (bMacGecko && !oUnderlay)) { 6635 if (this.cfg.getProperty("visible")) { 6636 var bNew = createUnderlay.call(this); 6637 if (!bNew && bIEQuirks) { 6638 this.sizeUnderlay(); 6639 } 6640 } else { 6641 if (!this._underlayDeferred) { 6642 this.beforeShowEvent.subscribe(onBeforeShow); 6643 this._underlayDeferred = true; 6644 } 6645 } 6646 } 6647 }, 6648 6649 /** 6650 * The default event handler fired when the "modal" property is 6651 * changed. This handler subscribes or unsubscribes to the show and hide 6652 * events to handle the display or hide of the modality mask. 6653 * @method configModal 6654 * @param {String} type The CustomEvent type (usually the property name) 6655 * @param {Object[]} args The CustomEvent arguments. For configuration 6656 * handlers, args[0] will equal the newly applied value for the property. 6657 * @param {Object} obj The scope object. For configuration handlers, 6658 * this will usually equal the owner. 6659 */ 6660 configModal: function (type, args, obj) { 6661 6662 var modal = args[0]; 6663 if (modal) { 6664 if (!this._hasModalityEventListeners) { 6665 6666 this.subscribe("beforeShow", this.buildMask); 6667 this.subscribe("beforeShow", this.bringToTop); 6668 this.subscribe("beforeShow", this.showMask); 6669 this.subscribe("hide", this.hideMask); 6670 6671 Overlay.windowResizeEvent.subscribe(this.sizeMask, 6672 this, true); 6673 6674 this._hasModalityEventListeners = true; 6675 } 6676 } else { 6677 if (this._hasModalityEventListeners) { 6678 6679 if (this.cfg.getProperty("visible")) { 6680 this.hideMask(); 6681 this.removeMask(); 6682 } 6683 6684 this.unsubscribe("beforeShow", this.buildMask); 6685 this.unsubscribe("beforeShow", this.bringToTop); 6686 this.unsubscribe("beforeShow", this.showMask); 6687 this.unsubscribe("hide", this.hideMask); 6688 6689 Overlay.windowResizeEvent.unsubscribe(this.sizeMask, this); 6690 6691 this._hasModalityEventListeners = false; 6692 } 6693 } 6694 }, 6695 6696 /** 6697 * Removes the modality mask. 6698 * @method removeMask 6699 */ 6700 removeMask: function () { 6701 6702 var oMask = this.mask, 6703 oParentNode; 6704 6705 if (oMask) { 6706 /* 6707 Hide the mask before destroying it to ensure that DOM 6708 event handlers on focusable elements get removed. 6709 */ 6710 this.hideMask(); 6711 6712 oParentNode = oMask.parentNode; 6713 if (oParentNode) { 6714 oParentNode.removeChild(oMask); 6715 } 6716 6717 this.mask = null; 6718 } 6719 }, 6720 6721 /** 6722 * The default event handler fired when the "keylisteners" property 6723 * is changed. 6724 * @method configKeyListeners 6725 * @param {String} type The CustomEvent type (usually the property name) 6726 * @param {Object[]} args The CustomEvent arguments. For configuration 6727 * handlers, args[0] will equal the newly applied value for the property. 6728 * @param {Object} obj The scope object. For configuration handlers, 6729 * this will usually equal the owner. 6730 */ 6731 configKeyListeners: function (type, args, obj) { 6732 6733 var listeners = args[0], 6734 listener, 6735 nListeners, 6736 i; 6737 6738 if (listeners) { 6739 6740 if (listeners instanceof Array) { 6741 6742 nListeners = listeners.length; 6743 6744 for (i = 0; i < nListeners; i++) { 6745 6746 listener = listeners[i]; 6747 6748 if (!Config.alreadySubscribed(this.showEvent, 6749 listener.enable, listener)) { 6750 6751 this.showEvent.subscribe(listener.enable, 6752 listener, true); 6753 6754 } 6755 6756 if (!Config.alreadySubscribed(this.hideEvent, 6757 listener.disable, listener)) { 6758 6759 this.hideEvent.subscribe(listener.disable, 6760 listener, true); 6761 6762 this.destroyEvent.subscribe(listener.disable, 6763 listener, true); 6764 } 6765 } 6766 6767 } else { 6768 6769 if (!Config.alreadySubscribed(this.showEvent, 6770 listeners.enable, listeners)) { 6771 6772 this.showEvent.subscribe(listeners.enable, 6773 listeners, true); 6774 } 6775 6776 if (!Config.alreadySubscribed(this.hideEvent, 6777 listeners.disable, listeners)) { 6778 6779 this.hideEvent.subscribe(listeners.disable, 6780 listeners, true); 6781 6782 this.destroyEvent.subscribe(listeners.disable, 6783 listeners, true); 6784 6785 } 6786 6787 } 6788 6789 } 6790 6791 }, 6792 6793 /** 6794 * The default handler for the "strings" property 6795 * @method configStrings 6796 */ 6797 configStrings : function(type, args, obj) { 6798 var val = Lang.merge(DEFAULT_CONFIG.STRINGS.value, args[0]); 6799 this.cfg.setProperty(DEFAULT_CONFIG.STRINGS.key, val, true); 6800 }, 6801 6802 /** 6803 * The default event handler fired when the "height" property is changed. 6804 * @method configHeight 6805 * @param {String} type The CustomEvent type (usually the property name) 6806 * @param {Object[]} args The CustomEvent arguments. For configuration 6807 * handlers, args[0] will equal the newly applied value for the property. 6808 * @param {Object} obj The scope object. For configuration handlers, 6809 * this will usually equal the owner. 6810 */ 6811 configHeight: function (type, args, obj) { 6812 var height = args[0], 6813 el = this.innerElement; 6814 6815 Dom.setStyle(el, "height", height); 6816 this.cfg.refireEvent("iframe"); 6817 }, 6818 6819 /** 6820 * The default custom event handler executed when the Panel's height is changed, 6821 * if the autofillheight property has been set. 6822 * 6823 * @method _autoFillOnHeightChange 6824 * @protected 6825 * @param {String} type The event type 6826 * @param {Array} args The array of arguments passed to event subscribers 6827 * @param {HTMLElement} el The header, body or footer element which is to be resized to fill 6828 * out the containers height 6829 */ 6830 _autoFillOnHeightChange : function(type, args, el) { 6831 Panel.superclass._autoFillOnHeightChange.apply(this, arguments); 6832 if (bIEQuirks) { 6833 var panel = this; 6834 setTimeout(function() { 6835 panel.sizeUnderlay(); 6836 },0); 6837 } 6838 }, 6839 6840 /** 6841 * The default event handler fired when the "width" property is changed. 6842 * @method configWidth 6843 * @param {String} type The CustomEvent type (usually the property name) 6844 * @param {Object[]} args The CustomEvent arguments. For configuration 6845 * handlers, args[0] will equal the newly applied value for the property. 6846 * @param {Object} obj The scope object. For configuration handlers, 6847 * this will usually equal the owner. 6848 */ 6849 configWidth: function (type, args, obj) { 6850 6851 var width = args[0], 6852 el = this.innerElement; 6853 6854 Dom.setStyle(el, "width", width); 6855 this.cfg.refireEvent("iframe"); 6856 6857 }, 6858 6859 /** 6860 * The default event handler fired when the "zIndex" property is changed. 6861 * @method configzIndex 6862 * @param {String} type The CustomEvent type (usually the property name) 6863 * @param {Object[]} args The CustomEvent arguments. For configuration 6864 * handlers, args[0] will equal the newly applied value for the property. 6865 * @param {Object} obj The scope object. For configuration handlers, 6866 * this will usually equal the owner. 6867 */ 6868 configzIndex: function (type, args, obj) { 6869 Panel.superclass.configzIndex.call(this, type, args, obj); 6870 6871 if (this.mask || this.cfg.getProperty("modal") === true) { 6872 var panelZ = Dom.getStyle(this.element, "zIndex"); 6873 if (!panelZ || isNaN(panelZ)) { 6874 panelZ = 0; 6875 } 6876 6877 if (panelZ === 0) { 6878 // Recursive call to configzindex (which should be stopped 6879 // from going further because panelZ should no longer === 0) 6880 this.cfg.setProperty("zIndex", 1); 6881 } else { 6882 this.stackMask(); 6883 } 6884 } 6885 }, 6886 6887 // END BUILT-IN PROPERTY EVENT HANDLERS // 6888 /** 6889 * Builds the wrapping container around the Panel that is used for 6890 * positioning the shadow and matte underlays. The container element is 6891 * assigned to a local instance variable called container, and the 6892 * element is reinserted inside of it. 6893 * @method buildWrapper 6894 */ 6895 buildWrapper: function () { 6896 6897 var elementParent = this.element.parentNode, 6898 originalElement = this.element, 6899 wrapper = document.createElement("div"); 6900 6901 wrapper.className = Panel.CSS_PANEL_CONTAINER; 6902 wrapper.id = originalElement.id + "_c"; 6903 6904 if (elementParent) { 6905 elementParent.insertBefore(wrapper, originalElement); 6906 } 6907 6908 wrapper.appendChild(originalElement); 6909 6910 this.element = wrapper; 6911 this.innerElement = originalElement; 6912 6913 Dom.setStyle(this.innerElement, "visibility", "inherit"); 6914 }, 6915 6916 /** 6917 * Adjusts the size of the shadow based on the size of the element. 6918 * @method sizeUnderlay 6919 */ 6920 sizeUnderlay: function () { 6921 var oUnderlay = this.underlay, 6922 oElement; 6923 6924 if (oUnderlay) { 6925 oElement = this.element; 6926 oUnderlay.style.width = oElement.offsetWidth + "px"; 6927 oUnderlay.style.height = oElement.offsetHeight + "px"; 6928 } 6929 }, 6930 6931 /** 6932 * Registers the Panel's header for drag & drop capability. 6933 * @method registerDragDrop 6934 */ 6935 registerDragDrop: function () { 6936 6937 var me = this; 6938 6939 if (this.header) { 6940 6941 if (!Util.DD) { 6942 YAHOO.log("DD dependency not met.", "error"); 6943 return; 6944 } 6945 6946 var bDragOnly = (this.cfg.getProperty("dragonly") === true); 6947 6948 /** 6949 * The YAHOO.util.DD instance, used to implement the draggable header for the panel if draggable is enabled 6950 * 6951 * @property dd 6952 * @type YAHOO.util.DD 6953 */ 6954 this.dd = new Util.DD(this.element.id, this.id, {dragOnly: bDragOnly}); 6955 6956 if (!this.header.id) { 6957 this.header.id = this.id + "_h"; 6958 } 6959 6960 this.dd.startDrag = function () { 6961 6962 var offsetHeight, 6963 offsetWidth, 6964 viewPortWidth, 6965 viewPortHeight, 6966 scrollX, 6967 scrollY; 6968 6969 if (YAHOO.env.ua.ie == 6) { 6970 Dom.addClass(me.element,"drag"); 6971 } 6972 6973 if (me.cfg.getProperty("constraintoviewport")) { 6974 6975 var nViewportOffset = Overlay.VIEWPORT_OFFSET; 6976 6977 offsetHeight = me.element.offsetHeight; 6978 offsetWidth = me.element.offsetWidth; 6979 6980 viewPortWidth = Dom.getViewportWidth(); 6981 viewPortHeight = Dom.getViewportHeight(); 6982 6983 scrollX = Dom.getDocumentScrollLeft(); 6984 scrollY = Dom.getDocumentScrollTop(); 6985 6986 if (offsetHeight + nViewportOffset < viewPortHeight) { 6987 this.minY = scrollY + nViewportOffset; 6988 this.maxY = scrollY + viewPortHeight - offsetHeight - nViewportOffset; 6989 } else { 6990 this.minY = scrollY + nViewportOffset; 6991 this.maxY = scrollY + nViewportOffset; 6992 } 6993 6994 if (offsetWidth + nViewportOffset < viewPortWidth) { 6995 this.minX = scrollX + nViewportOffset; 6996 this.maxX = scrollX + viewPortWidth - offsetWidth - nViewportOffset; 6997 } else { 6998 this.minX = scrollX + nViewportOffset; 6999 this.maxX = scrollX + nViewportOffset; 7000 } 7001 7002 this.constrainX = true; 7003 this.constrainY = true; 7004 } else { 7005 this.constrainX = false; 7006 this.constrainY = false; 7007 } 7008 7009 me.dragEvent.fire("startDrag", arguments); 7010 }; 7011 7012 this.dd.onDrag = function () { 7013 me.syncPosition(); 7014 me.cfg.refireEvent("iframe"); 7015 if (this.platform == "mac" && YAHOO.env.ua.gecko) { 7016 this.showMacGeckoScrollbars(); 7017 } 7018 7019 me.dragEvent.fire("onDrag", arguments); 7020 }; 7021 7022 this.dd.endDrag = function () { 7023 7024 if (YAHOO.env.ua.ie == 6) { 7025 Dom.removeClass(me.element,"drag"); 7026 } 7027 7028 me.dragEvent.fire("endDrag", arguments); 7029 me.moveEvent.fire(me.cfg.getProperty("xy")); 7030 7031 }; 7032 7033 this.dd.setHandleElId(this.header.id); 7034 this.dd.addInvalidHandleType("INPUT"); 7035 this.dd.addInvalidHandleType("SELECT"); 7036 this.dd.addInvalidHandleType("TEXTAREA"); 7037 } 7038 }, 7039 7040 /** 7041 * Builds the mask that is laid over the document when the Panel is 7042 * configured to be modal. 7043 * @method buildMask 7044 */ 7045 buildMask: function () { 7046 var oMask = this.mask; 7047 if (!oMask) { 7048 if (!m_oMaskTemplate) { 7049 m_oMaskTemplate = document.createElement("div"); 7050 m_oMaskTemplate.className = "mask"; 7051 m_oMaskTemplate.innerHTML = " "; 7052 } 7053 oMask = m_oMaskTemplate.cloneNode(true); 7054 oMask.id = this.id + "_mask"; 7055 7056 document.body.insertBefore(oMask, document.body.firstChild); 7057 7058 this.mask = oMask; 7059 7060 if (YAHOO.env.ua.gecko && this.platform == "mac") { 7061 Dom.addClass(this.mask, "block-scrollbars"); 7062 } 7063 7064 // Stack mask based on the element zindex 7065 this.stackMask(); 7066 } 7067 }, 7068 7069 /** 7070 * Hides the modality mask. 7071 * @method hideMask 7072 */ 7073 hideMask: function () { 7074 if (this.cfg.getProperty("modal") && this.mask && this.beforeHideMaskEvent.fire()) { 7075 this.mask.style.display = "none"; 7076 Dom.removeClass(document.body, "masked"); 7077 this.hideMaskEvent.fire(); 7078 } 7079 }, 7080 7081 /** 7082 * Shows the modality mask. 7083 * @method showMask 7084 */ 7085 showMask: function () { 7086 if (this.cfg.getProperty("modal") && this.mask && this.beforeShowMaskEvent.fire()) { 7087 Dom.addClass(document.body, "masked"); 7088 this.sizeMask(); 7089 this.mask.style.display = "block"; 7090 this.showMaskEvent.fire(); 7091 } 7092 }, 7093 7094 /** 7095 * Sets the size of the modality mask to cover the entire scrollable 7096 * area of the document 7097 * @method sizeMask 7098 */ 7099 sizeMask: function () { 7100 if (this.mask) { 7101 7102 // Shrink mask first, so it doesn't affect the document size. 7103 var mask = this.mask, 7104 viewWidth = Dom.getViewportWidth(), 7105 viewHeight = Dom.getViewportHeight(); 7106 7107 if (mask.offsetHeight > viewHeight) { 7108 mask.style.height = viewHeight + "px"; 7109 } 7110 7111 if (mask.offsetWidth > viewWidth) { 7112 mask.style.width = viewWidth + "px"; 7113 } 7114 7115 // Then size it to the document 7116 mask.style.height = Dom.getDocumentHeight() + "px"; 7117 mask.style.width = Dom.getDocumentWidth() + "px"; 7118 } 7119 }, 7120 7121 /** 7122 * Sets the zindex of the mask, if it exists, based on the zindex of 7123 * the Panel element. The zindex of the mask is set to be one less 7124 * than the Panel element's zindex. 7125 * 7126 * <p>NOTE: This method will not bump up the zindex of the Panel 7127 * to ensure that the mask has a non-negative zindex. If you require the 7128 * mask zindex to be 0 or higher, the zindex of the Panel 7129 * should be set to a value higher than 0, before this method is called. 7130 * </p> 7131 * @method stackMask 7132 */ 7133 stackMask: function() { 7134 if (this.mask) { 7135 var panelZ = Dom.getStyle(this.element, "zIndex"); 7136 if (!YAHOO.lang.isUndefined(panelZ) && !isNaN(panelZ)) { 7137 Dom.setStyle(this.mask, "zIndex", panelZ - 1); 7138 } 7139 } 7140 }, 7141 7142 /** 7143 * Renders the Panel by inserting the elements that are not already in 7144 * the main Panel into their correct places. Optionally appends the 7145 * Panel to the specified node prior to the render's execution. NOTE: 7146 * For Panels without existing markup, the appendToNode argument is 7147 * REQUIRED. If this argument is ommitted and the current element is 7148 * not present in the document, the function will return false, 7149 * indicating that the render was a failure. 7150 * @method render 7151 * @param {String} appendToNode The element id to which the Module 7152 * should be appended to prior to rendering <em>OR</em> 7153 * @param {HTMLElement} appendToNode The element to which the Module 7154 * should be appended to prior to rendering 7155 * @return {boolean} Success or failure of the render 7156 */ 7157 render: function (appendToNode) { 7158 return Panel.superclass.render.call(this, appendToNode, this.innerElement); 7159 }, 7160 7161 /** 7162 * Renders the currently set header into it's proper position under the 7163 * module element. If the module element is not provided, "this.innerElement" 7164 * is used. 7165 * 7166 * @method _renderHeader 7167 * @protected 7168 * @param {HTMLElement} moduleElement Optional. A reference to the module element 7169 */ 7170 _renderHeader: function(moduleElement){ 7171 moduleElement = moduleElement || this.innerElement; 7172 Panel.superclass._renderHeader.call(this, moduleElement); 7173 }, 7174 7175 /** 7176 * Renders the currently set body into it's proper position under the 7177 * module element. If the module element is not provided, "this.innerElement" 7178 * is used. 7179 * 7180 * @method _renderBody 7181 * @protected 7182 * @param {HTMLElement} moduleElement Optional. A reference to the module element. 7183 */ 7184 _renderBody: function(moduleElement){ 7185 moduleElement = moduleElement || this.innerElement; 7186 Panel.superclass._renderBody.call(this, moduleElement); 7187 }, 7188 7189 /** 7190 * Renders the currently set footer into it's proper position under the 7191 * module element. If the module element is not provided, "this.innerElement" 7192 * is used. 7193 * 7194 * @method _renderFooter 7195 * @protected 7196 * @param {HTMLElement} moduleElement Optional. A reference to the module element 7197 */ 7198 _renderFooter: function(moduleElement){ 7199 moduleElement = moduleElement || this.innerElement; 7200 Panel.superclass._renderFooter.call(this, moduleElement); 7201 }, 7202 7203 /** 7204 * Removes the Panel element from the DOM and sets all child elements 7205 * to null. 7206 * @method destroy 7207 * @param {boolean} shallowPurge If true, only the parent element's DOM event listeners are purged. If false, or not provided, all children are also purged of DOM event listeners. 7208 * NOTE: The flag is a "shallowPurge" flag, as opposed to what may be a more intuitive "purgeChildren" flag to maintain backwards compatibility with behavior prior to 2.9.0. 7209 */ 7210 destroy: function (shallowPurge) { 7211 Overlay.windowResizeEvent.unsubscribe(this.sizeMask, this); 7212 this.removeMask(); 7213 if (this.close) { 7214 Event.purgeElement(this.close); 7215 } 7216 Panel.superclass.destroy.call(this, shallowPurge); 7217 }, 7218 7219 /** 7220 * Forces the underlay element to be repainted through the application/removal 7221 * of a yui-force-redraw class to the underlay element. 7222 * 7223 * @method forceUnderlayRedraw 7224 */ 7225 forceUnderlayRedraw : function () { 7226 var u = this.underlay; 7227 Dom.addClass(u, "yui-force-redraw"); 7228 setTimeout(function(){Dom.removeClass(u, "yui-force-redraw");}, 0); 7229 }, 7230 7231 /** 7232 * Returns a String representation of the object. 7233 * @method toString 7234 * @return {String} The string representation of the Panel. 7235 */ 7236 toString: function () { 7237 return "Panel " + this.id; 7238 } 7239 7240 }); 7241 7242 }()); 7243 (function () { 7244 7245 /** 7246 * <p> 7247 * Dialog is an implementation of Panel that can be used to submit form 7248 * data. 7249 * </p> 7250 * <p> 7251 * Built-in functionality for buttons with event handlers is included. 7252 * If the optional YUI Button dependancy is included on the page, the buttons 7253 * created will be instances of YAHOO.widget.Button, otherwise regular HTML buttons 7254 * will be created. 7255 * </p> 7256 * <p> 7257 * Forms can be processed in 3 ways -- via an asynchronous Connection utility call, 7258 * a simple form POST or GET, or manually. The YUI Connection utility should be 7259 * included if you're using the default "async" postmethod, but is not required if 7260 * you're using any of the other postmethod values. 7261 * </p> 7262 * @namespace YAHOO.widget 7263 * @class Dialog 7264 * @extends YAHOO.widget.Panel 7265 * @constructor 7266 * @param {String} el The element ID representing the Dialog <em>OR</em> 7267 * @param {HTMLElement} el The element representing the Dialog 7268 * @param {Object} userConfig The configuration object literal containing 7269 * the configuration that should be set for this Dialog. See configuration 7270 * documentation for more details. 7271 */ 7272 YAHOO.widget.Dialog = function (el, userConfig) { 7273 YAHOO.widget.Dialog.superclass.constructor.call(this, el, userConfig); 7274 }; 7275 7276 var Event = YAHOO.util.Event, 7277 CustomEvent = YAHOO.util.CustomEvent, 7278 Dom = YAHOO.util.Dom, 7279 Dialog = YAHOO.widget.Dialog, 7280 Lang = YAHOO.lang, 7281 7282 /** 7283 * Constant representing the name of the Dialog's events 7284 * @property EVENT_TYPES 7285 * @private 7286 * @final 7287 * @type Object 7288 */ 7289 EVENT_TYPES = { 7290 "BEFORE_SUBMIT": "beforeSubmit", 7291 "SUBMIT": "submit", 7292 "MANUAL_SUBMIT": "manualSubmit", 7293 "ASYNC_SUBMIT": "asyncSubmit", 7294 "FORM_SUBMIT": "formSubmit", 7295 "CANCEL": "cancel" 7296 }, 7297 7298 /** 7299 * Constant representing the Dialog's configuration properties 7300 * @property DEFAULT_CONFIG 7301 * @private 7302 * @final 7303 * @type Object 7304 */ 7305 DEFAULT_CONFIG = { 7306 7307 "POST_METHOD": { 7308 key: "postmethod", 7309 value: "async" 7310 }, 7311 7312 "POST_DATA" : { 7313 key: "postdata", 7314 value: null 7315 }, 7316 7317 "BUTTONS": { 7318 key: "buttons", 7319 value: "none", 7320 supercedes: ["visible"] 7321 }, 7322 7323 "HIDEAFTERSUBMIT" : { 7324 key: "hideaftersubmit", 7325 value: true 7326 } 7327 7328 }; 7329 7330 /** 7331 * Constant representing the default CSS class used for a Dialog 7332 * @property YAHOO.widget.Dialog.CSS_DIALOG 7333 * @static 7334 * @final 7335 * @type String 7336 */ 7337 Dialog.CSS_DIALOG = "yui-dialog"; 7338 7339 function removeButtonEventHandlers() { 7340 7341 var aButtons = this._aButtons, 7342 nButtons, 7343 oButton, 7344 i; 7345 7346 if (Lang.isArray(aButtons)) { 7347 nButtons = aButtons.length; 7348 7349 if (nButtons > 0) { 7350 i = nButtons - 1; 7351 do { 7352 oButton = aButtons[i]; 7353 7354 if (YAHOO.widget.Button && oButton instanceof YAHOO.widget.Button) { 7355 oButton.destroy(); 7356 } 7357 else if (oButton.tagName.toUpperCase() == "BUTTON") { 7358 Event.purgeElement(oButton); 7359 Event.purgeElement(oButton, false); 7360 } 7361 } 7362 while (i--); 7363 } 7364 } 7365 } 7366 7367 YAHOO.extend(Dialog, YAHOO.widget.Panel, { 7368 7369 /** 7370 * @property form 7371 * @description Object reference to the Dialog's 7372 * <code><form></code> element. 7373 * @default null 7374 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ 7375 * level-one-html.html#ID-40002357">HTMLFormElement</a> 7376 */ 7377 form: null, 7378 7379 /** 7380 * Initializes the class's configurable properties which can be changed 7381 * using the Dialog's Config object (cfg). 7382 * @method initDefaultConfig 7383 */ 7384 initDefaultConfig: function () { 7385 Dialog.superclass.initDefaultConfig.call(this); 7386 7387 /** 7388 * The internally maintained callback object for use with the 7389 * Connection utility. The format of the callback object is 7390 * similar to Connection Manager's callback object and is 7391 * simply passed through to Connection Manager when the async 7392 * request is made. 7393 * @property callback 7394 * @type Object 7395 */ 7396 this.callback = { 7397 7398 /** 7399 * The function to execute upon success of the 7400 * Connection submission (when the form does not 7401 * contain a file input element). 7402 * 7403 * @property callback.success 7404 * @type Function 7405 */ 7406 success: null, 7407 7408 /** 7409 * The function to execute upon failure of the 7410 * Connection submission 7411 * @property callback.failure 7412 * @type Function 7413 */ 7414 failure: null, 7415 7416 /** 7417 *<p> 7418 * The function to execute upon success of the 7419 * Connection submission, when the form contains 7420 * a file input element. 7421 * </p> 7422 * <p> 7423 * <em>NOTE:</em> Connection manager will not 7424 * invoke the success or failure handlers for the file 7425 * upload use case. This will be the only callback 7426 * handler invoked. 7427 * </p> 7428 * <p> 7429 * For more information, see the <a href="http://developer.yahoo.com/yui/connection/#file"> 7430 * Connection Manager documenation on file uploads</a>. 7431 * </p> 7432 * @property callback.upload 7433 * @type Function 7434 */ 7435 7436 /** 7437 * The arbitrary argument or arguments to pass to the Connection 7438 * callback functions 7439 * @property callback.argument 7440 * @type Object 7441 */ 7442 argument: null 7443 7444 }; 7445 7446 // Add form dialog config properties // 7447 /** 7448 * The method to use for posting the Dialog's form. Possible values 7449 * are "async", "form", and "manual". 7450 * @config postmethod 7451 * @type String 7452 * @default async 7453 */ 7454 this.cfg.addProperty(DEFAULT_CONFIG.POST_METHOD.key, { 7455 handler: this.configPostMethod, 7456 value: DEFAULT_CONFIG.POST_METHOD.value, 7457 validator: function (val) { 7458 if (val != "form" && val != "async" && val != "none" && 7459 val != "manual") { 7460 return false; 7461 } else { 7462 return true; 7463 } 7464 } 7465 }); 7466 7467 /** 7468 * Any additional post data which needs to be sent when using the 7469 * <a href="#config_postmethod">async</a> postmethod for dialog POST submissions. 7470 * The format for the post data string is defined by Connection Manager's 7471 * <a href="YAHOO.util.Connect.html#method_asyncRequest">asyncRequest</a> 7472 * method. 7473 * @config postdata 7474 * @type String 7475 * @default null 7476 */ 7477 this.cfg.addProperty(DEFAULT_CONFIG.POST_DATA.key, { 7478 value: DEFAULT_CONFIG.POST_DATA.value 7479 }); 7480 7481 /** 7482 * This property is used to configure whether or not the 7483 * dialog should be automatically hidden after submit. 7484 * 7485 * @config hideaftersubmit 7486 * @type Boolean 7487 * @default true 7488 */ 7489 this.cfg.addProperty(DEFAULT_CONFIG.HIDEAFTERSUBMIT.key, { 7490 value: DEFAULT_CONFIG.HIDEAFTERSUBMIT.value 7491 }); 7492 7493 /** 7494 * Array of object literals, each containing a set of properties 7495 * defining a button to be appended into the Dialog's footer. 7496 * 7497 * <p>Each button object in the buttons array can have three properties:</p> 7498 * <dl> 7499 * <dt>text:</dt> 7500 * <dd> 7501 * The text that will display on the face of the button. The text can 7502 * include HTML, as long as it is compliant with HTML Button specifications. The text is added to the DOM as HTML, 7503 * and should be escaped by the implementor if coming from an external source. 7504 * </dd> 7505 * <dt>handler:</dt> 7506 * <dd>Can be either: 7507 * <ol> 7508 * <li>A reference to a function that should fire when the 7509 * button is clicked. (In this case scope of this function is 7510 * always its Dialog instance.)</li> 7511 * 7512 * <li>An object literal representing the code to be 7513 * executed when the button is clicked. 7514 * 7515 * <p>Format:</p> 7516 * 7517 * <p> 7518 * <code>{ 7519 * <br> 7520 * <strong>fn:</strong> Function, // 7521 * The handler to call when the event fires. 7522 * <br> 7523 * <strong>obj:</strong> Object, // 7524 * An object to pass back to the handler. 7525 * <br> 7526 * <strong>scope:</strong> Object // 7527 * The object to use for the scope of the handler. 7528 * <br> 7529 * }</code> 7530 * </p> 7531 * </li> 7532 * </ol> 7533 * </dd> 7534 * <dt>isDefault:</dt> 7535 * <dd> 7536 * An optional boolean value that specifies that a button 7537 * should be highlighted and focused by default. 7538 * </dd> 7539 * </dl> 7540 * 7541 * <em>NOTE:</em>If the YUI Button Widget is included on the page, 7542 * the buttons created will be instances of YAHOO.widget.Button. 7543 * Otherwise, HTML Buttons (<code><BUTTON></code>) will be 7544 * created. 7545 * 7546 * @config buttons 7547 * @type {Array|String} 7548 * @default "none" 7549 */ 7550 this.cfg.addProperty(DEFAULT_CONFIG.BUTTONS.key, { 7551 handler: this.configButtons, 7552 value: DEFAULT_CONFIG.BUTTONS.value, 7553 supercedes : DEFAULT_CONFIG.BUTTONS.supercedes 7554 }); 7555 7556 }, 7557 7558 /** 7559 * Initializes the custom events for Dialog which are fired 7560 * automatically at appropriate times by the Dialog class. 7561 * @method initEvents 7562 */ 7563 initEvents: function () { 7564 Dialog.superclass.initEvents.call(this); 7565 7566 var SIGNATURE = CustomEvent.LIST; 7567 7568 /** 7569 * CustomEvent fired prior to submission 7570 * @event beforeSubmitEvent 7571 */ 7572 this.beforeSubmitEvent = 7573 this.createEvent(EVENT_TYPES.BEFORE_SUBMIT); 7574 this.beforeSubmitEvent.signature = SIGNATURE; 7575 7576 /** 7577 * CustomEvent fired after submission 7578 * @event submitEvent 7579 */ 7580 this.submitEvent = this.createEvent(EVENT_TYPES.SUBMIT); 7581 this.submitEvent.signature = SIGNATURE; 7582 7583 /** 7584 * CustomEvent fired for manual submission, before the generic submit event is fired 7585 * @event manualSubmitEvent 7586 */ 7587 this.manualSubmitEvent = 7588 this.createEvent(EVENT_TYPES.MANUAL_SUBMIT); 7589 this.manualSubmitEvent.signature = SIGNATURE; 7590 7591 /** 7592 * CustomEvent fired after asynchronous submission, before the generic submit event is fired 7593 * 7594 * @event asyncSubmitEvent 7595 * @param {Object} conn The connection object, returned by YAHOO.util.Connect.asyncRequest 7596 */ 7597 this.asyncSubmitEvent = this.createEvent(EVENT_TYPES.ASYNC_SUBMIT); 7598 this.asyncSubmitEvent.signature = SIGNATURE; 7599 7600 /** 7601 * CustomEvent fired after form-based submission, before the generic submit event is fired 7602 * @event formSubmitEvent 7603 */ 7604 this.formSubmitEvent = this.createEvent(EVENT_TYPES.FORM_SUBMIT); 7605 this.formSubmitEvent.signature = SIGNATURE; 7606 7607 /** 7608 * CustomEvent fired after cancel 7609 * @event cancelEvent 7610 */ 7611 this.cancelEvent = this.createEvent(EVENT_TYPES.CANCEL); 7612 this.cancelEvent.signature = SIGNATURE; 7613 7614 }, 7615 7616 /** 7617 * The Dialog initialization method, which is executed for Dialog and 7618 * all of its subclasses. This method is automatically called by the 7619 * constructor, and sets up all DOM references for pre-existing markup, 7620 * and creates required markup if it is not already present. 7621 * 7622 * @method init 7623 * @param {String} el The element ID representing the Dialog <em>OR</em> 7624 * @param {HTMLElement} el The element representing the Dialog 7625 * @param {Object} userConfig The configuration object literal 7626 * containing the configuration that should be set for this Dialog. 7627 * See configuration documentation for more details. 7628 */ 7629 init: function (el, userConfig) { 7630 7631 /* 7632 Note that we don't pass the user config in here yet because 7633 we only want it executed once, at the lowest subclass level 7634 */ 7635 7636 Dialog.superclass.init.call(this, el/*, userConfig*/); 7637 7638 this.beforeInitEvent.fire(Dialog); 7639 7640 Dom.addClass(this.element, Dialog.CSS_DIALOG); 7641 7642 this.cfg.setProperty("visible", false); 7643 7644 if (userConfig) { 7645 this.cfg.applyConfig(userConfig, true); 7646 } 7647 7648 //this.showEvent.subscribe(this.focusFirst, this, true); 7649 this.beforeHideEvent.subscribe(this.blurButtons, this, true); 7650 7651 this.subscribe("changeBody", this.registerForm); 7652 7653 this.initEvent.fire(Dialog); 7654 }, 7655 7656 /** 7657 * Submits the Dialog's form depending on the value of the 7658 * "postmethod" configuration property. <strong>Please note: 7659 * </strong> As of version 2.3 this method will automatically handle 7660 * asyncronous file uploads should the Dialog instance's form contain 7661 * <code><input type="file"></code> elements. If a Dialog 7662 * instance will be handling asyncronous file uploads, its 7663 * <code>callback</code> property will need to be setup with a 7664 * <code>upload</code> handler rather than the standard 7665 * <code>success</code> and, or <code>failure</code> handlers. For more 7666 * information, see the <a href="http://developer.yahoo.com/yui/ 7667 * connection/#file">Connection Manager documenation on file uploads</a>. 7668 * @method doSubmit 7669 */ 7670 doSubmit: function () { 7671 7672 var Connect = YAHOO.util.Connect, 7673 oForm = this.form, 7674 bUseFileUpload = false, 7675 bUseSecureFileUpload = false, 7676 aElements, 7677 nElements, 7678 i, 7679 formAttrs; 7680 7681 switch (this.cfg.getProperty("postmethod")) { 7682 7683 case "async": 7684 aElements = oForm.elements; 7685 nElements = aElements.length; 7686 7687 if (nElements > 0) { 7688 i = nElements - 1; 7689 do { 7690 if (aElements[i].type == "file") { 7691 bUseFileUpload = true; 7692 break; 7693 } 7694 } 7695 while(i--); 7696 } 7697 7698 if (bUseFileUpload && YAHOO.env.ua.ie && this.isSecure) { 7699 bUseSecureFileUpload = true; 7700 } 7701 7702 formAttrs = this._getFormAttributes(oForm); 7703 7704 Connect.setForm(oForm, bUseFileUpload, bUseSecureFileUpload); 7705 7706 var postData = this.cfg.getProperty("postdata"); 7707 var c = Connect.asyncRequest(formAttrs.method, formAttrs.action, this.callback, postData); 7708 7709 this.asyncSubmitEvent.fire(c); 7710 7711 break; 7712 7713 case "form": 7714 oForm.submit(); 7715 this.formSubmitEvent.fire(); 7716 break; 7717 7718 case "none": 7719 case "manual": 7720 this.manualSubmitEvent.fire(); 7721 break; 7722 } 7723 }, 7724 7725 /** 7726 * Retrieves important attributes (currently method and action) from 7727 * the form element, accounting for any elements which may have the same name 7728 * as the attributes. Defaults to "POST" and "" for method and action respectively 7729 * if the attribute cannot be retrieved. 7730 * 7731 * @method _getFormAttributes 7732 * @protected 7733 * @param {HTMLFormElement} oForm The HTML Form element from which to retrieve the attributes 7734 * @return {Object} Object literal, with method and action String properties. 7735 */ 7736 _getFormAttributes : function(oForm){ 7737 var attrs = { 7738 method : null, 7739 action : null 7740 }; 7741 7742 if (oForm) { 7743 if (oForm.getAttributeNode) { 7744 var action = oForm.getAttributeNode("action"); 7745 var method = oForm.getAttributeNode("method"); 7746 7747 if (action) { 7748 attrs.action = action.value; 7749 } 7750 7751 if (method) { 7752 attrs.method = method.value; 7753 } 7754 7755 } else { 7756 attrs.action = oForm.getAttribute("action"); 7757 attrs.method = oForm.getAttribute("method"); 7758 } 7759 } 7760 7761 attrs.method = (Lang.isString(attrs.method) ? attrs.method : "POST").toUpperCase(); 7762 attrs.action = Lang.isString(attrs.action) ? attrs.action : ""; 7763 7764 return attrs; 7765 }, 7766 7767 /** 7768 * Prepares the Dialog's internal FORM object, creating one if one is 7769 * not currently present. 7770 * @method registerForm 7771 */ 7772 registerForm: function() { 7773 7774 var form = this.element.getElementsByTagName("form")[0]; 7775 7776 if (this.form) { 7777 if (this.form == form && Dom.isAncestor(this.element, this.form)) { 7778 return; 7779 } else { 7780 Event.purgeElement(this.form); 7781 this.form = null; 7782 } 7783 } 7784 7785 if (!form) { 7786 form = document.createElement("form"); 7787 form.name = "frm_" + this.id; 7788 this.body.appendChild(form); 7789 } 7790 7791 if (form) { 7792 this.form = form; 7793 Event.on(form, "submit", this._submitHandler, this, true); 7794 } 7795 }, 7796 7797 /** 7798 * Internal handler for the form submit event 7799 * 7800 * @method _submitHandler 7801 * @protected 7802 * @param {DOMEvent} e The DOM Event object 7803 */ 7804 _submitHandler : function(e) { 7805 Event.stopEvent(e); 7806 this.submit(); 7807 this.form.blur(); 7808 }, 7809 7810 /** 7811 * Sets up a tab, shift-tab loop between the first and last elements 7812 * provided. NOTE: Sets up the preventBackTab and preventTabOut KeyListener 7813 * instance properties, which are reset everytime this method is invoked. 7814 * 7815 * @method setTabLoop 7816 * @param {HTMLElement} firstElement 7817 * @param {HTMLElement} lastElement 7818 * 7819 */ 7820 setTabLoop : function(firstElement, lastElement) { 7821 7822 firstElement = firstElement || this.firstButton; 7823 lastElement = lastElement || this.lastButton; 7824 7825 Dialog.superclass.setTabLoop.call(this, firstElement, lastElement); 7826 }, 7827 7828 /** 7829 * Protected internal method for setTabLoop, which can be used by 7830 * subclasses to jump in and modify the arguments passed in if required. 7831 * 7832 * @method _setTabLoop 7833 * @param {HTMLElement} firstElement 7834 * @param {HTMLElement} lastElement 7835 * @protected 7836 */ 7837 _setTabLoop : function(firstElement, lastElement) { 7838 firstElement = firstElement || this.firstButton; 7839 lastElement = this.lastButton || lastElement; 7840 7841 this.setTabLoop(firstElement, lastElement); 7842 }, 7843 7844 /** 7845 * Configures instance properties, pointing to the 7846 * first and last focusable elements in the Dialog's form. 7847 * 7848 * @method setFirstLastFocusable 7849 */ 7850 setFirstLastFocusable : function() { 7851 7852 Dialog.superclass.setFirstLastFocusable.call(this); 7853 7854 var i, l, el, elements = this.focusableElements; 7855 7856 this.firstFormElement = null; 7857 this.lastFormElement = null; 7858 7859 if (this.form && elements && elements.length > 0) { 7860 l = elements.length; 7861 7862 for (i = 0; i < l; ++i) { 7863 el = elements[i]; 7864 if (this.form === el.form) { 7865 this.firstFormElement = el; 7866 break; 7867 } 7868 } 7869 7870 for (i = l-1; i >= 0; --i) { 7871 el = elements[i]; 7872 if (this.form === el.form) { 7873 this.lastFormElement = el; 7874 break; 7875 } 7876 } 7877 } 7878 }, 7879 7880 // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // 7881 /** 7882 * The default event handler fired when the "close" property is 7883 * changed. The method controls the appending or hiding of the close 7884 * icon at the top right of the Dialog. 7885 * @method configClose 7886 * @param {String} type The CustomEvent type (usually the property name) 7887 * @param {Object[]} args The CustomEvent arguments. For 7888 * configuration handlers, args[0] will equal the newly applied value 7889 * for the property. 7890 * @param {Object} obj The scope object. For configuration handlers, 7891 * this will usually equal the owner. 7892 */ 7893 configClose: function (type, args, obj) { 7894 Dialog.superclass.configClose.apply(this, arguments); 7895 }, 7896 7897 /** 7898 * Event handler for the close icon 7899 * 7900 * @method _doClose 7901 * @protected 7902 * 7903 * @param {DOMEvent} e 7904 */ 7905 _doClose : function(e) { 7906 Event.preventDefault(e); 7907 this.cancel(); 7908 }, 7909 7910 /** 7911 * The default event handler for the "buttons" configuration property 7912 * @method configButtons 7913 * @param {String} type The CustomEvent type (usually the property name) 7914 * @param {Object[]} args The CustomEvent arguments. For configuration 7915 * handlers, args[0] will equal the newly applied value for the property. 7916 * @param {Object} obj The scope object. For configuration handlers, 7917 * this will usually equal the owner. 7918 */ 7919 configButtons: function (type, args, obj) { 7920 7921 var Button = YAHOO.widget.Button, 7922 aButtons = args[0], 7923 oInnerElement = this.innerElement, 7924 oButton, 7925 oButtonEl, 7926 oYUIButton, 7927 nButtons, 7928 oSpan, 7929 oFooter, 7930 i; 7931 7932 removeButtonEventHandlers.call(this); 7933 7934 this._aButtons = null; 7935 7936 if (Lang.isArray(aButtons)) { 7937 7938 oSpan = document.createElement("span"); 7939 oSpan.className = "button-group"; 7940 nButtons = aButtons.length; 7941 7942 this._aButtons = []; 7943 this.defaultHtmlButton = null; 7944 7945 for (i = 0; i < nButtons; i++) { 7946 oButton = aButtons[i]; 7947 7948 if (Button) { 7949 oYUIButton = new Button({ label: oButton.text, type:oButton.type }); 7950 oYUIButton.appendTo(oSpan); 7951 7952 oButtonEl = oYUIButton.get("element"); 7953 7954 if (oButton.isDefault) { 7955 oYUIButton.addClass("default"); 7956 this.defaultHtmlButton = oButtonEl; 7957 } 7958 7959 if (Lang.isFunction(oButton.handler)) { 7960 7961 oYUIButton.set("onclick", { 7962 fn: oButton.handler, 7963 obj: this, 7964 scope: this 7965 }); 7966 7967 } else if (Lang.isObject(oButton.handler) && Lang.isFunction(oButton.handler.fn)) { 7968 7969 oYUIButton.set("onclick", { 7970 fn: oButton.handler.fn, 7971 obj: ((!Lang.isUndefined(oButton.handler.obj)) ? oButton.handler.obj : this), 7972 scope: (oButton.handler.scope || this) 7973 }); 7974 7975 } 7976 7977 this._aButtons[this._aButtons.length] = oYUIButton; 7978 7979 } else { 7980 7981 oButtonEl = document.createElement("button"); 7982 oButtonEl.setAttribute("type", "button"); 7983 7984 if (oButton.isDefault) { 7985 oButtonEl.className = "default"; 7986 this.defaultHtmlButton = oButtonEl; 7987 } 7988 7989 oButtonEl.innerHTML = oButton.text; 7990 7991 if (Lang.isFunction(oButton.handler)) { 7992 Event.on(oButtonEl, "click", oButton.handler, this, true); 7993 } else if (Lang.isObject(oButton.handler) && 7994 Lang.isFunction(oButton.handler.fn)) { 7995 7996 Event.on(oButtonEl, "click", 7997 oButton.handler.fn, 7998 ((!Lang.isUndefined(oButton.handler.obj)) ? oButton.handler.obj : this), 7999 (oButton.handler.scope || this)); 8000 } 8001 8002 oSpan.appendChild(oButtonEl); 8003 this._aButtons[this._aButtons.length] = oButtonEl; 8004 } 8005 8006 oButton.htmlButton = oButtonEl; 8007 8008 if (i === 0) { 8009 this.firstButton = oButtonEl; 8010 } 8011 8012 if (i == (nButtons - 1)) { 8013 this.lastButton = oButtonEl; 8014 } 8015 } 8016 8017 this.setFooter(oSpan); 8018 8019 oFooter = this.footer; 8020 8021 if (Dom.inDocument(this.element) && !Dom.isAncestor(oInnerElement, oFooter)) { 8022 oInnerElement.appendChild(oFooter); 8023 } 8024 8025 this.buttonSpan = oSpan; 8026 8027 } else { // Do cleanup 8028 oSpan = this.buttonSpan; 8029 oFooter = this.footer; 8030 if (oSpan && oFooter) { 8031 oFooter.removeChild(oSpan); 8032 this.buttonSpan = null; 8033 this.firstButton = null; 8034 this.lastButton = null; 8035 this.defaultHtmlButton = null; 8036 } 8037 } 8038 8039 this.changeContentEvent.fire(); 8040 }, 8041 8042 /** 8043 * @method getButtons 8044 * @description Returns an array containing each of the Dialog's 8045 * buttons, by default an array of HTML <code><BUTTON></code> 8046 * elements. If the Dialog's buttons were created using the 8047 * YAHOO.widget.Button class (via the inclusion of the optional Button 8048 * dependency on the page), an array of YAHOO.widget.Button instances 8049 * is returned. 8050 * @return {Array} 8051 */ 8052 getButtons: function () { 8053 return this._aButtons || null; 8054 }, 8055 8056 /** 8057 * <p> 8058 * Sets focus to the first focusable element in the Dialog's form if found, 8059 * else, the default button if found, else the first button defined via the 8060 * "buttons" configuration property. 8061 * </p> 8062 * <p> 8063 * This method is invoked when the Dialog is made visible. 8064 * </p> 8065 * @method focusFirst 8066 * @return {Boolean} true, if focused. false if not 8067 */ 8068 focusFirst: function (type, args, obj) { 8069 8070 var el = this.firstFormElement, 8071 focused = false; 8072 8073 if (args && args[1]) { 8074 Event.stopEvent(args[1]); 8075 8076 // When tabbing here, use firstElement instead of firstFormElement 8077 if (args[0] === 9 && this.firstElement) { 8078 el = this.firstElement; 8079 } 8080 } 8081 8082 if (el) { 8083 try { 8084 el.focus(); 8085 focused = true; 8086 } catch(oException) { 8087 // Ignore 8088 } 8089 } else { 8090 if (this.defaultHtmlButton) { 8091 focused = this.focusDefaultButton(); 8092 } else { 8093 focused = this.focusFirstButton(); 8094 } 8095 } 8096 return focused; 8097 }, 8098 8099 /** 8100 * Sets focus to the last element in the Dialog's form or the last 8101 * button defined via the "buttons" configuration property. 8102 * @method focusLast 8103 * @return {Boolean} true, if focused. false if not 8104 */ 8105 focusLast: function (type, args, obj) { 8106 8107 var aButtons = this.cfg.getProperty("buttons"), 8108 el = this.lastFormElement, 8109 focused = false; 8110 8111 if (args && args[1]) { 8112 Event.stopEvent(args[1]); 8113 8114 // When tabbing here, use lastElement instead of lastFormElement 8115 if (args[0] === 9 && this.lastElement) { 8116 el = this.lastElement; 8117 } 8118 } 8119 8120 if (aButtons && Lang.isArray(aButtons)) { 8121 focused = this.focusLastButton(); 8122 } else { 8123 if (el) { 8124 try { 8125 el.focus(); 8126 focused = true; 8127 } catch(oException) { 8128 // Ignore 8129 } 8130 } 8131 } 8132 8133 return focused; 8134 }, 8135 8136 /** 8137 * Helper method to normalize button references. It either returns the 8138 * YUI Button instance for the given element if found, 8139 * or the passes back the HTMLElement reference if a corresponding YUI Button 8140 * reference is not found or YAHOO.widget.Button does not exist on the page. 8141 * 8142 * @method _getButton 8143 * @private 8144 * @param {HTMLElement} button 8145 * @return {YAHOO.widget.Button|HTMLElement} 8146 */ 8147 _getButton : function(button) { 8148 var Button = YAHOO.widget.Button; 8149 8150 // If we have an HTML button and YUI Button is on the page, 8151 // get the YUI Button reference if available. 8152 if (Button && button && button.nodeName && button.id) { 8153 button = Button.getButton(button.id) || button; 8154 } 8155 8156 return button; 8157 }, 8158 8159 /** 8160 * Sets the focus to the button that is designated as the default via 8161 * the "buttons" configuration property. By default, this method is 8162 * called when the Dialog is made visible. 8163 * @method focusDefaultButton 8164 * @return {Boolean} true if focused, false if not 8165 */ 8166 focusDefaultButton: function () { 8167 var button = this._getButton(this.defaultHtmlButton), 8168 focused = false; 8169 8170 if (button) { 8171 /* 8172 Place the call to the "focus" method inside a try/catch 8173 block to prevent IE from throwing JavaScript errors if 8174 the element is disabled or hidden. 8175 */ 8176 try { 8177 button.focus(); 8178 focused = true; 8179 } catch(oException) { 8180 } 8181 } 8182 return focused; 8183 }, 8184 8185 /** 8186 * Blurs all the buttons defined via the "buttons" 8187 * configuration property. 8188 * @method blurButtons 8189 */ 8190 blurButtons: function () { 8191 8192 var aButtons = this.cfg.getProperty("buttons"), 8193 nButtons, 8194 oButton, 8195 oElement, 8196 i; 8197 8198 if (aButtons && Lang.isArray(aButtons)) { 8199 nButtons = aButtons.length; 8200 if (nButtons > 0) { 8201 i = (nButtons - 1); 8202 do { 8203 oButton = aButtons[i]; 8204 if (oButton) { 8205 oElement = this._getButton(oButton.htmlButton); 8206 if (oElement) { 8207 /* 8208 Place the call to the "blur" method inside 8209 a try/catch block to prevent IE from 8210 throwing JavaScript errors if the element 8211 is disabled or hidden. 8212 */ 8213 try { 8214 oElement.blur(); 8215 } catch(oException) { 8216 // ignore 8217 } 8218 } 8219 } 8220 } while(i--); 8221 } 8222 } 8223 }, 8224 8225 /** 8226 * Sets the focus to the first button created via the "buttons" 8227 * configuration property. 8228 * @method focusFirstButton 8229 * @return {Boolean} true, if focused. false if not 8230 */ 8231 focusFirstButton: function () { 8232 8233 var aButtons = this.cfg.getProperty("buttons"), 8234 oButton, 8235 oElement, 8236 focused = false; 8237 8238 if (aButtons && Lang.isArray(aButtons)) { 8239 oButton = aButtons[0]; 8240 if (oButton) { 8241 oElement = this._getButton(oButton.htmlButton); 8242 if (oElement) { 8243 /* 8244 Place the call to the "focus" method inside a 8245 try/catch block to prevent IE from throwing 8246 JavaScript errors if the element is disabled 8247 or hidden. 8248 */ 8249 try { 8250 oElement.focus(); 8251 focused = true; 8252 } catch(oException) { 8253 // ignore 8254 } 8255 } 8256 } 8257 } 8258 8259 return focused; 8260 }, 8261 8262 /** 8263 * Sets the focus to the last button created via the "buttons" 8264 * configuration property. 8265 * @method focusLastButton 8266 * @return {Boolean} true, if focused. false if not 8267 */ 8268 focusLastButton: function () { 8269 8270 var aButtons = this.cfg.getProperty("buttons"), 8271 nButtons, 8272 oButton, 8273 oElement, 8274 focused = false; 8275 8276 if (aButtons && Lang.isArray(aButtons)) { 8277 nButtons = aButtons.length; 8278 if (nButtons > 0) { 8279 oButton = aButtons[(nButtons - 1)]; 8280 8281 if (oButton) { 8282 oElement = this._getButton(oButton.htmlButton); 8283 if (oElement) { 8284 /* 8285 Place the call to the "focus" method inside a 8286 try/catch block to prevent IE from throwing 8287 JavaScript errors if the element is disabled 8288 or hidden. 8289 */ 8290 8291 try { 8292 oElement.focus(); 8293 focused = true; 8294 } catch(oException) { 8295 // Ignore 8296 } 8297 } 8298 } 8299 } 8300 } 8301 8302 return focused; 8303 }, 8304 8305 /** 8306 * The default event handler for the "postmethod" configuration property 8307 * @method configPostMethod 8308 * @param {String} type The CustomEvent type (usually the property name) 8309 * @param {Object[]} args The CustomEvent arguments. For 8310 * configuration handlers, args[0] will equal the newly applied value 8311 * for the property. 8312 * @param {Object} obj The scope object. For configuration handlers, 8313 * this will usually equal the owner. 8314 */ 8315 configPostMethod: function (type, args, obj) { 8316 this.registerForm(); 8317 }, 8318 8319 // END BUILT-IN PROPERTY EVENT HANDLERS // 8320 8321 /** 8322 * Built-in function hook for writing a validation function that will 8323 * be checked for a "true" value prior to a submit. This function, as 8324 * implemented by default, always returns true, so it should be 8325 * overridden if validation is necessary. 8326 * @method validate 8327 */ 8328 validate: function () { 8329 return true; 8330 }, 8331 8332 /** 8333 * Executes a submit of the Dialog if validation 8334 * is successful. By default the Dialog is hidden 8335 * after submission, but you can set the "hideaftersubmit" 8336 * configuration property to false, to prevent the Dialog 8337 * from being hidden. 8338 * 8339 * @method submit 8340 */ 8341 submit: function () { 8342 if (this.validate()) { 8343 if (this.beforeSubmitEvent.fire()) { 8344 this.doSubmit(); 8345 this.submitEvent.fire(); 8346 8347 if (this.cfg.getProperty("hideaftersubmit")) { 8348 this.hide(); 8349 } 8350 8351 return true; 8352 } else { 8353 return false; 8354 } 8355 } else { 8356 return false; 8357 } 8358 }, 8359 8360 /** 8361 * Executes the cancel of the Dialog followed by a hide. 8362 * @method cancel 8363 */ 8364 cancel: function () { 8365 this.cancelEvent.fire(); 8366 this.hide(); 8367 }, 8368 8369 /** 8370 * Returns a JSON-compatible data structure representing the data 8371 * currently contained in the form. 8372 * @method getData 8373 * @return {Object} A JSON object reprsenting the data of the 8374 * current form. 8375 */ 8376 getData: function () { 8377 8378 var oForm = this.form, 8379 aElements, 8380 nTotalElements, 8381 oData, 8382 sName, 8383 oElement, 8384 nElements, 8385 sType, 8386 sTagName, 8387 aOptions, 8388 nOptions, 8389 aValues, 8390 oOption, 8391 oRadio, 8392 oCheckbox, 8393 valueAttr, 8394 i, 8395 n; 8396 8397 function isFormElement(p_oElement) { 8398 var sTag = p_oElement.tagName.toUpperCase(); 8399 return ((sTag == "INPUT" || sTag == "TEXTAREA" || 8400 sTag == "SELECT") && p_oElement.name == sName); 8401 } 8402 8403 if (oForm) { 8404 8405 aElements = oForm.elements; 8406 nTotalElements = aElements.length; 8407 oData = {}; 8408 8409 for (i = 0; i < nTotalElements; i++) { 8410 sName = aElements[i].name; 8411 8412 /* 8413 Using "Dom.getElementsBy" to safeguard user from JS 8414 errors that result from giving a form field (or set of 8415 fields) the same name as a native method of a form 8416 (like "submit") or a DOM collection (such as the "item" 8417 method). Originally tried accessing fields via the 8418 "namedItem" method of the "element" collection, but 8419 discovered that it won't return a collection of fields 8420 in Gecko. 8421 */ 8422 8423 oElement = Dom.getElementsBy(isFormElement, "*", oForm); 8424 nElements = oElement.length; 8425 8426 if (nElements > 0) { 8427 if (nElements == 1) { 8428 oElement = oElement[0]; 8429 8430 sType = oElement.type; 8431 sTagName = oElement.tagName.toUpperCase(); 8432 8433 switch (sTagName) { 8434 case "INPUT": 8435 if (sType == "checkbox") { 8436 oData[sName] = oElement.checked; 8437 } else if (sType != "radio") { 8438 oData[sName] = oElement.value; 8439 } 8440 break; 8441 8442 case "TEXTAREA": 8443 oData[sName] = oElement.value; 8444 break; 8445 8446 case "SELECT": 8447 aOptions = oElement.options; 8448 nOptions = aOptions.length; 8449 aValues = []; 8450 8451 for (n = 0; n < nOptions; n++) { 8452 oOption = aOptions[n]; 8453 if (oOption.selected) { 8454 valueAttr = oOption.attributes.value; 8455 aValues[aValues.length] = (valueAttr && valueAttr.specified) ? oOption.value : oOption.text; 8456 } 8457 } 8458 oData[sName] = aValues; 8459 break; 8460 } 8461 8462 } else { 8463 sType = oElement[0].type; 8464 switch (sType) { 8465 case "radio": 8466 for (n = 0; n < nElements; n++) { 8467 oRadio = oElement[n]; 8468 if (oRadio.checked) { 8469 oData[sName] = oRadio.value; 8470 break; 8471 } 8472 } 8473 break; 8474 8475 case "checkbox": 8476 aValues = []; 8477 for (n = 0; n < nElements; n++) { 8478 oCheckbox = oElement[n]; 8479 if (oCheckbox.checked) { 8480 aValues[aValues.length] = oCheckbox.value; 8481 } 8482 } 8483 oData[sName] = aValues; 8484 break; 8485 } 8486 } 8487 } 8488 } 8489 } 8490 8491 return oData; 8492 }, 8493 8494 /** 8495 * Removes the Panel element from the DOM and sets all child elements 8496 * to null. 8497 * @method destroy 8498 * @param {boolean} shallowPurge If true, only the parent element's DOM event listeners are purged. If false, or not provided, all children are also purged of DOM event listeners. 8499 * NOTE: The flag is a "shallowPurge" flag, as opposed to what may be a more intuitive "purgeChildren" flag to maintain backwards compatibility with behavior prior to 2.9.0. 8500 */ 8501 destroy: function (shallowPurge) { 8502 removeButtonEventHandlers.call(this); 8503 8504 this._aButtons = null; 8505 8506 var aForms = this.element.getElementsByTagName("form"), 8507 oForm; 8508 8509 if (aForms.length > 0) { 8510 oForm = aForms[0]; 8511 8512 if (oForm) { 8513 Event.purgeElement(oForm); 8514 if (oForm.parentNode) { 8515 oForm.parentNode.removeChild(oForm); 8516 } 8517 this.form = null; 8518 } 8519 } 8520 Dialog.superclass.destroy.call(this, shallowPurge); 8521 }, 8522 8523 /** 8524 * Returns a string representation of the object. 8525 * @method toString 8526 * @return {String} The string representation of the Dialog 8527 */ 8528 toString: function () { 8529 return "Dialog " + this.id; 8530 } 8531 8532 }); 8533 8534 }()); 8535 (function () { 8536 8537 /** 8538 * SimpleDialog is a simple implementation of Dialog that can be used to 8539 * submit a single value. Forms can be processed in 3 ways -- via an 8540 * asynchronous Connection utility call, a simple form POST or GET, 8541 * or manually. 8542 * @namespace YAHOO.widget 8543 * @class SimpleDialog 8544 * @extends YAHOO.widget.Dialog 8545 * @constructor 8546 * @param {String} el The element ID representing the SimpleDialog 8547 * <em>OR</em> 8548 * @param {HTMLElement} el The element representing the SimpleDialog 8549 * @param {Object} userConfig The configuration object literal containing 8550 * the configuration that should be set for this SimpleDialog. See 8551 * configuration documentation for more details. 8552 */ 8553 YAHOO.widget.SimpleDialog = function (el, userConfig) { 8554 8555 YAHOO.widget.SimpleDialog.superclass.constructor.call(this, 8556 el, userConfig); 8557 8558 }; 8559 8560 var Dom = YAHOO.util.Dom, 8561 SimpleDialog = YAHOO.widget.SimpleDialog, 8562 8563 /** 8564 * Constant representing the SimpleDialog's configuration properties 8565 * @property DEFAULT_CONFIG 8566 * @private 8567 * @final 8568 * @type Object 8569 */ 8570 DEFAULT_CONFIG = { 8571 8572 "ICON": { 8573 key: "icon", 8574 value: "none", 8575 suppressEvent: true 8576 }, 8577 8578 "TEXT": { 8579 key: "text", 8580 value: "", 8581 suppressEvent: true, 8582 supercedes: ["icon"] 8583 } 8584 8585 }; 8586 8587 /** 8588 * Constant for the standard network icon for a blocking action 8589 * @property YAHOO.widget.SimpleDialog.ICON_BLOCK 8590 * @static 8591 * @final 8592 * @type String 8593 */ 8594 SimpleDialog.ICON_BLOCK = "blckicon"; 8595 8596 /** 8597 * Constant for the standard network icon for alarm 8598 * @property YAHOO.widget.SimpleDialog.ICON_ALARM 8599 * @static 8600 * @final 8601 * @type String 8602 */ 8603 SimpleDialog.ICON_ALARM = "alrticon"; 8604 8605 /** 8606 * Constant for the standard network icon for help 8607 * @property YAHOO.widget.SimpleDialog.ICON_HELP 8608 * @static 8609 * @final 8610 * @type String 8611 */ 8612 SimpleDialog.ICON_HELP = "hlpicon"; 8613 8614 /** 8615 * Constant for the standard network icon for info 8616 * @property YAHOO.widget.SimpleDialog.ICON_INFO 8617 * @static 8618 * @final 8619 * @type String 8620 */ 8621 SimpleDialog.ICON_INFO = "infoicon"; 8622 8623 /** 8624 * Constant for the standard network icon for warn 8625 * @property YAHOO.widget.SimpleDialog.ICON_WARN 8626 * @static 8627 * @final 8628 * @type String 8629 */ 8630 SimpleDialog.ICON_WARN = "warnicon"; 8631 8632 /** 8633 * Constant for the standard network icon for a tip 8634 * @property YAHOO.widget.SimpleDialog.ICON_TIP 8635 * @static 8636 * @final 8637 * @type String 8638 */ 8639 SimpleDialog.ICON_TIP = "tipicon"; 8640 8641 /** 8642 * Constant representing the name of the CSS class applied to the element 8643 * created by the "icon" configuration property. 8644 * @property YAHOO.widget.SimpleDialog.ICON_CSS_CLASSNAME 8645 * @static 8646 * @final 8647 * @type String 8648 */ 8649 SimpleDialog.ICON_CSS_CLASSNAME = "yui-icon"; 8650 8651 /** 8652 * Constant representing the default CSS class used for a SimpleDialog 8653 * @property YAHOO.widget.SimpleDialog.CSS_SIMPLEDIALOG 8654 * @static 8655 * @final 8656 * @type String 8657 */ 8658 SimpleDialog.CSS_SIMPLEDIALOG = "yui-simple-dialog"; 8659 8660 8661 YAHOO.extend(SimpleDialog, YAHOO.widget.Dialog, { 8662 8663 /** 8664 * Initializes the class's configurable properties which can be changed 8665 * using the SimpleDialog's Config object (cfg). 8666 * @method initDefaultConfig 8667 */ 8668 initDefaultConfig: function () { 8669 8670 SimpleDialog.superclass.initDefaultConfig.call(this); 8671 8672 // Add dialog config properties // 8673 8674 /** 8675 * Sets the informational icon for the SimpleDialog 8676 * @config icon 8677 * @type String 8678 * @default "none" 8679 */ 8680 this.cfg.addProperty(DEFAULT_CONFIG.ICON.key, { 8681 handler: this.configIcon, 8682 value: DEFAULT_CONFIG.ICON.value, 8683 suppressEvent: DEFAULT_CONFIG.ICON.suppressEvent 8684 }); 8685 8686 /** 8687 * Sets the text for the SimpleDialog. The text is inserted into the DOM as HTML, and should be escaped by the implementor if coming from an external source. 8688 * @config text 8689 * @type HTML 8690 * @default "" 8691 */ 8692 this.cfg.addProperty(DEFAULT_CONFIG.TEXT.key, { 8693 handler: this.configText, 8694 value: DEFAULT_CONFIG.TEXT.value, 8695 suppressEvent: DEFAULT_CONFIG.TEXT.suppressEvent, 8696 supercedes: DEFAULT_CONFIG.TEXT.supercedes 8697 }); 8698 8699 }, 8700 8701 8702 /** 8703 * The SimpleDialog initialization method, which is executed for 8704 * SimpleDialog and all of its subclasses. This method is automatically 8705 * called by the constructor, and sets up all DOM references for 8706 * pre-existing markup, and creates required markup if it is not 8707 * already present. 8708 * @method init 8709 * @param {String} el The element ID representing the SimpleDialog 8710 * <em>OR</em> 8711 * @param {HTMLElement} el The element representing the SimpleDialog 8712 * @param {Object} userConfig The configuration object literal 8713 * containing the configuration that should be set for this 8714 * SimpleDialog. See configuration documentation for more details. 8715 */ 8716 init: function (el, userConfig) { 8717 8718 /* 8719 Note that we don't pass the user config in here yet because we 8720 only want it executed once, at the lowest subclass level 8721 */ 8722 8723 SimpleDialog.superclass.init.call(this, el/*, userConfig*/); 8724 8725 this.beforeInitEvent.fire(SimpleDialog); 8726 8727 Dom.addClass(this.element, SimpleDialog.CSS_SIMPLEDIALOG); 8728 8729 this.cfg.queueProperty("postmethod", "manual"); 8730 8731 if (userConfig) { 8732 this.cfg.applyConfig(userConfig, true); 8733 } 8734 8735 this.beforeRenderEvent.subscribe(function () { 8736 if (! this.body) { 8737 this.setBody(""); 8738 } 8739 }, this, true); 8740 8741 this.initEvent.fire(SimpleDialog); 8742 8743 }, 8744 8745 /** 8746 * Prepares the SimpleDialog's internal FORM object, creating one if one 8747 * is not currently present, and adding the value hidden field. 8748 * @method registerForm 8749 */ 8750 registerForm: function () { 8751 SimpleDialog.superclass.registerForm.call(this); 8752 8753 var doc = this.form.ownerDocument, 8754 input = doc.createElement("input"); 8755 8756 input.type = "hidden"; 8757 input.name = this.id; 8758 input.value = ""; 8759 8760 this.form.appendChild(input); 8761 }, 8762 8763 // BEGIN BUILT-IN PROPERTY EVENT HANDLERS // 8764 8765 /** 8766 * Fired when the "icon" property is set. 8767 * @method configIcon 8768 * @param {String} type The CustomEvent type (usually the property name) 8769 * @param {Object[]} args The CustomEvent arguments. For configuration 8770 * handlers, args[0] will equal the newly applied value for the property. 8771 * @param {Object} obj The scope object. For configuration handlers, 8772 * this will usually equal the owner. 8773 */ 8774 configIcon: function (type,args,obj) { 8775 8776 var sIcon = args[0], 8777 oBody = this.body, 8778 sCSSClass = SimpleDialog.ICON_CSS_CLASSNAME, 8779 aElements, 8780 oIcon, 8781 oIconParent; 8782 8783 if (sIcon && sIcon != "none") { 8784 8785 aElements = Dom.getElementsByClassName(sCSSClass, "*" , oBody); 8786 8787 if (aElements.length === 1) { 8788 8789 oIcon = aElements[0]; 8790 oIconParent = oIcon.parentNode; 8791 8792 if (oIconParent) { 8793 8794 oIconParent.removeChild(oIcon); 8795 8796 oIcon = null; 8797 8798 } 8799 8800 } 8801 8802 8803 if (sIcon.indexOf(".") == -1) { 8804 8805 oIcon = document.createElement("span"); 8806 oIcon.className = (sCSSClass + " " + sIcon); 8807 oIcon.innerHTML = " "; 8808 8809 } else { 8810 8811 oIcon = document.createElement("img"); 8812 oIcon.src = (this.imageRoot + sIcon); 8813 oIcon.className = sCSSClass; 8814 8815 } 8816 8817 8818 if (oIcon) { 8819 8820 oBody.insertBefore(oIcon, oBody.firstChild); 8821 8822 } 8823 8824 } 8825 8826 }, 8827 8828 /** 8829 * Fired when the "text" property is set. 8830 * @method configText 8831 * @param {String} type The CustomEvent type (usually the property name) 8832 * @param {Object[]} args The CustomEvent arguments. For configuration 8833 * handlers, args[0] will equal the newly applied value for the property. 8834 * @param {Object} obj The scope object. For configuration handlers, 8835 * this will usually equal the owner. 8836 */ 8837 configText: function (type,args,obj) { 8838 var text = args[0]; 8839 if (text) { 8840 this.setBody(text); 8841 this.cfg.refireEvent("icon"); 8842 } 8843 }, 8844 8845 // END BUILT-IN PROPERTY EVENT HANDLERS // 8846 8847 /** 8848 * Returns a string representation of the object. 8849 * @method toString 8850 * @return {String} The string representation of the SimpleDialog 8851 */ 8852 toString: function () { 8853 return "SimpleDialog " + this.id; 8854 } 8855 8856 /** 8857 * <p> 8858 * Sets the SimpleDialog's body content to the HTML specified. 8859 * If no body is present, one will be automatically created. 8860 * An empty string can be passed to the method to clear the contents of the body. 8861 * </p> 8862 * <p><strong>NOTE:</strong> SimpleDialog provides the <a href="#config_text">text</a> 8863 * and <a href="#config_icon">icon</a> configuration properties to set the contents 8864 * of it's body element in accordance with the UI design for a SimpleDialog (an 8865 * icon and message text). Calling setBody on the SimpleDialog will not enforce this 8866 * UI design constraint and will replace the entire contents of the SimpleDialog body. 8867 * It should only be used if you wish the replace the default icon/text body structure 8868 * of a SimpleDialog with your own custom markup.</p> 8869 * 8870 * @method setBody 8871 * @param {HTML} bodyContent The HTML used to set the body. 8872 * As a convenience, non HTMLElement objects can also be passed into 8873 * the method, and will be treated as strings, with the body innerHTML 8874 * set to their default toString implementations. 8875 * 8876 * <p>NOTE: Markup passed into this method is added to the DOM as HTML, and should be escaped by the implementor if coming from an external source.</p> 8877 * 8878 * <em>OR</em> 8879 * @param {HTMLElement} bodyContent The HTMLElement to add as the first and only child of the body element. 8880 * <em>OR</em> 8881 * @param {DocumentFragment} bodyContent The document fragment 8882 * containing elements which are to be added to the body 8883 */ 8884 }); 8885 8886 }()); 8887 (function () { 8888 8889 /** 8890 * ContainerEffect encapsulates animation transitions that are executed when 8891 * an Overlay is shown or hidden. 8892 * @namespace YAHOO.widget 8893 * @class ContainerEffect 8894 * @constructor 8895 * @param {YAHOO.widget.Overlay} overlay The Overlay that the animation 8896 * should be associated with 8897 * @param {Object} attrIn The object literal representing the animation 8898 * arguments to be used for the animate-in transition. The arguments for 8899 * this literal are: attributes(object, see YAHOO.util.Anim for description), 8900 * duration(Number), and method(i.e. Easing.easeIn). 8901 * @param {Object} attrOut The object literal representing the animation 8902 * arguments to be used for the animate-out transition. The arguments for 8903 * this literal are: attributes(object, see YAHOO.util.Anim for description), 8904 * duration(Number), and method(i.e. Easing.easeIn). 8905 * @param {HTMLElement} targetElement Optional. The target element that 8906 * should be animated during the transition. Defaults to overlay.element. 8907 * @param {class} Optional. The animation class to instantiate. Defaults to 8908 * YAHOO.util.Anim. Other options include YAHOO.util.Motion. 8909 */ 8910 YAHOO.widget.ContainerEffect = function (overlay, attrIn, attrOut, targetElement, animClass) { 8911 8912 if (!animClass) { 8913 animClass = YAHOO.util.Anim; 8914 } 8915 8916 /** 8917 * The overlay to animate 8918 * @property overlay 8919 * @type YAHOO.widget.Overlay 8920 */ 8921 this.overlay = overlay; 8922 8923 /** 8924 * The animation attributes to use when transitioning into view 8925 * @property attrIn 8926 * @type Object 8927 */ 8928 this.attrIn = attrIn; 8929 8930 /** 8931 * The animation attributes to use when transitioning out of view 8932 * @property attrOut 8933 * @type Object 8934 */ 8935 this.attrOut = attrOut; 8936 8937 /** 8938 * The target element to be animated 8939 * @property targetElement 8940 * @type HTMLElement 8941 */ 8942 this.targetElement = targetElement || overlay.element; 8943 8944 /** 8945 * The animation class to use for animating the overlay 8946 * @property animClass 8947 * @type class 8948 */ 8949 this.animClass = animClass; 8950 }; 8951 8952 var Dom = YAHOO.util.Dom, 8953 CustomEvent = YAHOO.util.CustomEvent, 8954 ContainerEffect = YAHOO.widget.ContainerEffect; 8955 8956 /** 8957 * A pre-configured ContainerEffect instance that can be used for fading 8958 * an overlay in and out. 8959 * @method FADE 8960 * @static 8961 * @param {YAHOO.widget.Overlay} overlay The Overlay object to animate 8962 * @param {Number} dur The duration of the animation 8963 * @return {YAHOO.widget.ContainerEffect} The configured ContainerEffect object 8964 */ 8965 ContainerEffect.FADE = function (overlay, dur) { 8966 8967 var Easing = YAHOO.util.Easing, 8968 fin = { 8969 attributes: {opacity:{from:0, to:1}}, 8970 duration: dur, 8971 method: Easing.easeIn 8972 }, 8973 fout = { 8974 attributes: {opacity:{to:0}}, 8975 duration: dur, 8976 method: Easing.easeOut 8977 }, 8978 fade = new ContainerEffect(overlay, fin, fout, overlay.element); 8979 8980 fade.handleUnderlayStart = function() { 8981 var underlay = this.overlay.underlay; 8982 if (underlay && YAHOO.env.ua.ie) { 8983 var hasFilters = (underlay.filters && underlay.filters.length > 0); 8984 if(hasFilters) { 8985 Dom.addClass(overlay.element, "yui-effect-fade"); 8986 } 8987 } 8988 }; 8989 8990 fade.handleUnderlayComplete = function() { 8991 var underlay = this.overlay.underlay; 8992 if (underlay && YAHOO.env.ua.ie) { 8993 Dom.removeClass(overlay.element, "yui-effect-fade"); 8994 } 8995 }; 8996 8997 fade.handleStartAnimateIn = function (type, args, obj) { 8998 obj.overlay._fadingIn = true; 8999 9000 Dom.addClass(obj.overlay.element, "hide-select"); 9001 9002 if (!obj.overlay.underlay) { 9003 obj.overlay.cfg.refireEvent("underlay"); 9004 } 9005 9006 obj.handleUnderlayStart(); 9007 9008 obj.overlay._setDomVisibility(true); 9009 Dom.setStyle(obj.overlay.element, "opacity", 0); 9010 }; 9011 9012 fade.handleCompleteAnimateIn = function (type,args,obj) { 9013 obj.overlay._fadingIn = false; 9014 9015 Dom.removeClass(obj.overlay.element, "hide-select"); 9016 9017 if (obj.overlay.element.style.filter) { 9018 obj.overlay.element.style.filter = null; 9019 } 9020 9021 obj.handleUnderlayComplete(); 9022 9023 obj.overlay.cfg.refireEvent("iframe"); 9024 obj.animateInCompleteEvent.fire(); 9025 }; 9026 9027 fade.handleStartAnimateOut = function (type, args, obj) { 9028 obj.overlay._fadingOut = true; 9029 Dom.addClass(obj.overlay.element, "hide-select"); 9030 obj.handleUnderlayStart(); 9031 }; 9032 9033 fade.handleCompleteAnimateOut = function (type, args, obj) { 9034 obj.overlay._fadingOut = false; 9035 Dom.removeClass(obj.overlay.element, "hide-select"); 9036 9037 if (obj.overlay.element.style.filter) { 9038 obj.overlay.element.style.filter = null; 9039 } 9040 obj.overlay._setDomVisibility(false); 9041 Dom.setStyle(obj.overlay.element, "opacity", 1); 9042 9043 obj.handleUnderlayComplete(); 9044 9045 obj.overlay.cfg.refireEvent("iframe"); 9046 obj.animateOutCompleteEvent.fire(); 9047 }; 9048 9049 fade.init(); 9050 return fade; 9051 }; 9052 9053 9054 /** 9055 * A pre-configured ContainerEffect instance that can be used for sliding an 9056 * overlay in and out. 9057 * @method SLIDE 9058 * @static 9059 * @param {YAHOO.widget.Overlay} overlay The Overlay object to animate 9060 * @param {Number} dur The duration of the animation 9061 * @return {YAHOO.widget.ContainerEffect} The configured ContainerEffect object 9062 */ 9063 ContainerEffect.SLIDE = function (overlay, dur) { 9064 var Easing = YAHOO.util.Easing, 9065 9066 x = overlay.cfg.getProperty("x") || Dom.getX(overlay.element), 9067 y = overlay.cfg.getProperty("y") || Dom.getY(overlay.element), 9068 clientWidth = Dom.getClientWidth(), 9069 offsetWidth = overlay.element.offsetWidth, 9070 9071 sin = { 9072 attributes: { points: { to: [x, y] } }, 9073 duration: dur, 9074 method: Easing.easeIn 9075 }, 9076 9077 sout = { 9078 attributes: { points: { to: [(clientWidth + 25), y] } }, 9079 duration: dur, 9080 method: Easing.easeOut 9081 }, 9082 9083 slide = new ContainerEffect(overlay, sin, sout, overlay.element, YAHOO.util.Motion); 9084 9085 slide.handleStartAnimateIn = function (type,args,obj) { 9086 obj.overlay.element.style.left = ((-25) - offsetWidth) + "px"; 9087 obj.overlay.element.style.top = y + "px"; 9088 }; 9089 9090 slide.handleTweenAnimateIn = function (type, args, obj) { 9091 9092 var pos = Dom.getXY(obj.overlay.element), 9093 currentX = pos[0], 9094 currentY = pos[1]; 9095 9096 if (Dom.getStyle(obj.overlay.element, "visibility") == 9097 "hidden" && currentX < x) { 9098 9099 obj.overlay._setDomVisibility(true); 9100 9101 } 9102 9103 obj.overlay.cfg.setProperty("xy", [currentX, currentY], true); 9104 obj.overlay.cfg.refireEvent("iframe"); 9105 }; 9106 9107 slide.handleCompleteAnimateIn = function (type, args, obj) { 9108 obj.overlay.cfg.setProperty("xy", [x, y], true); 9109 obj.startX = x; 9110 obj.startY = y; 9111 obj.overlay.cfg.refireEvent("iframe"); 9112 obj.animateInCompleteEvent.fire(); 9113 }; 9114 9115 slide.handleStartAnimateOut = function (type, args, obj) { 9116 9117 var vw = Dom.getViewportWidth(), 9118 pos = Dom.getXY(obj.overlay.element), 9119 yso = pos[1]; 9120 9121 obj.animOut.attributes.points.to = [(vw + 25), yso]; 9122 }; 9123 9124 slide.handleTweenAnimateOut = function (type, args, obj) { 9125 9126 var pos = Dom.getXY(obj.overlay.element), 9127 xto = pos[0], 9128 yto = pos[1]; 9129 9130 obj.overlay.cfg.setProperty("xy", [xto, yto], true); 9131 obj.overlay.cfg.refireEvent("iframe"); 9132 }; 9133 9134 slide.handleCompleteAnimateOut = function (type, args, obj) { 9135 obj.overlay._setDomVisibility(false); 9136 9137 obj.overlay.cfg.setProperty("xy", [x, y]); 9138 obj.animateOutCompleteEvent.fire(); 9139 }; 9140 9141 slide.init(); 9142 return slide; 9143 }; 9144 9145 ContainerEffect.prototype = { 9146 9147 /** 9148 * Initializes the animation classes and events. 9149 * @method init 9150 */ 9151 init: function () { 9152 9153 this.beforeAnimateInEvent = this.createEvent("beforeAnimateIn"); 9154 this.beforeAnimateInEvent.signature = CustomEvent.LIST; 9155 9156 this.beforeAnimateOutEvent = this.createEvent("beforeAnimateOut"); 9157 this.beforeAnimateOutEvent.signature = CustomEvent.LIST; 9158 9159 this.animateInCompleteEvent = this.createEvent("animateInComplete"); 9160 this.animateInCompleteEvent.signature = CustomEvent.LIST; 9161 9162 this.animateOutCompleteEvent = this.createEvent("animateOutComplete"); 9163 this.animateOutCompleteEvent.signature = CustomEvent.LIST; 9164 9165 this.animIn = new this.animClass( 9166 this.targetElement, 9167 this.attrIn.attributes, 9168 this.attrIn.duration, 9169 this.attrIn.method); 9170 9171 this.animIn.onStart.subscribe(this.handleStartAnimateIn, this); 9172 this.animIn.onTween.subscribe(this.handleTweenAnimateIn, this); 9173 this.animIn.onComplete.subscribe(this.handleCompleteAnimateIn,this); 9174 9175 this.animOut = new this.animClass( 9176 this.targetElement, 9177 this.attrOut.attributes, 9178 this.attrOut.duration, 9179 this.attrOut.method); 9180 9181 this.animOut.onStart.subscribe(this.handleStartAnimateOut, this); 9182 this.animOut.onTween.subscribe(this.handleTweenAnimateOut, this); 9183 this.animOut.onComplete.subscribe(this.handleCompleteAnimateOut, this); 9184 9185 }, 9186 9187 /** 9188 * Triggers the in-animation. 9189 * @method animateIn 9190 */ 9191 animateIn: function () { 9192 this._stopAnims(this.lastFrameOnStop); 9193 this.beforeAnimateInEvent.fire(); 9194 this.animIn.animate(); 9195 }, 9196 9197 /** 9198 * Triggers the out-animation. 9199 * @method animateOut 9200 */ 9201 animateOut: function () { 9202 this._stopAnims(this.lastFrameOnStop); 9203 this.beforeAnimateOutEvent.fire(); 9204 this.animOut.animate(); 9205 }, 9206 9207 /** 9208 * Flag to define whether Anim should jump to the last frame, 9209 * when animateIn or animateOut is stopped. 9210 * 9211 * @property lastFrameOnStop 9212 * @default true 9213 * @type boolean 9214 */ 9215 lastFrameOnStop : true, 9216 9217 /** 9218 * Stops both animIn and animOut instances, if in progress. 9219 * 9220 * @method _stopAnims 9221 * @param {boolean} finish If true, animation will jump to final frame. 9222 * @protected 9223 */ 9224 _stopAnims : function(finish) { 9225 if (this.animOut && this.animOut.isAnimated()) { 9226 this.animOut.stop(finish); 9227 } 9228 9229 if (this.animIn && this.animIn.isAnimated()) { 9230 this.animIn.stop(finish); 9231 } 9232 }, 9233 9234 /** 9235 * The default onStart handler for the in-animation. 9236 * @method handleStartAnimateIn 9237 * @param {String} type The CustomEvent type 9238 * @param {Object[]} args The CustomEvent arguments 9239 * @param {Object} obj The scope object 9240 */ 9241 handleStartAnimateIn: function (type, args, obj) { }, 9242 9243 /** 9244 * The default onTween handler for the in-animation. 9245 * @method handleTweenAnimateIn 9246 * @param {String} type The CustomEvent type 9247 * @param {Object[]} args The CustomEvent arguments 9248 * @param {Object} obj The scope object 9249 */ 9250 handleTweenAnimateIn: function (type, args, obj) { }, 9251 9252 /** 9253 * The default onComplete handler for the in-animation. 9254 * @method handleCompleteAnimateIn 9255 * @param {String} type The CustomEvent type 9256 * @param {Object[]} args The CustomEvent arguments 9257 * @param {Object} obj The scope object 9258 */ 9259 handleCompleteAnimateIn: function (type, args, obj) { }, 9260 9261 /** 9262 * The default onStart handler for the out-animation. 9263 * @method handleStartAnimateOut 9264 * @param {String} type The CustomEvent type 9265 * @param {Object[]} args The CustomEvent arguments 9266 * @param {Object} obj The scope object 9267 */ 9268 handleStartAnimateOut: function (type, args, obj) { }, 9269 9270 /** 9271 * The default onTween handler for the out-animation. 9272 * @method handleTweenAnimateOut 9273 * @param {String} type The CustomEvent type 9274 * @param {Object[]} args The CustomEvent arguments 9275 * @param {Object} obj The scope object 9276 */ 9277 handleTweenAnimateOut: function (type, args, obj) { }, 9278 9279 /** 9280 * The default onComplete handler for the out-animation. 9281 * @method handleCompleteAnimateOut 9282 * @param {String} type The CustomEvent type 9283 * @param {Object[]} args The CustomEvent arguments 9284 * @param {Object} obj The scope object 9285 */ 9286 handleCompleteAnimateOut: function (type, args, obj) { }, 9287 9288 /** 9289 * Returns a string representation of the object. 9290 * @method toString 9291 * @return {String} The string representation of the ContainerEffect 9292 */ 9293 toString: function () { 9294 var output = "ContainerEffect"; 9295 if (this.overlay) { 9296 output += " [" + this.overlay.toString() + "]"; 9297 } 9298 return output; 9299 } 9300 }; 9301 9302 YAHOO.lang.augmentProto(ContainerEffect, YAHOO.util.EventProvider); 9303 9304 })(); 9305 YAHOO.register("container", YAHOO.widget.Module, {version: "2.9.0", build: "2800"}); 9306 9307 }, '2.9.0' ,{"requires": ["yui2-yahoo", "yui2-dom", "yui2-event", "yui2-skin-sam-container"], "supersedes": ["yui2-containercore"], "optional": ["yui2-animation", "yui2-dragdrop", "yui2-connection"]});
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 |