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