OSDN Git Service

Update YUI 2.8.0r4 -> 2.8.1
[feedblog/feedgenerator.git] / yui / build / container / container_core.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 2.8.1
6 */
7 (function () {
8
9     /**
10     * Config is a utility used within an Object to allow the implementer to
11     * maintain a list of local configuration properties and listen for changes 
12     * to those properties dynamically using CustomEvent. The initial values are 
13     * also maintained so that the configuration can be reset at any given point 
14     * to its initial state.
15     * @namespace YAHOO.util
16     * @class Config
17     * @constructor
18     * @param {Object} owner The owner Object to which this Config Object belongs
19     */
20     YAHOO.util.Config = function (owner) {
21
22         if (owner) {
23             this.init(owner);
24         }
25
26
27     };
28
29
30     var Lang = YAHOO.lang,
31         CustomEvent = YAHOO.util.CustomEvent,
32         Config = YAHOO.util.Config;
33
34
35     /**
36      * Constant representing the CustomEvent type for the config changed event.
37      * @property YAHOO.util.Config.CONFIG_CHANGED_EVENT
38      * @private
39      * @static
40      * @final
41      */
42     Config.CONFIG_CHANGED_EVENT = "configChanged";
43     
44     /**
45      * Constant representing the boolean type string
46      * @property YAHOO.util.Config.BOOLEAN_TYPE
47      * @private
48      * @static
49      * @final
50      */
51     Config.BOOLEAN_TYPE = "boolean";
52     
53     Config.prototype = {
54      
55         /**
56         * Object reference to the owner of this Config Object
57         * @property owner
58         * @type Object
59         */
60         owner: null,
61         
62         /**
63         * Boolean flag that specifies whether a queue is currently 
64         * being executed
65         * @property queueInProgress
66         * @type Boolean
67         */
68         queueInProgress: false,
69         
70         /**
71         * Maintains the local collection of configuration property objects and 
72         * their specified values
73         * @property config
74         * @private
75         * @type Object
76         */ 
77         config: null,
78         
79         /**
80         * Maintains the local collection of configuration property objects as 
81         * they were initially applied.
82         * This object is used when resetting a property.
83         * @property initialConfig
84         * @private
85         * @type Object
86         */ 
87         initialConfig: null,
88         
89         /**
90         * Maintains the local, normalized CustomEvent queue
91         * @property eventQueue
92         * @private
93         * @type Object
94         */ 
95         eventQueue: null,
96         
97         /**
98         * Custom Event, notifying subscribers when Config properties are set 
99         * (setProperty is called without the silent flag
100         * @event configChangedEvent
101         */
102         configChangedEvent: null,
103     
104         /**
105         * Initializes the configuration Object and all of its local members.
106         * @method init
107         * @param {Object} owner The owner Object to which this Config 
108         * Object belongs
109         */
110         init: function (owner) {
111     
112             this.owner = owner;
113     
114             this.configChangedEvent = 
115                 this.createEvent(Config.CONFIG_CHANGED_EVENT);
116     
117             this.configChangedEvent.signature = CustomEvent.LIST;
118             this.queueInProgress = false;
119             this.config = {};
120             this.initialConfig = {};
121             this.eventQueue = [];
122         
123         },
124         
125         /**
126         * Validates that the value passed in is a Boolean.
127         * @method checkBoolean
128         * @param {Object} val The value to validate
129         * @return {Boolean} true, if the value is valid
130         */ 
131         checkBoolean: function (val) {
132             return (typeof val == Config.BOOLEAN_TYPE);
133         },
134         
135         /**
136         * Validates that the value passed in is a number.
137         * @method checkNumber
138         * @param {Object} val The value to validate
139         * @return {Boolean} true, if the value is valid
140         */
141         checkNumber: function (val) {
142             return (!isNaN(val));
143         },
144         
145         /**
146         * Fires a configuration property event using the specified value. 
147         * @method fireEvent
148         * @private
149         * @param {String} key The configuration property's name
150         * @param {value} Object The value of the correct type for the property
151         */ 
152         fireEvent: function ( key, value ) {
153             var property = this.config[key];
154         
155             if (property && property.event) {
156                 property.event.fire(value);
157             } 
158         },
159         
160         /**
161         * Adds a property to the Config Object's private config hash.
162         * @method addProperty
163         * @param {String} key The configuration property's name
164         * @param {Object} propertyObject The Object containing all of this 
165         * property's arguments
166         */
167         addProperty: function ( key, propertyObject ) {
168             key = key.toLowerCase();
169         
170             this.config[key] = propertyObject;
171         
172             propertyObject.event = this.createEvent(key, { scope: this.owner });
173             propertyObject.event.signature = CustomEvent.LIST;
174             
175             
176             propertyObject.key = key;
177         
178             if (propertyObject.handler) {
179                 propertyObject.event.subscribe(propertyObject.handler, 
180                     this.owner);
181             }
182         
183             this.setProperty(key, propertyObject.value, true);
184             
185             if (! propertyObject.suppressEvent) {
186                 this.queueProperty(key, propertyObject.value);
187             }
188             
189         },
190         
191         /**
192         * Returns a key-value configuration map of the values currently set in  
193         * the Config Object.
194         * @method getConfig
195         * @return {Object} The current config, represented in a key-value map
196         */
197         getConfig: function () {
198         
199             var cfg = {},
200                 currCfg = this.config,
201                 prop,
202                 property;
203                 
204             for (prop in currCfg) {
205                 if (Lang.hasOwnProperty(currCfg, prop)) {
206                     property = currCfg[prop];
207                     if (property && property.event) {
208                         cfg[prop] = property.value;
209                     }
210                 }
211             }
212
213             return cfg;
214         },
215         
216         /**
217         * Returns the value of specified property.
218         * @method getProperty
219         * @param {String} key The name of the property
220         * @return {Object}  The value of the specified property
221         */
222         getProperty: function (key) {
223             var property = this.config[key.toLowerCase()];
224             if (property && property.event) {
225                 return property.value;
226             } else {
227                 return undefined;
228             }
229         },
230         
231         /**
232         * Resets the specified property's value to its initial value.
233         * @method resetProperty
234         * @param {String} key The name of the property
235         * @return {Boolean} True is the property was reset, false if not
236         */
237         resetProperty: function (key) {
238     
239             key = key.toLowerCase();
240         
241             var property = this.config[key];
242     
243             if (property && property.event) {
244     
245                 if (this.initialConfig[key] && 
246                     !Lang.isUndefined(this.initialConfig[key])) {
247     
248                     this.setProperty(key, this.initialConfig[key]);
249
250                     return true;
251     
252                 }
253     
254             } else {
255     
256                 return false;
257             }
258     
259         },
260         
261         /**
262         * Sets the value of a property. If the silent property is passed as 
263         * true, the property's event will not be fired.
264         * @method setProperty
265         * @param {String} key The name of the property
266         * @param {String} value The value to set the property to
267         * @param {Boolean} silent Whether the value should be set silently, 
268         * without firing the property event.
269         * @return {Boolean} True, if the set was successful, false if it failed.
270         */
271         setProperty: function (key, value, silent) {
272         
273             var property;
274         
275             key = key.toLowerCase();
276         
277             if (this.queueInProgress && ! silent) {
278                 // Currently running through a queue... 
279                 this.queueProperty(key,value);
280                 return true;
281     
282             } else {
283                 property = this.config[key];
284                 if (property && property.event) {
285                     if (property.validator && !property.validator(value)) {
286                         return false;
287                     } else {
288                         property.value = value;
289                         if (! silent) {
290                             this.fireEvent(key, value);
291                             this.configChangedEvent.fire([key, value]);
292                         }
293                         return true;
294                     }
295                 } else {
296                     return false;
297                 }
298             }
299         },
300         
301         /**
302         * Sets the value of a property and queues its event to execute. If the 
303         * event is already scheduled to execute, it is
304         * moved from its current position to the end of the queue.
305         * @method queueProperty
306         * @param {String} key The name of the property
307         * @param {String} value The value to set the property to
308         * @return {Boolean}  true, if the set was successful, false if 
309         * it failed.
310         */ 
311         queueProperty: function (key, value) {
312         
313             key = key.toLowerCase();
314         
315             var property = this.config[key],
316                 foundDuplicate = false,
317                 iLen,
318                 queueItem,
319                 queueItemKey,
320                 queueItemValue,
321                 sLen,
322                 supercedesCheck,
323                 qLen,
324                 queueItemCheck,
325                 queueItemCheckKey,
326                 queueItemCheckValue,
327                 i,
328                 s,
329                 q;
330                                 
331             if (property && property.event) {
332     
333                 if (!Lang.isUndefined(value) && property.validator && 
334                     !property.validator(value)) { // validator
335                     return false;
336                 } else {
337         
338                     if (!Lang.isUndefined(value)) {
339                         property.value = value;
340                     } else {
341                         value = property.value;
342                     }
343         
344                     foundDuplicate = false;
345                     iLen = this.eventQueue.length;
346         
347                     for (i = 0; i < iLen; i++) {
348                         queueItem = this.eventQueue[i];
349         
350                         if (queueItem) {
351                             queueItemKey = queueItem[0];
352                             queueItemValue = queueItem[1];
353
354                             if (queueItemKey == key) {
355     
356                                 /*
357                                     found a dupe... push to end of queue, null 
358                                     current item, and break
359                                 */
360     
361                                 this.eventQueue[i] = null;
362     
363                                 this.eventQueue.push(
364                                     [key, (!Lang.isUndefined(value) ? 
365                                     value : queueItemValue)]);
366     
367                                 foundDuplicate = true;
368                                 break;
369                             }
370                         }
371                     }
372                     
373                     // this is a refire, or a new property in the queue
374     
375                     if (! foundDuplicate && !Lang.isUndefined(value)) { 
376                         this.eventQueue.push([key, value]);
377                     }
378                 }
379         
380                 if (property.supercedes) {
381
382                     sLen = property.supercedes.length;
383
384                     for (s = 0; s < sLen; s++) {
385
386                         supercedesCheck = property.supercedes[s];
387                         qLen = this.eventQueue.length;
388
389                         for (q = 0; q < qLen; q++) {
390                             queueItemCheck = this.eventQueue[q];
391
392                             if (queueItemCheck) {
393                                 queueItemCheckKey = queueItemCheck[0];
394                                 queueItemCheckValue = queueItemCheck[1];
395
396                                 if (queueItemCheckKey == 
397                                     supercedesCheck.toLowerCase() ) {
398
399                                     this.eventQueue.push([queueItemCheckKey, 
400                                         queueItemCheckValue]);
401
402                                     this.eventQueue[q] = null;
403                                     break;
404
405                                 }
406                             }
407                         }
408                     }
409                 }
410
411
412                 return true;
413             } else {
414                 return false;
415             }
416         },
417         
418         /**
419         * Fires the event for a property using the property's current value.
420         * @method refireEvent
421         * @param {String} key The name of the property
422         */
423         refireEvent: function (key) {
424     
425             key = key.toLowerCase();
426         
427             var property = this.config[key];
428     
429             if (property && property.event && 
430     
431                 !Lang.isUndefined(property.value)) {
432     
433                 if (this.queueInProgress) {
434     
435                     this.queueProperty(key);
436     
437                 } else {
438     
439                     this.fireEvent(key, property.value);
440     
441                 }
442     
443             }
444         },
445         
446         /**
447         * Applies a key-value Object literal to the configuration, replacing  
448         * any existing values, and queueing the property events.
449         * Although the values will be set, fireQueue() must be called for their 
450         * associated events to execute.
451         * @method applyConfig
452         * @param {Object} userConfig The configuration Object literal
453         * @param {Boolean} init  When set to true, the initialConfig will 
454         * be set to the userConfig passed in, so that calling a reset will 
455         * reset the properties to the passed values.
456         */
457         applyConfig: function (userConfig, init) {
458         
459             var sKey,
460                 oConfig;
461
462             if (init) {
463                 oConfig = {};
464                 for (sKey in userConfig) {
465                     if (Lang.hasOwnProperty(userConfig, sKey)) {
466                         oConfig[sKey.toLowerCase()] = userConfig[sKey];
467                     }
468                 }
469                 this.initialConfig = oConfig;
470             }
471
472             for (sKey in userConfig) {
473                 if (Lang.hasOwnProperty(userConfig, sKey)) {
474                     this.queueProperty(sKey, userConfig[sKey]);
475                 }
476             }
477         },
478         
479         /**
480         * Refires the events for all configuration properties using their 
481         * current values.
482         * @method refresh
483         */
484         refresh: function () {
485
486             var prop;
487
488             for (prop in this.config) {
489                 if (Lang.hasOwnProperty(this.config, prop)) {
490                     this.refireEvent(prop);
491                 }
492             }
493         },
494         
495         /**
496         * Fires the normalized list of queued property change events
497         * @method fireQueue
498         */
499         fireQueue: function () {
500         
501             var i, 
502                 queueItem,
503                 key,
504                 value,
505                 property;
506         
507             this.queueInProgress = true;
508             for (i = 0;i < this.eventQueue.length; i++) {
509                 queueItem = this.eventQueue[i];
510                 if (queueItem) {
511         
512                     key = queueItem[0];
513                     value = queueItem[1];
514                     property = this.config[key];
515
516                     property.value = value;
517
518                     // Clear out queue entry, to avoid it being 
519                     // re-added to the queue by any queueProperty/supercedes
520                     // calls which are invoked during fireEvent
521                     this.eventQueue[i] = null;
522
523                     this.fireEvent(key,value);
524                 }
525             }
526             
527             this.queueInProgress = false;
528             this.eventQueue = [];
529         },
530         
531         /**
532         * Subscribes an external handler to the change event for any 
533         * given property. 
534         * @method subscribeToConfigEvent
535         * @param {String} key The property name
536         * @param {Function} handler The handler function to use subscribe to 
537         * the property's event
538         * @param {Object} obj The Object to use for scoping the event handler 
539         * (see CustomEvent documentation)
540         * @param {Boolean} overrideContext Optional. If true, will override
541         * "this" within the handler to map to the scope Object passed into the
542         * method.
543         * @return {Boolean} True, if the subscription was successful, 
544         * otherwise false.
545         */ 
546         subscribeToConfigEvent: function (key, handler, obj, overrideContext) {
547     
548             var property = this.config[key.toLowerCase()];
549     
550             if (property && property.event) {
551                 if (!Config.alreadySubscribed(property.event, handler, obj)) {
552                     property.event.subscribe(handler, obj, overrideContext);
553                 }
554                 return true;
555             } else {
556                 return false;
557             }
558     
559         },
560         
561         /**
562         * Unsubscribes an external handler from the change event for any 
563         * given property. 
564         * @method unsubscribeFromConfigEvent
565         * @param {String} key The property name
566         * @param {Function} handler The handler function to use subscribe to 
567         * the property's event
568         * @param {Object} obj The Object to use for scoping the event 
569         * handler (see CustomEvent documentation)
570         * @return {Boolean} True, if the unsubscription was successful, 
571         * otherwise false.
572         */
573         unsubscribeFromConfigEvent: function (key, handler, obj) {
574             var property = this.config[key.toLowerCase()];
575             if (property && property.event) {
576                 return property.event.unsubscribe(handler, obj);
577             } else {
578                 return false;
579             }
580         },
581         
582         /**
583         * Returns a string representation of the Config object
584         * @method toString
585         * @return {String} The Config object in string format.
586         */
587         toString: function () {
588             var output = "Config";
589             if (this.owner) {
590                 output += " [" + this.owner.toString() + "]";
591             }
592             return output;
593         },
594         
595         /**
596         * Returns a string representation of the Config object's current 
597         * CustomEvent queue
598         * @method outputEventQueue
599         * @return {String} The string list of CustomEvents currently queued 
600         * for execution
601         */
602         outputEventQueue: function () {
603
604             var output = "",
605                 queueItem,
606                 q,
607                 nQueue = this.eventQueue.length;
608               
609             for (q = 0; q < nQueue; q++) {
610                 queueItem = this.eventQueue[q];
611                 if (queueItem) {
612                     output += queueItem[0] + "=" + queueItem[1] + ", ";
613                 }
614             }
615             return output;
616         },
617
618         /**
619         * Sets all properties to null, unsubscribes all listeners from each 
620         * property's change event and all listeners from the configChangedEvent.
621         * @method destroy
622         */
623         destroy: function () {
624
625             var oConfig = this.config,
626                 sProperty,
627                 oProperty;
628
629
630             for (sProperty in oConfig) {
631             
632                 if (Lang.hasOwnProperty(oConfig, sProperty)) {
633
634                     oProperty = oConfig[sProperty];
635
636                     oProperty.event.unsubscribeAll();
637                     oProperty.event = null;
638
639                 }
640             
641             }
642             
643             this.configChangedEvent.unsubscribeAll();
644             
645             this.configChangedEvent = null;
646             this.owner = null;
647             this.config = null;
648             this.initialConfig = null;
649             this.eventQueue = null;
650         
651         }
652
653     };
654     
655     
656     
657     /**
658     * Checks to determine if a particular function/Object pair are already 
659     * subscribed to the specified CustomEvent
660     * @method YAHOO.util.Config.alreadySubscribed
661     * @static
662     * @param {YAHOO.util.CustomEvent} evt The CustomEvent for which to check 
663     * the subscriptions
664     * @param {Function} fn The function to look for in the subscribers list
665     * @param {Object} obj The execution scope Object for the subscription
666     * @return {Boolean} true, if the function/Object pair is already subscribed 
667     * to the CustomEvent passed in
668     */
669     Config.alreadySubscribed = function (evt, fn, obj) {
670     
671         var nSubscribers = evt.subscribers.length,
672             subsc,
673             i;
674
675         if (nSubscribers > 0) {
676             i = nSubscribers - 1;
677             do {
678                 subsc = evt.subscribers[i];
679                 if (subsc && subsc.obj == obj && subsc.fn == fn) {
680                     return true;
681                 }
682             }
683             while (i--);
684         }
685
686         return false;
687
688     };
689
690     YAHOO.lang.augmentProto(Config, YAHOO.util.EventProvider);
691
692 }());
693 (function () {
694
695     /**
696     * The Container family of components is designed to enable developers to 
697     * create different kinds of content-containing modules on the web. Module 
698     * and Overlay are the most basic containers, and they can be used directly 
699     * or extended to build custom containers. Also part of the Container family 
700     * are four UI controls that extend Module and Overlay: Tooltip, Panel, 
701     * Dialog, and SimpleDialog.
702     * @module container
703     * @title Container
704     * @requires yahoo, dom, event 
705     * @optional dragdrop, animation, button
706     */
707     
708     /**
709     * Module is a JavaScript representation of the Standard Module Format. 
710     * Standard Module Format is a simple standard for markup containers where 
711     * child nodes representing the header, body, and footer of the content are 
712     * denoted using the CSS classes "hd", "bd", and "ft" respectively. 
713     * Module is the base class for all other classes in the YUI 
714     * Container package.
715     * @namespace YAHOO.widget
716     * @class Module
717     * @constructor
718     * @param {String} el The element ID representing the Module <em>OR</em>
719     * @param {HTMLElement} el The element representing the Module
720     * @param {Object} userConfig The configuration Object literal containing 
721     * the configuration that should be set for this module. See configuration 
722     * documentation for more details.
723     */
724     YAHOO.widget.Module = function (el, userConfig) {
725         if (el) {
726             this.init(el, userConfig);
727         } else {
728         }
729     };
730
731     var Dom = YAHOO.util.Dom,
732         Config = YAHOO.util.Config,
733         Event = YAHOO.util.Event,
734         CustomEvent = YAHOO.util.CustomEvent,
735         Module = YAHOO.widget.Module,
736         UA = YAHOO.env.ua,
737
738         m_oModuleTemplate,
739         m_oHeaderTemplate,
740         m_oBodyTemplate,
741         m_oFooterTemplate,
742
743         /**
744         * Constant representing the name of the Module's events
745         * @property EVENT_TYPES
746         * @private
747         * @final
748         * @type Object
749         */
750         EVENT_TYPES = {
751             "BEFORE_INIT": "beforeInit",
752             "INIT": "init",
753             "APPEND": "append",
754             "BEFORE_RENDER": "beforeRender",
755             "RENDER": "render",
756             "CHANGE_HEADER": "changeHeader",
757             "CHANGE_BODY": "changeBody",
758             "CHANGE_FOOTER": "changeFooter",
759             "CHANGE_CONTENT": "changeContent",
760             "DESTROY": "destroy",
761             "BEFORE_SHOW": "beforeShow",
762             "SHOW": "show",
763             "BEFORE_HIDE": "beforeHide",
764             "HIDE": "hide"
765         },
766             
767         /**
768         * Constant representing the Module's configuration properties
769         * @property DEFAULT_CONFIG
770         * @private
771         * @final
772         * @type Object
773         */
774         DEFAULT_CONFIG = {
775         
776             "VISIBLE": { 
777                 key: "visible", 
778                 value: true, 
779                 validator: YAHOO.lang.isBoolean 
780             },
781
782             "EFFECT": {
783                 key: "effect",
784                 suppressEvent: true,
785                 supercedes: ["visible"]
786             },
787
788             "MONITOR_RESIZE": {
789                 key: "monitorresize",
790                 value: true
791             },
792
793             "APPEND_TO_DOCUMENT_BODY": {
794                 key: "appendtodocumentbody",
795                 value: false
796             }
797         };
798
799     /**
800     * Constant representing the prefix path to use for non-secure images
801     * @property YAHOO.widget.Module.IMG_ROOT
802     * @static
803     * @final
804     * @type String
805     */
806     Module.IMG_ROOT = null;
807     
808     /**
809     * Constant representing the prefix path to use for securely served images
810     * @property YAHOO.widget.Module.IMG_ROOT_SSL
811     * @static
812     * @final
813     * @type String
814     */
815     Module.IMG_ROOT_SSL = null;
816     
817     /**
818     * Constant for the default CSS class name that represents a Module
819     * @property YAHOO.widget.Module.CSS_MODULE
820     * @static
821     * @final
822     * @type String
823     */
824     Module.CSS_MODULE = "yui-module";
825     
826     /**
827     * Constant representing the module header
828     * @property YAHOO.widget.Module.CSS_HEADER
829     * @static
830     * @final
831     * @type String
832     */
833     Module.CSS_HEADER = "hd";
834
835     /**
836     * Constant representing the module body
837     * @property YAHOO.widget.Module.CSS_BODY
838     * @static
839     * @final
840     * @type String
841     */
842     Module.CSS_BODY = "bd";
843     
844     /**
845     * Constant representing the module footer
846     * @property YAHOO.widget.Module.CSS_FOOTER
847     * @static
848     * @final
849     * @type String
850     */
851     Module.CSS_FOOTER = "ft";
852     
853     /**
854     * Constant representing the url for the "src" attribute of the iframe 
855     * used to monitor changes to the browser's base font size
856     * @property YAHOO.widget.Module.RESIZE_MONITOR_SECURE_URL
857     * @static
858     * @final
859     * @type String
860     */
861     Module.RESIZE_MONITOR_SECURE_URL = "javascript:false;";
862
863     /**
864     * Constant representing the buffer amount (in pixels) to use when positioning
865     * the text resize monitor offscreen. The resize monitor is positioned
866     * offscreen by an amount eqaul to its offsetHeight + the buffer value.
867     * 
868     * @property YAHOO.widget.Module.RESIZE_MONITOR_BUFFER
869     * @static
870     * @type Number
871     */
872     // Set to 1, to work around pixel offset in IE8, which increases when zoom is used
873     Module.RESIZE_MONITOR_BUFFER = 1;
874
875     /**
876     * Singleton CustomEvent fired when the font size is changed in the browser.
877     * Opera's "zoom" functionality currently does not support text 
878     * size detection.
879     * @event YAHOO.widget.Module.textResizeEvent
880     */
881     Module.textResizeEvent = new CustomEvent("textResize");
882
883     /**
884      * Helper utility method, which forces a document level 
885      * redraw for Opera, which can help remove repaint
886      * irregularities after applying DOM changes.
887      *
888      * @method YAHOO.widget.Module.forceDocumentRedraw
889      * @static
890      */
891     Module.forceDocumentRedraw = function() {
892         var docEl = document.documentElement;
893         if (docEl) {
894             docEl.className += " ";
895             docEl.className = YAHOO.lang.trim(docEl.className);
896         }
897     };
898
899     function createModuleTemplate() {
900
901         if (!m_oModuleTemplate) {
902             m_oModuleTemplate = document.createElement("div");
903             
904             m_oModuleTemplate.innerHTML = ("<div class=\"" + 
905                 Module.CSS_HEADER + "\"></div>" + "<div class=\"" + 
906                 Module.CSS_BODY + "\"></div><div class=\"" + 
907                 Module.CSS_FOOTER + "\"></div>");
908
909             m_oHeaderTemplate = m_oModuleTemplate.firstChild;
910             m_oBodyTemplate = m_oHeaderTemplate.nextSibling;
911             m_oFooterTemplate = m_oBodyTemplate.nextSibling;
912         }
913
914         return m_oModuleTemplate;
915     }
916
917     function createHeader() {
918         if (!m_oHeaderTemplate) {
919             createModuleTemplate();
920         }
921         return (m_oHeaderTemplate.cloneNode(false));
922     }
923
924     function createBody() {
925         if (!m_oBodyTemplate) {
926             createModuleTemplate();
927         }
928         return (m_oBodyTemplate.cloneNode(false));
929     }
930
931     function createFooter() {
932         if (!m_oFooterTemplate) {
933             createModuleTemplate();
934         }
935         return (m_oFooterTemplate.cloneNode(false));
936     }
937
938     Module.prototype = {
939
940         /**
941         * The class's constructor function
942         * @property contructor
943         * @type Function
944         */
945         constructor: Module,
946         
947         /**
948         * The main module element that contains the header, body, and footer
949         * @property element
950         * @type HTMLElement
951         */
952         element: null,
953
954         /**
955         * The header element, denoted with CSS class "hd"
956         * @property header
957         * @type HTMLElement
958         */
959         header: null,
960
961         /**
962         * The body element, denoted with CSS class "bd"
963         * @property body
964         * @type HTMLElement
965         */
966         body: null,
967
968         /**
969         * The footer element, denoted with CSS class "ft"
970         * @property footer
971         * @type HTMLElement
972         */
973         footer: null,
974
975         /**
976         * The id of the element
977         * @property id
978         * @type String
979         */
980         id: null,
981
982         /**
983         * A string representing the root path for all images created by
984         * a Module instance.
985         * @deprecated It is recommend that any images for a Module be applied
986         * via CSS using the "background-image" property.
987         * @property imageRoot
988         * @type String
989         */
990         imageRoot: Module.IMG_ROOT,
991
992         /**
993         * Initializes the custom events for Module which are fired 
994         * automatically at appropriate times by the Module class.
995         * @method initEvents
996         */
997         initEvents: function () {
998
999             var SIGNATURE = CustomEvent.LIST;
1000
1001             /**
1002             * CustomEvent fired prior to class initalization.
1003             * @event beforeInitEvent
1004             * @param {class} classRef class reference of the initializing 
1005             * class, such as this.beforeInitEvent.fire(Module)
1006             */
1007             this.beforeInitEvent = this.createEvent(EVENT_TYPES.BEFORE_INIT);
1008             this.beforeInitEvent.signature = SIGNATURE;
1009
1010             /**
1011             * CustomEvent fired after class initalization.
1012             * @event initEvent
1013             * @param {class} classRef class reference of the initializing 
1014             * class, such as this.beforeInitEvent.fire(Module)
1015             */  
1016             this.initEvent = this.createEvent(EVENT_TYPES.INIT);
1017             this.initEvent.signature = SIGNATURE;
1018
1019             /**
1020             * CustomEvent fired when the Module is appended to the DOM
1021             * @event appendEvent
1022             */
1023             this.appendEvent = this.createEvent(EVENT_TYPES.APPEND);
1024             this.appendEvent.signature = SIGNATURE;
1025
1026             /**
1027             * CustomEvent fired before the Module is rendered
1028             * @event beforeRenderEvent
1029             */
1030             this.beforeRenderEvent = this.createEvent(EVENT_TYPES.BEFORE_RENDER);
1031             this.beforeRenderEvent.signature = SIGNATURE;
1032         
1033             /**
1034             * CustomEvent fired after the Module is rendered
1035             * @event renderEvent
1036             */
1037             this.renderEvent = this.createEvent(EVENT_TYPES.RENDER);
1038             this.renderEvent.signature = SIGNATURE;
1039         
1040             /**
1041             * CustomEvent fired when the header content of the Module 
1042             * is modified
1043             * @event changeHeaderEvent
1044             * @param {String/HTMLElement} content String/element representing 
1045             * the new header content
1046             */
1047             this.changeHeaderEvent = this.createEvent(EVENT_TYPES.CHANGE_HEADER);
1048             this.changeHeaderEvent.signature = SIGNATURE;
1049             
1050             /**
1051             * CustomEvent fired when the body content of the Module is modified
1052             * @event changeBodyEvent
1053             * @param {String/HTMLElement} content String/element representing 
1054             * the new body content
1055             */  
1056             this.changeBodyEvent = this.createEvent(EVENT_TYPES.CHANGE_BODY);
1057             this.changeBodyEvent.signature = SIGNATURE;
1058             
1059             /**
1060             * CustomEvent fired when the footer content of the Module 
1061             * is modified
1062             * @event changeFooterEvent
1063             * @param {String/HTMLElement} content String/element representing 
1064             * the new footer content
1065             */
1066             this.changeFooterEvent = this.createEvent(EVENT_TYPES.CHANGE_FOOTER);
1067             this.changeFooterEvent.signature = SIGNATURE;
1068         
1069             /**
1070             * CustomEvent fired when the content of the Module is modified
1071             * @event changeContentEvent
1072             */
1073             this.changeContentEvent = this.createEvent(EVENT_TYPES.CHANGE_CONTENT);
1074             this.changeContentEvent.signature = SIGNATURE;
1075
1076             /**
1077             * CustomEvent fired when the Module is destroyed
1078             * @event destroyEvent
1079             */
1080             this.destroyEvent = this.createEvent(EVENT_TYPES.DESTROY);
1081             this.destroyEvent.signature = SIGNATURE;
1082
1083             /**
1084             * CustomEvent fired before the Module is shown
1085             * @event beforeShowEvent
1086             */
1087             this.beforeShowEvent = this.createEvent(EVENT_TYPES.BEFORE_SHOW);
1088             this.beforeShowEvent.signature = SIGNATURE;
1089
1090             /**
1091             * CustomEvent fired after the Module is shown
1092             * @event showEvent
1093             */
1094             this.showEvent = this.createEvent(EVENT_TYPES.SHOW);
1095             this.showEvent.signature = SIGNATURE;
1096
1097             /**
1098             * CustomEvent fired before the Module is hidden
1099             * @event beforeHideEvent
1100             */
1101             this.beforeHideEvent = this.createEvent(EVENT_TYPES.BEFORE_HIDE);
1102             this.beforeHideEvent.signature = SIGNATURE;
1103
1104             /**
1105             * CustomEvent fired after the Module is hidden
1106             * @event hideEvent
1107             */
1108             this.hideEvent = this.createEvent(EVENT_TYPES.HIDE);
1109             this.hideEvent.signature = SIGNATURE;
1110         }, 
1111
1112         /**
1113         * String representing the current user-agent platform
1114         * @property platform
1115         * @type String
1116         */
1117         platform: function () {
1118             var ua = navigator.userAgent.toLowerCase();
1119
1120             if (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1) {
1121                 return "windows";
1122             } else if (ua.indexOf("macintosh") != -1) {
1123                 return "mac";
1124             } else {
1125                 return false;
1126             }
1127         }(),
1128         
1129         /**
1130         * String representing the user-agent of the browser
1131         * @deprecated Use YAHOO.env.ua
1132         * @property browser
1133         * @type String
1134         */
1135         browser: function () {
1136             var ua = navigator.userAgent.toLowerCase();
1137             /*
1138                  Check Opera first in case of spoof and check Safari before
1139                  Gecko since Safari's user agent string includes "like Gecko"
1140             */
1141             if (ua.indexOf('opera') != -1) { 
1142                 return 'opera';
1143             } else if (ua.indexOf('msie 7') != -1) {
1144                 return 'ie7';
1145             } else if (ua.indexOf('msie') != -1) {
1146                 return 'ie';
1147             } else if (ua.indexOf('safari') != -1) { 
1148                 return 'safari';
1149             } else if (ua.indexOf('gecko') != -1) {
1150                 return 'gecko';
1151             } else {
1152                 return false;
1153             }
1154         }(),
1155         
1156         /**
1157         * Boolean representing whether or not the current browsing context is 
1158         * secure (https)
1159         * @property isSecure
1160         * @type Boolean
1161         */
1162         isSecure: function () {
1163             if (window.location.href.toLowerCase().indexOf("https") === 0) {
1164                 return true;
1165             } else {
1166                 return false;
1167             }
1168         }(),
1169         
1170         /**
1171         * Initializes the custom events for Module which are fired 
1172         * automatically at appropriate times by the Module class.
1173         */
1174         initDefaultConfig: function () {
1175             // Add properties //
1176             /**
1177             * Specifies whether the Module is visible on the page.
1178             * @config visible
1179             * @type Boolean
1180             * @default true
1181             */
1182             this.cfg.addProperty(DEFAULT_CONFIG.VISIBLE.key, {
1183                 handler: this.configVisible, 
1184                 value: DEFAULT_CONFIG.VISIBLE.value, 
1185                 validator: DEFAULT_CONFIG.VISIBLE.validator
1186             });
1187
1188             /**
1189             * <p>
1190             * Object or array of objects representing the ContainerEffect 
1191             * classes that are active for animating the container.
1192             * </p>
1193             * <p>
1194             * <strong>NOTE:</strong> Although this configuration 
1195             * property is introduced at the Module level, an out of the box
1196             * implementation is not shipped for the Module class so setting
1197             * the proroperty on the Module class has no effect. The Overlay 
1198             * class is the first class to provide out of the box ContainerEffect 
1199             * support.
1200             * </p>
1201             * @config effect
1202             * @type Object
1203             * @default null
1204             */
1205             this.cfg.addProperty(DEFAULT_CONFIG.EFFECT.key, {
1206                 suppressEvent: DEFAULT_CONFIG.EFFECT.suppressEvent, 
1207                 supercedes: DEFAULT_CONFIG.EFFECT.supercedes
1208             });
1209
1210             /**
1211             * Specifies whether to create a special proxy iframe to monitor 
1212             * for user font resizing in the document
1213             * @config monitorresize
1214             * @type Boolean
1215             * @default true
1216             */
1217             this.cfg.addProperty(DEFAULT_CONFIG.MONITOR_RESIZE.key, {
1218                 handler: this.configMonitorResize,
1219                 value: DEFAULT_CONFIG.MONITOR_RESIZE.value
1220             });
1221
1222             /**
1223             * Specifies if the module should be rendered as the first child 
1224             * of document.body or appended as the last child when render is called
1225             * with document.body as the "appendToNode".
1226             * <p>
1227             * Appending to the body while the DOM is still being constructed can 
1228             * lead to Operation Aborted errors in IE hence this flag is set to 
1229             * false by default.
1230             * </p>
1231             * 
1232             * @config appendtodocumentbody
1233             * @type Boolean
1234             * @default false
1235             */
1236             this.cfg.addProperty(DEFAULT_CONFIG.APPEND_TO_DOCUMENT_BODY.key, {
1237                 value: DEFAULT_CONFIG.APPEND_TO_DOCUMENT_BODY.value
1238             });
1239         },
1240
1241         /**
1242         * The Module class's initialization method, which is executed for
1243         * Module and all of its subclasses. This method is automatically 
1244         * called by the constructor, and  sets up all DOM references for 
1245         * pre-existing markup, and creates required markup if it is not 
1246         * already present.
1247         * <p>
1248         * If the element passed in does not have an id, one will be generated
1249         * for it.
1250         * </p>
1251         * @method init
1252         * @param {String} el The element ID representing the Module <em>OR</em>
1253         * @param {HTMLElement} el The element representing the Module
1254         * @param {Object} userConfig The configuration Object literal 
1255         * containing the configuration that should be set for this module. 
1256         * See configuration documentation for more details.
1257         */
1258         init: function (el, userConfig) {
1259
1260             var elId, child;
1261
1262             this.initEvents();
1263             this.beforeInitEvent.fire(Module);
1264
1265             /**
1266             * The Module's Config object used for monitoring 
1267             * configuration properties.
1268             * @property cfg
1269             * @type YAHOO.util.Config
1270             */
1271             this.cfg = new Config(this);
1272
1273             if (this.isSecure) {
1274                 this.imageRoot = Module.IMG_ROOT_SSL;
1275             }
1276
1277             if (typeof el == "string") {
1278                 elId = el;
1279                 el = document.getElementById(el);
1280                 if (! el) {
1281                     el = (createModuleTemplate()).cloneNode(false);
1282                     el.id = elId;
1283                 }
1284             }
1285
1286             this.id = Dom.generateId(el);
1287             this.element = el;
1288
1289             child = this.element.firstChild;
1290
1291             if (child) {
1292                 var fndHd = false, fndBd = false, fndFt = false;
1293                 do {
1294                     // We're looking for elements
1295                     if (1 == child.nodeType) {
1296                         if (!fndHd && Dom.hasClass(child, Module.CSS_HEADER)) {
1297                             this.header = child;
1298                             fndHd = true;
1299                         } else if (!fndBd && Dom.hasClass(child, Module.CSS_BODY)) {
1300                             this.body = child;
1301                             fndBd = true;
1302                         } else if (!fndFt && Dom.hasClass(child, Module.CSS_FOOTER)){
1303                             this.footer = child;
1304                             fndFt = true;
1305                         }
1306                     }
1307                 } while ((child = child.nextSibling));
1308             }
1309
1310             this.initDefaultConfig();
1311
1312             Dom.addClass(this.element, Module.CSS_MODULE);
1313
1314             if (userConfig) {
1315                 this.cfg.applyConfig(userConfig, true);
1316             }
1317
1318             /*
1319                 Subscribe to the fireQueue() method of Config so that any 
1320                 queued configuration changes are excecuted upon render of 
1321                 the Module
1322             */ 
1323
1324             if (!Config.alreadySubscribed(this.renderEvent, this.cfg.fireQueue, this.cfg)) {
1325                 this.renderEvent.subscribe(this.cfg.fireQueue, this.cfg, true);
1326             }
1327
1328             this.initEvent.fire(Module);
1329         },
1330
1331         /**
1332         * Initialize an empty IFRAME that is placed out of the visible area 
1333         * that can be used to detect text resize.
1334         * @method initResizeMonitor
1335         */
1336         initResizeMonitor: function () {
1337
1338             var isGeckoWin = (UA.gecko && this.platform == "windows");
1339             if (isGeckoWin) {
1340                 // Help prevent spinning loading icon which 
1341                 // started with FireFox 2.0.0.8/Win
1342                 var self = this;
1343                 setTimeout(function(){self._initResizeMonitor();}, 0);
1344             } else {
1345                 this._initResizeMonitor();
1346             }
1347         },
1348
1349         /**
1350          * Create and initialize the text resize monitoring iframe.
1351          * 
1352          * @protected
1353          * @method _initResizeMonitor
1354          */
1355         _initResizeMonitor : function() {
1356
1357             var oDoc, 
1358                 oIFrame, 
1359                 sHTML;
1360
1361             function fireTextResize() {
1362                 Module.textResizeEvent.fire();
1363             }
1364
1365             if (!UA.opera) {
1366                 oIFrame = Dom.get("_yuiResizeMonitor");
1367
1368                 var supportsCWResize = this._supportsCWResize();
1369
1370                 if (!oIFrame) {
1371                     oIFrame = document.createElement("iframe");
1372
1373                     if (this.isSecure && Module.RESIZE_MONITOR_SECURE_URL && UA.ie) {
1374                         oIFrame.src = Module.RESIZE_MONITOR_SECURE_URL;
1375                     }
1376
1377                     if (!supportsCWResize) {
1378                         // Can't monitor on contentWindow, so fire from inside iframe
1379                         sHTML = ["<html><head><script ",
1380                                  "type=\"text/javascript\">",
1381                                  "window.onresize=function(){window.parent.",
1382                                  "YAHOO.widget.Module.textResizeEvent.",
1383                                  "fire();};<",
1384                                  "\/script></head>",
1385                                  "<body></body></html>"].join('');
1386
1387                         oIFrame.src = "data:text/html;charset=utf-8," + encodeURIComponent(sHTML);
1388                     }
1389
1390                     oIFrame.id = "_yuiResizeMonitor";
1391                     oIFrame.title = "Text Resize Monitor";
1392                     /*
1393                         Need to set "position" property before inserting the 
1394                         iframe into the document or Safari's status bar will 
1395                         forever indicate the iframe is loading 
1396                         (See YUILibrary bug #1723064)
1397                     */
1398                     oIFrame.style.position = "absolute";
1399                     oIFrame.style.visibility = "hidden";
1400
1401                     var db = document.body,
1402                         fc = db.firstChild;
1403                     if (fc) {
1404                         db.insertBefore(oIFrame, fc);
1405                     } else {
1406                         db.appendChild(oIFrame);
1407                     }
1408
1409                     // Setting the background color fixes an issue with IE6/IE7, where
1410                     // elements in the DOM, with -ve margin-top which positioned them 
1411                     // offscreen (so they would be overlapped by the iframe and its -ve top
1412                     // setting), would have their -ve margin-top ignored, when the iframe 
1413                     // was added.
1414                     oIFrame.style.backgroundColor = "transparent";
1415
1416                     oIFrame.style.borderWidth = "0";
1417                     oIFrame.style.width = "2em";
1418                     oIFrame.style.height = "2em";
1419                     oIFrame.style.left = "0";
1420                     oIFrame.style.top = (-1 * (oIFrame.offsetHeight + Module.RESIZE_MONITOR_BUFFER)) + "px";
1421                     oIFrame.style.visibility = "visible";
1422
1423                     /*
1424                        Don't open/close the document for Gecko like we used to, since it
1425                        leads to duplicate cookies. (See YUILibrary bug #1721755)
1426                     */
1427                     if (UA.webkit) {
1428                         oDoc = oIFrame.contentWindow.document;
1429                         oDoc.open();
1430                         oDoc.close();
1431                     }
1432                 }
1433
1434                 if (oIFrame && oIFrame.contentWindow) {
1435                     Module.textResizeEvent.subscribe(this.onDomResize, this, true);
1436
1437                     if (!Module.textResizeInitialized) {
1438                         if (supportsCWResize) {
1439                             if (!Event.on(oIFrame.contentWindow, "resize", fireTextResize)) {
1440                                 /*
1441                                      This will fail in IE if document.domain has 
1442                                      changed, so we must change the listener to 
1443                                      use the oIFrame element instead
1444                                 */
1445                                 Event.on(oIFrame, "resize", fireTextResize);
1446                             }
1447                         }
1448                         Module.textResizeInitialized = true;
1449                     }
1450                     this.resizeMonitor = oIFrame;
1451                 }
1452             }
1453         },
1454
1455         /**
1456          * Text resize monitor helper method.
1457          * Determines if the browser supports resize events on iframe content windows.
1458          * 
1459          * @private
1460          * @method _supportsCWResize
1461          */
1462         _supportsCWResize : function() {
1463             /*
1464                 Gecko 1.8.0 (FF1.5), 1.8.1.0-5 (FF2) won't fire resize on contentWindow.
1465                 Gecko 1.8.1.6+ (FF2.0.0.6+) and all other browsers will fire resize on contentWindow.
1466
1467                 We don't want to start sniffing for patch versions, so fire textResize the same
1468                 way on all FF2 flavors
1469              */
1470             var bSupported = true;
1471             if (UA.gecko && UA.gecko <= 1.8) {
1472                 bSupported = false;
1473             }
1474             return bSupported;
1475         },
1476
1477         /**
1478         * Event handler fired when the resize monitor element is resized.
1479         * @method onDomResize
1480         * @param {DOMEvent} e The DOM resize event
1481         * @param {Object} obj The scope object passed to the handler
1482         */
1483         onDomResize: function (e, obj) {
1484
1485             var nTop = -1 * (this.resizeMonitor.offsetHeight + Module.RESIZE_MONITOR_BUFFER);
1486
1487             this.resizeMonitor.style.top = nTop + "px";
1488             this.resizeMonitor.style.left = "0";
1489         },
1490
1491         /**
1492         * Sets the Module's header content to the string specified, or appends 
1493         * the passed element to the header. If no header is present, one will 
1494         * be automatically created. An empty string can be passed to the method
1495         * to clear the contents of the header.
1496         * 
1497         * @method setHeader
1498         * @param {String} headerContent The string used to set the header.
1499         * As a convenience, non HTMLElement objects can also be passed into 
1500         * the method, and will be treated as strings, with the header innerHTML
1501         * set to their default toString implementations.
1502         * <em>OR</em>
1503         * @param {HTMLElement} headerContent The HTMLElement to append to 
1504         * <em>OR</em>
1505         * @param {DocumentFragment} headerContent The document fragment 
1506         * containing elements which are to be added to the header
1507         */
1508         setHeader: function (headerContent) {
1509             var oHeader = this.header || (this.header = createHeader());
1510
1511             if (headerContent.nodeName) {
1512                 oHeader.innerHTML = "";
1513                 oHeader.appendChild(headerContent);
1514             } else {
1515                 oHeader.innerHTML = headerContent;
1516             }
1517
1518             if (this._rendered) {
1519                 this._renderHeader();
1520             }
1521
1522             this.changeHeaderEvent.fire(headerContent);
1523             this.changeContentEvent.fire();
1524
1525         },
1526
1527         /**
1528         * Appends the passed element to the header. If no header is present, 
1529         * one will be automatically created.
1530         * @method appendToHeader
1531         * @param {HTMLElement | DocumentFragment} element The element to 
1532         * append to the header. In the case of a document fragment, the
1533         * children of the fragment will be appended to the header.
1534         */
1535         appendToHeader: function (element) {
1536             var oHeader = this.header || (this.header = createHeader());
1537
1538             oHeader.appendChild(element);
1539
1540             this.changeHeaderEvent.fire(element);
1541             this.changeContentEvent.fire();
1542
1543         },
1544
1545         /**
1546         * Sets the Module's body content to the HTML specified. 
1547         * 
1548         * If no body is present, one will be automatically created. 
1549         * 
1550         * An empty string can be passed to the method to clear the contents of the body.
1551         * @method setBody
1552         * @param {String} bodyContent The HTML used to set the body. 
1553         * As a convenience, non HTMLElement objects can also be passed into 
1554         * the method, and will be treated as strings, with the body innerHTML
1555         * set to their default toString implementations.
1556         * <em>OR</em>
1557         * @param {HTMLElement} bodyContent The HTMLElement to add as the first and only
1558         * child of the body element.
1559         * <em>OR</em>
1560         * @param {DocumentFragment} bodyContent The document fragment 
1561         * containing elements which are to be added to the body
1562         */
1563         setBody: function (bodyContent) {
1564             var oBody = this.body || (this.body = createBody());
1565
1566             if (bodyContent.nodeName) {
1567                 oBody.innerHTML = "";
1568                 oBody.appendChild(bodyContent);
1569             } else {
1570                 oBody.innerHTML = bodyContent;
1571             }
1572
1573             if (this._rendered) {
1574                 this._renderBody();
1575             }
1576
1577             this.changeBodyEvent.fire(bodyContent);
1578             this.changeContentEvent.fire();
1579         },
1580
1581         /**
1582         * Appends the passed element to the body. If no body is present, one 
1583         * will be automatically created.
1584         * @method appendToBody
1585         * @param {HTMLElement | DocumentFragment} element The element to 
1586         * append to the body. In the case of a document fragment, the
1587         * children of the fragment will be appended to the body.
1588         * 
1589         */
1590         appendToBody: function (element) {
1591             var oBody = this.body || (this.body = createBody());
1592         
1593             oBody.appendChild(element);
1594
1595             this.changeBodyEvent.fire(element);
1596             this.changeContentEvent.fire();
1597
1598         },
1599         
1600         /**
1601         * Sets the Module's footer content to the HTML specified, or appends 
1602         * the passed element to the footer. If no footer is present, one will 
1603         * be automatically created. An empty string can be passed to the method
1604         * to clear the contents of the footer.
1605         * @method setFooter
1606         * @param {String} footerContent The HTML used to set the footer 
1607         * As a convenience, non HTMLElement objects can also be passed into 
1608         * the method, and will be treated as strings, with the footer innerHTML
1609         * set to their default toString implementations.
1610         * <em>OR</em>
1611         * @param {HTMLElement} footerContent The HTMLElement to append to 
1612         * the footer
1613         * <em>OR</em>
1614         * @param {DocumentFragment} footerContent The document fragment containing 
1615         * elements which are to be added to the footer
1616         */
1617         setFooter: function (footerContent) {
1618
1619             var oFooter = this.footer || (this.footer = createFooter());
1620
1621             if (footerContent.nodeName) {
1622                 oFooter.innerHTML = "";
1623                 oFooter.appendChild(footerContent);
1624             } else {
1625                 oFooter.innerHTML = footerContent;
1626             }
1627
1628             if (this._rendered) {
1629                 this._renderFooter();
1630             }
1631
1632             this.changeFooterEvent.fire(footerContent);
1633             this.changeContentEvent.fire();
1634         },
1635
1636         /**
1637         * Appends the passed element to the footer. If no footer is present, 
1638         * one will be automatically created.
1639         * @method appendToFooter
1640         * @param {HTMLElement | DocumentFragment} element The element to 
1641         * append to the footer. In the case of a document fragment, the
1642         * children of the fragment will be appended to the footer
1643         */
1644         appendToFooter: function (element) {
1645
1646             var oFooter = this.footer || (this.footer = createFooter());
1647
1648             oFooter.appendChild(element);
1649
1650             this.changeFooterEvent.fire(element);
1651             this.changeContentEvent.fire();
1652
1653         },
1654
1655         /**
1656         * Renders the Module by inserting the elements that are not already 
1657         * in the main Module into their correct places. Optionally appends 
1658         * the Module to the specified node prior to the render's execution. 
1659         * <p>
1660         * For Modules without existing markup, the appendToNode argument 
1661         * is REQUIRED. If this argument is ommitted and the current element is 
1662         * not present in the document, the function will return false, 
1663         * indicating that the render was a failure.
1664         * </p>
1665         * <p>
1666         * NOTE: As of 2.3.1, if the appendToNode is the document's body element
1667         * then the module is rendered as the first child of the body element, 
1668         * and not appended to it, to avoid Operation Aborted errors in IE when 
1669         * rendering the module before window's load event is fired. You can 
1670         * use the appendtodocumentbody configuration property to change this 
1671         * to append to document.body if required.
1672         * </p>
1673         * @method render
1674         * @param {String} appendToNode The element id to which the Module 
1675         * should be appended to prior to rendering <em>OR</em>
1676         * @param {HTMLElement} appendToNode The element to which the Module 
1677         * should be appended to prior to rendering
1678         * @param {HTMLElement} moduleElement OPTIONAL. The element that 
1679         * represents the actual Standard Module container.
1680         * @return {Boolean} Success or failure of the render
1681         */
1682         render: function (appendToNode, moduleElement) {
1683
1684             var me = this;
1685
1686             function appendTo(parentNode) {
1687                 if (typeof parentNode == "string") {
1688                     parentNode = document.getElementById(parentNode);
1689                 }
1690
1691                 if (parentNode) {
1692                     me._addToParent(parentNode, me.element);
1693                     me.appendEvent.fire();
1694                 }
1695             }
1696
1697             this.beforeRenderEvent.fire();
1698
1699             if (! moduleElement) {
1700                 moduleElement = this.element;
1701             }
1702
1703             if (appendToNode) {
1704                 appendTo(appendToNode);
1705             } else { 
1706                 // No node was passed in. If the element is not already in the Dom, this fails
1707                 if (! Dom.inDocument(this.element)) {
1708                     return false;
1709                 }
1710             }
1711
1712             this._renderHeader(moduleElement);
1713             this._renderBody(moduleElement);
1714             this._renderFooter(moduleElement);
1715
1716             this._rendered = true;
1717
1718             this.renderEvent.fire();
1719             return true;
1720         },
1721
1722         /**
1723          * Renders the currently set header into it's proper position under the 
1724          * module element. If the module element is not provided, "this.element" 
1725          * is used.
1726          * 
1727          * @method _renderHeader
1728          * @protected
1729          * @param {HTMLElement} moduleElement Optional. A reference to the module element
1730          */
1731         _renderHeader: function(moduleElement){
1732             moduleElement = moduleElement || this.element;
1733
1734             // Need to get everything into the DOM if it isn't already
1735             if (this.header && !Dom.inDocument(this.header)) {
1736                 // There is a header, but it's not in the DOM yet. Need to add it.
1737                 var firstChild = moduleElement.firstChild;
1738                 if (firstChild) {
1739                     moduleElement.insertBefore(this.header, firstChild);
1740                 } else {
1741                     moduleElement.appendChild(this.header);
1742                 }
1743             }
1744         },
1745
1746         /**
1747          * Renders the currently set body into it's proper position under the 
1748          * module element. If the module element is not provided, "this.element" 
1749          * is used.
1750          * 
1751          * @method _renderBody
1752          * @protected
1753          * @param {HTMLElement} moduleElement Optional. A reference to the module element.
1754          */
1755         _renderBody: function(moduleElement){
1756             moduleElement = moduleElement || this.element;
1757
1758             if (this.body && !Dom.inDocument(this.body)) {
1759                 // There is a body, but it's not in the DOM yet. Need to add it.
1760                 if (this.footer && Dom.isAncestor(moduleElement, this.footer)) {
1761                     moduleElement.insertBefore(this.body, this.footer);
1762                 } else {
1763                     moduleElement.appendChild(this.body);
1764                 }
1765             }
1766         },
1767
1768         /**
1769          * Renders the currently set footer into it's proper position under the 
1770          * module element. If the module element is not provided, "this.element" 
1771          * is used.
1772          * 
1773          * @method _renderFooter
1774          * @protected
1775          * @param {HTMLElement} moduleElement Optional. A reference to the module element
1776          */
1777         _renderFooter: function(moduleElement){
1778             moduleElement = moduleElement || this.element;
1779
1780             if (this.footer && !Dom.inDocument(this.footer)) {
1781                 // There is a footer, but it's not in the DOM yet. Need to add it.
1782                 moduleElement.appendChild(this.footer);
1783             }
1784         },
1785
1786         /**
1787         * Removes the Module element from the DOM and sets all child elements 
1788         * to null.
1789         * @method destroy
1790         */
1791         destroy: function () {
1792
1793             var parent;
1794
1795             if (this.element) {
1796                 Event.purgeElement(this.element, true);
1797                 parent = this.element.parentNode;
1798             }
1799
1800             if (parent) {
1801                 parent.removeChild(this.element);
1802             }
1803         
1804             this.element = null;
1805             this.header = null;
1806             this.body = null;
1807             this.footer = null;
1808
1809             Module.textResizeEvent.unsubscribe(this.onDomResize, this);
1810
1811             this.cfg.destroy();
1812             this.cfg = null;
1813
1814             this.destroyEvent.fire();
1815         },
1816
1817         /**
1818         * Shows the Module element by setting the visible configuration 
1819         * property to true. Also fires two events: beforeShowEvent prior to 
1820         * the visibility change, and showEvent after.
1821         * @method show
1822         */
1823         show: function () {
1824             this.cfg.setProperty("visible", true);
1825         },
1826
1827         /**
1828         * Hides the Module element by setting the visible configuration 
1829         * property to false. Also fires two events: beforeHideEvent prior to 
1830         * the visibility change, and hideEvent after.
1831         * @method hide
1832         */
1833         hide: function () {
1834             this.cfg.setProperty("visible", false);
1835         },
1836         
1837         // BUILT-IN EVENT HANDLERS FOR MODULE //
1838         /**
1839         * Default event handler for changing the visibility property of a 
1840         * Module. By default, this is achieved by switching the "display" style 
1841         * between "block" and "none".
1842         * This method is responsible for firing showEvent and hideEvent.
1843         * @param {String} type The CustomEvent type (usually the property name)
1844         * @param {Object[]} args The CustomEvent arguments. For configuration 
1845         * handlers, args[0] will equal the newly applied value for the property.
1846         * @param {Object} obj The scope object. For configuration handlers, 
1847         * this will usually equal the owner.
1848         * @method configVisible
1849         */
1850         configVisible: function (type, args, obj) {
1851             var visible = args[0];
1852             if (visible) {
1853                 this.beforeShowEvent.fire();
1854                 Dom.setStyle(this.element, "display", "block");
1855                 this.showEvent.fire();
1856             } else {
1857                 this.beforeHideEvent.fire();
1858                 Dom.setStyle(this.element, "display", "none");
1859                 this.hideEvent.fire();
1860             }
1861         },
1862
1863         /**
1864         * Default event handler for the "monitorresize" configuration property
1865         * @param {String} type The CustomEvent type (usually the property name)
1866         * @param {Object[]} args The CustomEvent arguments. For configuration 
1867         * handlers, args[0] will equal the newly applied value for the property.
1868         * @param {Object} obj The scope object. For configuration handlers, 
1869         * this will usually equal the owner.
1870         * @method configMonitorResize
1871         */
1872         configMonitorResize: function (type, args, obj) {
1873             var monitor = args[0];
1874             if (monitor) {
1875                 this.initResizeMonitor();
1876             } else {
1877                 Module.textResizeEvent.unsubscribe(this.onDomResize, this, true);
1878                 this.resizeMonitor = null;
1879             }
1880         },
1881
1882         /**
1883          * This method is a protected helper, used when constructing the DOM structure for the module 
1884          * to account for situations which may cause Operation Aborted errors in IE. It should not 
1885          * be used for general DOM construction.
1886          * <p>
1887          * If the parentNode is not document.body, the element is appended as the last element.
1888          * </p>
1889          * <p>
1890          * If the parentNode is document.body the element is added as the first child to help
1891          * prevent Operation Aborted errors in IE.
1892          * </p>
1893          *
1894          * @param {parentNode} The HTML element to which the element will be added
1895          * @param {element} The HTML element to be added to parentNode's children
1896          * @method _addToParent
1897          * @protected
1898          */
1899         _addToParent: function(parentNode, element) {
1900             if (!this.cfg.getProperty("appendtodocumentbody") && parentNode === document.body && parentNode.firstChild) {
1901                 parentNode.insertBefore(element, parentNode.firstChild);
1902             } else {
1903                 parentNode.appendChild(element);
1904             }
1905         },
1906
1907         /**
1908         * Returns a String representation of the Object.
1909         * @method toString
1910         * @return {String} The string representation of the Module
1911         */
1912         toString: function () {
1913             return "Module " + this.id;
1914         }
1915     };
1916
1917     YAHOO.lang.augmentProto(Module, YAHOO.util.EventProvider);
1918
1919 }());
1920 (function () {
1921
1922     /**
1923     * Overlay is a Module that is absolutely positioned above the page flow. It 
1924     * has convenience methods for positioning and sizing, as well as options for 
1925     * controlling zIndex and constraining the Overlay's position to the current 
1926     * visible viewport. Overlay also contains a dynamicly generated IFRAME which 
1927     * is placed beneath it for Internet Explorer 6 and 5.x so that it will be 
1928     * properly rendered above SELECT elements.
1929     * @namespace YAHOO.widget
1930     * @class Overlay
1931     * @extends YAHOO.widget.Module
1932     * @param {String} el The element ID representing the Overlay <em>OR</em>
1933     * @param {HTMLElement} el The element representing the Overlay
1934     * @param {Object} userConfig The configuration object literal containing 
1935     * the configuration that should be set for this Overlay. See configuration 
1936     * documentation for more details.
1937     * @constructor
1938     */
1939     YAHOO.widget.Overlay = function (el, userConfig) {
1940         YAHOO.widget.Overlay.superclass.constructor.call(this, el, userConfig);
1941     };
1942
1943     var Lang = YAHOO.lang,
1944         CustomEvent = YAHOO.util.CustomEvent,
1945         Module = YAHOO.widget.Module,
1946         Event = YAHOO.util.Event,
1947         Dom = YAHOO.util.Dom,
1948         Config = YAHOO.util.Config,
1949         UA = YAHOO.env.ua,
1950         Overlay = YAHOO.widget.Overlay,
1951
1952         _SUBSCRIBE = "subscribe",
1953         _UNSUBSCRIBE = "unsubscribe",
1954         _CONTAINED = "contained",
1955
1956         m_oIFrameTemplate,
1957
1958         /**
1959         * Constant representing the name of the Overlay's events
1960         * @property EVENT_TYPES
1961         * @private
1962         * @final
1963         * @type Object
1964         */
1965         EVENT_TYPES = {
1966             "BEFORE_MOVE": "beforeMove",
1967             "MOVE": "move"
1968         },
1969
1970         /**
1971         * Constant representing the Overlay's configuration properties
1972         * @property DEFAULT_CONFIG
1973         * @private
1974         * @final
1975         * @type Object
1976         */
1977         DEFAULT_CONFIG = {
1978
1979             "X": { 
1980                 key: "x", 
1981                 validator: Lang.isNumber, 
1982                 suppressEvent: true, 
1983                 supercedes: ["iframe"]
1984             },
1985
1986             "Y": { 
1987                 key: "y", 
1988                 validator: Lang.isNumber, 
1989                 suppressEvent: true, 
1990                 supercedes: ["iframe"]
1991             },
1992
1993             "XY": { 
1994                 key: "xy", 
1995                 suppressEvent: true, 
1996                 supercedes: ["iframe"] 
1997             },
1998
1999             "CONTEXT": { 
2000                 key: "context", 
2001                 suppressEvent: true, 
2002                 supercedes: ["iframe"] 
2003             },
2004
2005             "FIXED_CENTER": { 
2006                 key: "fixedcenter", 
2007                 value: false, 
2008                 supercedes: ["iframe", "visible"] 
2009             },
2010
2011             "WIDTH": { 
2012                 key: "width",
2013                 suppressEvent: true,
2014                 supercedes: ["context", "fixedcenter", "iframe"]
2015             }, 
2016
2017             "HEIGHT": { 
2018                 key: "height", 
2019                 suppressEvent: true, 
2020                 supercedes: ["context", "fixedcenter", "iframe"] 
2021             },
2022
2023             "AUTO_FILL_HEIGHT" : {
2024                 key: "autofillheight",
2025                 supercedes: ["height"],
2026                 value:"body"
2027             },
2028
2029             "ZINDEX": { 
2030                 key: "zindex", 
2031                 value: null 
2032             },
2033
2034             "CONSTRAIN_TO_VIEWPORT": { 
2035                 key: "constraintoviewport", 
2036                 value: false, 
2037                 validator: Lang.isBoolean, 
2038                 supercedes: ["iframe", "x", "y", "xy"]
2039             }, 
2040
2041             "IFRAME": { 
2042                 key: "iframe", 
2043                 value: (UA.ie == 6 ? true : false), 
2044                 validator: Lang.isBoolean, 
2045                 supercedes: ["zindex"] 
2046             },
2047
2048             "PREVENT_CONTEXT_OVERLAP": {
2049                 key: "preventcontextoverlap",
2050                 value: false,
2051                 validator: Lang.isBoolean,  
2052                 supercedes: ["constraintoviewport"]
2053             }
2054
2055         };
2056
2057     /**
2058     * The URL that will be placed in the iframe
2059     * @property YAHOO.widget.Overlay.IFRAME_SRC
2060     * @static
2061     * @final
2062     * @type String
2063     */
2064     Overlay.IFRAME_SRC = "javascript:false;";
2065
2066     /**
2067     * Number representing how much the iframe shim should be offset from each 
2068     * side of an Overlay instance, in pixels.
2069     * @property YAHOO.widget.Overlay.IFRAME_SRC
2070     * @default 3
2071     * @static
2072     * @final
2073     * @type Number
2074     */
2075     Overlay.IFRAME_OFFSET = 3;
2076
2077     /**
2078     * Number representing the minimum distance an Overlay instance should be 
2079     * positioned relative to the boundaries of the browser's viewport, in pixels.
2080     * @property YAHOO.widget.Overlay.VIEWPORT_OFFSET
2081     * @default 10
2082     * @static
2083     * @final
2084     * @type Number
2085     */
2086     Overlay.VIEWPORT_OFFSET = 10;
2087
2088     /**
2089     * Constant representing the top left corner of an element, used for 
2090     * configuring the context element alignment
2091     * @property YAHOO.widget.Overlay.TOP_LEFT
2092     * @static
2093     * @final
2094     * @type String
2095     */
2096     Overlay.TOP_LEFT = "tl";
2097
2098     /**
2099     * Constant representing the top right corner of an element, used for 
2100     * configuring the context element alignment
2101     * @property YAHOO.widget.Overlay.TOP_RIGHT
2102     * @static
2103     * @final
2104     * @type String
2105     */
2106     Overlay.TOP_RIGHT = "tr";
2107
2108     /**
2109     * Constant representing the top bottom left corner of an element, used for 
2110     * configuring the context element alignment
2111     * @property YAHOO.widget.Overlay.BOTTOM_LEFT
2112     * @static
2113     * @final
2114     * @type String
2115     */
2116     Overlay.BOTTOM_LEFT = "bl";
2117
2118     /**
2119     * Constant representing the bottom right corner of an element, used for 
2120     * configuring the context element alignment
2121     * @property YAHOO.widget.Overlay.BOTTOM_RIGHT
2122     * @static
2123     * @final
2124     * @type String
2125     */
2126     Overlay.BOTTOM_RIGHT = "br";
2127
2128     Overlay.PREVENT_OVERLAP_X = {
2129         "tltr": true,
2130         "blbr": true,
2131         "brbl": true,
2132         "trtl": true
2133     };
2134             
2135     Overlay.PREVENT_OVERLAP_Y = {
2136         "trbr": true,
2137         "tlbl": true,
2138         "bltl": true,
2139         "brtr": true
2140     };
2141
2142     /**
2143     * Constant representing the default CSS class used for an Overlay
2144     * @property YAHOO.widget.Overlay.CSS_OVERLAY
2145     * @static
2146     * @final
2147     * @type String
2148     */
2149     Overlay.CSS_OVERLAY = "yui-overlay";
2150
2151     /**
2152     * Constant representing the default hidden CSS class used for an Overlay. This class is 
2153     * applied to the overlay's outer DIV whenever it's hidden.
2154     *
2155     * @property YAHOO.widget.Overlay.CSS_HIDDEN
2156     * @static
2157     * @final
2158     * @type String
2159     */
2160     Overlay.CSS_HIDDEN = "yui-overlay-hidden";
2161
2162     /**
2163     * Constant representing the default CSS class used for an Overlay iframe shim.
2164     * 
2165     * @property YAHOO.widget.Overlay.CSS_IFRAME
2166     * @static
2167     * @final
2168     * @type String
2169     */
2170     Overlay.CSS_IFRAME = "yui-overlay-iframe";
2171
2172     /**
2173      * Constant representing the names of the standard module elements
2174      * used in the overlay.
2175      * @property YAHOO.widget.Overlay.STD_MOD_RE
2176      * @static
2177      * @final
2178      * @type RegExp
2179      */
2180     Overlay.STD_MOD_RE = /^\s*?(body|footer|header)\s*?$/i;
2181
2182     /**
2183     * A singleton CustomEvent used for reacting to the DOM event for 
2184     * window scroll
2185     * @event YAHOO.widget.Overlay.windowScrollEvent
2186     */
2187     Overlay.windowScrollEvent = new CustomEvent("windowScroll");
2188
2189     /**
2190     * A singleton CustomEvent used for reacting to the DOM event for
2191     * window resize
2192     * @event YAHOO.widget.Overlay.windowResizeEvent
2193     */
2194     Overlay.windowResizeEvent = new CustomEvent("windowResize");
2195
2196     /**
2197     * The DOM event handler used to fire the CustomEvent for window scroll
2198     * @method YAHOO.widget.Overlay.windowScrollHandler
2199     * @static
2200     * @param {DOMEvent} e The DOM scroll event
2201     */
2202     Overlay.windowScrollHandler = function (e) {
2203         var t = Event.getTarget(e);
2204
2205         // - Webkit (Safari 2/3) and Opera 9.2x bubble scroll events from elements to window
2206         // - FF2/3 and IE6/7, Opera 9.5x don't bubble scroll events from elements to window
2207         // - IE doesn't recognize scroll registered on the document.
2208         //
2209         // Also, when document view is scrolled, IE doesn't provide a target, 
2210         // rest of the browsers set target to window.document, apart from opera 
2211         // which sets target to window.
2212         if (!t || t === window || t === window.document) {
2213             if (UA.ie) {
2214
2215                 if (! window.scrollEnd) {
2216                     window.scrollEnd = -1;
2217                 }
2218
2219                 clearTimeout(window.scrollEnd);
2220         
2221                 window.scrollEnd = setTimeout(function () { 
2222                     Overlay.windowScrollEvent.fire(); 
2223                 }, 1);
2224         
2225             } else {
2226                 Overlay.windowScrollEvent.fire();
2227             }
2228         }
2229     };
2230
2231     /**
2232     * The DOM event handler used to fire the CustomEvent for window resize
2233     * @method YAHOO.widget.Overlay.windowResizeHandler
2234     * @static
2235     * @param {DOMEvent} e The DOM resize event
2236     */
2237     Overlay.windowResizeHandler = function (e) {
2238
2239         if (UA.ie) {
2240             if (! window.resizeEnd) {
2241                 window.resizeEnd = -1;
2242             }
2243
2244             clearTimeout(window.resizeEnd);
2245
2246             window.resizeEnd = setTimeout(function () {
2247                 Overlay.windowResizeEvent.fire(); 
2248             }, 100);
2249         } else {
2250             Overlay.windowResizeEvent.fire();
2251         }
2252     };
2253
2254     /**
2255     * A boolean that indicated whether the window resize and scroll events have 
2256     * already been subscribed to.
2257     * @property YAHOO.widget.Overlay._initialized
2258     * @private
2259     * @type Boolean
2260     */
2261     Overlay._initialized = null;
2262
2263     if (Overlay._initialized === null) {
2264         Event.on(window, "scroll", Overlay.windowScrollHandler);
2265         Event.on(window, "resize", Overlay.windowResizeHandler);
2266         Overlay._initialized = true;
2267     }
2268
2269     /**
2270      * Internal map of special event types, which are provided
2271      * by the instance. It maps the event type to the custom event 
2272      * instance. Contains entries for the "windowScroll", "windowResize" and
2273      * "textResize" static container events.
2274      *
2275      * @property YAHOO.widget.Overlay._TRIGGER_MAP
2276      * @type Object
2277      * @static
2278      * @private
2279      */
2280     Overlay._TRIGGER_MAP = {
2281         "windowScroll" : Overlay.windowScrollEvent,
2282         "windowResize" : Overlay.windowResizeEvent,
2283         "textResize"   : Module.textResizeEvent
2284     };
2285
2286     YAHOO.extend(Overlay, Module, {
2287
2288         /**
2289          * <p>
2290          * Array of default event types which will trigger
2291          * context alignment for the Overlay class.
2292          * </p>
2293          * <p>The array is empty by default for Overlay,
2294          * but maybe populated in future releases, so classes extending
2295          * Overlay which need to define their own set of CONTEXT_TRIGGERS
2296          * should concatenate their super class's prototype.CONTEXT_TRIGGERS 
2297          * value with their own array of values.
2298          * </p>
2299          * <p>
2300          * E.g.:
2301          * <code>CustomOverlay.prototype.CONTEXT_TRIGGERS = YAHOO.widget.Overlay.prototype.CONTEXT_TRIGGERS.concat(["windowScroll"]);</code>
2302          * </p>
2303          * 
2304          * @property CONTEXT_TRIGGERS
2305          * @type Array
2306          * @final
2307          */
2308         CONTEXT_TRIGGERS : [],
2309
2310         /**
2311         * The Overlay initialization method, which is executed for Overlay and  
2312         * all of its subclasses. This method is automatically called by the 
2313         * constructor, and  sets up all DOM references for pre-existing markup, 
2314         * and creates required markup if it is not already present.
2315         * @method init
2316         * @param {String} el The element ID representing the Overlay <em>OR</em>
2317         * @param {HTMLElement} el The element representing the Overlay
2318         * @param {Object} userConfig The configuration object literal 
2319         * containing the configuration that should be set for this Overlay. 
2320         * See configuration documentation for more details.
2321         */
2322         init: function (el, userConfig) {
2323
2324             /*
2325                  Note that we don't pass the user config in here yet because we
2326                  only want it executed once, at the lowest subclass level
2327             */
2328
2329             Overlay.superclass.init.call(this, el/*, userConfig*/);
2330
2331             this.beforeInitEvent.fire(Overlay);
2332
2333             Dom.addClass(this.element, Overlay.CSS_OVERLAY);
2334
2335             if (userConfig) {
2336                 this.cfg.applyConfig(userConfig, true);
2337             }
2338
2339             if (this.platform == "mac" && UA.gecko) {
2340
2341                 if (! Config.alreadySubscribed(this.showEvent,
2342                     this.showMacGeckoScrollbars, this)) {
2343
2344                     this.showEvent.subscribe(this.showMacGeckoScrollbars, 
2345                         this, true);
2346
2347                 }
2348
2349                 if (! Config.alreadySubscribed(this.hideEvent, 
2350                     this.hideMacGeckoScrollbars, this)) {
2351
2352                     this.hideEvent.subscribe(this.hideMacGeckoScrollbars, 
2353                         this, true);
2354
2355                 }
2356             }
2357
2358             this.initEvent.fire(Overlay);
2359         },
2360         
2361         /**
2362         * Initializes the custom events for Overlay which are fired  
2363         * automatically at appropriate times by the Overlay class.
2364         * @method initEvents
2365         */
2366         initEvents: function () {
2367
2368             Overlay.superclass.initEvents.call(this);
2369
2370             var SIGNATURE = CustomEvent.LIST;
2371
2372             /**
2373             * CustomEvent fired before the Overlay is moved.
2374             * @event beforeMoveEvent
2375             * @param {Number} x x coordinate
2376             * @param {Number} y y coordinate
2377             */
2378             this.beforeMoveEvent = this.createEvent(EVENT_TYPES.BEFORE_MOVE);
2379             this.beforeMoveEvent.signature = SIGNATURE;
2380
2381             /**
2382             * CustomEvent fired after the Overlay is moved.
2383             * @event moveEvent
2384             * @param {Number} x x coordinate
2385             * @param {Number} y y coordinate
2386             */
2387             this.moveEvent = this.createEvent(EVENT_TYPES.MOVE);
2388             this.moveEvent.signature = SIGNATURE;
2389
2390         },
2391         
2392         /**
2393         * Initializes the class's configurable properties which can be changed 
2394         * using the Overlay's Config object (cfg).
2395         * @method initDefaultConfig
2396         */
2397         initDefaultConfig: function () {
2398     
2399             Overlay.superclass.initDefaultConfig.call(this);
2400
2401             var cfg = this.cfg;
2402
2403             // Add overlay config properties //
2404             
2405             /**
2406             * The absolute x-coordinate position of the Overlay
2407             * @config x
2408             * @type Number
2409             * @default null
2410             */
2411             cfg.addProperty(DEFAULT_CONFIG.X.key, { 
2412     
2413                 handler: this.configX, 
2414                 validator: DEFAULT_CONFIG.X.validator, 
2415                 suppressEvent: DEFAULT_CONFIG.X.suppressEvent, 
2416                 supercedes: DEFAULT_CONFIG.X.supercedes
2417     
2418             });
2419
2420             /**
2421             * The absolute y-coordinate position of the Overlay
2422             * @config y
2423             * @type Number
2424             * @default null
2425             */
2426             cfg.addProperty(DEFAULT_CONFIG.Y.key, {
2427
2428                 handler: this.configY, 
2429                 validator: DEFAULT_CONFIG.Y.validator, 
2430                 suppressEvent: DEFAULT_CONFIG.Y.suppressEvent, 
2431                 supercedes: DEFAULT_CONFIG.Y.supercedes
2432
2433             });
2434
2435             /**
2436             * An array with the absolute x and y positions of the Overlay
2437             * @config xy
2438             * @type Number[]
2439             * @default null
2440             */
2441             cfg.addProperty(DEFAULT_CONFIG.XY.key, {
2442                 handler: this.configXY, 
2443                 suppressEvent: DEFAULT_CONFIG.XY.suppressEvent, 
2444                 supercedes: DEFAULT_CONFIG.XY.supercedes
2445             });
2446
2447             /**
2448             * <p>
2449             * The array of context arguments for context-sensitive positioning. 
2450             * </p>
2451             *
2452             * <p>
2453             * The format of the array is: <code>[contextElementOrId, overlayCorner, contextCorner, arrayOfTriggerEvents (optional), xyOffset (optional)]</code>, the
2454             * the 5 array elements described in detail below:
2455             * </p>
2456             *
2457             * <dl>
2458             * <dt>contextElementOrId &#60;String|HTMLElement&#62;</dt>
2459             * <dd>A reference to the context element to which the overlay should be aligned (or it's id).</dd>
2460             * <dt>overlayCorner &#60;String&#62;</dt>
2461             * <dd>The corner of the overlay which is to be used for alignment. This corner will be aligned to the 
2462             * corner of the context element defined by the "contextCorner" entry which follows. Supported string values are: 
2463             * "tr" (top right), "tl" (top left), "br" (bottom right), or "bl" (bottom left).</dd>
2464             * <dt>contextCorner &#60;String&#62;</dt>
2465             * <dd>The corner of the context element which is to be used for alignment. Supported string values are the same ones listed for the "overlayCorner" entry above.</dd>
2466             * <dt>arrayOfTriggerEvents (optional) &#60;Array[String|CustomEvent]&#62;</dt>
2467             * <dd>
2468             * <p>
2469             * By default, context alignment is a one time operation, aligning the Overlay to the context element when context configuration property is set, or when the <a href="#method_align">align</a> 
2470             * method is invoked. However, you can use the optional "arrayOfTriggerEvents" entry to define the list of events which should force the overlay to re-align itself with the context element. 
2471             * This is useful in situations where the layout of the document may change, resulting in the context element's position being modified.
2472             * </p>
2473             * <p>
2474             * The array can contain either event type strings for events the instance publishes (e.g. "beforeShow") or CustomEvent instances. Additionally the following
2475             * 3 static container event types are also currently supported : <code>"windowResize", "windowScroll", "textResize"</code> (defined in <a href="#property__TRIGGER_MAP">_TRIGGER_MAP</a> private property).
2476             * </p>
2477             * </dd>
2478             * <dt>xyOffset &#60;Number[]&#62;</dt>
2479             * <dd>
2480             * A 2 element Array specifying the X and Y pixel amounts by which the Overlay should be offset from the aligned corner. e.g. [5,0] offsets the Overlay 5 pixels to the left, <em>after</em> aligning the given context corners.
2481             * NOTE: If using this property and no triggers need to be defined, the arrayOfTriggerEvents property should be set to null to maintain correct array positions for the arguments. 
2482             * </dd>
2483             * </dl>
2484             *
2485             * <p>
2486             * For example, setting this property to <code>["img1", "tl", "bl"]</code> will 
2487             * align the Overlay's top left corner to the bottom left corner of the
2488             * context element with id "img1".
2489             * </p>
2490             * <p>
2491             * Setting this property to <code>["img1", "tl", "bl", null, [0,5]</code> will 
2492             * align the Overlay's top left corner to the bottom left corner of the
2493             * context element with id "img1", and then offset it by 5 pixels on the Y axis (providing a 5 pixel gap between the bottom of the context element and top of the overlay).
2494             * </p>
2495             * <p>
2496             * Adding the optional trigger values: <code>["img1", "tl", "bl", ["beforeShow", "windowResize"], [0,5]]</code>,
2497             * will re-align the overlay position, whenever the "beforeShow" or "windowResize" events are fired.
2498             * </p>
2499             *
2500             * @config context
2501             * @type Array
2502             * @default null
2503             */
2504             cfg.addProperty(DEFAULT_CONFIG.CONTEXT.key, {
2505                 handler: this.configContext, 
2506                 suppressEvent: DEFAULT_CONFIG.CONTEXT.suppressEvent, 
2507                 supercedes: DEFAULT_CONFIG.CONTEXT.supercedes
2508             });
2509
2510             /**
2511             * Determines whether or not the Overlay should be anchored 
2512             * to the center of the viewport.
2513             * 
2514             * <p>This property can be set to:</p>
2515             * 
2516             * <dl>
2517             * <dt>true</dt>
2518             * <dd>
2519             * To enable fixed center positioning
2520             * <p>
2521             * When enabled, the overlay will 
2522             * be positioned in the center of viewport when initially displayed, and 
2523             * will remain in the center of the viewport whenever the window is 
2524             * scrolled or resized.
2525             * </p>
2526             * <p>
2527             * If the overlay is too big for the viewport, 
2528             * it's top left corner will be aligned with the top left corner of the viewport.
2529             * </p>
2530             * </dd>
2531             * <dt>false</dt>
2532             * <dd>
2533             * To disable fixed center positioning.
2534             * <p>In this case the overlay can still be 
2535             * centered as a one-off operation, by invoking the <code>center()</code> method,
2536             * however it will not remain centered when the window is scrolled/resized.
2537             * </dd>
2538             * <dt>"contained"<dt>
2539             * <dd>To enable fixed center positioning, as with the <code>true</code> option.
2540             * <p>However, unlike setting the property to <code>true</code>, 
2541             * when the property is set to <code>"contained"</code>, if the overlay is 
2542             * too big for the viewport, it will not get automatically centered when the 
2543             * user scrolls or resizes the window (until the window is large enough to contain the 
2544             * overlay). This is useful in cases where the Overlay has both header and footer 
2545             * UI controls which the user may need to access.
2546             * </p>
2547             * </dd>
2548             * </dl>
2549             *
2550             * @config fixedcenter
2551             * @type Boolean | String
2552             * @default false
2553             */
2554             cfg.addProperty(DEFAULT_CONFIG.FIXED_CENTER.key, {
2555                 handler: this.configFixedCenter,
2556                 value: DEFAULT_CONFIG.FIXED_CENTER.value, 
2557                 validator: DEFAULT_CONFIG.FIXED_CENTER.validator, 
2558                 supercedes: DEFAULT_CONFIG.FIXED_CENTER.supercedes
2559             });
2560     
2561             /**
2562             * CSS width of the Overlay.
2563             * @config width
2564             * @type String
2565             * @default null
2566             */
2567             cfg.addProperty(DEFAULT_CONFIG.WIDTH.key, {
2568                 handler: this.configWidth, 
2569                 suppressEvent: DEFAULT_CONFIG.WIDTH.suppressEvent, 
2570                 supercedes: DEFAULT_CONFIG.WIDTH.supercedes
2571             });
2572
2573             /**
2574             * CSS height of the Overlay.
2575             * @config height
2576             * @type String
2577             * @default null
2578             */
2579             cfg.addProperty(DEFAULT_CONFIG.HEIGHT.key, {
2580                 handler: this.configHeight, 
2581                 suppressEvent: DEFAULT_CONFIG.HEIGHT.suppressEvent, 
2582                 supercedes: DEFAULT_CONFIG.HEIGHT.supercedes
2583             });
2584
2585             /**
2586             * Standard module element which should auto fill out the height of the Overlay if the height config property is set.
2587             * Supported values are "header", "body", "footer".
2588             *
2589             * @config autofillheight
2590             * @type String
2591             * @default null
2592             */
2593             cfg.addProperty(DEFAULT_CONFIG.AUTO_FILL_HEIGHT.key, {
2594                 handler: this.configAutoFillHeight, 
2595                 value : DEFAULT_CONFIG.AUTO_FILL_HEIGHT.value,
2596                 validator : this._validateAutoFill,
2597                 supercedes: DEFAULT_CONFIG.AUTO_FILL_HEIGHT.supercedes
2598             });
2599
2600             /**
2601             * CSS z-index of the Overlay.
2602             * @config zIndex
2603             * @type Number
2604             * @default null
2605             */
2606             cfg.addProperty(DEFAULT_CONFIG.ZINDEX.key, {
2607                 handler: this.configzIndex,
2608                 value: DEFAULT_CONFIG.ZINDEX.value
2609             });
2610
2611             /**
2612             * True if the Overlay should be prevented from being positioned 
2613             * out of the viewport.
2614             * @config constraintoviewport
2615             * @type Boolean
2616             * @default false
2617             */
2618             cfg.addProperty(DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.key, {
2619
2620                 handler: this.configConstrainToViewport, 
2621                 value: DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.value, 
2622                 validator: DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.validator, 
2623                 supercedes: DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.supercedes
2624
2625             });
2626
2627             /**
2628             * @config iframe
2629             * @description Boolean indicating whether or not the Overlay should 
2630             * have an IFRAME shim; used to prevent SELECT elements from 
2631             * poking through an Overlay instance in IE6.  When set to "true", 
2632             * the iframe shim is created when the Overlay instance is intially
2633             * made visible.
2634             * @type Boolean
2635             * @default true for IE6 and below, false for all other browsers.
2636             */
2637             cfg.addProperty(DEFAULT_CONFIG.IFRAME.key, {
2638
2639                 handler: this.configIframe, 
2640                 value: DEFAULT_CONFIG.IFRAME.value, 
2641                 validator: DEFAULT_CONFIG.IFRAME.validator, 
2642                 supercedes: DEFAULT_CONFIG.IFRAME.supercedes
2643
2644             });
2645
2646             /**
2647             * @config preventcontextoverlap
2648             * @description Boolean indicating whether or not the Overlay should overlap its 
2649             * context element (defined using the "context" configuration property) when the 
2650             * "constraintoviewport" configuration property is set to "true".
2651             * @type Boolean
2652             * @default false
2653             */
2654             cfg.addProperty(DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.key, {
2655                 value: DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.value, 
2656                 validator: DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.validator, 
2657                 supercedes: DEFAULT_CONFIG.PREVENT_CONTEXT_OVERLAP.supercedes
2658             });
2659         },
2660
2661         /**
2662         * Moves the Overlay to the specified position. This function is  
2663         * identical to calling this.cfg.setProperty("xy", [x,y]);
2664         * @method moveTo
2665         * @param {Number} x The Overlay's new x position
2666         * @param {Number} y The Overlay's new y position
2667         */
2668         moveTo: function (x, y) {
2669             this.cfg.setProperty("xy", [x, y]);
2670         },
2671
2672         /**
2673         * Adds a CSS class ("hide-scrollbars") and removes a CSS class 
2674         * ("show-scrollbars") to the Overlay to fix a bug in Gecko on Mac OS X 
2675         * (https://bugzilla.mozilla.org/show_bug.cgi?id=187435)
2676         * @method hideMacGeckoScrollbars
2677         */
2678         hideMacGeckoScrollbars: function () {
2679             Dom.replaceClass(this.element, "show-scrollbars", "hide-scrollbars");
2680         },
2681
2682         /**
2683         * Adds a CSS class ("show-scrollbars") and removes a CSS class 
2684         * ("hide-scrollbars") to the Overlay to fix a bug in Gecko on Mac OS X 
2685         * (https://bugzilla.mozilla.org/show_bug.cgi?id=187435)
2686         * @method showMacGeckoScrollbars
2687         */
2688         showMacGeckoScrollbars: function () {
2689             Dom.replaceClass(this.element, "hide-scrollbars", "show-scrollbars");
2690         },
2691
2692         /**
2693          * Internal implementation to set the visibility of the overlay in the DOM.
2694          *
2695          * @method _setDomVisibility
2696          * @param {boolean} visible Whether to show or hide the Overlay's outer element
2697          * @protected
2698          */
2699         _setDomVisibility : function(show) {
2700             Dom.setStyle(this.element, "visibility", (show) ? "visible" : "hidden");
2701             var hiddenClass = Overlay.CSS_HIDDEN;
2702
2703             if (show) {
2704                 Dom.removeClass(this.element, hiddenClass);
2705             } else {
2706                 Dom.addClass(this.element, hiddenClass);
2707             }
2708         },
2709
2710         // BEGIN BUILT-IN PROPERTY EVENT HANDLERS //
2711         /**
2712         * The default event handler fired when the "visible" property is 
2713         * changed.  This method is responsible for firing showEvent
2714         * and hideEvent.
2715         * @method configVisible
2716         * @param {String} type The CustomEvent type (usually the property name)
2717         * @param {Object[]} args The CustomEvent arguments. For configuration
2718         * handlers, args[0] will equal the newly applied value for the property.
2719         * @param {Object} obj The scope object. For configuration handlers, 
2720         * this will usually equal the owner.
2721         */
2722         configVisible: function (type, args, obj) {
2723
2724             var visible = args[0],
2725                 currentVis = Dom.getStyle(this.element, "visibility"),
2726                 effect = this.cfg.getProperty("effect"),
2727                 effectInstances = [],
2728                 isMacGecko = (this.platform == "mac" && UA.gecko),
2729                 alreadySubscribed = Config.alreadySubscribed,
2730                 eff, ei, e, i, j, k, h,
2731                 nEffects,
2732                 nEffectInstances;
2733
2734             if (currentVis == "inherit") {
2735                 e = this.element.parentNode;
2736
2737                 while (e.nodeType != 9 && e.nodeType != 11) {
2738                     currentVis = Dom.getStyle(e, "visibility");
2739
2740                     if (currentVis != "inherit") {
2741                         break;
2742                     }
2743
2744                     e = e.parentNode;
2745                 }
2746
2747                 if (currentVis == "inherit") {
2748                     currentVis = "visible";
2749                 }
2750             }
2751
2752             if (effect) {
2753                 if (effect instanceof Array) {
2754                     nEffects = effect.length;
2755
2756                     for (i = 0; i < nEffects; i++) {
2757                         eff = effect[i];
2758                         effectInstances[effectInstances.length] = 
2759                             eff.effect(this, eff.duration);
2760
2761                     }
2762                 } else {
2763                     effectInstances[effectInstances.length] = 
2764                         effect.effect(this, effect.duration);
2765                 }
2766             }
2767
2768             if (visible) { // Show
2769                 if (isMacGecko) {
2770                     this.showMacGeckoScrollbars();
2771                 }
2772
2773                 if (effect) { // Animate in
2774                     if (visible) { // Animate in if not showing
2775                         if (currentVis != "visible" || currentVis === "") {
2776                             this.beforeShowEvent.fire();
2777                             nEffectInstances = effectInstances.length;
2778
2779                             for (j = 0; j < nEffectInstances; j++) {
2780                                 ei = effectInstances[j];
2781                                 if (j === 0 && !alreadySubscribed(
2782                                         ei.animateInCompleteEvent, 
2783                                         this.showEvent.fire, this.showEvent)) {
2784
2785                                     /*
2786                                          Delegate showEvent until end 
2787                                          of animateInComplete
2788                                     */
2789
2790                                     ei.animateInCompleteEvent.subscribe(
2791                                      this.showEvent.fire, this.showEvent, true);
2792                                 }
2793                                 ei.animateIn();
2794                             }
2795                         }
2796                     }
2797                 } else { // Show
2798                     if (currentVis != "visible" || currentVis === "") {
2799                         this.beforeShowEvent.fire();
2800
2801                         this._setDomVisibility(true);
2802
2803                         this.cfg.refireEvent("iframe");
2804                         this.showEvent.fire();
2805                     } else {
2806                         this._setDomVisibility(true);
2807                     }
2808                 }
2809             } else { // Hide
2810
2811                 if (isMacGecko) {
2812                     this.hideMacGeckoScrollbars();
2813                 }
2814
2815                 if (effect) { // Animate out if showing
2816                     if (currentVis == "visible") {
2817                         this.beforeHideEvent.fire();
2818
2819                         nEffectInstances = effectInstances.length;
2820                         for (k = 0; k < nEffectInstances; k++) {
2821                             h = effectInstances[k];
2822     
2823                             if (k === 0 && !alreadySubscribed(
2824                                 h.animateOutCompleteEvent, this.hideEvent.fire, 
2825                                 this.hideEvent)) {
2826     
2827                                 /*
2828                                      Delegate hideEvent until end 
2829                                      of animateOutComplete
2830                                 */
2831     
2832                                 h.animateOutCompleteEvent.subscribe(
2833                                     this.hideEvent.fire, this.hideEvent, true);
2834     
2835                             }
2836                             h.animateOut();
2837                         }
2838
2839                     } else if (currentVis === "") {
2840                         this._setDomVisibility(false);
2841                     }
2842
2843                 } else { // Simple hide
2844
2845                     if (currentVis == "visible" || currentVis === "") {
2846                         this.beforeHideEvent.fire();
2847                         this._setDomVisibility(false);
2848                         this.hideEvent.fire();
2849                     } else {
2850                         this._setDomVisibility(false);
2851                     }
2852                 }
2853             }
2854         },
2855
2856         /**
2857         * Fixed center event handler used for centering on scroll/resize, but only if 
2858         * the overlay is visible and, if "fixedcenter" is set to "contained", only if 
2859         * the overlay fits within the viewport.
2860         *
2861         * @method doCenterOnDOMEvent
2862         */
2863         doCenterOnDOMEvent: function () {
2864             var cfg = this.cfg,
2865                 fc = cfg.getProperty("fixedcenter");
2866
2867             if (cfg.getProperty("visible")) {
2868                 if (fc && (fc !== _CONTAINED || this.fitsInViewport())) {
2869                     this.center();
2870                 }
2871             }
2872         },
2873
2874         /**
2875          * Determines if the Overlay (including the offset value defined by Overlay.VIEWPORT_OFFSET) 
2876          * will fit entirely inside the viewport, in both dimensions - width and height.
2877          * 
2878          * @method fitsInViewport
2879          * @return boolean true if the Overlay will fit, false if not
2880          */
2881         fitsInViewport : function() {
2882             var nViewportOffset = Overlay.VIEWPORT_OFFSET,
2883                 element = this.element,
2884                 elementWidth = element.offsetWidth,
2885                 elementHeight = element.offsetHeight,
2886                 viewportWidth = Dom.getViewportWidth(),
2887                 viewportHeight = Dom.getViewportHeight();
2888
2889             return ((elementWidth + nViewportOffset < viewportWidth) && (elementHeight + nViewportOffset < viewportHeight));
2890         },
2891
2892         /**
2893         * The default event handler fired when the "fixedcenter" property 
2894         * is changed.
2895         * @method configFixedCenter
2896         * @param {String} type The CustomEvent type (usually the property name)
2897         * @param {Object[]} args The CustomEvent arguments. For configuration 
2898         * handlers, args[0] will equal the newly applied value for the property.
2899         * @param {Object} obj The scope object. For configuration handlers, 
2900         * this will usually equal the owner.
2901         */
2902         configFixedCenter: function (type, args, obj) {
2903
2904             var val = args[0],
2905                 alreadySubscribed = Config.alreadySubscribed,
2906                 windowResizeEvent = Overlay.windowResizeEvent,
2907                 windowScrollEvent = Overlay.windowScrollEvent;
2908
2909             if (val) {
2910                 this.center();
2911
2912                 if (!alreadySubscribed(this.beforeShowEvent, this.center)) {
2913                     this.beforeShowEvent.subscribe(this.center);
2914                 }
2915
2916                 if (!alreadySubscribed(windowResizeEvent, this.doCenterOnDOMEvent, this)) {
2917                     windowResizeEvent.subscribe(this.doCenterOnDOMEvent, this, true);
2918                 }
2919
2920                 if (!alreadySubscribed(windowScrollEvent, this.doCenterOnDOMEvent, this)) {
2921                     windowScrollEvent.subscribe(this.doCenterOnDOMEvent, this, true);
2922                 }
2923
2924             } else {
2925                 this.beforeShowEvent.unsubscribe(this.center);
2926
2927                 windowResizeEvent.unsubscribe(this.doCenterOnDOMEvent, this);
2928                 windowScrollEvent.unsubscribe(this.doCenterOnDOMEvent, this);
2929             }
2930         },
2931
2932         /**
2933         * The default event handler fired when the "height" property is changed.
2934         * @method configHeight
2935         * @param {String} type The CustomEvent type (usually the property name)
2936         * @param {Object[]} args The CustomEvent arguments. For configuration 
2937         * handlers, args[0] will equal the newly applied value for the property.
2938         * @param {Object} obj The scope object. For configuration handlers, 
2939         * this will usually equal the owner.
2940         */
2941         configHeight: function (type, args, obj) {
2942
2943             var height = args[0],
2944                 el = this.element;
2945
2946             Dom.setStyle(el, "height", height);
2947             this.cfg.refireEvent("iframe");
2948         },
2949
2950         /**
2951          * The default event handler fired when the "autofillheight" property is changed.
2952          * @method configAutoFillHeight
2953          *
2954          * @param {String} type The CustomEvent type (usually the property name)
2955          * @param {Object[]} args The CustomEvent arguments. For configuration 
2956          * handlers, args[0] will equal the newly applied value for the property.
2957          * @param {Object} obj The scope object. For configuration handlers, 
2958          * this will usually equal the owner.
2959          */
2960         configAutoFillHeight: function (type, args, obj) {
2961             var fillEl = args[0],
2962                 cfg = this.cfg,
2963                 autoFillHeight = "autofillheight",
2964                 height = "height",
2965                 currEl = cfg.getProperty(autoFillHeight),
2966                 autoFill = this._autoFillOnHeightChange;
2967
2968             cfg.unsubscribeFromConfigEvent(height, autoFill);
2969             Module.textResizeEvent.unsubscribe(autoFill);
2970             this.changeContentEvent.unsubscribe(autoFill);
2971
2972             if (currEl && fillEl !== currEl && this[currEl]) {
2973                 Dom.setStyle(this[currEl], height, "");
2974             }
2975
2976             if (fillEl) {
2977                 fillEl = Lang.trim(fillEl.toLowerCase());
2978
2979                 cfg.subscribeToConfigEvent(height, autoFill, this[fillEl], this);
2980                 Module.textResizeEvent.subscribe(autoFill, this[fillEl], this);
2981                 this.changeContentEvent.subscribe(autoFill, this[fillEl], this);
2982
2983                 cfg.setProperty(autoFillHeight, fillEl, true);
2984             }
2985         },
2986
2987         /**
2988         * The default event handler fired when the "width" property is changed.
2989         * @method configWidth
2990         * @param {String} type The CustomEvent type (usually the property name)
2991         * @param {Object[]} args The CustomEvent arguments. For configuration 
2992         * handlers, args[0] will equal the newly applied value for the property.
2993         * @param {Object} obj The scope object. For configuration handlers, 
2994         * this will usually equal the owner.
2995         */
2996         configWidth: function (type, args, obj) {
2997
2998             var width = args[0],
2999                 el = this.element;
3000
3001             Dom.setStyle(el, "width", width);
3002             this.cfg.refireEvent("iframe");
3003         },
3004
3005         /**
3006         * The default event handler fired when the "zIndex" property is changed.
3007         * @method configzIndex
3008         * @param {String} type The CustomEvent type (usually the property name)
3009         * @param {Object[]} args The CustomEvent arguments. For configuration 
3010         * handlers, args[0] will equal the newly applied value for the property.
3011         * @param {Object} obj The scope object. For configuration handlers, 
3012         * this will usually equal the owner.
3013         */
3014         configzIndex: function (type, args, obj) {
3015
3016             var zIndex = args[0],
3017                 el = this.element;
3018
3019             if (! zIndex) {
3020                 zIndex = Dom.getStyle(el, "zIndex");
3021                 if (! zIndex || isNaN(zIndex)) {
3022                     zIndex = 0;
3023                 }
3024             }
3025
3026             if (this.iframe || this.cfg.getProperty("iframe") === true) {
3027                 if (zIndex <= 0) {
3028                     zIndex = 1;
3029                 }
3030             }
3031
3032             Dom.setStyle(el, "zIndex", zIndex);
3033             this.cfg.setProperty("zIndex", zIndex, true);
3034
3035             if (this.iframe) {
3036                 this.stackIframe();
3037             }
3038         },
3039
3040         /**
3041         * The default event handler fired when the "xy" property is changed.
3042         * @method configXY
3043         * @param {String} type The CustomEvent type (usually the property name)
3044         * @param {Object[]} args The CustomEvent arguments. For configuration 
3045         * handlers, args[0] will equal the newly applied value for the property.
3046         * @param {Object} obj The scope object. For configuration handlers, 
3047         * this will usually equal the owner.
3048         */
3049         configXY: function (type, args, obj) {
3050
3051             var pos = args[0],
3052                 x = pos[0],
3053                 y = pos[1];
3054
3055             this.cfg.setProperty("x", x);
3056             this.cfg.setProperty("y", y);
3057
3058             this.beforeMoveEvent.fire([x, y]);
3059
3060             x = this.cfg.getProperty("x");
3061             y = this.cfg.getProperty("y");
3062
3063
3064             this.cfg.refireEvent("iframe");
3065             this.moveEvent.fire([x, y]);
3066         },
3067
3068         /**
3069         * The default event handler fired when the "x" property is changed.
3070         * @method configX
3071         * @param {String} type The CustomEvent type (usually the property name)
3072         * @param {Object[]} args The CustomEvent arguments. For configuration 
3073         * handlers, args[0] will equal the newly applied value for the property.
3074         * @param {Object} obj The scope object. For configuration handlers, 
3075         * this will usually equal the owner.
3076         */
3077         configX: function (type, args, obj) {
3078
3079             var x = args[0],
3080                 y = this.cfg.getProperty("y");
3081
3082             this.cfg.setProperty("x", x, true);
3083             this.cfg.setProperty("y", y, true);
3084
3085             this.beforeMoveEvent.fire([x, y]);
3086
3087             x = this.cfg.getProperty("x");
3088             y = this.cfg.getProperty("y");
3089
3090             Dom.setX(this.element, x, true);
3091
3092             this.cfg.setProperty("xy", [x, y], true);
3093
3094             this.cfg.refireEvent("iframe");
3095             this.moveEvent.fire([x, y]);
3096         },
3097
3098         /**
3099         * The default event handler fired when the "y" property is changed.
3100         * @method configY
3101         * @param {String} type The CustomEvent type (usually the property name)
3102         * @param {Object[]} args The CustomEvent arguments. For configuration 
3103         * handlers, args[0] will equal the newly applied value for the property.
3104         * @param {Object} obj The scope object. For configuration handlers, 
3105         * this will usually equal the owner.
3106         */
3107         configY: function (type, args, obj) {
3108
3109             var x = this.cfg.getProperty("x"),
3110                 y = args[0];
3111
3112             this.cfg.setProperty("x", x, true);
3113             this.cfg.setProperty("y", y, true);
3114
3115             this.beforeMoveEvent.fire([x, y]);
3116
3117             x = this.cfg.getProperty("x");
3118             y = this.cfg.getProperty("y");
3119
3120             Dom.setY(this.element, y, true);
3121
3122             this.cfg.setProperty("xy", [x, y], true);
3123
3124             this.cfg.refireEvent("iframe");
3125             this.moveEvent.fire([x, y]);
3126         },
3127         
3128         /**
3129         * Shows the iframe shim, if it has been enabled.
3130         * @method showIframe
3131         */
3132         showIframe: function () {
3133
3134             var oIFrame = this.iframe,
3135                 oParentNode;
3136
3137             if (oIFrame) {
3138                 oParentNode = this.element.parentNode;
3139
3140                 if (oParentNode != oIFrame.parentNode) {
3141                     this._addToParent(oParentNode, oIFrame);
3142                 }
3143                 oIFrame.style.display = "block";
3144             }
3145         },
3146
3147         /**
3148         * Hides the iframe shim, if it has been enabled.
3149         * @method hideIframe
3150         */
3151         hideIframe: function () {
3152             if (this.iframe) {
3153                 this.iframe.style.display = "none";
3154             }
3155         },
3156
3157         /**
3158         * Syncronizes the size and position of iframe shim to that of its 
3159         * corresponding Overlay instance.
3160         * @method syncIframe
3161         */
3162         syncIframe: function () {
3163
3164             var oIFrame = this.iframe,
3165                 oElement = this.element,
3166                 nOffset = Overlay.IFRAME_OFFSET,
3167                 nDimensionOffset = (nOffset * 2),
3168                 aXY;
3169
3170             if (oIFrame) {
3171                 // Size <iframe>
3172                 oIFrame.style.width = (oElement.offsetWidth + nDimensionOffset + "px");
3173                 oIFrame.style.height = (oElement.offsetHeight + nDimensionOffset + "px");
3174
3175                 // Position <iframe>
3176                 aXY = this.cfg.getProperty("xy");
3177
3178                 if (!Lang.isArray(aXY) || (isNaN(aXY[0]) || isNaN(aXY[1]))) {
3179                     this.syncPosition();
3180                     aXY = this.cfg.getProperty("xy");
3181                 }
3182                 Dom.setXY(oIFrame, [(aXY[0] - nOffset), (aXY[1] - nOffset)]);
3183             }
3184         },
3185
3186         /**
3187          * Sets the zindex of the iframe shim, if it exists, based on the zindex of
3188          * the Overlay element. The zindex of the iframe is set to be one less 
3189          * than the Overlay element's zindex.
3190          * 
3191          * <p>NOTE: This method will not bump up the zindex of the Overlay element
3192          * to ensure that the iframe shim has a non-negative zindex.
3193          * If you require the iframe zindex to be 0 or higher, the zindex of 
3194          * the Overlay element should be set to a value greater than 0, before 
3195          * this method is called.
3196          * </p>
3197          * @method stackIframe
3198          */
3199         stackIframe: function () {
3200             if (this.iframe) {
3201                 var overlayZ = Dom.getStyle(this.element, "zIndex");
3202                 if (!YAHOO.lang.isUndefined(overlayZ) && !isNaN(overlayZ)) {
3203                     Dom.setStyle(this.iframe, "zIndex", (overlayZ - 1));
3204                 }
3205             }
3206         },
3207
3208         /**
3209         * The default event handler fired when the "iframe" property is changed.
3210         * @method configIframe
3211         * @param {String} type The CustomEvent type (usually the property name)
3212         * @param {Object[]} args The CustomEvent arguments. For configuration 
3213         * handlers, args[0] will equal the newly applied value for the property.
3214         * @param {Object} obj The scope object. For configuration handlers, 
3215         * this will usually equal the owner.
3216         */
3217         configIframe: function (type, args, obj) {
3218
3219             var bIFrame = args[0];
3220
3221             function createIFrame() {
3222
3223                 var oIFrame = this.iframe,
3224                     oElement = this.element,
3225                     oParent;
3226
3227                 if (!oIFrame) {
3228                     if (!m_oIFrameTemplate) {
3229                         m_oIFrameTemplate = document.createElement("iframe");
3230
3231                         if (this.isSecure) {
3232                             m_oIFrameTemplate.src = Overlay.IFRAME_SRC;
3233                         }
3234
3235                         /*
3236                             Set the opacity of the <iframe> to 0 so that it 
3237                             doesn't modify the opacity of any transparent 
3238                             elements that may be on top of it (like a shadow).
3239                         */
3240                         if (UA.ie) {
3241                             m_oIFrameTemplate.style.filter = "alpha(opacity=0)";
3242                             /*
3243                                  Need to set the "frameBorder" property to 0 
3244                                  supress the default <iframe> border in IE.  
3245                                  Setting the CSS "border" property alone 
3246                                  doesn't supress it.
3247                             */
3248                             m_oIFrameTemplate.frameBorder = 0;
3249                         }
3250                         else {
3251                             m_oIFrameTemplate.style.opacity = "0";
3252                         }
3253
3254                         m_oIFrameTemplate.style.position = "absolute";
3255                         m_oIFrameTemplate.style.border = "none";
3256                         m_oIFrameTemplate.style.margin = "0";
3257                         m_oIFrameTemplate.style.padding = "0";
3258                         m_oIFrameTemplate.style.display = "none";
3259                         m_oIFrameTemplate.tabIndex = -1;
3260                         m_oIFrameTemplate.className = Overlay.CSS_IFRAME;
3261                     }
3262
3263                     oIFrame = m_oIFrameTemplate.cloneNode(false);
3264                     oIFrame.id = this.id + "_f";
3265                     oParent = oElement.parentNode;
3266
3267                     var parentNode = oParent || document.body;
3268
3269                     this._addToParent(parentNode, oIFrame);
3270                     this.iframe = oIFrame;
3271                 }
3272
3273                 /*
3274                      Show the <iframe> before positioning it since the "setXY" 
3275                      method of DOM requires the element be in the document 
3276                      and visible.
3277                 */
3278                 this.showIframe();
3279
3280                 /*
3281                      Syncronize the size and position of the <iframe> to that 
3282                      of the Overlay.
3283                 */
3284                 this.syncIframe();
3285                 this.stackIframe();
3286
3287                 // Add event listeners to update the <iframe> when necessary
3288                 if (!this._hasIframeEventListeners) {
3289                     this.showEvent.subscribe(this.showIframe);
3290                     this.hideEvent.subscribe(this.hideIframe);
3291                     this.changeContentEvent.subscribe(this.syncIframe);
3292
3293                     this._hasIframeEventListeners = true;
3294                 }
3295             }
3296
3297             function onBeforeShow() {
3298                 createIFrame.call(this);
3299                 this.beforeShowEvent.unsubscribe(onBeforeShow);
3300                 this._iframeDeferred = false;
3301             }
3302
3303             if (bIFrame) { // <iframe> shim is enabled
3304
3305                 if (this.cfg.getProperty("visible")) {
3306                     createIFrame.call(this);
3307                 } else {
3308                     if (!this._iframeDeferred) {
3309                         this.beforeShowEvent.subscribe(onBeforeShow);
3310                         this._iframeDeferred = true;
3311                     }
3312                 }
3313
3314             } else {    // <iframe> shim is disabled
3315                 this.hideIframe();
3316
3317                 if (this._hasIframeEventListeners) {
3318                     this.showEvent.unsubscribe(this.showIframe);
3319                     this.hideEvent.unsubscribe(this.hideIframe);
3320                     this.changeContentEvent.unsubscribe(this.syncIframe);
3321
3322                     this._hasIframeEventListeners = false;
3323                 }
3324             }
3325         },
3326
3327         /**
3328          * Set's the container's XY value from DOM if not already set.
3329          * 
3330          * Differs from syncPosition, in that the XY value is only sync'd with DOM if 
3331          * not already set. The method also refire's the XY config property event, so any
3332          * beforeMove, Move event listeners are invoked.
3333          * 
3334          * @method _primeXYFromDOM
3335          * @protected
3336          */
3337         _primeXYFromDOM : function() {
3338             if (YAHOO.lang.isUndefined(this.cfg.getProperty("xy"))) {
3339                 // Set CFG XY based on DOM XY
3340                 this.syncPosition();
3341                 // Account for XY being set silently in syncPosition (no moveTo fired/called)
3342                 this.cfg.refireEvent("xy");
3343                 this.beforeShowEvent.unsubscribe(this._primeXYFromDOM);
3344             }
3345         },
3346
3347         /**
3348         * The default event handler fired when the "constraintoviewport" 
3349         * property is changed.
3350         * @method configConstrainToViewport
3351         * @param {String} type The CustomEvent type (usually the property name)
3352         * @param {Object[]} args The CustomEvent arguments. For configuration 
3353         * handlers, args[0] will equal the newly applied value for 
3354         * the property.
3355         * @param {Object} obj The scope object. For configuration handlers, 
3356         * this will usually equal the owner.
3357         */
3358         configConstrainToViewport: function (type, args, obj) {
3359             var val = args[0];
3360
3361             if (val) {
3362                 if (! Config.alreadySubscribed(this.beforeMoveEvent, this.enforceConstraints, this)) {
3363                     this.beforeMoveEvent.subscribe(this.enforceConstraints, this, true);
3364                 }
3365                 if (! Config.alreadySubscribed(this.beforeShowEvent, this._primeXYFromDOM)) {
3366                     this.beforeShowEvent.subscribe(this._primeXYFromDOM);
3367                 }
3368             } else {
3369                 this.beforeShowEvent.unsubscribe(this._primeXYFromDOM);
3370                 this.beforeMoveEvent.unsubscribe(this.enforceConstraints, this);
3371             }
3372         },
3373
3374          /**
3375         * The default event handler fired when the "context" property
3376         * is changed.
3377         *
3378         * @method configContext
3379         * @param {String} type The CustomEvent type (usually the property name)
3380         * @param {Object[]} args The CustomEvent arguments. For configuration 
3381         * handlers, args[0] will equal the newly applied value for the property.
3382         * @param {Object} obj The scope object. For configuration handlers, 
3383         * this will usually equal the owner.
3384         */
3385         configContext: function (type, args, obj) {
3386
3387             var contextArgs = args[0],
3388                 contextEl,
3389                 elementMagnetCorner,
3390                 contextMagnetCorner,
3391                 triggers,
3392                 offset,
3393                 defTriggers = this.CONTEXT_TRIGGERS;
3394
3395             if (contextArgs) {
3396
3397                 contextEl = contextArgs[0];
3398                 elementMagnetCorner = contextArgs[1];
3399                 contextMagnetCorner = contextArgs[2];
3400                 triggers = contextArgs[3];
3401                 offset = contextArgs[4];
3402
3403                 if (defTriggers && defTriggers.length > 0) {
3404                     triggers = (triggers || []).concat(defTriggers);
3405                 }
3406
3407                 if (contextEl) {
3408                     if (typeof contextEl == "string") {
3409                         this.cfg.setProperty("context", [
3410                                 document.getElementById(contextEl), 
3411                                 elementMagnetCorner,
3412                                 contextMagnetCorner,
3413                                 triggers,
3414                                 offset],
3415                                 true);
3416                     }
3417
3418                     if (elementMagnetCorner && contextMagnetCorner) {
3419                         this.align(elementMagnetCorner, contextMagnetCorner, offset);
3420                     }
3421
3422                     if (this._contextTriggers) {
3423                         // Unsubscribe Old Set
3424                         this._processTriggers(this._contextTriggers, _UNSUBSCRIBE, this._alignOnTrigger);
3425                     }
3426
3427                     if (triggers) {
3428                         // Subscribe New Set
3429                         this._processTriggers(triggers, _SUBSCRIBE, this._alignOnTrigger);
3430                         this._contextTriggers = triggers;
3431                     }
3432                 }
3433             }
3434         },
3435
3436         /**
3437          * Custom Event handler for context alignment triggers. Invokes the align method
3438          * 
3439          * @method _alignOnTrigger
3440          * @protected
3441          * 
3442          * @param {String} type The event type (not used by the default implementation)
3443          * @param {Any[]} args The array of arguments for the trigger event (not used by the default implementation)
3444          */
3445         _alignOnTrigger: function(type, args) {
3446             this.align();
3447         },
3448
3449         /**
3450          * Helper method to locate the custom event instance for the event name string
3451          * passed in. As a convenience measure, any custom events passed in are returned.
3452          *
3453          * @method _findTriggerCE
3454          * @private
3455          *
3456          * @param {String|CustomEvent} t Either a CustomEvent, or event type (e.g. "windowScroll") for which a 
3457          * custom event instance needs to be looked up from the Overlay._TRIGGER_MAP.
3458          */
3459         _findTriggerCE : function(t) {
3460             var tce = null;
3461             if (t instanceof CustomEvent) {
3462                 tce = t;
3463             } else if (Overlay._TRIGGER_MAP[t]) {
3464                 tce = Overlay._TRIGGER_MAP[t];
3465             }
3466             return tce;
3467         },
3468
3469         /**
3470          * Utility method that subscribes or unsubscribes the given 
3471          * function from the list of trigger events provided.
3472          *
3473          * @method _processTriggers
3474          * @protected 
3475          *
3476          * @param {Array[String|CustomEvent]} triggers An array of either CustomEvents, event type strings 
3477          * (e.g. "beforeShow", "windowScroll") to/from which the provided function should be 
3478          * subscribed/unsubscribed respectively.
3479          *
3480          * @param {String} mode Either "subscribe" or "unsubscribe", specifying whether or not
3481          * we are subscribing or unsubscribing trigger listeners
3482          * 
3483          * @param {Function} fn The function to be subscribed/unsubscribed to/from the trigger event.
3484          * Context is always set to the overlay instance, and no additional object argument 
3485          * get passed to the subscribed function.
3486          */
3487         _processTriggers : function(triggers, mode, fn) {
3488             var t, tce;
3489
3490             for (var i = 0, l = triggers.length; i < l; ++i) {
3491                 t = triggers[i];
3492                 tce = this._findTriggerCE(t);
3493                 if (tce) {
3494                     tce[mode](fn, this, true);
3495                 } else {
3496                     this[mode](t, fn);
3497                 }
3498             }
3499         },
3500
3501         // END BUILT-IN PROPERTY EVENT HANDLERS //
3502         /**
3503         * Aligns the Overlay to its context element using the specified corner 
3504         * points (represented by the constants TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, 
3505         * and BOTTOM_RIGHT.
3506         * @method align
3507         * @param {String} elementAlign  The String representing the corner of 
3508         * the Overlay that should be aligned to the context element
3509         * @param {String} contextAlign  The corner of the context element 
3510         * that the elementAlign corner should stick to.
3511         * @param {Number[]} xyOffset Optional. A 2 element array specifying the x and y pixel offsets which should be applied
3512         * after aligning the element and context corners. For example, passing in [5, -10] for this value, would offset the 
3513         * Overlay by 5 pixels along the X axis (horizontally) and -10 pixels along the Y axis (vertically) after aligning the specified corners.
3514         */
3515         align: function (elementAlign, contextAlign, xyOffset) {
3516
3517             var contextArgs = this.cfg.getProperty("context"),
3518                 me = this,
3519                 context,
3520                 element,
3521                 contextRegion;
3522
3523             function doAlign(v, h) {
3524
3525                 var alignX = null, alignY = null;
3526
3527                 switch (elementAlign) {
3528     
3529                     case Overlay.TOP_LEFT:
3530                         alignX = h;
3531                         alignY = v;
3532                         break;
3533         
3534                     case Overlay.TOP_RIGHT:
3535                         alignX = h - element.offsetWidth;
3536                         alignY = v;
3537                         break;
3538         
3539                     case Overlay.BOTTOM_LEFT:
3540                         alignX = h;
3541                         alignY = v - element.offsetHeight;
3542                         break;
3543         
3544                     case Overlay.BOTTOM_RIGHT:
3545                         alignX = h - element.offsetWidth; 
3546                         alignY = v - element.offsetHeight;
3547                         break;
3548                 }
3549
3550                 if (alignX !== null && alignY !== null) {
3551                     if (xyOffset) {
3552                         alignX += xyOffset[0];
3553                         alignY += xyOffset[1];
3554                     }
3555                     me.moveTo(alignX, alignY);
3556                 }
3557             }
3558
3559             if (contextArgs) {
3560                 context = contextArgs[0];
3561                 element = this.element;
3562                 me = this;
3563
3564                 if (! elementAlign) {
3565                     elementAlign = contextArgs[1];
3566                 }
3567
3568                 if (! contextAlign) {
3569                     contextAlign = contextArgs[2];
3570                 }
3571
3572                 if (!xyOffset && contextArgs[4]) {
3573                     xyOffset = contextArgs[4];
3574                 }
3575
3576                 if (element && context) {
3577                     contextRegion = Dom.getRegion(context);
3578
3579                     switch (contextAlign) {
3580     
3581                         case Overlay.TOP_LEFT:
3582                             doAlign(contextRegion.top, contextRegion.left);
3583                             break;
3584         
3585                         case Overlay.TOP_RIGHT:
3586                             doAlign(contextRegion.top, contextRegion.right);
3587                             break;
3588         
3589                         case Overlay.BOTTOM_LEFT:
3590                             doAlign(contextRegion.bottom, contextRegion.left);
3591                             break;
3592         
3593                         case Overlay.BOTTOM_RIGHT:
3594                             doAlign(contextRegion.bottom, contextRegion.right);
3595                             break;
3596                     }
3597                 }
3598             }
3599         },
3600
3601         /**
3602         * The default event handler executed when the moveEvent is fired, if the 
3603         * "constraintoviewport" is set to true.
3604         * @method enforceConstraints
3605         * @param {String} type The CustomEvent type (usually the property name)
3606         * @param {Object[]} args The CustomEvent arguments. For configuration 
3607         * handlers, args[0] will equal the newly applied value for the property.
3608         * @param {Object} obj The scope object. For configuration handlers, 
3609         * this will usually equal the owner.
3610         */
3611         enforceConstraints: function (type, args, obj) {
3612             var pos = args[0];
3613
3614             var cXY = this.getConstrainedXY(pos[0], pos[1]);
3615             this.cfg.setProperty("x", cXY[0], true);
3616             this.cfg.setProperty("y", cXY[1], true);
3617             this.cfg.setProperty("xy", cXY, true);
3618         },
3619
3620         /**
3621          * Shared implementation method for getConstrainedX and getConstrainedY.
3622          * 
3623          * <p>
3624          * Given a coordinate value, returns the calculated coordinate required to 
3625          * position the Overlay if it is to be constrained to the viewport, based on the 
3626          * current element size, viewport dimensions, scroll values and preventoverlap 
3627          * settings
3628          * </p>
3629          *
3630          * @method _getConstrainedPos
3631          * @protected
3632          * @param {String} pos The coordinate which needs to be constrained, either "x" or "y"
3633          * @param {Number} The coordinate value which needs to be constrained
3634          * @return {Number} The constrained coordinate value
3635          */
3636         _getConstrainedPos: function(pos, val) {
3637
3638             var overlayEl = this.element,
3639
3640                 buffer = Overlay.VIEWPORT_OFFSET,
3641
3642                 x = (pos == "x"),
3643
3644                 overlaySize      = (x) ? overlayEl.offsetWidth : overlayEl.offsetHeight,
3645                 viewportSize     = (x) ? Dom.getViewportWidth() : Dom.getViewportHeight(),
3646                 docScroll        = (x) ? Dom.getDocumentScrollLeft() : Dom.getDocumentScrollTop(),
3647                 overlapPositions = (x) ? Overlay.PREVENT_OVERLAP_X : Overlay.PREVENT_OVERLAP_Y,
3648
3649                 context = this.cfg.getProperty("context"),
3650
3651                 bOverlayFitsInViewport = (overlaySize + buffer < viewportSize),
3652                 bPreventContextOverlap = this.cfg.getProperty("preventcontextoverlap") && context && overlapPositions[(context[1] + context[2])],
3653
3654                 minConstraint = docScroll + buffer,
3655                 maxConstraint = docScroll + viewportSize - overlaySize - buffer,
3656
3657                 constrainedVal = val;
3658
3659             if (val < minConstraint || val > maxConstraint) {
3660                 if (bPreventContextOverlap) {
3661                     constrainedVal = this._preventOverlap(pos, context[0], overlaySize, viewportSize, docScroll);
3662                 } else {
3663                     if (bOverlayFitsInViewport) {
3664                         if (val < minConstraint) {
3665                             constrainedVal = minConstraint;
3666                         } else if (val > maxConstraint) {
3667                             constrainedVal = maxConstraint;
3668                         }
3669                     } else {
3670                         constrainedVal = minConstraint;
3671                     }
3672                 }
3673             }
3674
3675             return constrainedVal;
3676         },
3677
3678         /**
3679          * Helper method, used to position the Overlap to prevent overlap with the 
3680          * context element (used when preventcontextoverlap is enabled)
3681          *
3682          * @method _preventOverlap
3683          * @protected
3684          * @param {String} pos The coordinate to prevent overlap for, either "x" or "y".
3685          * @param {HTMLElement} contextEl The context element
3686          * @param {Number} overlaySize The related overlay dimension value (for "x", the width, for "y", the height)
3687          * @param {Number} viewportSize The related viewport dimension value (for "x", the width, for "y", the height)
3688          * @param {Object} docScroll  The related document scroll value (for "x", the scrollLeft, for "y", the scrollTop)
3689          *
3690          * @return {Number} The new coordinate value which was set to prevent overlap
3691          */
3692         _preventOverlap : function(pos, contextEl, overlaySize, viewportSize, docScroll) {
3693             
3694             var x = (pos == "x"),
3695
3696                 buffer = Overlay.VIEWPORT_OFFSET,
3697
3698                 overlay = this,
3699
3700                 contextElPos   = ((x) ? Dom.getX(contextEl) : Dom.getY(contextEl)) - docScroll,
3701                 contextElSize  = (x) ? contextEl.offsetWidth : contextEl.offsetHeight,
3702
3703                 minRegionSize = contextElPos - buffer,
3704                 maxRegionSize = (viewportSize - (contextElPos + contextElSize)) - buffer,
3705
3706                 bFlipped = false,
3707
3708                 flip = function () {
3709                     var flippedVal;
3710
3711                     if ((overlay.cfg.getProperty(pos) - docScroll) > contextElPos) {
3712                         flippedVal = (contextElPos - overlaySize);
3713                     } else {
3714                         flippedVal = (contextElPos + contextElSize);
3715                     }
3716
3717                     overlay.cfg.setProperty(pos, (flippedVal + docScroll), true);
3718
3719                     return flippedVal;
3720                 },
3721
3722                 setPosition = function () {
3723
3724                     var displayRegionSize = ((overlay.cfg.getProperty(pos) - docScroll) > contextElPos) ? maxRegionSize : minRegionSize,
3725                         position;
3726
3727                     if (overlaySize > displayRegionSize) {
3728                         if (bFlipped) {
3729                             /*
3730                                  All possible positions and values have been 
3731                                  tried, but none were successful, so fall back 
3732                                  to the original size and position.
3733                             */
3734                             flip();
3735                         } else {
3736                             flip();
3737                             bFlipped = true;
3738                             position = setPosition();
3739                         }
3740                     }
3741
3742                     return position;
3743                 };
3744
3745             setPosition();
3746
3747             return this.cfg.getProperty(pos);
3748         },
3749
3750         /**
3751          * Given x coordinate value, returns the calculated x coordinate required to 
3752          * position the Overlay if it is to be constrained to the viewport, based on the 
3753          * current element size, viewport dimensions and scroll values.
3754          *
3755          * @param {Number} x The X coordinate value to be constrained
3756          * @return {Number} The constrained x coordinate
3757          */             
3758         getConstrainedX: function (x) {
3759             return this._getConstrainedPos("x", x);
3760         },
3761
3762         /**
3763          * Given y coordinate value, returns the calculated y coordinate required to 
3764          * position the Overlay if it is to be constrained to the viewport, based on the 
3765          * current element size, viewport dimensions and scroll values.
3766          *
3767          * @param {Number} y The Y coordinate value to be constrained
3768          * @return {Number} The constrained y coordinate
3769          */             
3770         getConstrainedY : function (y) {
3771             return this._getConstrainedPos("y", y);
3772         },
3773
3774         /**
3775          * Given x, y coordinate values, returns the calculated coordinates required to 
3776          * position the Overlay if it is to be constrained to the viewport, based on the 
3777          * current element size, viewport dimensions and scroll values.
3778          *
3779          * @param {Number} x The X coordinate value to be constrained
3780          * @param {Number} y The Y coordinate value to be constrained
3781          * @return {Array} The constrained x and y coordinates at index 0 and 1 respectively;
3782          */
3783         getConstrainedXY: function(x, y) {
3784             return [this.getConstrainedX(x), this.getConstrainedY(y)];
3785         },
3786
3787         /**
3788         * Centers the container in the viewport.
3789         * @method center
3790         */
3791         center: function () {
3792
3793             var nViewportOffset = Overlay.VIEWPORT_OFFSET,
3794                 elementWidth = this.element.offsetWidth,
3795                 elementHeight = this.element.offsetHeight,
3796                 viewPortWidth = Dom.getViewportWidth(),
3797                 viewPortHeight = Dom.getViewportHeight(),
3798                 x,
3799                 y;
3800
3801             if (elementWidth < viewPortWidth) {
3802                 x = (viewPortWidth / 2) - (elementWidth / 2) + Dom.getDocumentScrollLeft();
3803             } else {
3804                 x = nViewportOffset + Dom.getDocumentScrollLeft();
3805             }
3806
3807             if (elementHeight < viewPortHeight) {
3808                 y = (viewPortHeight / 2) - (elementHeight / 2) + Dom.getDocumentScrollTop();
3809             } else {
3810                 y = nViewportOffset + Dom.getDocumentScrollTop();
3811             }
3812
3813             this.cfg.setProperty("xy", [parseInt(x, 10), parseInt(y, 10)]);
3814             this.cfg.refireEvent("iframe");
3815
3816             if (UA.webkit) {
3817                 this.forceContainerRedraw();
3818             }
3819         },
3820
3821         /**
3822         * Synchronizes the Panel's "xy", "x", and "y" properties with the 
3823         * Panel's position in the DOM. This is primarily used to update  
3824         * position information during drag & drop.
3825         * @method syncPosition
3826         */
3827         syncPosition: function () {
3828
3829             var pos = Dom.getXY(this.element);
3830
3831             this.cfg.setProperty("x", pos[0], true);
3832             this.cfg.setProperty("y", pos[1], true);
3833             this.cfg.setProperty("xy", pos, true);
3834
3835         },
3836
3837         /**
3838         * Event handler fired when the resize monitor element is resized.
3839         * @method onDomResize
3840         * @param {DOMEvent} e The resize DOM event
3841         * @param {Object} obj The scope object
3842         */
3843         onDomResize: function (e, obj) {
3844
3845             var me = this;
3846
3847             Overlay.superclass.onDomResize.call(this, e, obj);
3848
3849             setTimeout(function () {
3850                 me.syncPosition();
3851                 me.cfg.refireEvent("iframe");
3852                 me.cfg.refireEvent("context");
3853             }, 0);
3854         },
3855
3856         /**
3857          * Determines the content box height of the given element (height of the element, without padding or borders) in pixels.
3858          *
3859          * @method _getComputedHeight
3860          * @private
3861          * @param {HTMLElement} el The element for which the content height needs to be determined
3862          * @return {Number} The content box height of the given element, or null if it could not be determined.
3863          */
3864         _getComputedHeight : (function() {
3865
3866             if (document.defaultView && document.defaultView.getComputedStyle) {
3867                 return function(el) {
3868                     var height = null;
3869                     if (el.ownerDocument && el.ownerDocument.defaultView) {
3870                         var computed = el.ownerDocument.defaultView.getComputedStyle(el, '');
3871                         if (computed) {
3872                             height = parseInt(computed.height, 10);
3873                         }
3874                     }
3875                     return (Lang.isNumber(height)) ? height : null;
3876                 };
3877             } else {
3878                 return function(el) {
3879                     var height = null;
3880                     if (el.style.pixelHeight) {
3881                         height = el.style.pixelHeight;
3882                     }
3883                     return (Lang.isNumber(height)) ? height : null;
3884                 };
3885             }
3886         })(),
3887
3888         /**
3889          * autofillheight validator. Verifies that the autofill value is either null 
3890          * or one of the strings : "body", "header" or "footer".
3891          *
3892          * @method _validateAutoFillHeight
3893          * @protected
3894          * @param {String} val
3895          * @return true, if valid, false otherwise
3896          */
3897         _validateAutoFillHeight : function(val) {
3898             return (!val) || (Lang.isString(val) && Overlay.STD_MOD_RE.test(val));
3899         },
3900
3901         /**
3902          * The default custom event handler executed when the overlay's height is changed, 
3903          * if the autofillheight property has been set.
3904          *
3905          * @method _autoFillOnHeightChange
3906          * @protected
3907          * @param {String} type The event type
3908          * @param {Array} args The array of arguments passed to event subscribers
3909          * @param {HTMLElement} el The header, body or footer element which is to be resized to fill
3910          * out the containers height
3911          */
3912         _autoFillOnHeightChange : function(type, args, el) {
3913             var height = this.cfg.getProperty("height");
3914             if ((height && height !== "auto") || (height === 0)) {
3915                 this.fillHeight(el);
3916             }
3917         },
3918
3919         /**
3920          * Returns the sub-pixel height of the el, using getBoundingClientRect, if available,
3921          * otherwise returns the offsetHeight
3922          * @method _getPreciseHeight
3923          * @private
3924          * @param {HTMLElement} el
3925          * @return {Float} The sub-pixel height if supported by the browser, else the rounded height.
3926          */
3927         _getPreciseHeight : function(el) {
3928             var height = el.offsetHeight;
3929
3930             if (el.getBoundingClientRect) {
3931                 var rect = el.getBoundingClientRect();
3932                 height = rect.bottom - rect.top;
3933             }
3934
3935             return height;
3936         },
3937
3938         /**
3939          * <p>
3940          * Sets the height on the provided header, body or footer element to 
3941          * fill out the height of the container. It determines the height of the 
3942          * containers content box, based on it's configured height value, and 
3943          * sets the height of the autofillheight element to fill out any 
3944          * space remaining after the other standard module element heights 
3945          * have been accounted for.
3946          * </p>
3947          * <p><strong>NOTE:</strong> This method is not designed to work if an explicit 
3948          * height has not been set on the container, since for an "auto" height container, 
3949          * the heights of the header/body/footer will drive the height of the container.</p>
3950          *
3951          * @method fillHeight
3952          * @param {HTMLElement} el The element which should be resized to fill out the height
3953          * of the container element.
3954          */
3955         fillHeight : function(el) {
3956             if (el) {
3957                 var container = this.innerElement || this.element,
3958                     containerEls = [this.header, this.body, this.footer],
3959                     containerEl,
3960                     total = 0,
3961                     filled = 0,
3962                     remaining = 0,
3963                     validEl = false;
3964
3965                 for (var i = 0, l = containerEls.length; i < l; i++) {
3966                     containerEl = containerEls[i];
3967                     if (containerEl) {
3968                         if (el !== containerEl) {
3969                             filled += this._getPreciseHeight(containerEl);
3970                         } else {
3971                             validEl = true;
3972                         }
3973                     }
3974                 }
3975
3976                 if (validEl) {
3977
3978                     if (UA.ie || UA.opera) {
3979                         // Need to set height to 0, to allow height to be reduced
3980                         Dom.setStyle(el, 'height', 0 + 'px');
3981                     }
3982
3983                     total = this._getComputedHeight(container);
3984
3985                     // Fallback, if we can't get computed value for content height
3986                     if (total === null) {
3987                         Dom.addClass(container, "yui-override-padding");
3988                         total = container.clientHeight; // Content, No Border, 0 Padding (set by yui-override-padding)
3989                         Dom.removeClass(container, "yui-override-padding");
3990                     }
3991     
3992                     remaining = Math.max(total - filled, 0);
3993     
3994                     Dom.setStyle(el, "height", remaining + "px");
3995     
3996                     // Re-adjust height if required, to account for el padding and border
3997                     if (el.offsetHeight != remaining) {
3998                         remaining = Math.max(remaining - (el.offsetHeight - remaining), 0);
3999                     }
4000                     Dom.setStyle(el, "height", remaining + "px");
4001                 }
4002             }
4003         },
4004
4005         /**
4006         * Places the Overlay on top of all other instances of 
4007         * YAHOO.widget.Overlay.
4008         * @method bringToTop
4009         */
4010         bringToTop: function () {
4011
4012             var aOverlays = [],
4013                 oElement = this.element;
4014
4015             function compareZIndexDesc(p_oOverlay1, p_oOverlay2) {
4016
4017                 var sZIndex1 = Dom.getStyle(p_oOverlay1, "zIndex"),
4018                     sZIndex2 = Dom.getStyle(p_oOverlay2, "zIndex"),
4019
4020                     nZIndex1 = (!sZIndex1 || isNaN(sZIndex1)) ? 0 : parseInt(sZIndex1, 10),
4021                     nZIndex2 = (!sZIndex2 || isNaN(sZIndex2)) ? 0 : parseInt(sZIndex2, 10);
4022
4023                 if (nZIndex1 > nZIndex2) {
4024                     return -1;
4025                 } else if (nZIndex1 < nZIndex2) {
4026                     return 1;
4027                 } else {
4028                     return 0;
4029                 }
4030             }
4031
4032             function isOverlayElement(p_oElement) {
4033
4034                 var isOverlay = Dom.hasClass(p_oElement, Overlay.CSS_OVERLAY),
4035                     Panel = YAHOO.widget.Panel;
4036
4037                 if (isOverlay && !Dom.isAncestor(oElement, p_oElement)) {
4038                     if (Panel && Dom.hasClass(p_oElement, Panel.CSS_PANEL)) {
4039                         aOverlays[aOverlays.length] = p_oElement.parentNode;
4040                     } else {
4041                         aOverlays[aOverlays.length] = p_oElement;
4042                     }
4043                 }
4044             }
4045
4046             Dom.getElementsBy(isOverlayElement, "DIV", document.body);
4047
4048             aOverlays.sort(compareZIndexDesc);
4049
4050             var oTopOverlay = aOverlays[0],
4051                 nTopZIndex;
4052
4053             if (oTopOverlay) {
4054                 nTopZIndex = Dom.getStyle(oTopOverlay, "zIndex");
4055
4056                 if (!isNaN(nTopZIndex)) {
4057                     var bRequiresBump = false;
4058
4059                     if (oTopOverlay != oElement) {
4060                         bRequiresBump = true;
4061                     } else if (aOverlays.length > 1) {
4062                         var nNextZIndex = Dom.getStyle(aOverlays[1], "zIndex");
4063                         // Don't rely on DOM order to stack if 2 overlays are at the same zindex.
4064                         if (!isNaN(nNextZIndex) && (nTopZIndex == nNextZIndex)) {
4065                             bRequiresBump = true;
4066                         }
4067                     }
4068                     if (bRequiresBump) {
4069                         this.cfg.setProperty("zindex", (parseInt(nTopZIndex, 10) + 2));
4070                     }
4071                 }
4072             }
4073         },
4074
4075         /**
4076         * Removes the Overlay element from the DOM and sets all child 
4077         * elements to null.
4078         * @method destroy
4079         */
4080         destroy: function () {
4081
4082             if (this.iframe) {
4083                 this.iframe.parentNode.removeChild(this.iframe);
4084             }
4085
4086             this.iframe = null;
4087
4088             Overlay.windowResizeEvent.unsubscribe(
4089                 this.doCenterOnDOMEvent, this);
4090     
4091             Overlay.windowScrollEvent.unsubscribe(
4092                 this.doCenterOnDOMEvent, this);
4093
4094             Module.textResizeEvent.unsubscribe(this._autoFillOnHeightChange);
4095
4096             if (this._contextTriggers) {
4097                 // Unsubscribe context triggers - to cover context triggers which listen for global
4098                 // events such as windowResize and windowScroll. Easier just to unsubscribe all
4099                 this._processTriggers(this._contextTriggers, _UNSUBSCRIBE, this._alignOnTrigger);
4100             }
4101
4102             Overlay.superclass.destroy.call(this);
4103         },
4104
4105         /**
4106          * Can be used to force the container to repaint/redraw it's contents.
4107          * <p>
4108          * By default applies and then removes a 1px bottom margin through the 
4109          * application/removal of a "yui-force-redraw" class.
4110          * </p>
4111          * <p>
4112          * It is currently used by Overlay to force a repaint for webkit 
4113          * browsers, when centering.
4114          * </p>
4115          * @method forceContainerRedraw
4116          */
4117         forceContainerRedraw : function() {
4118             var c = this;
4119             Dom.addClass(c.element, "yui-force-redraw");
4120             setTimeout(function() {
4121                 Dom.removeClass(c.element, "yui-force-redraw");
4122             }, 0);
4123         },
4124
4125         /**
4126         * Returns a String representation of the object.
4127         * @method toString
4128         * @return {String} The string representation of the Overlay.
4129         */
4130         toString: function () {
4131             return "Overlay " + this.id;
4132         }
4133
4134     });
4135 }());
4136 (function () {
4137
4138     /**
4139     * OverlayManager is used for maintaining the focus status of 
4140     * multiple Overlays.
4141     * @namespace YAHOO.widget
4142     * @namespace YAHOO.widget
4143     * @class OverlayManager
4144     * @constructor
4145     * @param {Array} overlays Optional. A collection of Overlays to register 
4146     * with the manager.
4147     * @param {Object} userConfig  The object literal representing the user 
4148     * configuration of the OverlayManager
4149     */
4150     YAHOO.widget.OverlayManager = function (userConfig) {
4151         this.init(userConfig);
4152     };
4153
4154     var Overlay = YAHOO.widget.Overlay,
4155         Event = YAHOO.util.Event,
4156         Dom = YAHOO.util.Dom,
4157         Config = YAHOO.util.Config,
4158         CustomEvent = YAHOO.util.CustomEvent,
4159         OverlayManager = YAHOO.widget.OverlayManager;
4160
4161     /**
4162     * The CSS class representing a focused Overlay
4163     * @property OverlayManager.CSS_FOCUSED
4164     * @static
4165     * @final
4166     * @type String
4167     */
4168     OverlayManager.CSS_FOCUSED = "focused";
4169
4170     OverlayManager.prototype = {
4171
4172         /**
4173         * The class's constructor function
4174         * @property contructor
4175         * @type Function
4176         */
4177         constructor: OverlayManager,
4178
4179         /**
4180         * The array of Overlays that are currently registered
4181         * @property overlays
4182         * @type YAHOO.widget.Overlay[]
4183         */
4184         overlays: null,
4185
4186         /**
4187         * Initializes the default configuration of the OverlayManager
4188         * @method initDefaultConfig
4189         */
4190         initDefaultConfig: function () {
4191             /**
4192             * The collection of registered Overlays in use by 
4193             * the OverlayManager
4194             * @config overlays
4195             * @type YAHOO.widget.Overlay[]
4196             * @default null
4197             */
4198             this.cfg.addProperty("overlays", { suppressEvent: true } );
4199
4200             /**
4201             * The default DOM event that should be used to focus an Overlay
4202             * @config focusevent
4203             * @type String
4204             * @default "mousedown"
4205             */
4206             this.cfg.addProperty("focusevent", { value: "mousedown" } );
4207         },
4208
4209         /**
4210         * Initializes the OverlayManager
4211         * @method init
4212         * @param {Overlay[]} overlays Optional. A collection of Overlays to 
4213         * register with the manager.
4214         * @param {Object} userConfig  The object literal representing the user 
4215         * configuration of the OverlayManager
4216         */
4217         init: function (userConfig) {
4218
4219             /**
4220             * The OverlayManager's Config object used for monitoring 
4221             * configuration properties.
4222             * @property cfg
4223             * @type Config
4224             */
4225             this.cfg = new Config(this);
4226
4227             this.initDefaultConfig();
4228
4229             if (userConfig) {
4230                 this.cfg.applyConfig(userConfig, true);
4231             }
4232             this.cfg.fireQueue();
4233
4234             /**
4235             * The currently activated Overlay
4236             * @property activeOverlay
4237             * @private
4238             * @type YAHOO.widget.Overlay
4239             */
4240             var activeOverlay = null;
4241
4242             /**
4243             * Returns the currently focused Overlay
4244             * @method getActive
4245             * @return {Overlay} The currently focused Overlay
4246             */
4247             this.getActive = function () {
4248                 return activeOverlay;
4249             };
4250
4251             /**
4252             * Focuses the specified Overlay
4253             * @method focus
4254             * @param {Overlay} overlay The Overlay to focus
4255             * @param {String} overlay The id of the Overlay to focus
4256             */
4257             this.focus = function (overlay) {
4258                 var o = this.find(overlay);
4259                 if (o) {
4260                     o.focus();
4261                 }
4262             };
4263
4264             /**
4265             * Removes the specified Overlay from the manager
4266             * @method remove
4267             * @param {Overlay} overlay The Overlay to remove
4268             * @param {String} overlay The id of the Overlay to remove
4269             */
4270             this.remove = function (overlay) {
4271
4272                 var o = this.find(overlay), 
4273                         originalZ;
4274
4275                 if (o) {
4276                     if (activeOverlay == o) {
4277                         activeOverlay = null;
4278                     }
4279
4280                     var bDestroyed = (o.element === null && o.cfg === null) ? true : false;
4281
4282                     if (!bDestroyed) {
4283                         // Set it's zindex so that it's sorted to the end.
4284                         originalZ = Dom.getStyle(o.element, "zIndex");
4285                         o.cfg.setProperty("zIndex", -1000, true);
4286                     }
4287
4288                     this.overlays.sort(this.compareZIndexDesc);
4289                     this.overlays = this.overlays.slice(0, (this.overlays.length - 1));
4290
4291                     o.hideEvent.unsubscribe(o.blur);
4292                     o.destroyEvent.unsubscribe(this._onOverlayDestroy, o);
4293                     o.focusEvent.unsubscribe(this._onOverlayFocusHandler, o);
4294                     o.blurEvent.unsubscribe(this._onOverlayBlurHandler, o);
4295
4296                     if (!bDestroyed) {
4297                         Event.removeListener(o.element, this.cfg.getProperty("focusevent"), this._onOverlayElementFocus);
4298                         o.cfg.setProperty("zIndex", originalZ, true);
4299                         o.cfg.setProperty("manager", null);
4300                     }
4301
4302                     /* _managed Flag for custom or existing. Don't want to remove existing */
4303                     if (o.focusEvent._managed) { o.focusEvent = null; }
4304                     if (o.blurEvent._managed) { o.blurEvent = null; }
4305
4306                     if (o.focus._managed) { o.focus = null; }
4307                     if (o.blur._managed) { o.blur = null; }
4308                 }
4309             };
4310
4311             /**
4312             * Removes focus from all registered Overlays in the manager
4313             * @method blurAll
4314             */
4315             this.blurAll = function () {
4316
4317                 var nOverlays = this.overlays.length,
4318                     i;
4319
4320                 if (nOverlays > 0) {
4321                     i = nOverlays - 1;
4322                     do {
4323                         this.overlays[i].blur();
4324                     }
4325                     while(i--);
4326                 }
4327             };
4328
4329             /**
4330              * Updates the state of the OverlayManager and overlay, as a result of the overlay
4331              * being blurred.
4332              * 
4333              * @method _manageBlur
4334              * @param {Overlay} overlay The overlay instance which got blurred.
4335              * @protected
4336              */
4337             this._manageBlur = function (overlay) {
4338                 var changed = false;
4339                 if (activeOverlay == overlay) {
4340                     Dom.removeClass(activeOverlay.element, OverlayManager.CSS_FOCUSED);
4341                     activeOverlay = null;
4342                     changed = true;
4343                 }
4344                 return changed;
4345             };
4346
4347             /**
4348              * Updates the state of the OverlayManager and overlay, as a result of the overlay 
4349              * receiving focus.
4350              *
4351              * @method _manageFocus
4352              * @param {Overlay} overlay The overlay instance which got focus.
4353              * @protected
4354              */
4355             this._manageFocus = function(overlay) {
4356                 var changed = false;
4357                 if (activeOverlay != overlay) {
4358                     if (activeOverlay) {
4359                         activeOverlay.blur();
4360                     }
4361                     activeOverlay = overlay;
4362                     this.bringToTop(activeOverlay);
4363                     Dom.addClass(activeOverlay.element, OverlayManager.CSS_FOCUSED);
4364                     changed = true;
4365                 }
4366                 return changed;
4367             };
4368
4369             var overlays = this.cfg.getProperty("overlays");
4370
4371             if (! this.overlays) {
4372                 this.overlays = [];
4373             }
4374
4375             if (overlays) {
4376                 this.register(overlays);
4377                 this.overlays.sort(this.compareZIndexDesc);
4378             }
4379         },
4380
4381         /**
4382         * @method _onOverlayElementFocus
4383         * @description Event handler for the DOM event that is used to focus 
4384         * the Overlay instance as specified by the "focusevent" 
4385         * configuration property.
4386         * @private
4387         * @param {Event} p_oEvent Object representing the DOM event 
4388         * object passed back by the event utility (Event).
4389         */
4390         _onOverlayElementFocus: function (p_oEvent) {
4391
4392             var oTarget = Event.getTarget(p_oEvent),
4393                 oClose = this.close;
4394
4395             if (oClose && (oTarget == oClose || Dom.isAncestor(oClose, oTarget))) {
4396                 this.blur();
4397             } else {
4398                 this.focus();
4399             }
4400         },
4401
4402         /**
4403         * @method _onOverlayDestroy
4404         * @description "destroy" event handler for the Overlay.
4405         * @private
4406         * @param {String} p_sType String representing the name of the event  
4407         * that was fired.
4408         * @param {Array} p_aArgs Array of arguments sent when the event 
4409         * was fired.
4410         * @param {Overlay} p_oOverlay Object representing the overlay that 
4411         * fired the event.
4412         */
4413         _onOverlayDestroy: function (p_sType, p_aArgs, p_oOverlay) {
4414             this.remove(p_oOverlay);
4415         },
4416
4417         /**
4418         * @method _onOverlayFocusHandler
4419         *
4420         * @description focusEvent Handler, used to delegate to _manageFocus with the correct arguments.
4421         *
4422         * @private
4423         * @param {String} p_sType String representing the name of the event  
4424         * that was fired.
4425         * @param {Array} p_aArgs Array of arguments sent when the event 
4426         * was fired.
4427         * @param {Overlay} p_oOverlay Object representing the overlay that 
4428         * fired the event.
4429         */
4430         _onOverlayFocusHandler: function(p_sType, p_aArgs, p_oOverlay) {
4431             this._manageFocus(p_oOverlay);
4432         },
4433
4434         /**
4435         * @method _onOverlayBlurHandler
4436         * @description blurEvent Handler, used to delegate to _manageBlur with the correct arguments.
4437         *
4438         * @private
4439         * @param {String} p_sType String representing the name of the event  
4440         * that was fired.
4441         * @param {Array} p_aArgs Array of arguments sent when the event 
4442         * was fired.
4443         * @param {Overlay} p_oOverlay Object representing the overlay that 
4444         * fired the event.
4445         */
4446         _onOverlayBlurHandler: function(p_sType, p_aArgs, p_oOverlay) {
4447             this._manageBlur(p_oOverlay);
4448         },
4449
4450         /**
4451          * Subscribes to the Overlay based instance focusEvent, to allow the OverlayManager to
4452          * monitor focus state.
4453          * 
4454          * If the instance already has a focusEvent (e.g. Menu), OverlayManager will subscribe 
4455          * to the existing focusEvent, however if a focusEvent or focus method does not exist
4456          * on the instance, the _bindFocus method will add them, and the focus method will 
4457          * update the OverlayManager's state directly.
4458          * 
4459          * @method _bindFocus
4460          * @param {Overlay} overlay The overlay for which focus needs to be managed
4461          * @protected
4462          */
4463         _bindFocus : function(overlay) {
4464             var mgr = this;
4465
4466             if (!overlay.focusEvent) {
4467                 overlay.focusEvent = overlay.createEvent("focus");
4468                 overlay.focusEvent.signature = CustomEvent.LIST;
4469                 overlay.focusEvent._managed = true;
4470             } else {
4471                 overlay.focusEvent.subscribe(mgr._onOverlayFocusHandler, overlay, mgr);
4472             }
4473
4474             if (!overlay.focus) {
4475                 Event.on(overlay.element, mgr.cfg.getProperty("focusevent"), mgr._onOverlayElementFocus, null, overlay);
4476                 overlay.focus = function () {
4477                     if (mgr._manageFocus(this)) {
4478                         // For Panel/Dialog
4479                         if (this.cfg.getProperty("visible") && this.focusFirst) {
4480                             this.focusFirst();
4481                         }
4482                         this.focusEvent.fire();
4483                     }
4484                 };
4485                 overlay.focus._managed = true;
4486             }
4487         },
4488
4489         /**
4490          * Subscribes to the Overlay based instance's blurEvent to allow the OverlayManager to
4491          * monitor blur state.
4492          *
4493          * If the instance already has a blurEvent (e.g. Menu), OverlayManager will subscribe 
4494          * to the existing blurEvent, however if a blurEvent or blur method does not exist
4495          * on the instance, the _bindBlur method will add them, and the blur method 
4496          * update the OverlayManager's state directly.
4497          *
4498          * @method _bindBlur
4499          * @param {Overlay} overlay The overlay for which blur needs to be managed
4500          * @protected
4501          */
4502         _bindBlur : function(overlay) {
4503             var mgr = this;
4504
4505             if (!overlay.blurEvent) {
4506                 overlay.blurEvent = overlay.createEvent("blur");
4507                 overlay.blurEvent.signature = CustomEvent.LIST;
4508                 overlay.focusEvent._managed = true;
4509             } else {
4510                 overlay.blurEvent.subscribe(mgr._onOverlayBlurHandler, overlay, mgr);
4511             }
4512
4513             if (!overlay.blur) {
4514                 overlay.blur = function () {
4515                     if (mgr._manageBlur(this)) {
4516                         this.blurEvent.fire();
4517                     }
4518                 };
4519                 overlay.blur._managed = true;
4520             }
4521
4522             overlay.hideEvent.subscribe(overlay.blur);
4523         },
4524
4525         /**
4526          * Subscribes to the Overlay based instance's destroyEvent, to allow the Overlay
4527          * to be removed for the OverlayManager when destroyed.
4528          * 
4529          * @method _bindDestroy
4530          * @param {Overlay} overlay The overlay instance being managed
4531          * @protected
4532          */
4533         _bindDestroy : function(overlay) {
4534             var mgr = this;
4535             overlay.destroyEvent.subscribe(mgr._onOverlayDestroy, overlay, mgr);
4536         },
4537
4538         /**
4539          * Ensures the zIndex configuration property on the managed overlay based instance
4540          * is set to the computed zIndex value from the DOM (with "auto" translating to 0).
4541          *
4542          * @method _syncZIndex
4543          * @param {Overlay} overlay The overlay instance being managed
4544          * @protected
4545          */
4546         _syncZIndex : function(overlay) {
4547             var zIndex = Dom.getStyle(overlay.element, "zIndex");
4548             if (!isNaN(zIndex)) {
4549                 overlay.cfg.setProperty("zIndex", parseInt(zIndex, 10));
4550             } else {
4551                 overlay.cfg.setProperty("zIndex", 0);
4552             }
4553         },
4554
4555         /**
4556         * Registers an Overlay or an array of Overlays with the manager. Upon 
4557         * registration, the Overlay receives functions for focus and blur, 
4558         * along with CustomEvents for each.
4559         *
4560         * @method register
4561         * @param {Overlay} overlay  An Overlay to register with the manager.
4562         * @param {Overlay[]} overlay  An array of Overlays to register with 
4563         * the manager.
4564         * @return {boolean} true if any Overlays are registered.
4565         */
4566         register: function (overlay) {
4567
4568             var registered = false,
4569                 i,
4570                 n;
4571
4572             if (overlay instanceof Overlay) {
4573
4574                 overlay.cfg.addProperty("manager", { value: this } );
4575
4576                 this._bindFocus(overlay);
4577                 this._bindBlur(overlay);
4578                 this._bindDestroy(overlay);
4579                 this._syncZIndex(overlay);
4580
4581                 this.overlays.push(overlay);
4582                 this.bringToTop(overlay);
4583
4584                 registered = true;
4585
4586             } else if (overlay instanceof Array) {
4587
4588                 for (i = 0, n = overlay.length; i < n; i++) {
4589                     registered = this.register(overlay[i]) || registered;
4590                 }
4591
4592             }
4593
4594             return registered;
4595         },
4596
4597         /**
4598         * Places the specified Overlay instance on top of all other 
4599         * Overlay instances.
4600         * @method bringToTop
4601         * @param {YAHOO.widget.Overlay} p_oOverlay Object representing an 
4602         * Overlay instance.
4603         * @param {String} p_oOverlay String representing the id of an 
4604         * Overlay instance.
4605         */        
4606         bringToTop: function (p_oOverlay) {
4607
4608             var oOverlay = this.find(p_oOverlay),
4609                 nTopZIndex,
4610                 oTopOverlay,
4611                 aOverlays;
4612
4613             if (oOverlay) {
4614
4615                 aOverlays = this.overlays;
4616                 aOverlays.sort(this.compareZIndexDesc);
4617
4618                 oTopOverlay = aOverlays[0];
4619
4620                 if (oTopOverlay) {
4621                     nTopZIndex = Dom.getStyle(oTopOverlay.element, "zIndex");
4622
4623                     if (!isNaN(nTopZIndex)) {
4624
4625                         var bRequiresBump = false;
4626
4627                         if (oTopOverlay !== oOverlay) {
4628                             bRequiresBump = true;
4629                         } else if (aOverlays.length > 1) {
4630                             var nNextZIndex = Dom.getStyle(aOverlays[1].element, "zIndex");
4631                             // Don't rely on DOM order to stack if 2 overlays are at the same zindex.
4632                             if (!isNaN(nNextZIndex) && (nTopZIndex == nNextZIndex)) {
4633                                 bRequiresBump = true;
4634                             }
4635                         }
4636
4637                         if (bRequiresBump) {
4638                             oOverlay.cfg.setProperty("zindex", (parseInt(nTopZIndex, 10) + 2));
4639                         }
4640                     }
4641                     aOverlays.sort(this.compareZIndexDesc);
4642                 }
4643             }
4644         },
4645
4646         /**
4647         * Attempts to locate an Overlay by instance or ID.
4648         * @method find
4649         * @param {Overlay} overlay  An Overlay to locate within the manager
4650         * @param {String} overlay  An Overlay id to locate within the manager
4651         * @return {Overlay} The requested Overlay, if found, or null if it 
4652         * cannot be located.
4653         */
4654         find: function (overlay) {
4655
4656             var isInstance = overlay instanceof Overlay,
4657                 overlays = this.overlays,
4658                 n = overlays.length,
4659                 found = null,
4660                 o,
4661                 i;
4662
4663             if (isInstance || typeof overlay == "string") {
4664                 for (i = n-1; i >= 0; i--) {
4665                     o = overlays[i];
4666                     if ((isInstance && (o === overlay)) || (o.id == overlay)) {
4667                         found = o;
4668                         break;
4669                     }
4670                 }
4671             }
4672
4673             return found;
4674         },
4675
4676         /**
4677         * Used for sorting the manager's Overlays by z-index.
4678         * @method compareZIndexDesc
4679         * @private
4680         * @return {Number} 0, 1, or -1, depending on where the Overlay should 
4681         * fall in the stacking order.
4682         */
4683         compareZIndexDesc: function (o1, o2) {
4684
4685             var zIndex1 = (o1.cfg) ? o1.cfg.getProperty("zIndex") : null, // Sort invalid (destroyed)
4686                 zIndex2 = (o2.cfg) ? o2.cfg.getProperty("zIndex") : null; // objects at bottom.
4687
4688             if (zIndex1 === null && zIndex2 === null) {
4689                 return 0;
4690             } else if (zIndex1 === null){
4691                 return 1;
4692             } else if (zIndex2 === null) {
4693                 return -1;
4694             } else if (zIndex1 > zIndex2) {
4695                 return -1;
4696             } else if (zIndex1 < zIndex2) {
4697                 return 1;
4698             } else {
4699                 return 0;
4700             }
4701         },
4702
4703         /**
4704         * Shows all Overlays in the manager.
4705         * @method showAll
4706         */
4707         showAll: function () {
4708             var overlays = this.overlays,
4709                 n = overlays.length,
4710                 i;
4711
4712             for (i = n - 1; i >= 0; i--) {
4713                 overlays[i].show();
4714             }
4715         },
4716
4717         /**
4718         * Hides all Overlays in the manager.
4719         * @method hideAll
4720         */
4721         hideAll: function () {
4722             var overlays = this.overlays,
4723                 n = overlays.length,
4724                 i;
4725
4726             for (i = n - 1; i >= 0; i--) {
4727                 overlays[i].hide();
4728             }
4729         },
4730
4731         /**
4732         * Returns a string representation of the object.
4733         * @method toString
4734         * @return {String} The string representation of the OverlayManager
4735         */
4736         toString: function () {
4737             return "OverlayManager";
4738         }
4739     };
4740 }());
4741 (function () {
4742
4743     /**
4744     * ContainerEffect encapsulates animation transitions that are executed when 
4745     * an Overlay is shown or hidden.
4746     * @namespace YAHOO.widget
4747     * @class ContainerEffect
4748     * @constructor
4749     * @param {YAHOO.widget.Overlay} overlay The Overlay that the animation 
4750     * should be associated with
4751     * @param {Object} attrIn The object literal representing the animation 
4752     * arguments to be used for the animate-in transition. The arguments for 
4753     * this literal are: attributes(object, see YAHOO.util.Anim for description), 
4754     * duration(Number), and method(i.e. Easing.easeIn).
4755     * @param {Object} attrOut The object literal representing the animation 
4756     * arguments to be used for the animate-out transition. The arguments for  
4757     * this literal are: attributes(object, see YAHOO.util.Anim for description), 
4758     * duration(Number), and method(i.e. Easing.easeIn).
4759     * @param {HTMLElement} targetElement Optional. The target element that  
4760     * should be animated during the transition. Defaults to overlay.element.
4761     * @param {class} Optional. The animation class to instantiate. Defaults to 
4762     * YAHOO.util.Anim. Other options include YAHOO.util.Motion.
4763     */
4764     YAHOO.widget.ContainerEffect = function (overlay, attrIn, attrOut, targetElement, animClass) {
4765
4766         if (!animClass) {
4767             animClass = YAHOO.util.Anim;
4768         }
4769
4770         /**
4771         * The overlay to animate
4772         * @property overlay
4773         * @type YAHOO.widget.Overlay
4774         */
4775         this.overlay = overlay;
4776     
4777         /**
4778         * The animation attributes to use when transitioning into view
4779         * @property attrIn
4780         * @type Object
4781         */
4782         this.attrIn = attrIn;
4783     
4784         /**
4785         * The animation attributes to use when transitioning out of view
4786         * @property attrOut
4787         * @type Object
4788         */
4789         this.attrOut = attrOut;
4790     
4791         /**
4792         * The target element to be animated
4793         * @property targetElement
4794         * @type HTMLElement
4795         */
4796         this.targetElement = targetElement || overlay.element;
4797     
4798         /**
4799         * The animation class to use for animating the overlay
4800         * @property animClass
4801         * @type class
4802         */
4803         this.animClass = animClass;
4804     
4805     };
4806
4807
4808     var Dom = YAHOO.util.Dom,
4809         CustomEvent = YAHOO.util.CustomEvent,
4810         ContainerEffect = YAHOO.widget.ContainerEffect;
4811
4812
4813     /**
4814     * A pre-configured ContainerEffect instance that can be used for fading 
4815     * an overlay in and out.
4816     * @method FADE
4817     * @static
4818     * @param {YAHOO.widget.Overlay} overlay The Overlay object to animate
4819     * @param {Number} dur The duration of the animation
4820     * @return {YAHOO.widget.ContainerEffect} The configured ContainerEffect object
4821     */
4822     ContainerEffect.FADE = function (overlay, dur) {
4823
4824         var Easing = YAHOO.util.Easing,
4825             fin = {
4826                 attributes: {opacity:{from:0, to:1}},
4827                 duration: dur,
4828                 method: Easing.easeIn
4829             },
4830             fout = {
4831                 attributes: {opacity:{to:0}},
4832                 duration: dur,
4833                 method: Easing.easeOut
4834             },
4835             fade = new ContainerEffect(overlay, fin, fout, overlay.element);
4836
4837         fade.handleUnderlayStart = function() {
4838             var underlay = this.overlay.underlay;
4839             if (underlay && YAHOO.env.ua.ie) {
4840                 var hasFilters = (underlay.filters && underlay.filters.length > 0);
4841                 if(hasFilters) {
4842                     Dom.addClass(overlay.element, "yui-effect-fade");
4843                 }
4844             }
4845         };
4846
4847         fade.handleUnderlayComplete = function() {
4848             var underlay = this.overlay.underlay;
4849             if (underlay && YAHOO.env.ua.ie) {
4850                 Dom.removeClass(overlay.element, "yui-effect-fade");
4851             }
4852         };
4853
4854         fade.handleStartAnimateIn = function (type, args, obj) {
4855             Dom.addClass(obj.overlay.element, "hide-select");
4856
4857             if (!obj.overlay.underlay) {
4858                 obj.overlay.cfg.refireEvent("underlay");
4859             }
4860
4861             obj.handleUnderlayStart();
4862
4863             obj.overlay._setDomVisibility(true);
4864             Dom.setStyle(obj.overlay.element, "opacity", 0);
4865         };
4866
4867         fade.handleCompleteAnimateIn = function (type,args,obj) {
4868             Dom.removeClass(obj.overlay.element, "hide-select");
4869
4870             if (obj.overlay.element.style.filter) {
4871                 obj.overlay.element.style.filter = null;
4872             }
4873
4874             obj.handleUnderlayComplete();
4875
4876             obj.overlay.cfg.refireEvent("iframe");
4877             obj.animateInCompleteEvent.fire();
4878         };
4879
4880         fade.handleStartAnimateOut = function (type, args, obj) {
4881             Dom.addClass(obj.overlay.element, "hide-select");
4882             obj.handleUnderlayStart();
4883         };
4884
4885         fade.handleCompleteAnimateOut =  function (type, args, obj) {
4886             Dom.removeClass(obj.overlay.element, "hide-select");
4887             if (obj.overlay.element.style.filter) {
4888                 obj.overlay.element.style.filter = null;
4889             }
4890             obj.overlay._setDomVisibility(false);
4891             Dom.setStyle(obj.overlay.element, "opacity", 1);
4892
4893             obj.handleUnderlayComplete();
4894
4895             obj.overlay.cfg.refireEvent("iframe");
4896             obj.animateOutCompleteEvent.fire();
4897         };
4898
4899         fade.init();
4900         return fade;
4901     };
4902     
4903     
4904     /**
4905     * A pre-configured ContainerEffect instance that can be used for sliding an 
4906     * overlay in and out.
4907     * @method SLIDE
4908     * @static
4909     * @param {YAHOO.widget.Overlay} overlay The Overlay object to animate
4910     * @param {Number} dur The duration of the animation
4911     * @return {YAHOO.widget.ContainerEffect} The configured ContainerEffect object
4912     */
4913     ContainerEffect.SLIDE = function (overlay, dur) {
4914         var Easing = YAHOO.util.Easing,
4915
4916             x = overlay.cfg.getProperty("x") || Dom.getX(overlay.element),
4917             y = overlay.cfg.getProperty("y") || Dom.getY(overlay.element),
4918             clientWidth = Dom.getClientWidth(),
4919             offsetWidth = overlay.element.offsetWidth,
4920
4921             sin =  { 
4922                 attributes: { points: { to: [x, y] } },
4923                 duration: dur,
4924                 method: Easing.easeIn 
4925             },
4926
4927             sout = {
4928                 attributes: { points: { to: [(clientWidth + 25), y] } },
4929                 duration: dur,
4930                 method: Easing.easeOut 
4931             },
4932
4933             slide = new ContainerEffect(overlay, sin, sout, overlay.element, YAHOO.util.Motion);
4934
4935         slide.handleStartAnimateIn = function (type,args,obj) {
4936             obj.overlay.element.style.left = ((-25) - offsetWidth) + "px";
4937             obj.overlay.element.style.top  = y + "px";
4938         };
4939
4940         slide.handleTweenAnimateIn = function (type, args, obj) {
4941         
4942             var pos = Dom.getXY(obj.overlay.element),
4943                 currentX = pos[0],
4944                 currentY = pos[1];
4945         
4946             if (Dom.getStyle(obj.overlay.element, "visibility") == 
4947                 "hidden" && currentX < x) {
4948
4949                 obj.overlay._setDomVisibility(true);
4950
4951             }
4952         
4953             obj.overlay.cfg.setProperty("xy", [currentX, currentY], true);
4954             obj.overlay.cfg.refireEvent("iframe");
4955         };
4956         
4957         slide.handleCompleteAnimateIn = function (type, args, obj) {
4958             obj.overlay.cfg.setProperty("xy", [x, y], true);
4959             obj.startX = x;
4960             obj.startY = y;
4961             obj.overlay.cfg.refireEvent("iframe");
4962             obj.animateInCompleteEvent.fire();
4963         };
4964         
4965         slide.handleStartAnimateOut = function (type, args, obj) {
4966     
4967             var vw = Dom.getViewportWidth(),
4968                 pos = Dom.getXY(obj.overlay.element),
4969                 yso = pos[1];
4970     
4971             obj.animOut.attributes.points.to = [(vw + 25), yso];
4972         };
4973         
4974         slide.handleTweenAnimateOut = function (type, args, obj) {
4975     
4976             var pos = Dom.getXY(obj.overlay.element),
4977                 xto = pos[0],
4978                 yto = pos[1];
4979         
4980             obj.overlay.cfg.setProperty("xy", [xto, yto], true);
4981             obj.overlay.cfg.refireEvent("iframe");
4982         };
4983         
4984         slide.handleCompleteAnimateOut = function (type, args, obj) {
4985             obj.overlay._setDomVisibility(false);
4986
4987             obj.overlay.cfg.setProperty("xy", [x, y]);
4988             obj.animateOutCompleteEvent.fire();
4989         };
4990
4991         slide.init();
4992         return slide;
4993     };
4994
4995     ContainerEffect.prototype = {
4996
4997         /**
4998         * Initializes the animation classes and events.
4999         * @method init
5000         */
5001         init: function () {
5002
5003             this.beforeAnimateInEvent = this.createEvent("beforeAnimateIn");
5004             this.beforeAnimateInEvent.signature = CustomEvent.LIST;
5005             
5006             this.beforeAnimateOutEvent = this.createEvent("beforeAnimateOut");
5007             this.beforeAnimateOutEvent.signature = CustomEvent.LIST;
5008         
5009             this.animateInCompleteEvent = this.createEvent("animateInComplete");
5010             this.animateInCompleteEvent.signature = CustomEvent.LIST;
5011         
5012             this.animateOutCompleteEvent = 
5013                 this.createEvent("animateOutComplete");
5014             this.animateOutCompleteEvent.signature = CustomEvent.LIST;
5015         
5016             this.animIn = new this.animClass(this.targetElement, 
5017                 this.attrIn.attributes, this.attrIn.duration, 
5018                 this.attrIn.method);
5019
5020             this.animIn.onStart.subscribe(this.handleStartAnimateIn, this);
5021             this.animIn.onTween.subscribe(this.handleTweenAnimateIn, this);
5022
5023             this.animIn.onComplete.subscribe(this.handleCompleteAnimateIn, 
5024                 this);
5025         
5026             this.animOut = new this.animClass(this.targetElement, 
5027                 this.attrOut.attributes, this.attrOut.duration, 
5028                 this.attrOut.method);
5029
5030             this.animOut.onStart.subscribe(this.handleStartAnimateOut, this);
5031             this.animOut.onTween.subscribe(this.handleTweenAnimateOut, this);
5032             this.animOut.onComplete.subscribe(this.handleCompleteAnimateOut, 
5033                 this);
5034
5035         },
5036         
5037         /**
5038         * Triggers the in-animation.
5039         * @method animateIn
5040         */
5041         animateIn: function () {
5042             this.beforeAnimateInEvent.fire();
5043             this.animIn.animate();
5044         },
5045
5046         /**
5047         * Triggers the out-animation.
5048         * @method animateOut
5049         */
5050         animateOut: function () {
5051             this.beforeAnimateOutEvent.fire();
5052             this.animOut.animate();
5053         },
5054
5055         /**
5056         * The default onStart handler for the in-animation.
5057         * @method handleStartAnimateIn
5058         * @param {String} type The CustomEvent type
5059         * @param {Object[]} args The CustomEvent arguments
5060         * @param {Object} obj The scope object
5061         */
5062         handleStartAnimateIn: function (type, args, obj) { },
5063
5064         /**
5065         * The default onTween handler for the in-animation.
5066         * @method handleTweenAnimateIn
5067         * @param {String} type The CustomEvent type
5068         * @param {Object[]} args The CustomEvent arguments
5069         * @param {Object} obj The scope object
5070         */
5071         handleTweenAnimateIn: function (type, args, obj) { },
5072
5073         /**
5074         * The default onComplete handler for the in-animation.
5075         * @method handleCompleteAnimateIn
5076         * @param {String} type The CustomEvent type
5077         * @param {Object[]} args The CustomEvent arguments
5078         * @param {Object} obj The scope object
5079         */
5080         handleCompleteAnimateIn: function (type, args, obj) { },
5081
5082         /**
5083         * The default onStart handler for the out-animation.
5084         * @method handleStartAnimateOut
5085         * @param {String} type The CustomEvent type
5086         * @param {Object[]} args The CustomEvent arguments
5087         * @param {Object} obj The scope object
5088         */
5089         handleStartAnimateOut: function (type, args, obj) { },
5090
5091         /**
5092         * The default onTween handler for the out-animation.
5093         * @method handleTweenAnimateOut
5094         * @param {String} type The CustomEvent type
5095         * @param {Object[]} args The CustomEvent arguments
5096         * @param {Object} obj The scope object
5097         */
5098         handleTweenAnimateOut: function (type, args, obj) { },
5099
5100         /**
5101         * The default onComplete handler for the out-animation.
5102         * @method handleCompleteAnimateOut
5103         * @param {String} type The CustomEvent type
5104         * @param {Object[]} args The CustomEvent arguments
5105         * @param {Object} obj The scope object
5106         */
5107         handleCompleteAnimateOut: function (type, args, obj) { },
5108         
5109         /**
5110         * Returns a string representation of the object.
5111         * @method toString
5112         * @return {String} The string representation of the ContainerEffect
5113         */
5114         toString: function () {
5115             var output = "ContainerEffect";
5116             if (this.overlay) {
5117                 output += " [" + this.overlay.toString() + "]";
5118             }
5119             return output;
5120         }
5121     };
5122
5123     YAHOO.lang.augmentProto(ContainerEffect, YAHOO.util.EventProvider);
5124
5125 })();
5126 YAHOO.register("containercore", YAHOO.widget.Module, {version: "2.8.1", build: "19"});