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