~michael.nelson/ubuntu-webcatalog/1267731-import-sca-apps-error

« back to all changes in this revision

Viewing changes to src/webcatalog/static/yui/3.10.3/build/widget-modality/widget-modality-debug.js

  • Committer: Tarmac
  • Author(s): Stephen Stewart
  • Date: 2013-06-26 09:19:32 UTC
  • mfrom: (184.1.4 ubuntu-global-nav)
  • Revision ID: tarmac-20130626091932-8urtuli368k8p7ds
[r=beuno,jonas-drange] add ubuntu global nav to apps.ubuntu.com

Show diffs side-by-side

added added

removed removed

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