#

API Docs for: 3.10.3
Show: Inherited Protected Private Deprecated

# File: widget-child/js/Widget-Child.js

    /**
     * Extension enabling a Widget to be a child of another Widget.
     *
     * @module widget-child
     */

    var Lang = Y.Lang;

    /**
     * Widget extension providing functionality enabling a Widget to be a
     * child of another Widget.
     *
     * @class WidgetChild
     * @param {Object} config User configuration object.
    */
    function Child() {

        //  Widget method overlap
        Y.after(this._syncUIChild, this, "syncUI");
        Y.after(this._bindUIChild, this, "bindUI");

    }

    Child.ATTRS = {

        /**
         * @attribute selected
         * @type Number
         * @default 0
         *
         * @description Number indicating if the Widget is selected.  Possible
         * values are:
         * <dl>
         * <dt>0</dt> <dd>(Default) Not selected</dd>
         * <dt>1</dt> <dd>Fully selected</dd>
         * <dt>2</dt> <dd>Partially selected</dd>
         * </dl>
        */
        selected: {
            value: 0,
            validator: Lang.isNumber
        },

        /**
         * @attribute index
         * @type Number
         * @readOnly
         *
         * @description Number representing the Widget's ordinal position in its
         * parent Widget.
         */
        index: {
            readOnly: true,
            getter: function () {

                var parent = this.get("parent"),
                    index = -1;

                if (parent) {
                    index = parent.indexOf(this);
                }

                return index;

            }
        },

        /**
         * @attribute parent
         * @type Widget
         * @readOnly
         *
         * @description Retrieves the parent of the Widget in the object hierarchy.
        */
        parent: {
            readOnly: true
        },

        /**
         * @attribute depth
         * @type Number
         * @default -1
         * @readOnly
         *
         * @description Number representing the depth of this Widget relative to
         * the root Widget in the object heirarchy.
         */
        depth: {
            readOnly: true,
            getter: function () {

                var parent = this.get("parent"),
                    root = this.get("root"),
                    depth = -1;

                while (parent) {

                    depth = (depth + 1);

                    if (parent == root) {
                        break;
                    }

                    parent = parent.get("parent");

                }

                return depth;

            }
        },

        /**
         * @attribute root
         * @type Widget
         * @readOnly
         *
         * @description Returns the root Widget in the object hierarchy.  If the
         * ROOT_TYPE property is set, the search for the root Widget will be
         * constrained to parent Widgets of the specified type.
         */
        root: {
            readOnly: true,
            getter: function () {

                var getParent = function (child) {

                    var parent = child.get("parent"),
                        FnRootType = child.ROOT_TYPE,
                        criteria = parent;

                    if (FnRootType) {
                        criteria = (parent && Y.instanceOf(parent, FnRootType));
                    }

                    return (criteria ? getParent(parent) : child);

                };

                return getParent(this);

            }
        }

    };

    Child.prototype = {

        /**
         * Constructor reference used to determine the root of a Widget-based
         * object tree.
         * <p>
         * Currently used to control the behavior of the <code>root</code>
         * attribute so that recursing up the object heirarchy can be constrained
         * to a specific type of Widget.  Widget authors should set this property
         * to the constructor function for a given Widget implementation.
         * </p>
         *
         * @property ROOT_TYPE
         * @type Object
         */
        ROOT_TYPE: null,

        /**
         * Returns the node on which to bind delegate listeners.
         *
         * Override of Widget's implementation of _getUIEventNode() to ensure that
         * all event listeners are bound to the Widget's topmost DOM element.
         * This ensures that the firing of each type of Widget UI event (click,
         * mousedown, etc.) is facilitated by a single, top-level, delegated DOM
         * event listener.
         *
         * @method _getUIEventNode
         * @for Widget
         * @protected
         */
        _getUIEventNode: function () {
            var root = this.get("root"),
                returnVal;

            if (root) {
                returnVal = root.get("boundingBox");
            }

            return returnVal;
        },

        /**
        * @method next
        * @description Returns the Widget's next sibling.
        * @param {Boolean} circular Boolean indicating if the parent's first child
        * should be returned if the child has no next sibling.
        * @return {Widget} Widget instance.
        */
        next: function (circular) {

            var parent = this.get("parent"),
                sibling;

            if (parent) {
                sibling = parent.item((this.get("index")+1));
            }

            if (!sibling && circular) {
                sibling = parent.item(0);
            }

            return sibling;

        },

        /**
        * @method previous
        * @description Returns the Widget's previous sibling.
        * @param {Boolean} circular Boolean indicating if the parent's last child
        * should be returned if the child has no previous sibling.
        * @return {Widget} Widget instance.
        */
        previous: function (circular) {

            var parent = this.get("parent"),
                index = this.get("index"),
                sibling;

            if (parent && index > 0) {
                sibling = parent.item([(index-1)]);
            }

            if (!sibling && circular) {
                sibling = parent.item((parent.size() - 1));
            }

            return sibling;

        },

        //  Override of Y.WidgetParent.remove()
        //  Sugar implementation allowing a child to remove itself from its parent.
        remove: function (index) {

            var parent,
                removed;

            if (Lang.isNumber(index)) {
                removed = Y.WidgetParent.prototype.remove.apply(this, arguments);
            }
            else {

                parent = this.get("parent");

                if (parent) {
                    removed = parent.remove(this.get("index"));
                }

            }

            return removed;

        },

        /**
        * @method isRoot
        * @description Determines if the Widget is the root Widget in the
        * object hierarchy.
        * @return {Boolean} Boolean indicating if Widget is the root Widget in the
        * object hierarchy.
        */
        isRoot: function () {
            return (this == this.get("root"));
        },

        /**
        * @method ancestor
        * @description Returns the Widget instance at the specified depth.
        * @param {number} depth Number representing the depth of the ancestor.
        * @return {Widget} Widget instance.
        */
        ancestor: function (depth) {

            var root = this.get("root"),
                parent;

            if (this.get("depth") > depth)  {

                parent = this.get("parent");

                while (parent != root && parent.get("depth") > depth) {
                    parent = parent.get("parent");
                }

            }

            return parent;

        },

        /**
         * Updates the UI to reflect the <code>selected</code> attribute value.
         *
         * @method _uiSetChildSelected
         * @protected
         * @param {number} selected The selected value to be reflected in the UI.
         */
        _uiSetChildSelected: function (selected) {

            var box = this.get("boundingBox"),
                sClassName = this.getClassName("selected");

            if (selected === 0) {
                box.removeClass(sClassName);
            }
            else {
                box.addClass(sClassName);
            }

        },

        /**
         * Default attribute change listener for the <code>selected</code>
         * attribute, responsible for updating the UI, in response to
         * attribute changes.
         *
         * @method _afterChildSelectedChange
         * @protected
         * @param {EventFacade} event The event facade for the attribute change.
         */
        _afterChildSelectedChange: function (event) {
            this._uiSetChildSelected(event.newVal);
        },

        /**
         * Synchronizes the UI to match the WidgetChild state.
         * <p>
         * This method is invoked after bindUI is invoked for the Widget class
         * using YUI's aop infrastructure.
         * </p>
         *
         * @method _syncUIChild
         * @protected
         */
        _syncUIChild: function () {
            this._uiSetChildSelected(this.get("selected"));
        },

        /**
         * Binds event listeners responsible for updating the UI state in response
         * to WidgetChild related state changes.
         * <p>
         * This method is invoked after bindUI is invoked for the Widget class
         * using YUI's aop infrastructure.
         * </p>
         * @method _bindUIChild
         * @protected
         */
        _bindUIChild: function () {
            this.after("selectedChange", this._afterChildSelectedChange);
        }

    };

    Y.WidgetChild = Child;