    /**
     * Provides advanced positioning support for Node via a Plugin
     * for centering and alignment.
     * @module align-plugin
     */

    var OFFSET_WIDTH = 'offsetWidth',
        OFFSET_HEIGHT = 'offsetHeight',
        undefined = undefined;

    /**
     * Node plugin which can be used to align a node with another node,
     * region, or the viewport.
     *
     * @class Plugin.Align
     * @param {Object} User configuration object
     */
    function Align(config) {
        if (config.host) {
            this._host = config.host;
        }
    }

    Align.prototype = {
        /**
         * Aligns node with a point on another node or region.
         * Possible alignment points are:
         * <dl>
         *      <dt>tl</dt>
         *      <dd>top left</dd>
         *      <dt>tr</dt>
         *      <dd>top right</dd>
         *      <dt>bl</dt>
         *      <dd>bottom left</dd>
         *      <dt>br</dt>
         *      <dd>bottom right</dd>
         *      <dt>tc</dt>
         *      <dd>top center</dd>
         *      <dt>bc</dt>
         *      <dd>bottom center</dd>
         *      <dt>rc</dt>
         *      <dd>right center</dd>
         *      <dt>lc</dt>
         *      <dd>left center</dd>
         *      <dt>cc</dt>
         *      <dd>center center</dd>
         * </dl>
         * @method to
         * @param region {String|Node|HTMLElement|Object} The node or
         * region to align with. Defaults to the viewport region.
         * @param regionPoint {String} The point of the region to align with.
         * @param point {String} The point of the node aligned to the region.
         * @param resize {Boolean} Whether or not the node should re-align when
         * the window is resized. Defaults to false.
         */
        to: function(region, regionPoint, point, syncOnResize) {
            // cache original args for syncing
            this._syncArgs = Y.Array(arguments);

            if (region.top === undefined) {
                region = Y.one(region).get('region');
            }

            if (region) {
                var xy = [region.left, region.top],
                    offxy = [region.width, region.height],
                    points = Align.points,
                    node = this._host,
                    NULL = null,
                    size = node.getAttrs([OFFSET_HEIGHT, OFFSET_WIDTH]),
                    nodeoff = [0 - size[OFFSET_WIDTH], 0 - size[OFFSET_HEIGHT]], // reverse offsets
                    regionFn0 = regionPoint ? points[regionPoint.charAt(0)]: NULL,
                    regionFn1 = (regionPoint && regionPoint !== 'cc') ? points[regionPoint.charAt(1)] : NULL,
                    nodeFn0 = point ? points[point.charAt(0)] : NULL,
                    nodeFn1 = (point && point !== 'cc') ? points[point.charAt(1)] : NULL;

                if (regionFn0) {
                    xy = regionFn0(xy, offxy, regionPoint);
                }
                if (regionFn1) {
                    xy = regionFn1(xy, offxy, regionPoint);
                }

                if (nodeFn0) {
                    xy = nodeFn0(xy, nodeoff, point);
                }
                if (nodeFn1) {
                    xy = nodeFn1(xy, nodeoff, point);
                }

                if (xy && node) {
                    node.setXY(xy);
                }

                this._resize(syncOnResize);

            }
            return this;
        },

        sync: function() {
            this.to.apply(this, this._syncArgs);
            return this;
        },

        _resize: function(add) {
            var handle = this._handle;
            if (add && !handle) {
                this._handle = Y.on('resize', this._onresize, window, this);
            } else if (!add && handle) {
                handle.detach();
            }

        },

        _onresize: function() {
            var self = this;
            setTimeout(function() { // for performance
                self.sync();
            });
        },

        /**
         * Aligns the center of a node to the center of another node or region.
         * @method center
         * @param region {Node|HTMLElement|Object} optional The node or
         * region to align with. Defaults to the viewport region.
         * the window is resized. If centering to viewport, this defaults
         * to true, otherwise default is false.
         */
        center: function(region, resize) {
            this.to(region, 'cc', 'cc', resize);
            return this;
        },

        /**
         * Removes the resize handler, if any. This is called automatically
         * when unplugged from the host node.
         * @method destroy
         */
        destroy: function() {
            var handle = this._handle;
            if (handle) {
                handle.detach();
            }
        }
    };

    Align.points = {
        't': function(xy, off) {
            return xy;
        },

        'r': function(xy, off) {
            return [xy[0] + off[0], xy[1]];
        },

        'b': function(xy, off) {
            return [xy[0], xy[1] + off[1]];
        },

        'l': function(xy, off) {
            return xy;
        },

        'c': function(xy, off, point) {
            var axis = (point[0] === 't' || point[0] === 'b') ?  0 : 1,
                ret, val;

            if (point === 'cc') {
                ret = [xy[0] + off[0] / 2, xy[1] + off[1] / 2];
            } else {
                val = xy[axis] + off[axis] / 2;
                ret = (axis) ? [xy[0], val] : [val, xy[1]];
            }

             return ret;
        }
    };

    Align.NAME = 'Align';
    Align.NS = 'align';

    Align.prototype.constructor = Align;

    Y.namespace('Plugin');
    Y.Plugin.Align = Align;

