Show:
                            /**
                            Provides extended/advanced XY positioning support for Widgets, through an
                            extension.
                            
                            It builds on top of the `widget-position` module, to provide alignment and
                            centering support. Future releases aim to add constrained and fixed positioning
                            support.
                            
                            @module widget-position-align
                            **/
                            var Lang = Y.Lang,
                            
                                ALIGN        = 'align',
                                ALIGN_ON     = 'alignOn',
                            
                                VISIBLE      = 'visible',
                                BOUNDING_BOX = 'boundingBox',
                            
                                OFFSET_WIDTH    = 'offsetWidth',
                                OFFSET_HEIGHT   = 'offsetHeight',
                                REGION          = 'region',
                                VIEWPORT_REGION = 'viewportRegion';
                            
                            /**
                            Widget extension, which can be used to add extended XY positioning support to
                            the base Widget class, through the `Base.create` method.
                            
                            **Note:** This extension requires that the `WidgetPosition` extension be added
                            to the Widget (before `WidgetPositionAlign`, if part of the same extension list
                            passed to `Base.build`).
                            
                            @class WidgetPositionAlign
                            @param {Object} config User configuration object.
                            @constructor
                            **/
                            function PositionAlign (config) {}
                            
                            PositionAlign.ATTRS = {
                            
                                /**
                                The alignment configuration for this widget.
                            
                                The `align` attribute is used to align a reference point on the widget, with
                                the reference point on another `Node`, or the viewport. The object which
                                `align` expects has the following properties:
                            
                                  * __`node`__: The `Node` to which the widget is to be aligned. If set to
                                    `null`, or not provided, the widget is aligned to the viewport.
                            
                                  * __`points`__: A two element Array, defining the two points on the widget
                                    and `Node`/viewport which are to be aligned. The first element is the
                                    point on the widget, and the second element is the point on the
                                    `Node`/viewport. Supported alignment points are defined as static
                                    properties on `WidgetPositionAlign`.
                            
                                @example Aligns the top-right corner of the widget with the top-left corner
                                of the viewport:
                            
                                    myWidget.set('align', {
                                        points: [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.TL]
                                    });
                            
                                @attribute align
                                @type Object
                                @default null
                                **/
                                align: {
                                    value: null
                                },
                            
                                /**
                                A convenience Attribute, which can be used as a shortcut for the `align`
                                Attribute.
                            
                                If set to `true`, the widget is centered in the viewport. If set to a `Node`
                                reference or valid selector String, the widget will be centered within the
                                `Node`. If set to `false`, no center positioning is applied.
                            
                                @attribute centered
                                @type Boolean|Node
                                @default false
                                **/
                                centered: {
                                    setter : '_setAlignCenter',
                                    lazyAdd:false,
                                    value  :false
                                },
                            
                                /**
                                An Array of Objects corresponding to the `Node`s and events that will cause
                                the alignment of this widget to be synced to the DOM.
                            
                                The `alignOn` Attribute is expected to be an Array of Objects with the
                                following properties:
                            
                                  * __`eventName`__: The String event name to listen for.
                            
                                  * __`node`__: The optional `Node` that will fire the event, it can be a
                                    `Node` reference or a selector String. This will default to the widget's
                                    `boundingBox`.
                            
                                @example Sync this widget's alignment on window resize:
                            
                                    myWidget.set('alignOn', [
                                        {
                                            node     : Y.one('win'),
                                            eventName: 'resize'
                                        }
                                    ]);
                            
                                @attribute alignOn
                                @type Array
                                @default []
                                **/
                                alignOn: {
                                    value    : [],
                                    validator: Y.Lang.isArray
                                }
                            };
                            
                            /**
                            Constant used to specify the top-left corner for alignment
                            
                            @property TL
                            @type String
                            @value 'tl'
                            @static
                            **/
                            PositionAlign.TL = 'tl';
                            
                            /**
                            Constant used to specify the top-right corner for alignment
                            
                            @property TR
                            @type String
                            @value 'tr'
                            @static
                            **/
                            PositionAlign.TR = 'tr';
                            
                            /**
                            Constant used to specify the bottom-left corner for alignment
                            
                            @property BL
                            @type String
                            @value 'bl'
                            @static
                            **/
                            PositionAlign.BL = 'bl';
                            
                            /**
                            Constant used to specify the bottom-right corner for alignment
                            
                            @property BR
                            @type String
                            @value 'br'
                            @static
                            **/
                            PositionAlign.BR = 'br';
                            
                            /**
                            Constant used to specify the top edge-center point for alignment
                            
                            @property TC
                            @type String
                            @value 'tc'
                            @static
                            **/
                            PositionAlign.TC = 'tc';
                            
                            /**
                            Constant used to specify the right edge, center point for alignment
                            
                            @property RC
                            @type String
                            @value 'rc'
                            @static
                            **/
                            PositionAlign.RC = 'rc';
                            
                            /**
                            Constant used to specify the bottom edge, center point for alignment
                            
                            @property BC
                            @type String
                            @value 'bc'
                            @static
                            **/
                            PositionAlign.BC = 'bc';
                            
                            /**
                            Constant used to specify the left edge, center point for alignment
                            
                            @property LC
                            @type String
                            @value 'lc'
                            @static
                            **/
                            PositionAlign.LC = 'lc';
                            
                            /**
                            Constant used to specify the center of widget/node/viewport for alignment
                            
                            @property CC
                            @type String
                            @value 'cc'
                            @static
                            */
                            PositionAlign.CC = 'cc';
                            
                            PositionAlign.prototype = {
                                // -- Protected Properties -------------------------------------------------
                            
                            
                                initializer : function() {
                                    if (!this._posNode) {
                                        Y.error('WidgetPosition needs to be added to the Widget, ' +
                                            'before WidgetPositionAlign is added');
                                    }
                            
                                    Y.after(this._bindUIPosAlign, this, 'bindUI');
                                    Y.after(this._syncUIPosAlign, this, 'syncUI');
                                },
                            
                                /**
                                Holds the alignment-syncing event handles.
                            
                                @property _posAlignUIHandles
                                @type Array
                                @default null
                                @protected
                                **/
                                _posAlignUIHandles: null,
                            
                                // -- Lifecycle Methods ----------------------------------------------------
                            
                                initializer: function() {
                                    if ( ! this._posNode) {
                                        Y.error('WidgetPosition needs to be added to the Widget, ' +
                                            'before WidgetPositionAlign is added');
                                    }
                            
                                    Y.after(this._bindUIPosAlign, this, 'bindUI');
                                    Y.after(this._syncUIPosAlign, this, 'syncUI');
                                },
                            
                                destructor: function () {
                                    this._detachPosAlignUIHandles();
                                },
                            
                                /**
                                Bind event listeners responsible for updating the UI state in response to
                                the widget's position-align related state changes.
                            
                                This method is invoked after `bindUI` has been invoked for the `Widget`
                                class using the AOP infrastructure.
                            
                                @method _bindUIPosAlign
                                @protected
                                **/
                                _bindUIPosAlign: function () {
                                    this.after('alignChange', this._afterAlignChange);
                                    this.after('alignOnChange', this._afterAlignOnChange);
                                    this.after('visibleChange', this._syncUIPosAlign);
                                },
                            
                                /**
                                Synchronizes the current `align` Attribute value to the DOM.
                            
                                This method is invoked after `syncUI` has been invoked for the `Widget`
                                class using the AOP infrastructure.
                            
                                @method _syncUIPosAlign
                                @protected
                                **/
                                _syncUIPosAlign: function () {
                                    var align = this.get(ALIGN);
                            
                                    this._uiSetVisiblePosAlign(this.get(VISIBLE));
                            
                                    if (align) {
                                        this._uiSetAlign(align.node, align.points);
                                    }
                                },
                            
                                // -- Public Methods -------------------------------------------------------
                            
                                /**
                                Aligns this widget to the provided `Node` (or viewport) using the provided
                                points. This method can be invoked with no arguments which will cause the
                                widget's current `align` Attribute value to be synced to the DOM.
                            
                                @example Aligning to the top-left corner of the `<body>`:
                            
                                    myWidget.align('body',
                                        [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.TR]);
                            
                                @method align
                                @param {Node|String|null} [node] A reference (or selector String) for the
                                  `Node` which with the widget is to be aligned. If null is passed in, the
                                  widget will be aligned with the viewport.
                                @param {Array[2]} [points] A two item array specifying the points on the
                                  widget and `Node`/viewport which will to be aligned. The first entry is
                                  the point on the widget, and the second entry is the point on the
                                  `Node`/viewport. Valid point references are defined as static constants on
                                  the `WidgetPositionAlign` extension.
                                @chainable
                                **/
                                align: function (node, points) {
                                    if (arguments.length) {
                                        // Set the `align` Attribute.
                                        this.set(ALIGN, {
                                            node  : node,
                                            points: points
                                        });
                                    } else {
                                        // Sync the current `align` Attribute value to the DOM.
                                        this._syncUIPosAlign();
                                    }
                            
                                    return this;
                                },
                            
                                /**
                                Centers the widget in the viewport, or if a `Node` is passed in, it will
                                be centered to that `Node`.
                            
                                @method centered
                                @param {Node|String} [node] A `Node` reference or selector String defining
                                  the `Node` which the widget should be centered. If a `Node` is not  passed
                                  in, then the widget will be centered to the viewport.
                                @chainable
                                **/
                                centered: function (node) {
                                    return this.align(node, [PositionAlign.CC, PositionAlign.CC]);
                                },
                            
                                // -- Protected Methods ----------------------------------------------------
                            
                                /**
                                Returns coordinates realative to the passed `Node` alignment.
                            
                                @method _getAlignToXY
                                @param {Node} 'Node' The node to align to.
                                @param {Array} [point] The node alignment points.
                                @param {Number} 'Node' x coordinate.
                                @param {Number} 'Node' y coordinate.
                                @return {Array} the coordinates.
                                @private
                                **/
                                _getAlignToXY: function (node, point, x, y) {
                                    var xy;
                            
                                    switch (point) {
                                    case PositionAlign.TL:
                                        xy = [x, y];
                                        break;
                            
                                    case PositionAlign.TR:
                                        xy = [
                                            x - node.get(OFFSET_WIDTH),
                                            y
                                        ];
                                        break;
                            
                                    case PositionAlign.BL:
                                        xy = [
                                            x,
                                            y - node.get(OFFSET_HEIGHT)
                                        ];
                                        break;
                            
                                    case PositionAlign.BR:
                                        xy = [
                                            x - node.get(OFFSET_WIDTH),
                                            y - node.get(OFFSET_HEIGHT)
                                        ];
                                        break;
                            
                                    case PositionAlign.TC:
                                        xy = [
                                            x - (node.get(OFFSET_WIDTH) / 2),
                                            y
                                        ];
                                        break;
                            
                                    case PositionAlign.BC:
                                        xy = [
                                            x - (node.get(OFFSET_WIDTH) / 2),
                                            y - node.get(OFFSET_HEIGHT)
                                        ];
                                        break;
                            
                                    case PositionAlign.LC:
                                        xy = [
                                            x,
                                            y - (node.get(OFFSET_HEIGHT) / 2)
                                        ];
                                        break;
                            
                                    case PositionAlign.RC:
                                        xy = [
                                            x - node.get(OFFSET_WIDTH),
                                            y - (node.get(OFFSET_HEIGHT) / 2)
                                        ];
                                        break;
                            
                                    case PositionAlign.CC:
                                        xy = [
                                            x - (node.get(OFFSET_WIDTH) / 2),
                                            y - (node.get(OFFSET_HEIGHT) / 2)
                                        ];
                                        break;
                            
                                    default:
                                        Y.log('align: Invalid Points Argument', 'info',
                                            'widget-position-align');
                                        break;
                            
                                    }
                            
                                    return xy;
                                },
                            
                                /**
                                Returns `Widget` alignment coordinates realative to the given `Node`.
                            
                                @method _getAlignedXY
                                @param {Node|String|null} [node] The node to align to, or null to indicate
                                  the viewport.
                                @param {Array} points The alignment points.
                                @return {Array} the coordinates.
                                @protected
                                **/
                                _getAlignedXY: function (node, points) {
                                    if ( ! Lang.isArray(points) || points.length !== 2) {
                                        Y.error('align: Invalid Points Arguments');
                                        return;
                                    }
                            
                                    var nodeRegion = this._getRegion(node), nodePoint, xy;
                            
                                    if ( ! nodeRegion) {
                                        // No-op, nothing to align to.
                                        return;
                                    }
                            
                                    nodePoint   = points[1];
                            
                                    // TODO: Optimize KWeight - Would lookup table help?
                                    switch (nodePoint) {
                                    case PositionAlign.TL:
                                        xy = [nodeRegion.left, nodeRegion.top];
                                        break;
                            
                                    case PositionAlign.TR:
                                        xy = [nodeRegion.right, nodeRegion.top];
                                        break;
                            
                                    case PositionAlign.BL:
                                        xy = [nodeRegion.left, nodeRegion.bottom];
                                        break;
                            
                                    case PositionAlign.BR:
                                        xy = [nodeRegion.right, nodeRegion.bottom];
                                        break;
                            
                                    case PositionAlign.TC:
                                        xy = [
                                            nodeRegion.left + Math.floor(nodeRegion.width / 2),
                                            nodeRegion.top
                                        ];
                                        break;
                            
                                    case PositionAlign.BC:
                                        xy = [
                                            nodeRegion.left + Math.floor(nodeRegion.width / 2),
                                            nodeRegion.bottom
                                        ];
                                        break;
                            
                                    case PositionAlign.LC:
                                        xy = [
                                            nodeRegion.left,
                                            nodeRegion.top + Math.floor(nodeRegion.height / 2)
                                        ];
                                        break;
                            
                                    case PositionAlign.RC:
                                        xy = [
                                            nodeRegion.right,
                                            nodeRegion.top + Math.floor(nodeRegion.height / 2)
                                        ];
                                        break;
                            
                                    case PositionAlign.CC:
                                        xy = [
                                            nodeRegion.left + Math.floor(nodeRegion.width / 2),
                                            nodeRegion.top + Math.floor(nodeRegion.height / 2)
                                        ];
                                        break;
                            
                                    default:
                                        Y.log('align: Invalid Points Argument', 'info',
                                            'widget-position-align');
                                        break;
                            
                                    }
                            
                                    return this._getAlignToXY(this._posNode, points[0], xy[0], xy[1]);
                                },
                            
                                /**
                                Default setter for `center` Attribute changes. Sets up the appropriate
                                value, and passes it through the to the align attribute.
                            
                                @method _setAlignCenter
                                @param {Boolean|Node} val The Attribute value being set.
                                @return {Boolean|Node} the value passed in.
                                @protected
                                **/
                                _setAlignCenter: function (val) {
                                    if (val) {
                                        this.set(ALIGN, {
                                            node  : val === true ? null : val,
                                            points: [PositionAlign.CC, PositionAlign.CC]
                                        });
                                    }
                            
                                    return val;
                                },
                            
                                /**
                                Updates the UI to reflect the `align` value passed in.
                            
                                **Note:** See the `align` Attribute documentation, for the Object structure
                                expected.
                            
                                @method _uiSetAlign
                                @param {Node|String|null} [node] The node to align to, or null to indicate
                                  the viewport.
                                @param {Array} points The alignment points.
                                @protected
                                **/
                                _uiSetAlign: function (node, points) {
                                    var xy = this._getAlignedXY(node, points);
                            
                                    if (xy) {
                                        this._doAlign(xy);
                                    }
                                },
                            
                                /**
                                Attaches or detaches alignment-syncing event handlers based on the widget's
                                `visible` Attribute state.
                            
                                @method _uiSetVisiblePosAlign
                                @param {Boolean} visible The current value of the widget's `visible`
                                  Attribute.
                                @protected
                                **/
                                _uiSetVisiblePosAlign: function (visible) {
                                    if (visible) {
                                        this._attachPosAlignUIHandles();
                                    } else {
                                        this._detachPosAlignUIHandles();
                                    }
                                },
                            
                                /**
                                Attaches the alignment-syncing event handlers.
                            
                                @method _attachPosAlignUIHandles
                                @protected
                                **/
                                _attachPosAlignUIHandles: function () {
                                    if (this._posAlignUIHandles) {
                                        // No-op if we have already setup the event handlers.
                                        return;
                                    }
                            
                                    var bb        = this.get(BOUNDING_BOX),
                                        syncAlign = Y.bind(this._syncUIPosAlign, this),
                                        handles   = [];
                            
                                    Y.Array.each(this.get(ALIGN_ON), function (o) {
                                        var event = o.eventName,
                                            node  = Y.one(o.node) || bb;
                            
                                        if (event) {
                                            handles.push(node.on(event, syncAlign));
                                        }
                                    });
                            
                                    this._posAlignUIHandles = handles;
                                },
                            
                                /**
                                Detaches the alignment-syncing event handlers.
                            
                                @method _detachPosAlignUIHandles
                                @protected
                                **/
                                _detachPosAlignUIHandles: function () {
                                    var handles = this._posAlignUIHandles;
                                    if (handles) {
                                        new Y.EventHandle(handles).detach();
                                        this._posAlignUIHandles = null;
                                    }
                                },
                            
                                // -- Private Methods ------------------------------------------------------
                            
                                /**
                                Helper method, used to align the given point on the widget, with the XY page
                                coordinates provided.
                            
                                @method _doAlign
                                @param {Array} xy XY page coordinates to align to.
                                @private
                                **/
                                _doAlign: function(xy) {
                                    if (xy) {
                                        this.move(xy);
                                    }
                                },
                            
                                /**
                                Returns the region of the passed-in `Node`, or the viewport region if
                                calling with passing in a `Node`.
                            
                                @method _getRegion
                                @param {Node} [node] The node to get the region of.
                                @return {Object} The node's region.
                                @private
                                **/
                                _getRegion: function (node) {
                                    var nodeRegion;
                            
                                    if ( ! node) {
                                        nodeRegion = this._posNode.get(VIEWPORT_REGION);
                                    } else {
                                        node = Y.Node.one(node);
                                        if (node) {
                                            nodeRegion = node.get(REGION);
                                        }
                                    }
                            
                                    return nodeRegion;
                                },
                            
                                // -- Protected Event Handlers ---------------------------------------------
                            
                                /**
                                Handles `alignChange` events by updating the UI in response to `align`
                                Attribute changes.
                            
                                @method _afterAlignChange
                                @param {EventFacade} e
                                @protected
                                **/
                                _afterAlignChange: function (e) {
                                    var align = e.newVal;
                                    if (align) {
                                        this._uiSetAlign(align.node, align.points);
                                    }
                                },
                            
                                /**
                                Handles `alignOnChange` events by updating the alignment-syncing event
                                handlers.
                            
                                @method _afterAlignOnChange
                                @param {EventFacade} e
                                @protected
                                **/
                                _afterAlignOnChange: function(e) {
                                    this._detachPosAlignUIHandles();
                            
                                    if (this.get(VISIBLE)) {
                                        this._attachPosAlignUIHandles();
                                    }
                                }
                            };
                            
                            Y.WidgetPositionAlign = PositionAlign;