2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
8 YUI.add('widget-stack', function(Y) {
11 * Provides stackable (z-index) support for Widgets through an extension.
13 * @module widget-stack
24 BOUNDING_BOX = "boundingBox",
26 RENDER_UI = "renderUI",
30 OFFSET_WIDTH = "offsetWidth",
31 OFFSET_HEIGHT = "offsetHeight",
32 PARENT_NODE = "parentNode",
33 FIRST_CHILD = "firstChild",
34 OWNER_DOCUMENT = "ownerDocument",
41 SHIM_DEFERRED = "shimdeferred",
42 SHIM_RESIZE = "shimresize",
45 VisibleChange = "visibleChange",
46 WidthChange = "widthChange",
47 HeightChange = "heightChange",
48 ShimChange = "shimChange",
49 ZIndexChange = "zIndexChange",
50 ContentUpdate = "contentUpdate",
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.
61 * @param {Object} User configuration object
63 function Stack(config) {
64 this._stackNode = this.get(BOUNDING_BOX);
65 this._stackHandles = {};
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);
75 * Static property used to define the default attribute
76 * configuration introduced by WidgetStack.
78 * @property WidgetStack.ATTRS
86 * @default false, for all browsers other than IE6, for which a shim is enabled by default.
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.
99 * @description The z-index to apply to the Widgets boundingBox. Non-numerical values for
100 * zIndex will be converted to 0
104 setter: function(val) {
105 return this._setZIndex(val);
111 * The HTML parsing rules for the WidgetStack class.
113 * @property WidgetStack.HTML_PARSER
117 Stack.HTML_PARSER = {
118 zIndex: function(contentBox) {
119 return contentBox.getStyle(ZINDEX);
124 * Default class used to mark the shim element
126 * @property WidgetStack.SHIM_CLASS_NAME
129 * @default "yui-widget-shim"
131 Stack.SHIM_CLASS_NAME = Widget.getClassName(SHIM);
134 * Default class used to mark the boundingBox of a stacked widget.
136 * @property WidgetStack.STACKED_CLASS_NAME
139 * @default "yui-widget-stacked"
141 Stack.STACKED_CLASS_NAME = Widget.getClassName(STACKED);
144 * Default markup template used to generate the shim element.
146 * @property WidgetStack.SHIM_TEMPLATE
150 Stack.SHIM_TEMPLATE = '<iframe class="' + Stack.SHIM_CLASS_NAME + '" frameborder="0" title="Widget Stacking Shim" src="javascript:false" tabindex="-1" role="presentation"></iframe>';
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.
158 * @method _syncUIStack
161 _syncUIStack: function() {
162 this._uiSetShim(this.get(SHIM));
163 this._uiSetZIndex(this.get(ZINDEX));
167 * Binds event listeners responsible for updating the UI state in response to
168 * Widget stack related state changes.
170 * This method is invoked after bindUI is invoked for the Widget class
171 * using YUI's aop infrastructure.
173 * @method _bindUIStack
176 _bindUIStack: function() {
177 this.after(ShimChange, this._afterShimChange);
178 this.after(ZIndexChange, this._afterZIndexChange);
182 * Creates/Initializes the DOM to support stackability.
184 * This method in invoked after renderUI is invoked for the Widget class
185 * using YUI's aop infrastructure.
187 * @method _renderUIStack
190 _renderUIStack: function() {
191 this._stackNode.addClass(Stack.STACKED_CLASS_NAME);
195 * Default setter for zIndex attribute changes. Normalizes zIndex values to
196 * numbers, converting non-numerical values to 0.
200 * @param {String | Number} zIndex
201 * @return {Number} Normalized zIndex
203 _setZIndex: function(zIndex) {
204 if (L.isString(zIndex)) {
205 zIndex = parseInt(zIndex, 10);
207 if (!L.isNumber(zIndex)) {
214 * Default attribute change listener for the shim attribute, responsible
215 * for updating the UI, in response to attribute changes.
217 * @method _afterShimChange
219 * @param {EventFacade} e The event facade for the attribute change
221 _afterShimChange : function(e) {
222 this._uiSetShim(e.newVal);
226 * Default attribute change listener for the zIndex attribute, responsible
227 * for updating the UI, in response to attribute changes.
229 * @method _afterZIndexChange
231 * @param {EventFacade} e The event facade for the attribute change
233 _afterZIndexChange : function(e) {
234 this._uiSetZIndex(e.newVal);
238 * Updates the UI to reflect the zIndex value passed in.
240 * @method _uiSetZIndex
242 * @param {number} zIndex The zindex to be reflected in the UI
244 _uiSetZIndex: function (zIndex) {
245 this._stackNode.setStyle(ZINDEX, zIndex);
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.
254 * @param {boolean} enable If true, creates/renders the shim, if false, removes it.
256 _uiSetShim: function (enable) {
259 if (this.get(VISIBLE)) {
262 this._renderShimDeferred();
270 * Sets up change handlers for the visible attribute, to defer shim creation/rendering
271 * until the Widget is made visible.
273 * @method _renderShimDeferred
276 _renderShimDeferred : function() {
278 this._stackHandles[SHIM_DEFERRED] = this._stackHandles[SHIM_DEFERRED] || [];
280 var handles = this._stackHandles[SHIM_DEFERRED],
281 createBeforeVisible = function(e) {
287 handles.push(this.on(VisibleChange, createBeforeVisible));
291 * Sets up event listeners to resize the shim when the size of the Widget changes.
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
297 * @method _addShimResizeHandlers
300 _addShimResizeHandlers : function() {
302 this._stackHandles[SHIM_RESIZE] = this._stackHandles[SHIM_RESIZE] || [];
304 var sizeShim = this.sizeShim,
305 handles = this._stackHandles[SHIM_RESIZE];
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));
316 * Detaches any handles stored for the provided key
318 * @method _detachStackHandles
319 * @param String handleKey The key defining the group of handles which should be detached
322 _detachStackHandles : function(handleKey) {
323 var handles = this._stackHandles[handleKey],
326 if (handles && handles.length > 0) {
327 while((handle = handles.pop())) {
334 * Creates the shim element and adds it to the DOM
336 * @method _renderShim
339 _renderShim : function() {
340 var shimEl = this._shimNode,
341 stackEl = this._stackNode;
344 shimEl = this._shimNode = this._getShimTemplate();
345 stackEl.insertBefore(shimEl, stackEl.get(FIRST_CHILD));
348 this._addShimResizeHandlers();
350 this._detachStackHandles(SHIM_DEFERRED);
355 * Removes the shim from the DOM, and detaches any related event
358 * @method _destroyShim
361 _destroyShim : function() {
362 if (this._shimNode) {
363 this._shimNode.get(PARENT_NODE).removeChild(this._shimNode);
364 this._shimNode = null;
366 this._detachStackHandles(SHIM_DEFERRED);
367 this._detachStackHandles(SHIM_RESIZE);
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.
378 sizeShim: function () {
379 var shim = this._shimNode,
380 node = this._stackNode;
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);
389 * Creates a cloned shim node, using the SHIM_TEMPLATE html template, for use on a new instance.
391 * @method _getShimTemplate
393 * @return {Node} node A new shim Node instance.
395 _getShimTemplate : function() {
396 return Node.create(Stack.SHIM_TEMPLATE, this._stackNode.get(OWNER_DOCUMENT));
400 Y.WidgetStack = Stack;
403 }, '3.3.0' ,{requires:['base-build', 'widget']});