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