]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/widget/widget-stack.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / widget / widget-stack.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: 3.3.0
6 build: 3167
7 */
8 YUI.add('widget-stack', function(Y) {
9
10 /**
11  * Provides stackable (z-index) support for Widgets through an extension.
12  *
13  * @module widget-stack
14  */
15     var L = Y.Lang,
16         UA = Y.UA,
17         Node = Y.Node,
18         Widget = Y.Widget,
19
20         ZINDEX = "zIndex",
21         SHIM = "shim",
22         VISIBLE = "visible",
23
24         BOUNDING_BOX = "boundingBox",
25
26         RENDER_UI = "renderUI",
27         BIND_UI = "bindUI",
28         SYNC_UI = "syncUI",
29
30         OFFSET_WIDTH = "offsetWidth",
31         OFFSET_HEIGHT = "offsetHeight",
32         PARENT_NODE = "parentNode",
33         FIRST_CHILD = "firstChild",
34         OWNER_DOCUMENT = "ownerDocument",
35
36         WIDTH = "width",
37         HEIGHT = "height",
38         PX = "px",
39
40         // HANDLE KEYS
41         SHIM_DEFERRED = "shimdeferred",
42         SHIM_RESIZE = "shimresize",
43
44         // Events
45         VisibleChange = "visibleChange",
46         WidthChange = "widthChange",
47         HeightChange = "heightChange",
48         ShimChange = "shimChange",
49         ZIndexChange = "zIndexChange",
50         ContentUpdate = "contentUpdate",
51
52         // CSS
53         STACKED = "stacked";
54
55     /**
56      * Widget extension, which can be used to add stackable (z-index) support to the 
57      * base Widget class along with a shimming solution, through the 
58      * <a href="Base.html#method_build">Base.build</a> method.
59      *
60      * @class WidgetStack
61      * @param {Object} User configuration object
62      */
63     function Stack(config) {
64         this._stackNode = this.get(BOUNDING_BOX);
65         this._stackHandles = {};
66
67         // WIDGET METHOD OVERLAP
68         Y.after(this._renderUIStack, this, RENDER_UI);
69         Y.after(this._syncUIStack, this, SYNC_UI);
70         Y.after(this._bindUIStack, this, BIND_UI);
71     }
72
73     // Static Properties
74     /**
75      * Static property used to define the default attribute 
76      * configuration introduced by WidgetStack.
77      * 
78      * @property WidgetStack.ATTRS
79      * @type Object
80      * @static
81      */
82     Stack.ATTRS = {
83         /**
84          * @attribute shim
85          * @type boolean
86          * @default false, for all browsers other than IE6, for which a shim is enabled by default.
87          * 
88          * @description Boolean flag to indicate whether or not a shim should be added to the Widgets
89          * boundingBox, to protect it from select box bleedthrough.
90          */
91         shim: {
92             value: (UA.ie == 6)
93         },
94
95         /**
96          * @attribute zIndex
97          * @type number
98          * @default 0
99          * @description The z-index to apply to the Widgets boundingBox. Non-numerical values for 
100          * zIndex will be converted to 0
101          */
102         zIndex: {
103             value:0,
104             setter: function(val) {
105                 return this._setZIndex(val);
106             }
107         }
108     };
109
110     /**
111      * The HTML parsing rules for the WidgetStack class.
112      * 
113      * @property WidgetStack.HTML_PARSER
114      * @static
115      * @type Object
116      */
117     Stack.HTML_PARSER = {
118         zIndex: function(contentBox) {
119             return contentBox.getStyle(ZINDEX);
120         }
121     };
122
123     /**
124      * Default class used to mark the shim element
125      *
126      * @property WidgetStack.SHIM_CLASS_NAME
127      * @type String
128      * @static
129      * @default "yui-widget-shim"
130      */
131     Stack.SHIM_CLASS_NAME = Widget.getClassName(SHIM);
132
133     /**
134      * Default class used to mark the boundingBox of a stacked widget.
135      * 
136      * @property WidgetStack.STACKED_CLASS_NAME
137      * @type String
138      * @static
139      * @default "yui-widget-stacked"
140      */
141     Stack.STACKED_CLASS_NAME = Widget.getClassName(STACKED);
142
143     /**
144      * Default markup template used to generate the shim element.
145      * 
146      * @property WidgetStack.SHIM_TEMPLATE
147      * @type String
148      * @static
149      */
150     Stack.SHIM_TEMPLATE = '<iframe class="' + Stack.SHIM_CLASS_NAME + '" frameborder="0" title="Widget Stacking Shim" src="javascript:false" tabindex="-1" role="presentation"></iframe>';
151
152     Stack.prototype = {
153
154         /**
155          * Synchronizes the UI to match the Widgets stack state. This method in 
156          * invoked after syncUI is invoked for the Widget class using YUI's aop infrastructure.
157          *
158          * @method _syncUIStack
159          * @protected
160          */
161         _syncUIStack: function() {
162             this._uiSetShim(this.get(SHIM));
163             this._uiSetZIndex(this.get(ZINDEX));
164         },
165
166         /**
167          * Binds event listeners responsible for updating the UI state in response to 
168          * Widget stack related state changes.
169          * <p>
170          * This method is invoked after bindUI is invoked for the Widget class
171          * using YUI's aop infrastructure.
172          * </p>
173          * @method _bindUIStack
174          * @protected
175          */
176         _bindUIStack: function() {
177             this.after(ShimChange, this._afterShimChange);
178             this.after(ZIndexChange, this._afterZIndexChange);
179         },
180
181         /**
182          * Creates/Initializes the DOM to support stackability.
183          * <p>
184          * This method in invoked after renderUI is invoked for the Widget class
185          * using YUI's aop infrastructure.
186          * </p>
187          * @method _renderUIStack
188          * @protected
189          */
190         _renderUIStack: function() {
191             this._stackNode.addClass(Stack.STACKED_CLASS_NAME);
192         },
193
194         /**
195          * Default setter for zIndex attribute changes. Normalizes zIndex values to 
196          * numbers, converting non-numerical values to 0.
197          *
198          * @method _setZIndex
199          * @protected
200          * @param {String | Number} zIndex
201          * @return {Number} Normalized zIndex
202          */
203         _setZIndex: function(zIndex) {
204             if (L.isString(zIndex)) {
205                 zIndex = parseInt(zIndex, 10);
206             }
207             if (!L.isNumber(zIndex)) {
208                 zIndex = 0;
209             }
210             return zIndex;
211         },
212
213         /**
214          * Default attribute change listener for the shim attribute, responsible
215          * for updating the UI, in response to attribute changes.
216          *
217          * @method _afterShimChange
218          * @protected
219          * @param {EventFacade} e The event facade for the attribute change
220          */
221         _afterShimChange : function(e) {
222             this._uiSetShim(e.newVal);
223         },
224
225         /**
226          * Default attribute change listener for the zIndex attribute, responsible
227          * for updating the UI, in response to attribute changes.
228          * 
229          * @method _afterZIndexChange
230          * @protected
231          * @param {EventFacade} e The event facade for the attribute change
232          */
233         _afterZIndexChange : function(e) {
234             this._uiSetZIndex(e.newVal);
235         },
236
237         /**
238          * Updates the UI to reflect the zIndex value passed in.
239          *
240          * @method _uiSetZIndex
241          * @protected
242          * @param {number} zIndex The zindex to be reflected in the UI
243          */
244         _uiSetZIndex: function (zIndex) {
245             this._stackNode.setStyle(ZINDEX, zIndex);
246         },
247
248         /**
249          * Updates the UI to enable/disable the shim. If the widget is not currently visible,
250          * creation of the shim is deferred until it is made visible, for performance reasons.
251          *
252          * @method _uiSetShim
253          * @protected
254          * @param {boolean} enable If true, creates/renders the shim, if false, removes it.
255          */
256         _uiSetShim: function (enable) {
257             if (enable) {
258                 // Lazy creation
259                 if (this.get(VISIBLE)) {
260                     this._renderShim();
261                 } else {
262                     this._renderShimDeferred();
263                 }
264             } else {
265                 this._destroyShim();
266             }
267         },
268
269         /**
270          * Sets up change handlers for the visible attribute, to defer shim creation/rendering 
271          * until the Widget is made visible.
272          * 
273          * @method _renderShimDeferred
274          * @private
275          */
276         _renderShimDeferred : function() {
277
278             this._stackHandles[SHIM_DEFERRED] = this._stackHandles[SHIM_DEFERRED] || [];
279
280             var handles = this._stackHandles[SHIM_DEFERRED],
281                 createBeforeVisible = function(e) {
282                     if (e.newVal) {
283                         this._renderShim();
284                     }
285                 };
286
287             handles.push(this.on(VisibleChange, createBeforeVisible));
288         },
289
290         /**
291          * Sets up event listeners to resize the shim when the size of the Widget changes.
292          * <p>
293          * NOTE: This method is only used for IE6 currently, since IE6 doesn't support a way to
294          * resize the shim purely through CSS, when the Widget does not have an explicit width/height 
295          * set.
296          * </p>
297          * @method _addShimResizeHandlers
298          * @private
299          */
300         _addShimResizeHandlers : function() {
301
302             this._stackHandles[SHIM_RESIZE] = this._stackHandles[SHIM_RESIZE] || [];
303
304             var sizeShim = this.sizeShim,
305                 handles = this._stackHandles[SHIM_RESIZE];
306
307             this.sizeShim();
308
309             handles.push(this.after(VisibleChange, sizeShim));
310             handles.push(this.after(WidthChange, sizeShim));
311             handles.push(this.after(HeightChange, sizeShim));
312             handles.push(this.after(ContentUpdate, sizeShim));
313         },
314
315         /**
316          * Detaches any handles stored for the provided key
317          *
318          * @method _detachStackHandles
319          * @param String handleKey The key defining the group of handles which should be detached
320          * @private
321          */
322         _detachStackHandles : function(handleKey) {
323             var handles = this._stackHandles[handleKey],
324                 handle;
325
326             if (handles && handles.length > 0) {
327                 while((handle = handles.pop())) {
328                     handle.detach();
329                 }
330             }
331         },
332
333         /**
334          * Creates the shim element and adds it to the DOM
335          *
336          * @method _renderShim
337          * @private
338          */
339         _renderShim : function() {
340             var shimEl = this._shimNode,
341                 stackEl = this._stackNode;
342
343             if (!shimEl) {
344                 shimEl = this._shimNode = this._getShimTemplate();
345                 stackEl.insertBefore(shimEl, stackEl.get(FIRST_CHILD));
346
347                 if (UA.ie == 6) {
348                     this._addShimResizeHandlers();
349                 }
350                 this._detachStackHandles(SHIM_DEFERRED);
351             }
352         },
353
354         /**
355          * Removes the shim from the DOM, and detaches any related event
356          * listeners.
357          *
358          * @method _destroyShim
359          * @private
360          */
361         _destroyShim : function() {
362             if (this._shimNode) {
363                 this._shimNode.get(PARENT_NODE).removeChild(this._shimNode);
364                 this._shimNode = null;
365
366                 this._detachStackHandles(SHIM_DEFERRED);
367                 this._detachStackHandles(SHIM_RESIZE);
368             }
369         },
370
371         /**
372          * For IE6, synchronizes the size and position of iframe shim to that of 
373          * Widget bounding box which it is protecting. For all other browsers,
374          * this method does not do anything.
375          *
376          * @method sizeShim
377          */
378         sizeShim: function () {
379             var shim = this._shimNode,
380                 node = this._stackNode;
381
382             if (shim && UA.ie === 6 && this.get(VISIBLE)) {
383                 shim.setStyle(WIDTH, node.get(OFFSET_WIDTH) + PX);
384                 shim.setStyle(HEIGHT, node.get(OFFSET_HEIGHT) + PX);
385             }
386         },
387
388         /**
389          * Creates a cloned shim node, using the SHIM_TEMPLATE html template, for use on a new instance.
390          *
391          * @method _getShimTemplate
392          * @private
393          * @return {Node} node A new shim Node instance.
394          */
395         _getShimTemplate : function() {
396             return Node.create(Stack.SHIM_TEMPLATE, this._stackNode.get(OWNER_DOCUMENT));
397         }
398     };
399
400     Y.WidgetStack = Stack;
401
402
403 }, '3.3.0' ,{requires:['base-build', 'widget']});