Version 3.18.1
Show:

File: widget/js/Widget.js

            /**
             * Provides the base Widget class, with HTML Parser support
             *
             * @module widget
             * @main widget
             */
            
            /**
             * Provides the base Widget class
             *
             * @module widget
             * @submodule widget-base
             */
            var L = Y.Lang,
                Node = Y.Node,
            
                ClassNameManager = Y.ClassNameManager,
            
                _getClassName = ClassNameManager.getClassName,
                _getWidgetClassName,
            
                _toInitialCap = Y.cached(function(str) {
                    return str.substring(0, 1).toUpperCase() + str.substring(1);
                }),
            
                // K-Weight, IE GC optimizations
                CONTENT = "content",
                VISIBLE = "visible",
                HIDDEN = "hidden",
                DISABLED = "disabled",
                FOCUSED = "focused",
                WIDTH = "width",
                HEIGHT = "height",
                BOUNDING_BOX = "boundingBox",
                CONTENT_BOX = "contentBox",
                PARENT_NODE = "parentNode",
                OWNER_DOCUMENT = "ownerDocument",
                AUTO = "auto",
                SRC_NODE = "srcNode",
                BODY = "body",
                TAB_INDEX = "tabIndex",
                ID = "id",
                RENDER = "render",
                RENDERED = "rendered",
                DESTROYED = "destroyed",
                STRINGS = "strings",
                DIV = "<div></div>",
                CHANGE = "Change",
                LOADING = "loading",
            
                _UISET = "_uiSet",
            
                EMPTY_STR = "",
                EMPTY_FN = function() {},
            
                TRUE = true,
                FALSE = false,
            
                UI,
                ATTRS = {},
                UI_ATTRS = [VISIBLE, DISABLED, HEIGHT, WIDTH, FOCUSED, TAB_INDEX],
            
                WEBKIT = Y.UA.webkit,
            
                // Widget nodeid-to-instance map.
                _instances = {};
            
            /**
             * A base class for widgets, providing:
             * <ul>
             *    <li>The render lifecycle method, in addition to the init and destroy
             *        lifecycle methods provide by Base</li>
             *    <li>Abstract methods to support consistent MVC structure across
             *        widgets: renderer, renderUI, bindUI, syncUI</li>
             *    <li>Support for common widget attributes, such as boundingBox, contentBox, visible,
             *        disabled, focused, strings</li>
             * </ul>
             *
             * @param config {Object} Object literal specifying widget configuration properties.
             *
             * @class Widget
             * @constructor
             * @extends Base
             */
            function Widget(config) {
                Y.log('constructor called', 'life', 'widget');
            
                // kweight
                var widget = this,
                    parentNode,
                    render,
                    constructor = widget.constructor;
            
                widget._strs = {};
                widget._cssPrefix = constructor.CSS_PREFIX || _getClassName(constructor.NAME.toLowerCase());
            
                // We need a config for HTML_PARSER to work.
                config = config || {};
            
                Widget.superclass.constructor.call(widget, config);
            
                render = widget.get(RENDER);
            
                if (render) {
                    // Render could be a node or boolean
                    if (render !== TRUE) {
                        parentNode = render;
                    }
                    widget.render(parentNode);
                }
            }
            
            /**
             * Static property provides a string to identify the class.
             * <p>
             * Currently used to apply class identifiers to the bounding box
             * and to classify events fired by the widget.
             * </p>
             *
             * @property NAME
             * @type String
             * @static
             */
            Widget.NAME = "widget";
            
            /**
             * Constant used to identify state changes originating from
             * the DOM (as opposed to the JavaScript model).
             *
             * @property UI_SRC
             * @type String
             * @static
             * @final
             */
            UI = Widget.UI_SRC = "ui";
            
            /**
             * Static property used to define the default attribute
             * configuration for the Widget.
             *
             * @property ATTRS
             * @type Object
             * @static
             */
            Widget.ATTRS = ATTRS;
            
            // Trying to optimize kweight by setting up attrs this way saves about 0.4K min'd
            
            /**
             * @attribute id
             * @writeOnce
             * @default Generated using guid()
             * @type String
             */
            
            ATTRS[ID] = {
                valueFn: "_guid",
                writeOnce: TRUE
            };
            
            /**
             * Flag indicating whether or not this Widget
             * has been through the render lifecycle phase.
             *
             * @attribute rendered
             * @readOnly
             * @default false
             * @type boolean
             */
            ATTRS[RENDERED] = {
                value:FALSE,
                readOnly: TRUE
            };
            
            /**
             * @attribute boundingBox
             * @description The outermost DOM node for the Widget, used for sizing and positioning
             * of a Widget as well as a containing element for any decorator elements used
             * for skinning.
             * @type String | Node
             * @writeOnce
             */
            ATTRS[BOUNDING_BOX] = {
                valueFn:"_defaultBB",
                setter: "_setBB",
                writeOnce: TRUE
            };
            
            /**
             * @attribute contentBox
             * @description A DOM node that is a direct descendant of a Widget's bounding box that
             * houses its content.
             * @type String | Node
             * @writeOnce
             */
            ATTRS[CONTENT_BOX] = {
                valueFn:"_defaultCB",
                setter: "_setCB",
                writeOnce: TRUE
            };
            
            /**
             * @attribute tabIndex
             * @description Number (between -32767 to 32767) indicating the widget's
             * position in the default tab flow.  The value is used to set the
             * "tabIndex" attribute on the widget's bounding box.  Negative values allow
             * the widget to receive DOM focus programmatically (by calling the focus
             * method), while being removed from the default tab flow.  A value of
             * null removes the "tabIndex" attribute from the widget's bounding box.
             * @type Number
             * @default null
             */
            ATTRS[TAB_INDEX] = {
                value: null,
                validator: "_validTabIndex"
            };
            
            /**
             * @attribute focused
             * @description Boolean indicating if the Widget, or one of its descendants,
             * has focus.
             * @readOnly
             * @default false
             * @type boolean
             */
            ATTRS[FOCUSED] = {
                value: FALSE,
                readOnly:TRUE
            };
            
            /**
             * @attribute disabled
             * @description Boolean indicating if the Widget should be disabled. The disabled implementation
             * is left to the specific classes extending widget.
             * @default false
             * @type boolean
             */
            ATTRS[DISABLED] = {
                value: FALSE
            };
            
            /**
             * @attribute visible
             * @description Boolean indicating whether or not the Widget is visible.
             * @default TRUE
             * @type boolean
             */
            ATTRS[VISIBLE] = {
                value: TRUE
            };
            
            /**
             * @attribute height
             * @description String with units, or number, representing the height of the Widget. If a number is provided,
             * the default unit, defined by the Widgets DEF_UNIT, property is used.
             * @default EMPTY_STR
             * @type {String | Number}
             */
            ATTRS[HEIGHT] = {
                value: EMPTY_STR
            };
            
            /**
             * @attribute width
             * @description String with units, or number, representing the width of the Widget. If a number is provided,
             * the default unit, defined by the Widgets DEF_UNIT, property is used.
             * @default EMPTY_STR
             * @type {String | Number}
             */
            ATTRS[WIDTH] = {
                value: EMPTY_STR
            };
            
            /**
             * @attribute strings
             * @description Collection of strings used to label elements of the Widget's UI.
             * @default null
             * @type Object
             */
            ATTRS[STRINGS] = {
                value: {},
                setter: "_strSetter",
                getter: "_strGetter"
            };
            
            /**
             * Whether or not to render the widget automatically after init, and optionally, to which parent node.
             *
             * @attribute render
             * @type boolean | Node
             * @writeOnce
             */
            ATTRS[RENDER] = {
                value:FALSE,
                writeOnce:TRUE
            };
            
            /**
             * The css prefix which the static Widget.getClassName method should use when constructing class names
             *
             * @property CSS_PREFIX
             * @type String
             * @default Widget.NAME.toLowerCase()
             * @private
             * @static
             */
            Widget.CSS_PREFIX = _getClassName(Widget.NAME.toLowerCase());
            
            /**
             * Generate a standard prefixed classname for the Widget, prefixed by the default prefix defined
             * by the <code>Y.config.classNamePrefix</code> attribute used by <code>ClassNameManager</code> and
             * <code>Widget.NAME.toLowerCase()</code> (e.g. "yui-widget-xxxxx-yyyyy", based on default values for
             * the prefix and widget class name).
             * <p>
             * The instance based version of this method can be used to generate standard prefixed classnames,
             * based on the instances NAME, as opposed to Widget.NAME. This method should be used when you
             * need to use a constant class name across different types instances.
             * </p>
             * @method getClassName
             * @param {String*} args* 0..n strings which should be concatenated, using the default separator defined by ClassNameManager, to create the class name
             */
            Widget.getClassName = function() {
                // arguments needs to be array'fied to concat
                return _getClassName.apply(ClassNameManager, [Widget.CSS_PREFIX].concat(Y.Array(arguments), true));
            };
            
            _getWidgetClassName = Widget.getClassName;
            
            /**
             * Returns the widget instance whose bounding box contains, or is, the given node.
             * <p>
             * In the case of nested widgets, the nearest bounding box ancestor is used to
             * return the widget instance.
             * </p>
             * @method getByNode
             * @static
             * @param node {Node | String} The node for which to return a Widget instance. If a selector
             * string is passed in, which selects more than one node, the first node found is used.
             * @return {Widget} Widget instance, or null if not found.
             */
            Widget.getByNode = function(node) {
                var widget,
                    widgetMarker = _getWidgetClassName();
            
                node = Node.one(node);
                if (node) {
                    node = node.ancestor("." + widgetMarker, true);
                    if (node) {
                        widget = _instances[Y.stamp(node, true)];
                    }
                }
            
                return widget || null;
            };
            
            Y.extend(Widget, Y.Base, {
            
                /**
                 * Returns a class name prefixed with the the value of the
                 * <code>YUI.config.classNamePrefix</code> attribute + the instances <code>NAME</code> property.
                 * Uses <code>YUI.config.classNameDelimiter</code> attribute to delimit the provided strings.
                 * e.g.
                 * <code>
                 * <pre>
                 *    // returns "yui-slider-foo-bar", for a slider instance
                 *    var scn = slider.getClassName('foo','bar');
                 *
                 *    // returns "yui-overlay-foo-bar", for an overlay instance
                 *    var ocn = overlay.getClassName('foo','bar');
                 * </pre>
                 * </code>
                 *
                 * @method getClassName
                 * @param {String} [classnames*] One or more classname bits to be joined and prefixed
                 */
                getClassName: function () {
                    return _getClassName.apply(ClassNameManager, [this._cssPrefix].concat(Y.Array(arguments), true));
                },
            
                /**
                 * Initializer lifecycle implementation for the Widget class. Registers the
                 * widget instance, and runs through the Widget's HTML_PARSER definition.
                 *
                 * @method initializer
                 * @protected
                 * @param  config {Object} Configuration object literal for the widget
                 */
                initializer: function(config) {
                    Y.log('initializer called', 'life', 'widget');
            
                    var bb = this.get(BOUNDING_BOX);
            
                    if (bb instanceof Node) {
                        this._mapInstance(Y.stamp(bb));
                    }
            
                    /**
                     * Notification event, which widget implementations can fire, when
                     * they change the content of the widget. This event has no default
                     * behavior and cannot be prevented, so the "on" or "after"
                     * moments are effectively equivalent (with on listeners being invoked before
                     * after listeners).
                     *
                     * @event widget:contentUpdate
                     * @preventable false
                     * @param {EventFacade} e The Event Facade
                     */
                },
            
                /**
                 * Utility method used to add an entry to the boundingBox id to instance map.
                 *
                 * This method can be used to populate the instance with lazily created boundingBox Node references.
                 *
                 * @method _mapInstance
                 * @param {String} The boundingBox id
                 * @protected
                 */
                _mapInstance : function(id) {
                    _instances[id] = this;
                },
            
                /**
                 * Destructor lifecycle implementation for the Widget class. Purges events attached
                 * to the bounding box and content box, removes them from the DOM and removes
                 * the Widget from the list of registered widgets.
                 *
                 * @method destructor
                 * @protected
                 */
                destructor: function() {
                    Y.log('destructor called', 'life', 'widget');
            
                    var boundingBox = this.get(BOUNDING_BOX),
                        bbGuid;
            
                    if (boundingBox instanceof Node) {
                        bbGuid = Y.stamp(boundingBox,true);
            
                        if (bbGuid in _instances) {
                            delete _instances[bbGuid];
                        }
            
                        this._destroyBox();
                    }
                },
            
                /**
                 * <p>
                 * Destroy lifecycle method. Fires the destroy
                 * event, prior to invoking destructors for the
                 * class hierarchy.
                 *
                 * Overrides Base's implementation, to support arguments to destroy
                 * </p>
                 * <p>
                 * Subscribers to the destroy
                 * event can invoke preventDefault on the event object, to prevent destruction
                 * from proceeding.
                 * </p>
                 * @method destroy
                 * @param destroyAllNodes {Boolean} If true, all nodes contained within the Widget are
                 * removed and destroyed. Defaults to false due to potentially high run-time cost.
                 * @return {Widget} A reference to this object
                 * @chainable
                 */
                destroy: function(destroyAllNodes) {
                    this._destroyAllNodes = destroyAllNodes;
                    return Widget.superclass.destroy.apply(this);
                },
            
                /**
                 * Removes and destroys the widgets rendered boundingBox, contentBox,
                 * and detaches bound UI events.
                 *
                 * @method _destroyBox
                 * @protected
                 */
                _destroyBox : function() {
            
                    var boundingBox = this.get(BOUNDING_BOX),
                        contentBox = this.get(CONTENT_BOX),
                        deep = this._destroyAllNodes,
                        same;
            
                    same = boundingBox && boundingBox.compareTo(contentBox);
            
                    if (this.UI_EVENTS) {
                        this._destroyUIEvents();
                    }
            
                    this._unbindUI(boundingBox);
            
                    if (contentBox) {
                        if (deep) {
                            contentBox.empty();
                        }
                        contentBox.remove(TRUE);
                    }
            
                    if (!same) {
                        if (deep) {
                            boundingBox.empty();
                        }
                        boundingBox.remove(TRUE);
                    }
                },
            
                /**
                 * Establishes the initial DOM for the widget. Invoking this
                 * method will lead to the creating of all DOM elements for
                 * the widget (or the manipulation of existing DOM elements
                 * for the progressive enhancement use case).
                 * <p>
                 * This method should only be invoked once for an initialized
                 * widget.
                 * </p>
                 * <p>
                 * It delegates to the widget specific renderer method to do
                 * the actual work.
                 * </p>
                 *
                 * @method render
                 * @chainable
                 * @final
                 * @param  parentNode {Object | String} Optional. The Node under which the
                 * Widget is to be rendered. This can be a Node instance or a CSS selector string.
                 * <p>
                 * If the selector string returns more than one Node, the first node will be used
                 * as the parentNode. NOTE: This argument is required if both the boundingBox and contentBox
                 * are not currently in the document. If it's not provided, the Widget will be rendered
                 * to the body of the current document in this case.
                 * </p>
                 */
                render: function(parentNode) {
                    if (this.get(DESTROYED)) { Y.log("Render failed; widget has been destroyed", "error", "widget"); }
            
                    if (!this.get(DESTROYED) && !this.get(RENDERED)) {
                         /**
                          * Lifecycle event for the render phase, fired prior to rendering the UI
                          * for the widget (prior to invoking the widget's renderer method).
                          * <p>
                          * Subscribers to the "on" moment of this event, will be notified
                          * before the widget is rendered.
                          * </p>
                          * <p>
                          * Subscribers to the "after" moment of this event, will be notified
                          * after rendering is complete.
                          * </p>
                          *
                          * @event render
                          * @preventable _defRenderFn
                          * @param {EventFacade} e The Event Facade
                          */
                        this.publish(RENDER, {
                            queuable:FALSE,
                            fireOnce:TRUE,
                            defaultTargetOnly:TRUE,
                            defaultFn: this._defRenderFn
                        });
            
                        this.fire(RENDER, {parentNode: (parentNode) ? Node.one(parentNode) : null});
                    }
                    return this;
                },
            
                /**
                 * Default render handler
                 *
                 * @method _defRenderFn
                 * @protected
                 * @param {EventFacade} e The Event object
                 * @param {Node} parentNode The parent node to render to, if passed in to the <code>render</code> method
                 */
                _defRenderFn : function(e) {
                    this._parentNode = e.parentNode;
            
                    this.renderer();
                    this._set(RENDERED, TRUE);
            
                    this._removeLoadingClassNames();
                },
            
                /**
                 * Creates DOM (or manipulates DOM for progressive enhancement)
                 * This method is invoked by render() and is not chained
                 * automatically for the class hierarchy (unlike initializer, destructor)
                 * so it should be chained manually for subclasses if required.
                 *
                 * @method renderer
                 * @protected
                 */
                renderer: function() {
                    // kweight
                    var widget = this;
            
                    widget._renderUI();
                    widget.renderUI();
            
                    widget._bindUI();
                    widget.bindUI();
            
                    widget._syncUI();
                    widget.syncUI();
                },
            
                /**
                 * Configures/Sets up listeners to bind Widget State to UI/DOM
                 *
                 * This method is not called by framework and is not chained
                 * automatically for the class hierarchy.
                 *
                 * @method bindUI
                 * @protected
                 */
                bindUI: EMPTY_FN,
            
                /**
                 * Adds nodes to the DOM
                 *
                 * This method is not called by framework and is not chained
                 * automatically for the class hierarchy.
                 *
                 * @method renderUI
                 * @protected
                 */
                renderUI: EMPTY_FN,
            
                /**
                 * Refreshes the rendered UI, based on Widget State
                 *
                 * This method is not called by framework and is not chained
                 * automatically for the class hierarchy.
                 *
                 * @method syncUI
                 * @protected
                 *
                 */
                syncUI: EMPTY_FN,
            
                /**
                 * @method hide
                 * @description Hides the Widget by setting the "visible" attribute to "false".
                 * @chainable
                 */
                hide: function() {
                    return this.set(VISIBLE, FALSE);
                },
            
                /**
                 * @method show
                 * @description Shows the Widget by setting the "visible" attribute to "true".
                 * @chainable
                 */
                show: function() {
                    return this.set(VISIBLE, TRUE);
                },
            
                /**
                 * @method focus
                 * @description Causes the Widget to receive the focus by setting the "focused"
                 * attribute to "true".
                 * @chainable
                 */
                focus: function () {
                    return this._set(FOCUSED, TRUE);
                },
            
                /**
                 * @method blur
                 * @description Causes the Widget to lose focus by setting the "focused" attribute
                 * to "false"
                 * @chainable
                 */
                blur: function () {
                    return this._set(FOCUSED, FALSE);
                },
            
                /**
                 * @method enable
                 * @description Set the Widget's "disabled" attribute to "false".
                 * @chainable
                 */
                enable: function() {
                    return this.set(DISABLED, FALSE);
                },
            
                /**
                 * @method disable
                 * @description Set the Widget's "disabled" attribute to "true".
                 * @chainable
                 */
                disable: function() {
                    return this.set(DISABLED, TRUE);
                },
            
                /**
                 * @method _uiSizeCB
                 * @protected
                 * @param {boolean} expand
                 */
                _uiSizeCB : function(expand) {
                    this.get(CONTENT_BOX).toggleClass(_getWidgetClassName(CONTENT, "expanded"), expand);
                },
            
                /**
                 * Helper method to collect the boundingBox and contentBox and append to the provided parentNode, if not
                 * already a child. The owner document of the boundingBox, or the owner document of the contentBox will be used
                 * as the document into which the Widget is rendered if a parentNode is node is not provided. If both the boundingBox and
                 * the contentBox are not currently in the document, and no parentNode is provided, the widget will be rendered
                 * to the current document's body.
                 *
                 * @method _renderBox
                 * @private
                 * @param {Node} parentNode The parentNode to render the widget to. If not provided, and both the boundingBox and
                 * the contentBox are not currently in the document, the widget will be rendered to the current document's body.
                 */
                _renderBox: function(parentNode) {
            
                    // TODO: Performance Optimization [ More effective algo to reduce Node refs, compares, replaces? ]
            
                    var widget = this, // kweight
                        contentBox = widget.get(CONTENT_BOX),
                        boundingBox = widget.get(BOUNDING_BOX),
                        srcNode = widget.get(SRC_NODE),
                        defParentNode = widget.DEF_PARENT_NODE,
            
                        doc = (srcNode && srcNode.get(OWNER_DOCUMENT)) || boundingBox.get(OWNER_DOCUMENT) || contentBox.get(OWNER_DOCUMENT);
            
                    // If srcNode (assume it's always in doc), have contentBox take its place (widget render responsible for re-use of srcNode contents)
                    if (srcNode && !srcNode.compareTo(contentBox) && !contentBox.inDoc(doc)) {
                        srcNode.replace(contentBox);
                    }
            
                    if (!boundingBox.compareTo(contentBox.get(PARENT_NODE)) && !boundingBox.compareTo(contentBox)) {
                        // If contentBox box is already in the document, have boundingBox box take it's place
                        if (contentBox.inDoc(doc)) {
                            contentBox.replace(boundingBox);
                        }
                        boundingBox.appendChild(contentBox);
                    }
            
                    parentNode = parentNode || (defParentNode && Node.one(defParentNode));
            
                    if (parentNode) {
                        parentNode.appendChild(boundingBox);
                    } else if (!boundingBox.inDoc(doc)) {
                        Node.one(BODY).insert(boundingBox, 0);
                    }
                },
            
                /**
                 * Setter for the boundingBox attribute
                 *
                 * @method _setBB
                 * @private
                 * @param {Node|String} node
                 * @return Node
                 */
                _setBB: function(node) {
                    return this._setBox(this.get(ID), node, this.BOUNDING_TEMPLATE, true);
                },
            
                /**
                 * Setter for the contentBox attribute
                 *
                 * @method _setCB
                 * @private
                 * @param {Node|String} node
                 * @return Node
                 */
                _setCB: function(node) {
                    return (this.CONTENT_TEMPLATE === null) ? this.get(BOUNDING_BOX) : this._setBox(null, node, this.CONTENT_TEMPLATE, false);
                },
            
                /**
                 * Returns the default value for the boundingBox attribute.
                 *
                 * For the Widget class, this will most commonly be null (resulting in a new
                 * boundingBox node instance being created), unless a srcNode was provided
                 * and CONTENT_TEMPLATE is null, in which case it will be srcNode.
                 * This behavior was introduced in @VERSION@ to accomodate single-box widgets
                 * whose BB & CB both point to srcNode (e.g. Y.Button).
                 *
                 * @method _defaultBB
                 * @protected
                 */
                _defaultBB : function() {
                    var node = this.get(SRC_NODE),
                        nullCT = (this.CONTENT_TEMPLATE === null);
            
                    return ((node && nullCT) ? node : null);
                },
            
                /**
                 * Returns the default value for the contentBox attribute.
                 *
                 * For the Widget class, this will be the srcNode if provided, otherwise null (resulting in
                 * a new contentBox node instance being created)
                 *
                 * @method _defaultCB
                 * @protected
                 */
                _defaultCB : function(node) {
                    return this.get(SRC_NODE) || null;
                },
            
                /**
                 * Helper method to set the bounding/content box, or create it from
                 * the provided template if not found.
                 *
                 * @method _setBox
                 * @private
                 *
                 * @param {String} id The node's id attribute
                 * @param {Node|String} node The node reference
                 * @param {String} template HTML string template for the node
                 * @param {boolean} isBounding true if this is the boundingBox, false if it's the contentBox
                 * @return {Node} The node
                 */
                _setBox : function(id, node, template, isBounding) {
            
                    node = Node.one(node);
            
                    if (!node) {
                        node = Node.create(template);
            
                        if (isBounding) {
                            this._bbFromTemplate = true;
                        } else {
                            this._cbFromTemplate = true;
                        }
                    }
            
                    if (!node.get(ID)) {
                        node.set(ID, id || Y.guid());
                    }
            
                    return node;
                },
            
                /**
                 * Initializes the UI state for the Widget's bounding/content boxes.
                 *
                 * @method _renderUI
                 * @protected
                 */
                _renderUI: function() {
                    this._renderBoxClassNames();
                    this._renderBox(this._parentNode);
                },
            
                /**
                 * Applies standard class names to the boundingBox and contentBox
                 *
                 * @method _renderBoxClassNames
                 * @protected
                 */
                _renderBoxClassNames : function() {
                    var classes = this._getClasses(),
                        cl,
                        boundingBox = this.get(BOUNDING_BOX),
                        i;
            
                    boundingBox.addClass(_getWidgetClassName());
            
                    // Start from Widget Sub Class
                    for (i = classes.length-3; i >= 0; i--) {
                        cl = classes[i];
                        boundingBox.addClass(cl.CSS_PREFIX || _getClassName(cl.NAME.toLowerCase()));
                    }
            
                    // Use instance based name for content box
                    this.get(CONTENT_BOX).addClass(this.getClassName(CONTENT));
                },
            
                /**
                 * Removes class names representative of the widget's loading state from
                 * the boundingBox.
                 *
                 * @method _removeLoadingClassNames
                 * @protected
                 */
                _removeLoadingClassNames: function () {
            
                    var boundingBox = this.get(BOUNDING_BOX),
                        contentBox = this.get(CONTENT_BOX),
                        instClass = this.getClassName(LOADING),
                        widgetClass = _getWidgetClassName(LOADING);
            
                    boundingBox.removeClass(widgetClass)
                               .removeClass(instClass);
            
                    contentBox.removeClass(widgetClass)
                              .removeClass(instClass);
                },
            
                /**
                 * Sets up DOM and CustomEvent listeners for the widget.
                 *
                 * @method _bindUI
                 * @protected
                 */
                _bindUI: function() {
                    this._bindAttrUI(this._UI_ATTRS.BIND);
                    this._bindDOM();
                },
            
                /**
                 * @method _unbindUI
                 * @protected
                 */
                _unbindUI : function(boundingBox) {
                    this._unbindDOM(boundingBox);
                },
            
                /**
                 * Sets up DOM listeners, on elements rendered by the widget.
                 *
                 * @method _bindDOM
                 * @protected
                 */
                _bindDOM : function() {
                    var oDocument = this.get(BOUNDING_BOX).get(OWNER_DOCUMENT),
                        focusHandle = Widget._hDocFocus;
            
                    // Shared listener across all Widgets.
                    if (!focusHandle) {
                        focusHandle = Widget._hDocFocus = oDocument.on("focus", this._onDocFocus, this);
                        focusHandle.listeners = {
                            count: 0
                        };
                    }
            
                    focusHandle.listeners[Y.stamp(this, true)] = true;
                    focusHandle.listeners.count++;
            
                    //	Fix for Webkit:
                    //	Document doesn't receive focus in Webkit when the user mouses
                    //	down on it, so the "focused" attribute won't get set to the
                    //	correct value. Keeping this instance based for now, potential better performance.
                    //  Otherwise we'll end up looking up widgets from the DOM on every mousedown.
                    if (WEBKIT){
                        this._hDocMouseDown = oDocument.on("mousedown", this._onDocMouseDown, this);
                    }
                },
            
                /**
                 * @method _unbindDOM
                 * @protected
                 */
                _unbindDOM : function(boundingBox) {
            
                    var focusHandle = Widget._hDocFocus,
                        yuid = Y.stamp(this, true),
                        focusListeners,
                        mouseHandle = this._hDocMouseDown;
            
                    if (focusHandle) {
            
                        focusListeners = focusHandle.listeners;
            
                        if (focusListeners[yuid]) {
                            delete focusListeners[yuid];
                            focusListeners.count--;
                        }
            
                        if (focusListeners.count === 0) {
                            focusHandle.detach();
                            Widget._hDocFocus = null;
                        }
                    }
            
                    if (WEBKIT && mouseHandle) {
                        mouseHandle.detach();
                    }
                },
            
                /**
                 * Updates the widget UI to reflect the attribute state.
                 *
                 * @method _syncUI
                 * @protected
                 */
                _syncUI: function() {
                    this._syncAttrUI(this._UI_ATTRS.SYNC);
                },
            
                /**
                 * Sets the height on the widget's bounding box element
                 *
                 * @method _uiSetHeight
                 * @protected
                 * @param {String | Number} val
                 */
                _uiSetHeight: function(val) {
                    this._uiSetDim(HEIGHT, val);
                    this._uiSizeCB((val !== EMPTY_STR && val !== AUTO));
                },
            
                /**
                 * Sets the width on the widget's bounding box element
                 *
                 * @method _uiSetWidth
                 * @protected
                 * @param {String | Number} val
                 */
                _uiSetWidth: function(val) {
                    this._uiSetDim(WIDTH, val);
                },
            
                /**
                 * @method _uiSetDim
                 * @private
                 * @param {String} dim The dimension - "width" or "height"
                 * @param {Number | String} val The value to set
                 */
                _uiSetDim: function(dimension, val) {
                    this.get(BOUNDING_BOX).setStyle(dimension, L.isNumber(val) ? val + this.DEF_UNIT : val);
                },
            
                /**
                 * Sets the visible state for the UI
                 *
                 * @method _uiSetVisible
                 * @protected
                 * @param {boolean} val
                 */
                _uiSetVisible: function(val) {
                    this.get(BOUNDING_BOX).toggleClass(this.getClassName(HIDDEN), !val);
                },
            
                /**
                 * Sets the disabled state for the UI
                 *
                 * @method _uiSetDisabled
                 * @protected
                 * @param {boolean} val
                 */
                _uiSetDisabled: function(val) {
                    this.get(BOUNDING_BOX).toggleClass(this.getClassName(DISABLED), val);
                },
            
                /**
                 * Sets the focused state for the UI
                 *
                 * @method _uiSetFocused
                 * @protected
                 * @param {boolean} val
                 * @param {string} src String representing the source that triggered an update to
                 * the UI.
                 */
                _uiSetFocused: function(val, src) {
                     var boundingBox = this.get(BOUNDING_BOX);
                     boundingBox.toggleClass(this.getClassName(FOCUSED), val);
            
                     if (src !== UI) {
                        if (val) {
                            boundingBox.focus();
                        } else {
                            boundingBox.blur();
                        }
                     }
                },
            
                /**
                 * Set the tabIndex on the widget's rendered UI
                 *
                 * @method _uiSetTabIndex
                 * @protected
                 * @param Number
                 */
                _uiSetTabIndex: function(index) {
                    var boundingBox = this.get(BOUNDING_BOX);
            
                    if (L.isNumber(index)) {
                        boundingBox.set(TAB_INDEX, index);
                    } else {
                        boundingBox.removeAttribute(TAB_INDEX);
                    }
                },
            
                /**
                 * @method _onDocMouseDown
                 * @description "mousedown" event handler for the owner document of the
                 * widget's bounding box.
                 * @protected
                 * @param {EventFacade} evt The event facade for the DOM focus event
                 */
                _onDocMouseDown: function (evt) {
                    if (this._domFocus) {
                        this._onDocFocus(evt);
                    }
                },
            
                /**
                 * DOM focus event handler, used to sync the state of the Widget with the DOM
                 *
                 * @method _onDocFocus
                 * @protected
                 * @param {EventFacade} evt The event facade for the DOM focus event
                 */
                _onDocFocus: function (evt) {
                    var widget = Widget.getByNode(evt.target),
                        activeWidget = Widget._active;
            
                    if (activeWidget && (activeWidget !== widget)) {
                        activeWidget._domFocus = false;
                        activeWidget._set(FOCUSED, false, {src:UI});
            
                        Widget._active = null;
                    }
            
                    if (widget) {
                        widget._domFocus = true;
                        widget._set(FOCUSED, true, {src:UI});
            
                        Widget._active = widget;
                    }
                },
            
                /**
                 * Generic toString implementation for all widgets.
                 *
                 * @method toString
                 * @return {String} The default string value for the widget [ displays the NAME of the instance, and the unique id ]
                 */
                toString: function() {
                    // Using deprecated name prop for kweight squeeze.
                    return this.name + "[" + this.get(ID) + "]";
                },
            
                /**
                 * Default unit to use for dimension values
                 *
                 * @property DEF_UNIT
                 * @type String
                 */
                DEF_UNIT : "px",
            
                /**
                 * Default node to render the bounding box to. If not set,
                 * will default to the current document body.
                 *
                 * @property DEF_PARENT_NODE
                 * @type String | Node
                 */
                DEF_PARENT_NODE : null,
            
                /**
                 * Property defining the markup template for content box. If your Widget doesn't
                 * need the dual boundingBox/contentBox structure, set CONTENT_TEMPLATE to null,
                 * and contentBox and boundingBox will both point to the same Node.
                 *
                 * @property CONTENT_TEMPLATE
                 * @type String
                 */
                CONTENT_TEMPLATE : DIV,
            
                /**
                 * Property defining the markup template for bounding box.
                 *
                 * @property BOUNDING_TEMPLATE
                 * @type String
                 */
                BOUNDING_TEMPLATE : DIV,
            
                /**
                 * @method _guid
                 * @protected
                 */
                _guid : function() {
                    return Y.guid();
                },
            
                /**
                 * @method _validTabIndex
                 * @protected
                 * @param {Number} tabIndex
                 */
                _validTabIndex : function (tabIndex) {
                    return (L.isNumber(tabIndex) || L.isNull(tabIndex));
                },
            
                /**
                 * Binds after listeners for the list of attributes provided
                 *
                 * @method _bindAttrUI
                 * @private
                 * @param {Array} attrs
                 */
                _bindAttrUI : function(attrs) {
                    var i,
                        l = attrs.length;
            
                    for (i = 0; i < l; i++) {
                        this.after(attrs[i] + CHANGE, this._setAttrUI);
                    }
                },
            
                /**
                 * Invokes the _uiSet&#61;ATTR NAME&#62; method for the list of attributes provided
                 *
                 * @method _syncAttrUI
                 * @private
                 * @param {Array} attrs
                 */
                _syncAttrUI : function(attrs) {
                    var i, l = attrs.length, attr;
                    for (i = 0; i < l; i++) {
                        attr = attrs[i];
                        this[_UISET + _toInitialCap(attr)](this.get(attr));
                    }
                },
            
                /**
                 * @method _setAttrUI
                 * @private
                 * @param {EventFacade} e
                 */
                _setAttrUI : function(e) {
                    if (e.target === this) {
                        this[_UISET + _toInitialCap(e.attrName)](e.newVal, e.src);
                    }
                },
            
                /**
                 * The default setter for the strings attribute. Merges partial sets
                 * into the full string set, to allow users to partial sets of strings
                 *
                 * @method _strSetter
                 * @protected
                 * @param {Object} strings
                 * @return {String} The full set of strings to set
                 */
                _strSetter : function(strings) {
                    return Y.merge(this.get(STRINGS), strings);
                },
            
                /**
                 * Helper method to get a specific string value
                 *
                 * @deprecated Used by deprecated WidgetLocale implementations.
                 * @method getString
                 * @param {String} key
                 * @return {String} The string
                 */
                getString : function(key) {
                    return this.get(STRINGS)[key];
                },
            
                /**
                 * Helper method to get the complete set of strings for the widget
                 *
                 * @deprecated  Used by deprecated WidgetLocale implementations.
                 * @method getStrings
                 * @param {String} key
                 * @return {String} The strings
                 */
                getStrings : function() {
                    return this.get(STRINGS);
                },
            
                /**
                 * The lists of UI attributes to bind and sync for widget's _bindUI and _syncUI implementations
                 *
                 * @property _UI_ATTRS
                 * @type Object
                 * @private
                 */
                _UI_ATTRS : {
                    BIND: UI_ATTRS,
                    SYNC: UI_ATTRS
                }
            });
            
            Y.Widget = Widget;