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