]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/yui/build/container/container_core.js
Release 6.5.0
[Github/sugarcrm.git] / include / javascript / yui / build / container / container_core.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     * ContainerEffect encapsulates animation transitions that are executed when 
4786     * an Overlay is shown or hidden.
4787     * @namespace YAHOO.widget
4788     * @class ContainerEffect
4789     * @constructor
4790     * @param {YAHOO.widget.Overlay} overlay The Overlay that the animation 
4791     * should be associated with
4792     * @param {Object} attrIn The object literal representing the animation 
4793     * arguments to be used for the animate-in transition. The arguments for 
4794     * this literal are: attributes(object, see YAHOO.util.Anim for description), 
4795     * duration(Number), and method(i.e. Easing.easeIn).
4796     * @param {Object} attrOut The object literal representing the animation 
4797     * arguments to be used for the animate-out transition. The arguments for  
4798     * this literal are: attributes(object, see YAHOO.util.Anim for description), 
4799     * duration(Number), and method(i.e. Easing.easeIn).
4800     * @param {HTMLElement} targetElement Optional. The target element that  
4801     * should be animated during the transition. Defaults to overlay.element.
4802     * @param {class} Optional. The animation class to instantiate. Defaults to 
4803     * YAHOO.util.Anim. Other options include YAHOO.util.Motion.
4804     */
4805     YAHOO.widget.ContainerEffect = function (overlay, attrIn, attrOut, targetElement, animClass) {
4806
4807         if (!animClass) {
4808             animClass = YAHOO.util.Anim;
4809         }
4810
4811         /**
4812         * The overlay to animate
4813         * @property overlay
4814         * @type YAHOO.widget.Overlay
4815         */
4816         this.overlay = overlay;
4817     
4818         /**
4819         * The animation attributes to use when transitioning into view
4820         * @property attrIn
4821         * @type Object
4822         */
4823         this.attrIn = attrIn;
4824     
4825         /**
4826         * The animation attributes to use when transitioning out of view
4827         * @property attrOut
4828         * @type Object
4829         */
4830         this.attrOut = attrOut;
4831     
4832         /**
4833         * The target element to be animated
4834         * @property targetElement
4835         * @type HTMLElement
4836         */
4837         this.targetElement = targetElement || overlay.element;
4838     
4839         /**
4840         * The animation class to use for animating the overlay
4841         * @property animClass
4842         * @type class
4843         */
4844         this.animClass = animClass;
4845     };
4846
4847     var Dom = YAHOO.util.Dom,
4848         CustomEvent = YAHOO.util.CustomEvent,
4849         ContainerEffect = YAHOO.widget.ContainerEffect;
4850
4851     /**
4852     * A pre-configured ContainerEffect instance that can be used for fading 
4853     * an overlay in and out.
4854     * @method FADE
4855     * @static
4856     * @param {YAHOO.widget.Overlay} overlay The Overlay object to animate
4857     * @param {Number} dur The duration of the animation
4858     * @return {YAHOO.widget.ContainerEffect} The configured ContainerEffect object
4859     */
4860     ContainerEffect.FADE = function (overlay, dur) {
4861
4862         var Easing = YAHOO.util.Easing,
4863             fin = {
4864                 attributes: {opacity:{from:0, to:1}},
4865                 duration: dur,
4866                 method: Easing.easeIn
4867             },
4868             fout = {
4869                 attributes: {opacity:{to:0}},
4870                 duration: dur,
4871                 method: Easing.easeOut
4872             },
4873             fade = new ContainerEffect(overlay, fin, fout, overlay.element);
4874
4875         fade.handleUnderlayStart = function() {
4876             var underlay = this.overlay.underlay;
4877             if (underlay && YAHOO.env.ua.ie) {
4878                 var hasFilters = (underlay.filters && underlay.filters.length > 0);
4879                 if(hasFilters) {
4880                     Dom.addClass(overlay.element, "yui-effect-fade");
4881                 }
4882             }
4883         };
4884
4885         fade.handleUnderlayComplete = function() {
4886             var underlay = this.overlay.underlay;
4887             if (underlay && YAHOO.env.ua.ie) {
4888                 Dom.removeClass(overlay.element, "yui-effect-fade");
4889             }
4890         };
4891
4892         fade.handleStartAnimateIn = function (type, args, obj) {
4893             obj.overlay._fadingIn = true;
4894
4895             Dom.addClass(obj.overlay.element, "hide-select");
4896
4897             if (!obj.overlay.underlay) {
4898                 obj.overlay.cfg.refireEvent("underlay");
4899             }
4900
4901             obj.handleUnderlayStart();
4902
4903             obj.overlay._setDomVisibility(true);
4904             Dom.setStyle(obj.overlay.element, "opacity", 0);
4905         };
4906
4907         fade.handleCompleteAnimateIn = function (type,args,obj) {
4908             obj.overlay._fadingIn = false;
4909             
4910             Dom.removeClass(obj.overlay.element, "hide-select");
4911
4912             if (obj.overlay.element.style.filter) {
4913                 obj.overlay.element.style.filter = null;
4914             }
4915
4916             obj.handleUnderlayComplete();
4917
4918             obj.overlay.cfg.refireEvent("iframe");
4919             obj.animateInCompleteEvent.fire();
4920         };
4921
4922         fade.handleStartAnimateOut = function (type, args, obj) {
4923             obj.overlay._fadingOut = true;
4924             Dom.addClass(obj.overlay.element, "hide-select");
4925             obj.handleUnderlayStart();
4926         };
4927
4928         fade.handleCompleteAnimateOut =  function (type, args, obj) {
4929             obj.overlay._fadingOut = false;
4930             Dom.removeClass(obj.overlay.element, "hide-select");
4931
4932             if (obj.overlay.element.style.filter) {
4933                 obj.overlay.element.style.filter = null;
4934             }
4935             obj.overlay._setDomVisibility(false);
4936             Dom.setStyle(obj.overlay.element, "opacity", 1);
4937
4938             obj.handleUnderlayComplete();
4939
4940             obj.overlay.cfg.refireEvent("iframe");
4941             obj.animateOutCompleteEvent.fire();
4942         };
4943
4944         fade.init();
4945         return fade;
4946     };
4947     
4948     
4949     /**
4950     * A pre-configured ContainerEffect instance that can be used for sliding an 
4951     * overlay in and out.
4952     * @method SLIDE
4953     * @static
4954     * @param {YAHOO.widget.Overlay} overlay The Overlay object to animate
4955     * @param {Number} dur The duration of the animation
4956     * @return {YAHOO.widget.ContainerEffect} The configured ContainerEffect object
4957     */
4958     ContainerEffect.SLIDE = function (overlay, dur) {
4959         var Easing = YAHOO.util.Easing,
4960
4961             x = overlay.cfg.getProperty("x") || Dom.getX(overlay.element),
4962             y = overlay.cfg.getProperty("y") || Dom.getY(overlay.element),
4963             clientWidth = Dom.getClientWidth(),
4964             offsetWidth = overlay.element.offsetWidth,
4965
4966             sin =  { 
4967                 attributes: { points: { to: [x, y] } },
4968                 duration: dur,
4969                 method: Easing.easeIn 
4970             },
4971
4972             sout = {
4973                 attributes: { points: { to: [(clientWidth + 25), y] } },
4974                 duration: dur,
4975                 method: Easing.easeOut 
4976             },
4977
4978             slide = new ContainerEffect(overlay, sin, sout, overlay.element, YAHOO.util.Motion);
4979
4980         slide.handleStartAnimateIn = function (type,args,obj) {
4981             obj.overlay.element.style.left = ((-25) - offsetWidth) + "px";
4982             obj.overlay.element.style.top  = y + "px";
4983         };
4984
4985         slide.handleTweenAnimateIn = function (type, args, obj) {
4986         
4987             var pos = Dom.getXY(obj.overlay.element),
4988                 currentX = pos[0],
4989                 currentY = pos[1];
4990         
4991             if (Dom.getStyle(obj.overlay.element, "visibility") == 
4992                 "hidden" && currentX < x) {
4993
4994                 obj.overlay._setDomVisibility(true);
4995
4996             }
4997         
4998             obj.overlay.cfg.setProperty("xy", [currentX, currentY], true);
4999             obj.overlay.cfg.refireEvent("iframe");
5000         };
5001         
5002         slide.handleCompleteAnimateIn = function (type, args, obj) {
5003             obj.overlay.cfg.setProperty("xy", [x, y], true);
5004             obj.startX = x;
5005             obj.startY = y;
5006             obj.overlay.cfg.refireEvent("iframe");
5007             obj.animateInCompleteEvent.fire();
5008         };
5009
5010         slide.handleStartAnimateOut = function (type, args, obj) {
5011     
5012             var vw = Dom.getViewportWidth(),
5013                 pos = Dom.getXY(obj.overlay.element),
5014                 yso = pos[1];
5015     
5016             obj.animOut.attributes.points.to = [(vw + 25), yso];
5017         };
5018         
5019         slide.handleTweenAnimateOut = function (type, args, obj) {
5020     
5021             var pos = Dom.getXY(obj.overlay.element),
5022                 xto = pos[0],
5023                 yto = pos[1];
5024         
5025             obj.overlay.cfg.setProperty("xy", [xto, yto], true);
5026             obj.overlay.cfg.refireEvent("iframe");
5027         };
5028         
5029         slide.handleCompleteAnimateOut = function (type, args, obj) {
5030             obj.overlay._setDomVisibility(false);
5031
5032             obj.overlay.cfg.setProperty("xy", [x, y]);
5033             obj.animateOutCompleteEvent.fire();
5034         };
5035
5036         slide.init();
5037         return slide;
5038     };
5039
5040     ContainerEffect.prototype = {
5041
5042         /**
5043         * Initializes the animation classes and events.
5044         * @method init
5045         */
5046         init: function () {
5047
5048             this.beforeAnimateInEvent = this.createEvent("beforeAnimateIn");
5049             this.beforeAnimateInEvent.signature = CustomEvent.LIST;
5050             
5051             this.beforeAnimateOutEvent = this.createEvent("beforeAnimateOut");
5052             this.beforeAnimateOutEvent.signature = CustomEvent.LIST;
5053         
5054             this.animateInCompleteEvent = this.createEvent("animateInComplete");
5055             this.animateInCompleteEvent.signature = CustomEvent.LIST;
5056         
5057             this.animateOutCompleteEvent = this.createEvent("animateOutComplete");
5058             this.animateOutCompleteEvent.signature = CustomEvent.LIST;
5059
5060             this.animIn = new this.animClass(
5061                 this.targetElement, 
5062                 this.attrIn.attributes, 
5063                 this.attrIn.duration, 
5064                 this.attrIn.method);
5065
5066             this.animIn.onStart.subscribe(this.handleStartAnimateIn, this);
5067             this.animIn.onTween.subscribe(this.handleTweenAnimateIn, this);
5068             this.animIn.onComplete.subscribe(this.handleCompleteAnimateIn,this);
5069         
5070             this.animOut = new this.animClass(
5071                 this.targetElement, 
5072                 this.attrOut.attributes, 
5073                 this.attrOut.duration, 
5074                 this.attrOut.method);
5075
5076             this.animOut.onStart.subscribe(this.handleStartAnimateOut, this);
5077             this.animOut.onTween.subscribe(this.handleTweenAnimateOut, this);
5078             this.animOut.onComplete.subscribe(this.handleCompleteAnimateOut, this);
5079
5080         },
5081
5082         /**
5083         * Triggers the in-animation.
5084         * @method animateIn
5085         */
5086         animateIn: function () {
5087             this._stopAnims(this.lastFrameOnStop);
5088             this.beforeAnimateInEvent.fire();
5089             this.animIn.animate();
5090         },
5091
5092         /**
5093         * Triggers the out-animation.
5094         * @method animateOut
5095         */
5096         animateOut: function () {
5097             this._stopAnims(this.lastFrameOnStop);
5098             this.beforeAnimateOutEvent.fire();
5099             this.animOut.animate();
5100         },
5101         
5102         /**
5103          * Flag to define whether Anim should jump to the last frame,
5104          * when animateIn or animateOut is stopped.
5105          *
5106          * @property lastFrameOnStop
5107          * @default true
5108          * @type boolean
5109          */
5110         lastFrameOnStop : true,
5111
5112         /**
5113          * Stops both animIn and animOut instances, if in progress.
5114          *
5115          * @method _stopAnims
5116          * @param {boolean} finish If true, animation will jump to final frame.
5117          * @protected
5118          */
5119         _stopAnims : function(finish) {
5120             if (this.animOut && this.animOut.isAnimated()) {
5121                 this.animOut.stop(finish);
5122             }
5123
5124             if (this.animIn && this.animIn.isAnimated()) {
5125                 this.animIn.stop(finish);
5126             }
5127         },
5128
5129         /**
5130         * The default onStart handler for the in-animation.
5131         * @method handleStartAnimateIn
5132         * @param {String} type The CustomEvent type
5133         * @param {Object[]} args The CustomEvent arguments
5134         * @param {Object} obj The scope object
5135         */
5136         handleStartAnimateIn: function (type, args, obj) { },
5137
5138         /**
5139         * The default onTween handler for the in-animation.
5140         * @method handleTweenAnimateIn
5141         * @param {String} type The CustomEvent type
5142         * @param {Object[]} args The CustomEvent arguments
5143         * @param {Object} obj The scope object
5144         */
5145         handleTweenAnimateIn: function (type, args, obj) { },
5146
5147         /**
5148         * The default onComplete handler for the in-animation.
5149         * @method handleCompleteAnimateIn
5150         * @param {String} type The CustomEvent type
5151         * @param {Object[]} args The CustomEvent arguments
5152         * @param {Object} obj The scope object
5153         */
5154         handleCompleteAnimateIn: function (type, args, obj) { },
5155
5156         /**
5157         * The default onStart handler for the out-animation.
5158         * @method handleStartAnimateOut
5159         * @param {String} type The CustomEvent type
5160         * @param {Object[]} args The CustomEvent arguments
5161         * @param {Object} obj The scope object
5162         */
5163         handleStartAnimateOut: function (type, args, obj) { },
5164
5165         /**
5166         * The default onTween handler for the out-animation.
5167         * @method handleTweenAnimateOut
5168         * @param {String} type The CustomEvent type
5169         * @param {Object[]} args The CustomEvent arguments
5170         * @param {Object} obj The scope object
5171         */
5172         handleTweenAnimateOut: function (type, args, obj) { },
5173
5174         /**
5175         * The default onComplete handler for the out-animation.
5176         * @method handleCompleteAnimateOut
5177         * @param {String} type The CustomEvent type
5178         * @param {Object[]} args The CustomEvent arguments
5179         * @param {Object} obj The scope object
5180         */
5181         handleCompleteAnimateOut: function (type, args, obj) { },
5182         
5183         /**
5184         * Returns a string representation of the object.
5185         * @method toString
5186         * @return {String} The string representation of the ContainerEffect
5187         */
5188         toString: function () {
5189             var output = "ContainerEffect";
5190             if (this.overlay) {
5191                 output += " [" + this.overlay.toString() + "]";
5192             }
5193             return output;
5194         }
5195     };
5196
5197     YAHOO.lang.augmentProto(ContainerEffect, YAHOO.util.EventProvider);
5198
5199 })();
5200 YAHOO.register("containercore", YAHOO.widget.Module, {version: "2.9.0", build: "2800"});