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