~ubuntu-branches/ubuntu/precise/maas/precise-updates

« back to all changes in this revision

Viewing changes to debian/extras/jslibs/yui/widget-modality/widget-modality-debug.js

Tags: 1.2+bzr1373+dfsg-0ubuntu1~12.04.4
* SECURITY UPDATE: failure to authenticate downloaded content (LP: #1039513)
  - debian/patches/CVE-2013-1058.patch: Authenticate downloaded files with
    GnuPG and MD5SUM files. Thanks to Julian Edwards.
  - CVE-2013-1058
* SECURITY UPDATE: configuration options may be loaded from current working
  directory (LP: #1158425)
  - debian/patches/CVE-2013-1057-1-2.patch: Do not load configuration
    options from the current working directory. Thanks to Julian Edwards.
  - CVE-2013-1057

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
YUI 3.5.1 (build 22)
 
3
Copyright 2012 Yahoo! Inc. All rights reserved.
 
4
Licensed under the BSD License.
 
5
http://yuilibrary.com/license/
 
6
*/
 
7
YUI.add('widget-modality', function(Y) {
 
8
 
 
9
/**
 
10
 * Provides modality support for Widgets, though an extension
 
11
 *
 
12
 * @module widget-modality
 
13
 */
 
14
 
 
15
var WIDGET       = 'widget',
 
16
    RENDER_UI    = 'renderUI',
 
17
    BIND_UI      = 'bindUI',
 
18
    SYNC_UI      = 'syncUI',
 
19
    BOUNDING_BOX = 'boundingBox',
 
20
    CONTENT_BOX  = 'contentBox',
 
21
    VISIBLE      = 'visible',
 
22
    Z_INDEX      = 'zIndex',
 
23
    CHANGE       = 'Change',
 
24
    isBoolean    = Y.Lang.isBoolean,
 
25
    getCN        = Y.ClassNameManager.getClassName,
 
26
    MaskShow     = "maskShow",
 
27
    MaskHide     = "maskHide",
 
28
    ClickOutside = "clickoutside",
 
29
    FocusOutside = "focusoutside",
 
30
 
 
31
    supportsPosFixed = (function(){
 
32
 
 
33
        /*! IS_POSITION_FIXED_SUPPORTED - Juriy Zaytsev (kangax) - http://yura.thinkweb2.com/cft/ */
 
34
 
 
35
        var doc         = Y.config.doc,
 
36
            isSupported = null,
 
37
            el, root;
 
38
 
 
39
        if (doc.createElement) {
 
40
            el = doc.createElement('div');
 
41
            if (el && el.style) {
 
42
                el.style.position = 'fixed';
 
43
                el.style.top = '10px';
 
44
                root = doc.body;
 
45
                if (root && root.appendChild && root.removeChild) {
 
46
                    root.appendChild(el);
 
47
                    isSupported = (el.offsetTop === 10);
 
48
                    root.removeChild(el);
 
49
                }
 
50
            }
 
51
        }
 
52
 
 
53
        return isSupported;
 
54
    }());
 
55
 
 
56
    /**
 
57
     * Widget extension, which can be used to add modality support to the base Widget class,
 
58
     * through the Base.create method.
 
59
     *
 
60
     * @class WidgetModality
 
61
     * @param {Object} config User configuration object
 
62
     */
 
63
    function WidgetModal(config) {}
 
64
 
 
65
    var MODAL           = 'modal',
 
66
        MASK            = 'mask',
 
67
        MODAL_CLASSES   = {
 
68
            modal   : getCN(WIDGET, MODAL),
 
69
            mask    : getCN(WIDGET, MASK)
 
70
        };
 
71
 
 
72
    /**
 
73
    * Static property used to define the default attribute
 
74
    * configuration introduced by WidgetModality.
 
75
    *
 
76
    * @property ATTRS
 
77
    * @static
 
78
    * @type Object
 
79
    */
 
80
    WidgetModal.ATTRS = {
 
81
            /**
 
82
             * @attribute maskNode
 
83
             * @type Y.Node
 
84
             *
 
85
             * @description Returns a Y.Node instance of the node being used as the mask.
 
86
             */
 
87
            maskNode : {
 
88
                getter      : '_getMaskNode',
 
89
                readOnly    : true
 
90
            },
 
91
 
 
92
 
 
93
            /**
 
94
             * @attribute modal
 
95
             * @type boolean
 
96
             *
 
97
             * @description Whether the widget should be modal or not.
 
98
             */
 
99
            modal: {
 
100
                value:false,
 
101
                validator: isBoolean
 
102
            },
 
103
 
 
104
            /**
 
105
             * @attribute focusOn
 
106
             * @type array
 
107
             *
 
108
             * @description An array of objects corresponding to the nodes and events that will trigger a re-focus back on the widget.
 
109
             * The implementer can supply an array of objects, with each object having the following properties:
 
110
             * <p>eventName: (string, required): The eventName to listen to.</p>
 
111
             * <p>node: (Y.Node, optional): The Y.Node that will fire the event (defaults to the boundingBox of the widget)</p>
 
112
             * <p>By default, this attribute consists of two objects which will cause the widget to re-focus if anything
 
113
             * outside the widget is clicked on or focussed upon.</p>
 
114
             */
 
115
            focusOn: {
 
116
                valueFn: function() {
 
117
                    return [
 
118
                        {
 
119
                            // node: this.get(BOUNDING_BOX),
 
120
                            eventName: ClickOutside
 
121
                        },
 
122
                        {
 
123
                            //node: this.get(BOUNDING_BOX),
 
124
                            eventName: FocusOutside
 
125
                        }
 
126
                    ];
 
127
                },
 
128
 
 
129
                validator: Y.Lang.isArray
 
130
            }
 
131
 
 
132
    };
 
133
 
 
134
 
 
135
    WidgetModal.CLASSES = MODAL_CLASSES;
 
136
 
 
137
 
 
138
    /**
 
139
     * Returns the mask if it exists on the page - otherwise creates a mask. There's only
 
140
     * one mask on a page at a given time.
 
141
     * <p>
 
142
     * This method in invoked internally by the getter of the maskNode ATTR.
 
143
     * </p>
 
144
     * @method _GET_MASK
 
145
     * @static
 
146
     */
 
147
    WidgetModal._GET_MASK = function() {
 
148
 
 
149
        var mask = Y.one(".yui3-widget-mask") || null,
 
150
        win = Y.one('window');
 
151
 
 
152
        if (mask) {
 
153
            return mask;
 
154
        }
 
155
        else {
 
156
 
 
157
            mask = Y.Node.create('<div></div>');
 
158
            mask.addClass(MODAL_CLASSES.mask);
 
159
            if (supportsPosFixed) {
 
160
                mask.setStyles({
 
161
                    position    : 'fixed',
 
162
                    width       : '100%',
 
163
                    height      : '100%',
 
164
                    top         : '0',
 
165
                    left        : '0',
 
166
                    display     : 'block'
 
167
                });
 
168
            }
 
169
            else {
 
170
                mask.setStyles({
 
171
                    position    : 'absolute',
 
172
                    width       : win.get('winWidth') +'px',
 
173
                    height      : win.get('winHeight') + 'px',
 
174
                    top         : '0',
 
175
                    left        : '0',
 
176
                    display     : 'block'
 
177
                });
 
178
            }
 
179
 
 
180
 
 
181
 
 
182
            return mask;
 
183
        }
 
184
 
 
185
    };
 
186
 
 
187
    /**
 
188
     * A stack of Y.Widget objects representing the current hierarchy of modal widgets presently displayed on the screen
 
189
     * @property STACK
 
190
     */
 
191
    WidgetModal.STACK = [];
 
192
 
 
193
 
 
194
    WidgetModal.prototype = {
 
195
 
 
196
        initializer: function () {
 
197
            Y.after(this._renderUIModal, this, RENDER_UI);
 
198
            Y.after(this._syncUIModal, this, SYNC_UI);
 
199
            Y.after(this._bindUIModal, this, BIND_UI);
 
200
        },
 
201
 
 
202
        destructor: function () {
 
203
            // Hack to remove this thing from the STACK.
 
204
            this._uiSetHostVisibleModal(false);
 
205
        },
 
206
 
 
207
        // *** Instance Members *** //
 
208
 
 
209
        _uiHandlesModal: null,
 
210
 
 
211
 
 
212
        /**
 
213
         * Adds modal class to the bounding box of the widget
 
214
         * <p>
 
215
         * This method in invoked after renderUI is invoked for the Widget class
 
216
         * using YUI's aop infrastructure.
 
217
         * </p>
 
218
         * @method _renderUIModal
 
219
         * @protected
 
220
         */
 
221
        _renderUIModal : function () {
 
222
 
 
223
            var bb = this.get(BOUNDING_BOX);
 
224
                //cb = this.get(CONTENT_BOX);
 
225
 
 
226
            //this makes the content box content appear over the mask
 
227
            // cb.setStyles({
 
228
            //     position: ""
 
229
            // });
 
230
 
 
231
            this._repositionMask(this);
 
232
            bb.addClass(MODAL_CLASSES.modal);
 
233
 
 
234
        },
 
235
 
 
236
 
 
237
        /**
 
238
         * Hooks up methods to be executed when the widget's visibility or z-index changes
 
239
         * <p>
 
240
         * This method in invoked after bindUI is invoked for the Widget class
 
241
         * using YUI's aop infrastructure.
 
242
         * </p>
 
243
         * @method _bindUIModal
 
244
         * @protected
 
245
         */
 
246
        _bindUIModal : function () {
 
247
 
 
248
            this.after(VISIBLE+CHANGE, this._afterHostVisibleChangeModal);
 
249
            this.after(Z_INDEX+CHANGE, this._afterHostZIndexChangeModal);
 
250
            this.after("focusOnChange", this._afterFocusOnChange);
 
251
 
 
252
            // Re-align the mask in the viewport if `position: fixed;` is not
 
253
            // supported. iOS < 5 and Android < 3 don't actually support it even
 
254
            // though they both pass the feature test; the UA sniff is here to
 
255
            // account for that. Ideally this should be replaced with a better
 
256
            // feature test.
 
257
            if (!supportsPosFixed ||
 
258
                    (Y.UA.ios && Y.UA.ios < 5) ||
 
259
                    (Y.UA.android && Y.UA.android < 3)) {
 
260
 
 
261
                Y.one('win').on('scroll', this._resyncMask, this);
 
262
            }
 
263
        },
 
264
 
 
265
        /**
 
266
         * Syncs the mask with the widget's current state, namely the visibility and z-index of the widget
 
267
         * <p>
 
268
         * This method in invoked after syncUI is invoked for the Widget class
 
269
         * using YUI's aop infrastructure.
 
270
         * </p>
 
271
         * @method _syncUIModal
 
272
         * @protected
 
273
         */
 
274
        _syncUIModal : function () {
 
275
 
 
276
            //var host = this.get(HOST);
 
277
 
 
278
            this._uiSetHostVisibleModal(this.get(VISIBLE));
 
279
            this._uiSetHostZIndexModal(this.get(Z_INDEX));
 
280
 
 
281
        },
 
282
 
 
283
        /**
 
284
         * Provides mouse and tab focus to the widget's bounding box.
 
285
         *
 
286
         * @method _focus
 
287
         */
 
288
        _focus : function (e) {
 
289
 
 
290
            var bb = this.get(BOUNDING_BOX),
 
291
            oldTI = bb.get('tabIndex');
 
292
 
 
293
            bb.set('tabIndex', oldTI >= 0 ? oldTI : 0);
 
294
            this.focus();
 
295
        },
 
296
        /**
 
297
         * Blurs the widget.
 
298
         *
 
299
         * @method _blur
 
300
         */
 
301
        _blur : function () {
 
302
 
 
303
            this.blur();
 
304
        },
 
305
 
 
306
        /**
 
307
         * Returns the Y.Node instance of the maskNode
 
308
         *
 
309
         * @method _getMaskNode
 
310
         * @return {Node} The Y.Node instance of the mask, as returned from WidgetModal._GET_MASK
 
311
         */
 
312
        _getMaskNode : function () {
 
313
 
 
314
            return WidgetModal._GET_MASK();
 
315
        },
 
316
 
 
317
        /**
 
318
         * Performs events attaching/detaching, stack shifting and mask repositioning based on the visibility of the widget
 
319
         *
 
320
         * @method _uiSetHostVisibleModal
 
321
         * @param {boolean} Whether the widget is visible or not
 
322
         */
 
323
        _uiSetHostVisibleModal : function (visible) {
 
324
            var stack    = WidgetModal.STACK,
 
325
                maskNode = this.get('maskNode'),
 
326
                isModal  = this.get('modal'),
 
327
                topModal, index;
 
328
 
 
329
            if (visible) {
 
330
 
 
331
                Y.Array.each(stack, function(modal){
 
332
                    modal._detachUIHandlesModal();
 
333
                    modal._blur();
 
334
                });
 
335
 
 
336
                // push on top of stack
 
337
                stack.unshift(this);
 
338
 
 
339
                this._repositionMask(this);
 
340
                this._uiSetHostZIndexModal(this.get(Z_INDEX));
 
341
 
 
342
                if (isModal) {
 
343
                    maskNode.show();
 
344
                    Y.later(1, this, '_attachUIHandlesModal');
 
345
                    this._focus();
 
346
                }
 
347
 
 
348
 
 
349
            } else {
 
350
 
 
351
                index = Y.Array.indexOf(stack, this);
 
352
                if (index >= 0) {
 
353
                    // Remove modal widget from global stack.
 
354
                    stack.splice(index, 1);
 
355
                }
 
356
 
 
357
                this._detachUIHandlesModal();
 
358
                this._blur();
 
359
 
 
360
                if (stack.length) {
 
361
                    topModal = stack[0];
 
362
                    this._repositionMask(topModal);
 
363
                    //topModal._attachUIHandlesModal();
 
364
                    topModal._uiSetHostZIndexModal(topModal.get(Z_INDEX));
 
365
 
 
366
                    if (topModal.get('modal')) {
 
367
                        //topModal._attachUIHandlesModal();
 
368
                        Y.later(1, topModal, '_attachUIHandlesModal');
 
369
                        topModal._focus();
 
370
                    }
 
371
 
 
372
                } else {
 
373
 
 
374
                    if (maskNode.getStyle('display') === 'block') {
 
375
                        maskNode.hide();
 
376
                    }
 
377
 
 
378
                }
 
379
 
 
380
            }
 
381
        },
 
382
 
 
383
        /**
 
384
         * Sets the z-index of the mask node.
 
385
         *
 
386
         * @method _uiSetHostZIndexModal
 
387
         * @param {Number} Z-Index of the widget
 
388
         */
 
389
        _uiSetHostZIndexModal : function (zIndex) {
 
390
 
 
391
            if (this.get('modal')) {
 
392
                this.get('maskNode').setStyle(Z_INDEX, zIndex || 0);
 
393
            }
 
394
 
 
395
        },
 
396
 
 
397
        /**
 
398
         * Attaches UI Listeners for "clickoutside" and "focusoutside" on the
 
399
         * widget. When these events occur, and the widget is modal, focus is
 
400
         * shifted back onto the widget.
 
401
         *
 
402
         * @method _attachUIHandlesModal
 
403
         */
 
404
        _attachUIHandlesModal : function () {
 
405
 
 
406
            if (this._uiHandlesModal || WidgetModal.STACK[0] !== this) {
 
407
                // Quit early if we have ui handles, or if we not at the top
 
408
                // of the global stack.
 
409
                return;
 
410
            }
 
411
 
 
412
            var bb          = this.get(BOUNDING_BOX),
 
413
                maskNode    = this.get('maskNode'),
 
414
                focusOn     = this.get('focusOn'),
 
415
                focus       = Y.bind(this._focus, this),
 
416
                uiHandles   = [],
 
417
                i, len, o;
 
418
 
 
419
            for (i = 0, len = focusOn.length; i < len; i++) {
 
420
 
 
421
                o = {};
 
422
                o.node = focusOn[i].node;
 
423
                o.ev = focusOn[i].eventName;
 
424
                o.keyCode = focusOn[i].keyCode;
 
425
 
 
426
                //no keycode or node defined
 
427
                if (!o.node && !o.keyCode && o.ev) {
 
428
                    uiHandles.push(bb.on(o.ev, focus));
 
429
                }
 
430
 
 
431
                //node defined, no keycode (not a keypress)
 
432
                else if (o.node && !o.keyCode && o.ev) {
 
433
                    uiHandles.push(o.node.on(o.ev, focus));
 
434
                }
 
435
 
 
436
                //node defined, keycode defined, event defined (its a key press)
 
437
                else if (o.node && o.keyCode && o.ev) {
 
438
                    uiHandles.push(o.node.on(o.ev, focus, o.keyCode));
 
439
                }
 
440
 
 
441
                else {
 
442
                    Y.Log('focusOn ATTR Error: The event with name "'+o.ev+'" could not be attached.');
 
443
                }
 
444
 
 
445
            }
 
446
 
 
447
            if ( ! supportsPosFixed) {
 
448
                uiHandles.push(Y.one('win').on('scroll', Y.bind(function(e){
 
449
                    maskNode.setStyle('top', maskNode.get('docScrollY'));
 
450
                }, this)));
 
451
            }
 
452
 
 
453
            this._uiHandlesModal = uiHandles;
 
454
        },
 
455
 
 
456
        /**
 
457
         * Detaches all UI Listeners that were set in _attachUIHandlesModal from the widget.
 
458
         *
 
459
         * @method _detachUIHandlesModal
 
460
         */
 
461
        _detachUIHandlesModal : function () {
 
462
            Y.each(this._uiHandlesModal, function(h){
 
463
                h.detach();
 
464
            });
 
465
            this._uiHandlesModal = null;
 
466
        },
 
467
 
 
468
        /**
 
469
         * Default function that is called when visibility is changed on the widget.
 
470
         *
 
471
         * @method _afterHostVisibleChangeModal
 
472
         * @param {EventFacade} e The event facade of the change
 
473
         */
 
474
        _afterHostVisibleChangeModal : function (e) {
 
475
 
 
476
            this._uiSetHostVisibleModal(e.newVal);
 
477
        },
 
478
 
 
479
        /**
 
480
         * Default function that is called when z-index is changed on the widget.
 
481
         *
 
482
         * @method _afterHostZIndexChangeModal
 
483
         * @param {EventFacade} e The event facade of the change
 
484
         */
 
485
        _afterHostZIndexChangeModal : function (e) {
 
486
 
 
487
            this._uiSetHostZIndexModal(e.newVal);
 
488
        },
 
489
 
 
490
        /**
 
491
         * Returns a boolean representing whether the current widget is in a "nested modality" state.
 
492
         * This is done by checking the number of widgets currently on the stack.
 
493
         *
 
494
         * @method isNested
 
495
         * @public
 
496
         */
 
497
        isNested: function() {
 
498
            var length = WidgetModal.STACK.length,
 
499
            retval = (length > 1) ? true : false;
 
500
            return retval;
 
501
        },
 
502
 
 
503
        /**
 
504
         * Repositions the mask in the DOM for nested modality cases.
 
505
         *
 
506
         * @method _repositionMask
 
507
         * @param {Widget} nextElem The Y.Widget instance that will be visible in the stack once the current widget is closed.
 
508
         */
 
509
        _repositionMask: function(nextElem) {
 
510
 
 
511
            var currentModal = this.get('modal'),
 
512
                nextModal    = nextElem.get('modal'),
 
513
                maskNode     = this.get('maskNode'),
 
514
                bb, bbParent;
 
515
 
 
516
            //if this is modal and host is not modal
 
517
            if (currentModal && !nextModal) {
 
518
                //leave the mask where it is, since the host is not modal.
 
519
                maskNode.remove();
 
520
                this.fire(MaskHide);
 
521
            }
 
522
 
 
523
            //if the main widget is not modal but the host is modal, or both of them are modal
 
524
            else if ((!currentModal && nextModal) || (currentModal && nextModal)) {
 
525
 
 
526
                //then remove the mask off DOM, reposition it, and reinsert it into the DOM
 
527
                maskNode.remove();
 
528
                this.fire(MaskHide);
 
529
                bb = nextElem.get(BOUNDING_BOX);
 
530
                bbParent = bb.get('parentNode') || Y.one('body');
 
531
                bbParent.insert(maskNode, bbParent.get('firstChild'));
 
532
                this.fire(MaskShow);
 
533
            }
 
534
 
 
535
        },
 
536
 
 
537
        /**
 
538
         * Resyncs the mask in the viewport for browsers that don't support fixed positioning
 
539
         *
 
540
         * @method _resyncMask
 
541
         * @param {Y.Widget} nextElem The Y.Widget instance that will be visible in the stack once the current widget is closed.
 
542
         * @private
 
543
         */
 
544
        _resyncMask: function (e) {
 
545
            var o       = e.currentTarget,
 
546
                offsetX = o.get('docScrollX'),
 
547
                offsetY = o.get('docScrollY'),
 
548
                w       = o.get('innerWidth') || o.get('winWidth'),
 
549
                h       = o.get('innerHeight') || o.get('winHeight'),
 
550
                mask    = this.get('maskNode');
 
551
 
 
552
            mask.setStyles({
 
553
                "top": offsetY + "px",
 
554
                "left": offsetX + "px",
 
555
                "width": w + 'px',
 
556
                "height": h + 'px'
 
557
            });
 
558
        },
 
559
 
 
560
        /**
 
561
         * Default function called when focusOn Attribute is changed. Remove existing listeners and create new listeners.
 
562
         *
 
563
         * @method _afterFocusOnChange
 
564
         */
 
565
        _afterFocusOnChange : function(e) {
 
566
            this._detachUIHandlesModal();
 
567
 
 
568
            if (this.get(VISIBLE)) {
 
569
                this._attachUIHandlesModal();
 
570
            }
 
571
        }
 
572
    };
 
573
 
 
574
    Y.WidgetModality = WidgetModal;
 
575
 
 
576
 
 
577
 
 
578
}, '3.5.1' ,{requires:['base-build', 'event-outside', 'widget'], skinnable:true});