src/cm/media/js/lib/yui/yui_3.10.3/build/widget-modality/widget-modality.js
changeset 525 89ef5ed3c48b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cm/media/js/lib/yui/yui_3.10.3/build/widget-modality/widget-modality.js	Tue Jul 16 14:29:46 2013 +0200
@@ -0,0 +1,572 @@
+/*
+YUI 3.10.3 (build 2fb5187)
+Copyright 2013 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('widget-modality', function (Y, NAME) {
+
+/**
+ * Provides modality support for Widgets, though an extension
+ *
+ * @module widget-modality
+ */
+
+var WIDGET       = 'widget',
+    RENDER_UI    = 'renderUI',
+    BIND_UI      = 'bindUI',
+    SYNC_UI      = 'syncUI',
+    BOUNDING_BOX = 'boundingBox',
+    CONTENT_BOX  = 'contentBox',
+    VISIBLE      = 'visible',
+    Z_INDEX      = 'zIndex',
+    CHANGE       = 'Change',
+    isBoolean    = Y.Lang.isBoolean,
+    getCN        = Y.ClassNameManager.getClassName,
+    MaskShow     = "maskShow",
+    MaskHide     = "maskHide",
+    ClickOutside = "clickoutside",
+    FocusOutside = "focusoutside",
+
+    supportsPosFixed = (function(){
+
+        /*! IS_POSITION_FIXED_SUPPORTED - Juriy Zaytsev (kangax) - http://yura.thinkweb2.com/cft/ */
+
+        var doc         = Y.config.doc,
+            isSupported = null,
+            el, root;
+
+        if (doc.createElement) {
+            el = doc.createElement('div');
+            if (el && el.style) {
+                el.style.position = 'fixed';
+                el.style.top = '10px';
+                root = doc.body;
+                if (root && root.appendChild && root.removeChild) {
+                    root.appendChild(el);
+                    isSupported = (el.offsetTop === 10);
+                    root.removeChild(el);
+                }
+            }
+        }
+
+        return isSupported;
+    }());
+
+    /**
+     * Widget extension, which can be used to add modality support to the base Widget class,
+     * through the Base.create method.
+     *
+     * @class WidgetModality
+     * @param {Object} config User configuration object
+     */
+    function WidgetModal(config) {}
+
+    var MODAL           = 'modal',
+        MASK            = 'mask',
+        MODAL_CLASSES   = {
+            modal   : getCN(WIDGET, MODAL),
+            mask    : getCN(WIDGET, MASK)
+        };
+
+    /**
+    * Static property used to define the default attribute
+    * configuration introduced by WidgetModality.
+    *
+    * @property ATTRS
+    * @static
+    * @type Object
+    */
+    WidgetModal.ATTRS = {
+            /**
+             * @attribute maskNode
+             * @type Y.Node
+             *
+             * @description Returns a Y.Node instance of the node being used as the mask.
+             */
+            maskNode : {
+                getter      : '_getMaskNode',
+                readOnly    : true
+            },
+
+
+            /**
+             * @attribute modal
+             * @type boolean
+             *
+             * @description Whether the widget should be modal or not.
+             */
+            modal: {
+                value:false,
+                validator: isBoolean
+            },
+
+            /**
+             * @attribute focusOn
+             * @type array
+             *
+             * @description An array of objects corresponding to the nodes and events that will trigger a re-focus back on the widget.
+             * The implementer can supply an array of objects, with each object having the following properties:
+             * <p>eventName: (string, required): The eventName to listen to.</p>
+             * <p>node: (Y.Node, optional): The Y.Node that will fire the event (defaults to the boundingBox of the widget)</p>
+             * <p>By default, this attribute consists of two objects which will cause the widget to re-focus if anything
+             * outside the widget is clicked on or focussed upon.</p>
+             */
+            focusOn: {
+                valueFn: function() {
+                    return [
+                        {
+                            // node: this.get(BOUNDING_BOX),
+                            eventName: ClickOutside
+                        },
+                        {
+                            //node: this.get(BOUNDING_BOX),
+                            eventName: FocusOutside
+                        }
+                    ];
+                },
+
+                validator: Y.Lang.isArray
+            }
+
+    };
+
+
+    WidgetModal.CLASSES = MODAL_CLASSES;
+
+
+    /**
+     * Returns the mask if it exists on the page - otherwise creates a mask. There's only
+     * one mask on a page at a given time.
+     * <p>
+     * This method in invoked internally by the getter of the maskNode ATTR.
+     * </p>
+     * @method _GET_MASK
+     * @static
+     */
+    WidgetModal._GET_MASK = function() {
+
+        var mask = Y.one('.' + MODAL_CLASSES.mask),
+            win  = Y.one('win');
+
+        if (mask) {
+            return mask;
+        }
+
+        mask = Y.Node.create('<div></div>').addClass(MODAL_CLASSES.mask);
+
+        if (supportsPosFixed) {
+            mask.setStyles({
+                position: 'fixed',
+                width   : '100%',
+                height  : '100%',
+                top     : '0',
+                left    : '0',
+                display : 'block'
+            });
+        } else {
+            mask.setStyles({
+                position: 'absolute',
+                width   : win.get('winWidth') +'px',
+                height  : win.get('winHeight') + 'px',
+                top     : '0',
+                left    : '0',
+                display : 'block'
+            });
+        }
+
+        return mask;
+    };
+
+    /**
+     * A stack of Y.Widget objects representing the current hierarchy of modal widgets presently displayed on the screen
+     * @property STACK
+     */
+    WidgetModal.STACK = [];
+
+
+    WidgetModal.prototype = {
+
+        initializer: function () {
+            Y.after(this._renderUIModal, this, RENDER_UI);
+            Y.after(this._syncUIModal, this, SYNC_UI);
+            Y.after(this._bindUIModal, this, BIND_UI);
+        },
+
+        destructor: function () {
+            // Hack to remove this thing from the STACK.
+            this._uiSetHostVisibleModal(false);
+        },
+
+        // *** Instance Members *** //
+
+        _uiHandlesModal: null,
+
+
+        /**
+         * Adds modal class to the bounding box of the widget
+         * <p>
+         * This method in invoked after renderUI is invoked for the Widget class
+         * using YUI's aop infrastructure.
+         * </p>
+         * @method _renderUIModal
+         * @protected
+         */
+        _renderUIModal : function () {
+
+            var bb = this.get(BOUNDING_BOX);
+                //cb = this.get(CONTENT_BOX);
+
+            //this makes the content box content appear over the mask
+            // cb.setStyles({
+            //     position: ""
+            // });
+
+            this._repositionMask(this);
+            bb.addClass(MODAL_CLASSES.modal);
+
+        },
+
+
+        /**
+         * Hooks up methods to be executed when the widget's visibility or z-index changes
+         * <p>
+         * This method in invoked after bindUI is invoked for the Widget class
+         * using YUI's aop infrastructure.
+         * </p>
+         * @method _bindUIModal
+         * @protected
+         */
+        _bindUIModal : function () {
+
+            this.after(VISIBLE+CHANGE, this._afterHostVisibleChangeModal);
+            this.after(Z_INDEX+CHANGE, this._afterHostZIndexChangeModal);
+            this.after("focusOnChange", this._afterFocusOnChange);
+
+            // Re-align the mask in the viewport if `position: fixed;` is not
+            // supported. iOS < 5 and Android < 3 don't actually support it even
+            // though they both pass the feature test; the UA sniff is here to
+            // account for that. Ideally this should be replaced with a better
+            // feature test.
+            if (!supportsPosFixed ||
+                    (Y.UA.ios && Y.UA.ios < 5) ||
+                    (Y.UA.android && Y.UA.android < 3)) {
+
+                Y.one('win').on('scroll', this._resyncMask, this);
+            }
+        },
+
+        /**
+         * Syncs the mask with the widget's current state, namely the visibility and z-index of the widget
+         * <p>
+         * This method in invoked after syncUI is invoked for the Widget class
+         * using YUI's aop infrastructure.
+         * </p>
+         * @method _syncUIModal
+         * @protected
+         */
+        _syncUIModal : function () {
+
+            //var host = this.get(HOST);
+
+            this._uiSetHostVisibleModal(this.get(VISIBLE));
+
+        },
+
+        /**
+         * Provides mouse and tab focus to the widget's bounding box.
+         *
+         * @method _focus
+         */
+        _focus : function (e) {
+
+            var bb = this.get(BOUNDING_BOX),
+            oldTI = bb.get('tabIndex');
+
+            bb.set('tabIndex', oldTI >= 0 ? oldTI : 0);
+            this.focus();
+        },
+        /**
+         * Blurs the widget.
+         *
+         * @method _blur
+         */
+        _blur : function () {
+
+            this.blur();
+        },
+
+        /**
+         * Returns the Y.Node instance of the maskNode
+         *
+         * @method _getMaskNode
+         * @return {Node} The Y.Node instance of the mask, as returned from WidgetModal._GET_MASK
+         */
+        _getMaskNode : function () {
+
+            return WidgetModal._GET_MASK();
+        },
+
+        /**
+         * Performs events attaching/detaching, stack shifting and mask repositioning based on the visibility of the widget
+         *
+         * @method _uiSetHostVisibleModal
+         * @param {boolean} Whether the widget is visible or not
+         */
+        _uiSetHostVisibleModal : function (visible) {
+            var stack    = WidgetModal.STACK,
+                maskNode = this.get('maskNode'),
+                isModal  = this.get('modal'),
+                topModal, index;
+
+            if (visible) {
+
+                Y.Array.each(stack, function(modal){
+                    modal._detachUIHandlesModal();
+                    modal._blur();
+                });
+
+                // push on top of stack
+                stack.unshift(this);
+
+                this._repositionMask(this);
+                this._uiSetHostZIndexModal(this.get(Z_INDEX));
+
+                if (isModal) {
+                    maskNode.show();
+                    Y.later(1, this, '_attachUIHandlesModal');
+                    this._focus();
+                }
+
+
+            } else {
+
+                index = Y.Array.indexOf(stack, this);
+                if (index >= 0) {
+                    // Remove modal widget from global stack.
+                    stack.splice(index, 1);
+                }
+
+                this._detachUIHandlesModal();
+                this._blur();
+
+                if (stack.length) {
+                    topModal = stack[0];
+                    this._repositionMask(topModal);
+                    //topModal._attachUIHandlesModal();
+                    topModal._uiSetHostZIndexModal(topModal.get(Z_INDEX));
+
+                    if (topModal.get('modal')) {
+                        //topModal._attachUIHandlesModal();
+                        Y.later(1, topModal, '_attachUIHandlesModal');
+                        topModal._focus();
+                    }
+
+                } else {
+
+                    if (maskNode.getStyle('display') === 'block') {
+                        maskNode.hide();
+                    }
+
+                }
+
+            }
+        },
+
+        /**
+         * Sets the z-index of the mask node.
+         *
+         * @method _uiSetHostZIndexModal
+         * @param {Number} Z-Index of the widget
+         */
+        _uiSetHostZIndexModal : function (zIndex) {
+
+            if (this.get('modal')) {
+                this.get('maskNode').setStyle(Z_INDEX, zIndex || 0);
+            }
+
+        },
+
+        /**
+         * Attaches UI Listeners for "clickoutside" and "focusoutside" on the
+         * widget. When these events occur, and the widget is modal, focus is
+         * shifted back onto the widget.
+         *
+         * @method _attachUIHandlesModal
+         */
+        _attachUIHandlesModal : function () {
+
+            if (this._uiHandlesModal || WidgetModal.STACK[0] !== this) {
+                // Quit early if we have ui handles, or if we not at the top
+                // of the global stack.
+                return;
+            }
+
+            var bb          = this.get(BOUNDING_BOX),
+                maskNode    = this.get('maskNode'),
+                focusOn     = this.get('focusOn'),
+                focus       = Y.bind(this._focus, this),
+                uiHandles   = [],
+                i, len, o;
+
+            for (i = 0, len = focusOn.length; i < len; i++) {
+
+                o = {};
+                o.node = focusOn[i].node;
+                o.ev = focusOn[i].eventName;
+                o.keyCode = focusOn[i].keyCode;
+
+                //no keycode or node defined
+                if (!o.node && !o.keyCode && o.ev) {
+                    uiHandles.push(bb.on(o.ev, focus));
+                }
+
+                //node defined, no keycode (not a keypress)
+                else if (o.node && !o.keyCode && o.ev) {
+                    uiHandles.push(o.node.on(o.ev, focus));
+                }
+
+                //node defined, keycode defined, event defined (its a key press)
+                else if (o.node && o.keyCode && o.ev) {
+                    uiHandles.push(o.node.on(o.ev, focus, o.keyCode));
+                }
+
+                else {
+                    Y.Log('focusOn ATTR Error: The event with name "'+o.ev+'" could not be attached.');
+                }
+
+            }
+
+            if ( ! supportsPosFixed) {
+                uiHandles.push(Y.one('win').on('scroll', Y.bind(function(e){
+                    maskNode.setStyle('top', maskNode.get('docScrollY'));
+                }, this)));
+            }
+
+            this._uiHandlesModal = uiHandles;
+        },
+
+        /**
+         * Detaches all UI Listeners that were set in _attachUIHandlesModal from the widget.
+         *
+         * @method _detachUIHandlesModal
+         */
+        _detachUIHandlesModal : function () {
+            Y.each(this._uiHandlesModal, function(h){
+                h.detach();
+            });
+            this._uiHandlesModal = null;
+        },
+
+        /**
+         * Default function that is called when visibility is changed on the widget.
+         *
+         * @method _afterHostVisibleChangeModal
+         * @param {EventFacade} e The event facade of the change
+         */
+        _afterHostVisibleChangeModal : function (e) {
+
+            this._uiSetHostVisibleModal(e.newVal);
+        },
+
+        /**
+         * Default function that is called when z-index is changed on the widget.
+         *
+         * @method _afterHostZIndexChangeModal
+         * @param {EventFacade} e The event facade of the change
+         */
+        _afterHostZIndexChangeModal : function (e) {
+
+            this._uiSetHostZIndexModal(e.newVal);
+        },
+
+        /**
+         * Returns a boolean representing whether the current widget is in a "nested modality" state.
+         * This is done by checking the number of widgets currently on the stack.
+         *
+         * @method isNested
+         * @public
+         */
+        isNested: function() {
+            var length = WidgetModal.STACK.length,
+            retval = (length > 1) ? true : false;
+            return retval;
+        },
+
+        /**
+         * Repositions the mask in the DOM for nested modality cases.
+         *
+         * @method _repositionMask
+         * @param {Widget} nextElem The Y.Widget instance that will be visible in the stack once the current widget is closed.
+         */
+        _repositionMask: function(nextElem) {
+
+            var currentModal = this.get('modal'),
+                nextModal    = nextElem.get('modal'),
+                maskNode     = this.get('maskNode'),
+                bb, bbParent;
+
+            //if this is modal and host is not modal
+            if (currentModal && !nextModal) {
+                //leave the mask where it is, since the host is not modal.
+                maskNode.remove();
+                this.fire(MaskHide);
+            }
+
+            //if the main widget is not modal but the host is modal, or both of them are modal
+            else if ((!currentModal && nextModal) || (currentModal && nextModal)) {
+
+                //then remove the mask off DOM, reposition it, and reinsert it into the DOM
+                maskNode.remove();
+                this.fire(MaskHide);
+                bb = nextElem.get(BOUNDING_BOX);
+                bbParent = bb.get('parentNode') || Y.one('body');
+                bbParent.insert(maskNode, bbParent.get('firstChild'));
+                this.fire(MaskShow);
+            }
+
+        },
+
+        /**
+         * Resyncs the mask in the viewport for browsers that don't support fixed positioning
+         *
+         * @method _resyncMask
+         * @param {Y.Widget} nextElem The Y.Widget instance that will be visible in the stack once the current widget is closed.
+         * @private
+         */
+        _resyncMask: function (e) {
+            var o       = e.currentTarget,
+                offsetX = o.get('docScrollX'),
+                offsetY = o.get('docScrollY'),
+                w       = o.get('innerWidth') || o.get('winWidth'),
+                h       = o.get('innerHeight') || o.get('winHeight'),
+                mask    = this.get('maskNode');
+
+            mask.setStyles({
+                "top": offsetY + "px",
+                "left": offsetX + "px",
+                "width": w + 'px',
+                "height": h + 'px'
+            });
+        },
+
+        /**
+         * Default function called when focusOn Attribute is changed. Remove existing listeners and create new listeners.
+         *
+         * @method _afterFocusOnChange
+         */
+        _afterFocusOnChange : function(e) {
+            this._detachUIHandlesModal();
+
+            if (this.get(VISIBLE)) {
+                this._attachUIHandlesModal();
+            }
+        }
+    };
+
+    Y.WidgetModality = WidgetModal;
+
+
+
+}, '3.10.3', {"requires": ["base-build", "event-outside", "widget"], "skinnable": true});