Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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