src/cm/media/js/lib/yui/yui3-3.15.0/build/widget-base/widget-base-debug.js
changeset 602 e16a97fb364a
equal deleted inserted replaced
601:d334a616c023 602:e16a97fb364a
       
     1 YUI.add('widget-base', function (Y, NAME) {
       
     2 
       
     3 /**
       
     4  * Provides the base Widget class, with HTML Parser support
       
     5  *
       
     6  * @module widget
       
     7  * @main widget
       
     8  */
       
     9 
       
    10 /**
       
    11  * Provides the base Widget class
       
    12  *
       
    13  * @module widget
       
    14  * @submodule widget-base
       
    15  */
       
    16 var L = Y.Lang,
       
    17     Node = Y.Node,
       
    18 
       
    19     ClassNameManager = Y.ClassNameManager,
       
    20 
       
    21     _getClassName = ClassNameManager.getClassName,
       
    22     _getWidgetClassName,
       
    23 
       
    24     _toInitialCap = Y.cached(function(str) {
       
    25         return str.substring(0, 1).toUpperCase() + str.substring(1);
       
    26     }),
       
    27 
       
    28     // K-Weight, IE GC optimizations
       
    29     CONTENT = "content",
       
    30     VISIBLE = "visible",
       
    31     HIDDEN = "hidden",
       
    32     DISABLED = "disabled",
       
    33     FOCUSED = "focused",
       
    34     WIDTH = "width",
       
    35     HEIGHT = "height",
       
    36     BOUNDING_BOX = "boundingBox",
       
    37     CONTENT_BOX = "contentBox",
       
    38     PARENT_NODE = "parentNode",
       
    39     OWNER_DOCUMENT = "ownerDocument",
       
    40     AUTO = "auto",
       
    41     SRC_NODE = "srcNode",
       
    42     BODY = "body",
       
    43     TAB_INDEX = "tabIndex",
       
    44     ID = "id",
       
    45     RENDER = "render",
       
    46     RENDERED = "rendered",
       
    47     DESTROYED = "destroyed",
       
    48     STRINGS = "strings",
       
    49     DIV = "<div></div>",
       
    50     CHANGE = "Change",
       
    51     LOADING = "loading",
       
    52 
       
    53     _UISET = "_uiSet",
       
    54 
       
    55     EMPTY_STR = "",
       
    56     EMPTY_FN = function() {},
       
    57 
       
    58     TRUE = true,
       
    59     FALSE = false,
       
    60 
       
    61     UI,
       
    62     ATTRS = {},
       
    63     UI_ATTRS = [VISIBLE, DISABLED, HEIGHT, WIDTH, FOCUSED, TAB_INDEX],
       
    64 
       
    65     WEBKIT = Y.UA.webkit,
       
    66 
       
    67     // Widget nodeid-to-instance map.
       
    68     _instances = {};
       
    69 
       
    70 /**
       
    71  * A base class for widgets, providing:
       
    72  * <ul>
       
    73  *    <li>The render lifecycle method, in addition to the init and destroy
       
    74  *        lifecycle methods provide by Base</li>
       
    75  *    <li>Abstract methods to support consistent MVC structure across
       
    76  *        widgets: renderer, renderUI, bindUI, syncUI</li>
       
    77  *    <li>Support for common widget attributes, such as boundingBox, contentBox, visible,
       
    78  *        disabled, focused, strings</li>
       
    79  * </ul>
       
    80  *
       
    81  * @param config {Object} Object literal specifying widget configuration properties.
       
    82  *
       
    83  * @class Widget
       
    84  * @constructor
       
    85  * @extends Base
       
    86  */
       
    87 function Widget(config) {
       
    88     Y.log('constructor called', 'life', 'widget');
       
    89 
       
    90     // kweight
       
    91     var widget = this,
       
    92         parentNode,
       
    93         render,
       
    94         constructor = widget.constructor;
       
    95 
       
    96     widget._strs = {};
       
    97     widget._cssPrefix = constructor.CSS_PREFIX || _getClassName(constructor.NAME.toLowerCase());
       
    98 
       
    99     // We need a config for HTML_PARSER to work.
       
   100     config = config || {};
       
   101 
       
   102     Widget.superclass.constructor.call(widget, config);
       
   103 
       
   104     render = widget.get(RENDER);
       
   105 
       
   106     if (render) {
       
   107         // Render could be a node or boolean
       
   108         if (render !== TRUE) {
       
   109             parentNode = render;
       
   110         }
       
   111         widget.render(parentNode);
       
   112     }
       
   113 }
       
   114 
       
   115 /**
       
   116  * Static property provides a string to identify the class.
       
   117  * <p>
       
   118  * Currently used to apply class identifiers to the bounding box
       
   119  * and to classify events fired by the widget.
       
   120  * </p>
       
   121  *
       
   122  * @property NAME
       
   123  * @type String
       
   124  * @static
       
   125  */
       
   126 Widget.NAME = "widget";
       
   127 
       
   128 /**
       
   129  * Constant used to identify state changes originating from
       
   130  * the DOM (as opposed to the JavaScript model).
       
   131  *
       
   132  * @property UI_SRC
       
   133  * @type String
       
   134  * @static
       
   135  * @final
       
   136  */
       
   137 UI = Widget.UI_SRC = "ui";
       
   138 
       
   139 /**
       
   140  * Static property used to define the default attribute
       
   141  * configuration for the Widget.
       
   142  *
       
   143  * @property ATTRS
       
   144  * @type Object
       
   145  * @static
       
   146  */
       
   147 Widget.ATTRS = ATTRS;
       
   148 
       
   149 // Trying to optimize kweight by setting up attrs this way saves about 0.4K min'd
       
   150 
       
   151 /**
       
   152  * @attribute id
       
   153  * @writeOnce
       
   154  * @default Generated using guid()
       
   155  * @type String
       
   156  */
       
   157 
       
   158 ATTRS[ID] = {
       
   159     valueFn: "_guid",
       
   160     writeOnce: TRUE
       
   161 };
       
   162 
       
   163 /**
       
   164  * Flag indicating whether or not this Widget
       
   165  * has been through the render lifecycle phase.
       
   166  *
       
   167  * @attribute rendered
       
   168  * @readOnly
       
   169  * @default false
       
   170  * @type boolean
       
   171  */
       
   172 ATTRS[RENDERED] = {
       
   173     value:FALSE,
       
   174     readOnly: TRUE
       
   175 };
       
   176 
       
   177 /**
       
   178  * @attribute boundingBox
       
   179  * @description The outermost DOM node for the Widget, used for sizing and positioning
       
   180  * of a Widget as well as a containing element for any decorator elements used
       
   181  * for skinning.
       
   182  * @type String | Node
       
   183  * @writeOnce
       
   184  */
       
   185 ATTRS[BOUNDING_BOX] = {
       
   186     valueFn:"_defaultBB",
       
   187     setter: "_setBB",
       
   188     writeOnce: TRUE
       
   189 };
       
   190 
       
   191 /**
       
   192  * @attribute contentBox
       
   193  * @description A DOM node that is a direct descendant of a Widget's bounding box that
       
   194  * houses its content.
       
   195  * @type String | Node
       
   196  * @writeOnce
       
   197  */
       
   198 ATTRS[CONTENT_BOX] = {
       
   199     valueFn:"_defaultCB",
       
   200     setter: "_setCB",
       
   201     writeOnce: TRUE
       
   202 };
       
   203 
       
   204 /**
       
   205  * @attribute tabIndex
       
   206  * @description Number (between -32767 to 32767) indicating the widget's
       
   207  * position in the default tab flow.  The value is used to set the
       
   208  * "tabIndex" attribute on the widget's bounding box.  Negative values allow
       
   209  * the widget to receive DOM focus programmatically (by calling the focus
       
   210  * method), while being removed from the default tab flow.  A value of
       
   211  * null removes the "tabIndex" attribute from the widget's bounding box.
       
   212  * @type Number
       
   213  * @default null
       
   214  */
       
   215 ATTRS[TAB_INDEX] = {
       
   216     value: null,
       
   217     validator: "_validTabIndex"
       
   218 };
       
   219 
       
   220 /**
       
   221  * @attribute focused
       
   222  * @description Boolean indicating if the Widget, or one of its descendants,
       
   223  * has focus.
       
   224  * @readOnly
       
   225  * @default false
       
   226  * @type boolean
       
   227  */
       
   228 ATTRS[FOCUSED] = {
       
   229     value: FALSE,
       
   230     readOnly:TRUE
       
   231 };
       
   232 
       
   233 /**
       
   234  * @attribute disabled
       
   235  * @description Boolean indicating if the Widget should be disabled. The disabled implementation
       
   236  * is left to the specific classes extending widget.
       
   237  * @default false
       
   238  * @type boolean
       
   239  */
       
   240 ATTRS[DISABLED] = {
       
   241     value: FALSE
       
   242 };
       
   243 
       
   244 /**
       
   245  * @attribute visible
       
   246  * @description Boolean indicating whether or not the Widget is visible.
       
   247  * @default TRUE
       
   248  * @type boolean
       
   249  */
       
   250 ATTRS[VISIBLE] = {
       
   251     value: TRUE
       
   252 };
       
   253 
       
   254 /**
       
   255  * @attribute height
       
   256  * @description String with units, or number, representing the height of the Widget. If a number is provided,
       
   257  * the default unit, defined by the Widgets DEF_UNIT, property is used.
       
   258  * @default EMPTY_STR
       
   259  * @type {String | Number}
       
   260  */
       
   261 ATTRS[HEIGHT] = {
       
   262     value: EMPTY_STR
       
   263 };
       
   264 
       
   265 /**
       
   266  * @attribute width
       
   267  * @description String with units, or number, representing the width of the Widget. If a number is provided,
       
   268  * the default unit, defined by the Widgets DEF_UNIT, property is used.
       
   269  * @default EMPTY_STR
       
   270  * @type {String | Number}
       
   271  */
       
   272 ATTRS[WIDTH] = {
       
   273     value: EMPTY_STR
       
   274 };
       
   275 
       
   276 /**
       
   277  * @attribute strings
       
   278  * @description Collection of strings used to label elements of the Widget's UI.
       
   279  * @default null
       
   280  * @type Object
       
   281  */
       
   282 ATTRS[STRINGS] = {
       
   283     value: {},
       
   284     setter: "_strSetter",
       
   285     getter: "_strGetter"
       
   286 };
       
   287 
       
   288 /**
       
   289  * Whether or not to render the widget automatically after init, and optionally, to which parent node.
       
   290  *
       
   291  * @attribute render
       
   292  * @type boolean | Node
       
   293  * @writeOnce
       
   294  */
       
   295 ATTRS[RENDER] = {
       
   296     value:FALSE,
       
   297     writeOnce:TRUE
       
   298 };
       
   299 
       
   300 /**
       
   301  * The css prefix which the static Widget.getClassName method should use when constructing class names
       
   302  *
       
   303  * @property CSS_PREFIX
       
   304  * @type String
       
   305  * @default Widget.NAME.toLowerCase()
       
   306  * @private
       
   307  * @static
       
   308  */
       
   309 Widget.CSS_PREFIX = _getClassName(Widget.NAME.toLowerCase());
       
   310 
       
   311 /**
       
   312  * Generate a standard prefixed classname for the Widget, prefixed by the default prefix defined
       
   313  * by the <code>Y.config.classNamePrefix</code> attribute used by <code>ClassNameManager</code> and
       
   314  * <code>Widget.NAME.toLowerCase()</code> (e.g. "yui-widget-xxxxx-yyyyy", based on default values for
       
   315  * the prefix and widget class name).
       
   316  * <p>
       
   317  * The instance based version of this method can be used to generate standard prefixed classnames,
       
   318  * based on the instances NAME, as opposed to Widget.NAME. This method should be used when you
       
   319  * need to use a constant class name across different types instances.
       
   320  * </p>
       
   321  * @method getClassName
       
   322  * @param {String*} args* 0..n strings which should be concatenated, using the default separator defined by ClassNameManager, to create the class name
       
   323  */
       
   324 Widget.getClassName = function() {
       
   325     // arguments needs to be array'fied to concat
       
   326     return _getClassName.apply(ClassNameManager, [Widget.CSS_PREFIX].concat(Y.Array(arguments), true));
       
   327 };
       
   328 
       
   329 _getWidgetClassName = Widget.getClassName;
       
   330 
       
   331 /**
       
   332  * Returns the widget instance whose bounding box contains, or is, the given node.
       
   333  * <p>
       
   334  * In the case of nested widgets, the nearest bounding box ancestor is used to
       
   335  * return the widget instance.
       
   336  * </p>
       
   337  * @method getByNode
       
   338  * @static
       
   339  * @param node {Node | String} The node for which to return a Widget instance. If a selector
       
   340  * string is passed in, which selects more than one node, the first node found is used.
       
   341  * @return {Widget} Widget instance, or null if not found.
       
   342  */
       
   343 Widget.getByNode = function(node) {
       
   344     var widget,
       
   345         widgetMarker = _getWidgetClassName();
       
   346 
       
   347     node = Node.one(node);
       
   348     if (node) {
       
   349         node = node.ancestor("." + widgetMarker, true);
       
   350         if (node) {
       
   351             widget = _instances[Y.stamp(node, true)];
       
   352         }
       
   353     }
       
   354 
       
   355     return widget || null;
       
   356 };
       
   357 
       
   358 Y.extend(Widget, Y.Base, {
       
   359 
       
   360     /**
       
   361      * Returns a class name prefixed with the the value of the
       
   362      * <code>YUI.config.classNamePrefix</code> attribute + the instances <code>NAME</code> property.
       
   363      * Uses <code>YUI.config.classNameDelimiter</code> attribute to delimit the provided strings.
       
   364      * e.g.
       
   365      * <code>
       
   366      * <pre>
       
   367      *    // returns "yui-slider-foo-bar", for a slider instance
       
   368      *    var scn = slider.getClassName('foo','bar');
       
   369      *
       
   370      *    // returns "yui-overlay-foo-bar", for an overlay instance
       
   371      *    var ocn = overlay.getClassName('foo','bar');
       
   372      * </pre>
       
   373      * </code>
       
   374      *
       
   375      * @method getClassName
       
   376      * @param {String} [classnames*] One or more classname bits to be joined and prefixed
       
   377      */
       
   378     getClassName: function () {
       
   379         return _getClassName.apply(ClassNameManager, [this._cssPrefix].concat(Y.Array(arguments), true));
       
   380     },
       
   381 
       
   382     /**
       
   383      * Initializer lifecycle implementation for the Widget class. Registers the
       
   384      * widget instance, and runs through the Widget's HTML_PARSER definition.
       
   385      *
       
   386      * @method initializer
       
   387      * @protected
       
   388      * @param  config {Object} Configuration object literal for the widget
       
   389      */
       
   390     initializer: function(config) {
       
   391         Y.log('initializer called', 'life', 'widget');
       
   392 
       
   393         var bb = this.get(BOUNDING_BOX);
       
   394 
       
   395         if (bb instanceof Node) {
       
   396             this._mapInstance(Y.stamp(bb));
       
   397         }
       
   398 
       
   399         /**
       
   400          * Notification event, which widget implementations can fire, when
       
   401          * they change the content of the widget. This event has no default
       
   402          * behavior and cannot be prevented, so the "on" or "after"
       
   403          * moments are effectively equivalent (with on listeners being invoked before
       
   404          * after listeners).
       
   405          *
       
   406          * @event widget:contentUpdate
       
   407          * @preventable false
       
   408          * @param {EventFacade} e The Event Facade
       
   409          */
       
   410     },
       
   411 
       
   412     /**
       
   413      * Utility method used to add an entry to the boundingBox id to instance map.
       
   414      *
       
   415      * This method can be used to populate the instance with lazily created boundingBox Node references.
       
   416      *
       
   417      * @method _mapInstance
       
   418      * @param {String} The boundingBox id
       
   419      * @protected
       
   420      */
       
   421     _mapInstance : function(id) {
       
   422         _instances[id] = this;
       
   423     },
       
   424 
       
   425     /**
       
   426      * Destructor lifecycle implementation for the Widget class. Purges events attached
       
   427      * to the bounding box and content box, removes them from the DOM and removes
       
   428      * the Widget from the list of registered widgets.
       
   429      *
       
   430      * @method destructor
       
   431      * @protected
       
   432      */
       
   433     destructor: function() {
       
   434         Y.log('destructor called', 'life', 'widget');
       
   435 
       
   436         var boundingBox = this.get(BOUNDING_BOX),
       
   437             bbGuid;
       
   438 
       
   439         if (boundingBox instanceof Node) {
       
   440             bbGuid = Y.stamp(boundingBox,true);
       
   441 
       
   442             if (bbGuid in _instances) {
       
   443                 delete _instances[bbGuid];
       
   444             }
       
   445 
       
   446             this._destroyBox();
       
   447         }
       
   448     },
       
   449 
       
   450     /**
       
   451      * <p>
       
   452      * Destroy lifecycle method. Fires the destroy
       
   453      * event, prior to invoking destructors for the
       
   454      * class hierarchy.
       
   455      *
       
   456      * Overrides Base's implementation, to support arguments to destroy
       
   457      * </p>
       
   458      * <p>
       
   459      * Subscribers to the destroy
       
   460      * event can invoke preventDefault on the event object, to prevent destruction
       
   461      * from proceeding.
       
   462      * </p>
       
   463      * @method destroy
       
   464      * @param destroyAllNodes {Boolean} If true, all nodes contained within the Widget are
       
   465      * removed and destroyed. Defaults to false due to potentially high run-time cost.
       
   466      * @return {Widget} A reference to this object
       
   467      * @chainable
       
   468      */
       
   469     destroy: function(destroyAllNodes) {
       
   470         this._destroyAllNodes = destroyAllNodes;
       
   471         return Widget.superclass.destroy.apply(this);
       
   472     },
       
   473 
       
   474     /**
       
   475      * Removes and destroys the widgets rendered boundingBox, contentBox,
       
   476      * and detaches bound UI events.
       
   477      *
       
   478      * @method _destroyBox
       
   479      * @protected
       
   480      */
       
   481     _destroyBox : function() {
       
   482 
       
   483         var boundingBox = this.get(BOUNDING_BOX),
       
   484             contentBox = this.get(CONTENT_BOX),
       
   485             deep = this._destroyAllNodes,
       
   486             same;
       
   487 
       
   488         same = boundingBox && boundingBox.compareTo(contentBox);
       
   489 
       
   490         if (this.UI_EVENTS) {
       
   491             this._destroyUIEvents();
       
   492         }
       
   493 
       
   494         this._unbindUI(boundingBox);
       
   495 
       
   496         if (contentBox) {
       
   497             if (deep) {
       
   498                 contentBox.empty();
       
   499             }
       
   500             contentBox.remove(TRUE);
       
   501         }
       
   502 
       
   503         if (!same) {
       
   504             if (deep) {
       
   505                 boundingBox.empty();
       
   506             }
       
   507             boundingBox.remove(TRUE);
       
   508         }
       
   509     },
       
   510 
       
   511     /**
       
   512      * Establishes the initial DOM for the widget. Invoking this
       
   513      * method will lead to the creating of all DOM elements for
       
   514      * the widget (or the manipulation of existing DOM elements
       
   515      * for the progressive enhancement use case).
       
   516      * <p>
       
   517      * This method should only be invoked once for an initialized
       
   518      * widget.
       
   519      * </p>
       
   520      * <p>
       
   521      * It delegates to the widget specific renderer method to do
       
   522      * the actual work.
       
   523      * </p>
       
   524      *
       
   525      * @method render
       
   526      * @chainable
       
   527      * @final
       
   528      * @param  parentNode {Object | String} Optional. The Node under which the
       
   529      * Widget is to be rendered. This can be a Node instance or a CSS selector string.
       
   530      * <p>
       
   531      * If the selector string returns more than one Node, the first node will be used
       
   532      * as the parentNode. NOTE: This argument is required if both the boundingBox and contentBox
       
   533      * are not currently in the document. If it's not provided, the Widget will be rendered
       
   534      * to the body of the current document in this case.
       
   535      * </p>
       
   536      */
       
   537     render: function(parentNode) {
       
   538         if (this.get(DESTROYED)) { Y.log("Render failed; widget has been destroyed", "error", "widget"); }
       
   539 
       
   540         if (!this.get(DESTROYED) && !this.get(RENDERED)) {
       
   541              /**
       
   542               * Lifecycle event for the render phase, fired prior to rendering the UI
       
   543               * for the widget (prior to invoking the widget's renderer method).
       
   544               * <p>
       
   545               * Subscribers to the "on" moment of this event, will be notified
       
   546               * before the widget is rendered.
       
   547               * </p>
       
   548               * <p>
       
   549               * Subscribers to the "after" moment of this event, will be notified
       
   550               * after rendering is complete.
       
   551               * </p>
       
   552               *
       
   553               * @event render
       
   554               * @preventable _defRenderFn
       
   555               * @param {EventFacade} e The Event Facade
       
   556               */
       
   557             this.publish(RENDER, {
       
   558                 queuable:FALSE,
       
   559                 fireOnce:TRUE,
       
   560                 defaultTargetOnly:TRUE,
       
   561                 defaultFn: this._defRenderFn
       
   562             });
       
   563 
       
   564             this.fire(RENDER, {parentNode: (parentNode) ? Node.one(parentNode) : null});
       
   565         }
       
   566         return this;
       
   567     },
       
   568 
       
   569     /**
       
   570      * Default render handler
       
   571      *
       
   572      * @method _defRenderFn
       
   573      * @protected
       
   574      * @param {EventFacade} e The Event object
       
   575      * @param {Node} parentNode The parent node to render to, if passed in to the <code>render</code> method
       
   576      */
       
   577     _defRenderFn : function(e) {
       
   578         this._parentNode = e.parentNode;
       
   579 
       
   580         this.renderer();
       
   581         this._set(RENDERED, TRUE);
       
   582 
       
   583         this._removeLoadingClassNames();
       
   584     },
       
   585 
       
   586     /**
       
   587      * Creates DOM (or manipulates DOM for progressive enhancement)
       
   588      * This method is invoked by render() and is not chained
       
   589      * automatically for the class hierarchy (unlike initializer, destructor)
       
   590      * so it should be chained manually for subclasses if required.
       
   591      *
       
   592      * @method renderer
       
   593      * @protected
       
   594      */
       
   595     renderer: function() {
       
   596         // kweight
       
   597         var widget = this;
       
   598 
       
   599         widget._renderUI();
       
   600         widget.renderUI();
       
   601 
       
   602         widget._bindUI();
       
   603         widget.bindUI();
       
   604 
       
   605         widget._syncUI();
       
   606         widget.syncUI();
       
   607     },
       
   608 
       
   609     /**
       
   610      * Configures/Sets up listeners to bind Widget State to UI/DOM
       
   611      *
       
   612      * This method is not called by framework and is not chained
       
   613      * automatically for the class hierarchy.
       
   614      *
       
   615      * @method bindUI
       
   616      * @protected
       
   617      */
       
   618     bindUI: EMPTY_FN,
       
   619 
       
   620     /**
       
   621      * Adds nodes to the DOM
       
   622      *
       
   623      * This method is not called by framework and is not chained
       
   624      * automatically for the class hierarchy.
       
   625      *
       
   626      * @method renderUI
       
   627      * @protected
       
   628      */
       
   629     renderUI: EMPTY_FN,
       
   630 
       
   631     /**
       
   632      * Refreshes the rendered UI, based on Widget State
       
   633      *
       
   634      * This method is not called by framework and is not chained
       
   635      * automatically for the class hierarchy.
       
   636      *
       
   637      * @method syncUI
       
   638      * @protected
       
   639      *
       
   640      */
       
   641     syncUI: EMPTY_FN,
       
   642 
       
   643     /**
       
   644      * @method hide
       
   645      * @description Hides the Widget by setting the "visible" attribute to "false".
       
   646      * @chainable
       
   647      */
       
   648     hide: function() {
       
   649         return this.set(VISIBLE, FALSE);
       
   650     },
       
   651 
       
   652     /**
       
   653      * @method show
       
   654      * @description Shows the Widget by setting the "visible" attribute to "true".
       
   655      * @chainable
       
   656      */
       
   657     show: function() {
       
   658         return this.set(VISIBLE, TRUE);
       
   659     },
       
   660 
       
   661     /**
       
   662      * @method focus
       
   663      * @description Causes the Widget to receive the focus by setting the "focused"
       
   664      * attribute to "true".
       
   665      * @chainable
       
   666      */
       
   667     focus: function () {
       
   668         return this._set(FOCUSED, TRUE);
       
   669     },
       
   670 
       
   671     /**
       
   672      * @method blur
       
   673      * @description Causes the Widget to lose focus by setting the "focused" attribute
       
   674      * to "false"
       
   675      * @chainable
       
   676      */
       
   677     blur: function () {
       
   678         return this._set(FOCUSED, FALSE);
       
   679     },
       
   680 
       
   681     /**
       
   682      * @method enable
       
   683      * @description Set the Widget's "disabled" attribute to "false".
       
   684      * @chainable
       
   685      */
       
   686     enable: function() {
       
   687         return this.set(DISABLED, FALSE);
       
   688     },
       
   689 
       
   690     /**
       
   691      * @method disable
       
   692      * @description Set the Widget's "disabled" attribute to "true".
       
   693      * @chainable
       
   694      */
       
   695     disable: function() {
       
   696         return this.set(DISABLED, TRUE);
       
   697     },
       
   698 
       
   699     /**
       
   700      * @method _uiSizeCB
       
   701      * @protected
       
   702      * @param {boolean} expand
       
   703      */
       
   704     _uiSizeCB : function(expand) {
       
   705         this.get(CONTENT_BOX).toggleClass(_getWidgetClassName(CONTENT, "expanded"), expand);
       
   706     },
       
   707 
       
   708     /**
       
   709      * Helper method to collect the boundingBox and contentBox and append to the provided parentNode, if not
       
   710      * already a child. The owner document of the boundingBox, or the owner document of the contentBox will be used
       
   711      * as the document into which the Widget is rendered if a parentNode is node is not provided. If both the boundingBox and
       
   712      * the contentBox are not currently in the document, and no parentNode is provided, the widget will be rendered
       
   713      * to the current document's body.
       
   714      *
       
   715      * @method _renderBox
       
   716      * @private
       
   717      * @param {Node} parentNode The parentNode to render the widget to. If not provided, and both the boundingBox and
       
   718      * the contentBox are not currently in the document, the widget will be rendered to the current document's body.
       
   719      */
       
   720     _renderBox: function(parentNode) {
       
   721 
       
   722         // TODO: Performance Optimization [ More effective algo to reduce Node refs, compares, replaces? ]
       
   723 
       
   724         var widget = this, // kweight
       
   725             contentBox = widget.get(CONTENT_BOX),
       
   726             boundingBox = widget.get(BOUNDING_BOX),
       
   727             srcNode = widget.get(SRC_NODE),
       
   728             defParentNode = widget.DEF_PARENT_NODE,
       
   729 
       
   730             doc = (srcNode && srcNode.get(OWNER_DOCUMENT)) || boundingBox.get(OWNER_DOCUMENT) || contentBox.get(OWNER_DOCUMENT);
       
   731 
       
   732         // If srcNode (assume it's always in doc), have contentBox take its place (widget render responsible for re-use of srcNode contents)
       
   733         if (srcNode && !srcNode.compareTo(contentBox) && !contentBox.inDoc(doc)) {
       
   734             srcNode.replace(contentBox);
       
   735         }
       
   736 
       
   737         if (!boundingBox.compareTo(contentBox.get(PARENT_NODE)) && !boundingBox.compareTo(contentBox)) {
       
   738             // If contentBox box is already in the document, have boundingBox box take it's place
       
   739             if (contentBox.inDoc(doc)) {
       
   740                 contentBox.replace(boundingBox);
       
   741             }
       
   742             boundingBox.appendChild(contentBox);
       
   743         }
       
   744 
       
   745         parentNode = parentNode || (defParentNode && Node.one(defParentNode));
       
   746 
       
   747         if (parentNode) {
       
   748             parentNode.appendChild(boundingBox);
       
   749         } else if (!boundingBox.inDoc(doc)) {
       
   750             Node.one(BODY).insert(boundingBox, 0);
       
   751         }
       
   752     },
       
   753 
       
   754     /**
       
   755      * Setter for the boundingBox attribute
       
   756      *
       
   757      * @method _setBB
       
   758      * @private
       
   759      * @param {Node|String} node
       
   760      * @return Node
       
   761      */
       
   762     _setBB: function(node) {
       
   763         return this._setBox(this.get(ID), node, this.BOUNDING_TEMPLATE, true);
       
   764     },
       
   765 
       
   766     /**
       
   767      * Setter for the contentBox attribute
       
   768      *
       
   769      * @method _setCB
       
   770      * @private
       
   771      * @param {Node|String} node
       
   772      * @return Node
       
   773      */
       
   774     _setCB: function(node) {
       
   775         return (this.CONTENT_TEMPLATE === null) ? this.get(BOUNDING_BOX) : this._setBox(null, node, this.CONTENT_TEMPLATE, false);
       
   776     },
       
   777 
       
   778     /**
       
   779      * Returns the default value for the boundingBox attribute.
       
   780      *
       
   781      * For the Widget class, this will most commonly be null (resulting in a new
       
   782      * boundingBox node instance being created), unless a srcNode was provided
       
   783      * and CONTENT_TEMPLATE is null, in which case it will be srcNode.
       
   784      * This behavior was introduced in @VERSION@ to accomodate single-box widgets
       
   785      * whose BB & CB both point to srcNode (e.g. Y.Button).
       
   786      *
       
   787      * @method _defaultBB
       
   788      * @protected
       
   789      */
       
   790     _defaultBB : function() {
       
   791         var node = this.get(SRC_NODE),
       
   792             nullCT = (this.CONTENT_TEMPLATE === null);
       
   793 
       
   794         return ((node && nullCT) ? node : null);
       
   795     },
       
   796 
       
   797     /**
       
   798      * Returns the default value for the contentBox attribute.
       
   799      *
       
   800      * For the Widget class, this will be the srcNode if provided, otherwise null (resulting in
       
   801      * a new contentBox node instance being created)
       
   802      *
       
   803      * @method _defaultCB
       
   804      * @protected
       
   805      */
       
   806     _defaultCB : function(node) {
       
   807         return this.get(SRC_NODE) || null;
       
   808     },
       
   809 
       
   810     /**
       
   811      * Helper method to set the bounding/content box, or create it from
       
   812      * the provided template if not found.
       
   813      *
       
   814      * @method _setBox
       
   815      * @private
       
   816      *
       
   817      * @param {String} id The node's id attribute
       
   818      * @param {Node|String} node The node reference
       
   819      * @param {String} template HTML string template for the node
       
   820      * @param {boolean} isBounding true if this is the boundingBox, false if it's the contentBox
       
   821      * @return {Node} The node
       
   822      */
       
   823     _setBox : function(id, node, template, isBounding) {
       
   824 
       
   825         node = Node.one(node);
       
   826 
       
   827         if (!node) {
       
   828             node = Node.create(template);
       
   829 
       
   830             if (isBounding) {
       
   831                 this._bbFromTemplate = true;
       
   832             } else {
       
   833                 this._cbFromTemplate = true;
       
   834             }
       
   835         }
       
   836 
       
   837         if (!node.get(ID)) {
       
   838             node.set(ID, id || Y.guid());
       
   839         }
       
   840 
       
   841         return node;
       
   842     },
       
   843 
       
   844     /**
       
   845      * Initializes the UI state for the Widget's bounding/content boxes.
       
   846      *
       
   847      * @method _renderUI
       
   848      * @protected
       
   849      */
       
   850     _renderUI: function() {
       
   851         this._renderBoxClassNames();
       
   852         this._renderBox(this._parentNode);
       
   853     },
       
   854 
       
   855     /**
       
   856      * Applies standard class names to the boundingBox and contentBox
       
   857      *
       
   858      * @method _renderBoxClassNames
       
   859      * @protected
       
   860      */
       
   861     _renderBoxClassNames : function() {
       
   862         var classes = this._getClasses(),
       
   863             cl,
       
   864             boundingBox = this.get(BOUNDING_BOX),
       
   865             i;
       
   866 
       
   867         boundingBox.addClass(_getWidgetClassName());
       
   868 
       
   869         // Start from Widget Sub Class
       
   870         for (i = classes.length-3; i >= 0; i--) {
       
   871             cl = classes[i];
       
   872             boundingBox.addClass(cl.CSS_PREFIX || _getClassName(cl.NAME.toLowerCase()));
       
   873         }
       
   874 
       
   875         // Use instance based name for content box
       
   876         this.get(CONTENT_BOX).addClass(this.getClassName(CONTENT));
       
   877     },
       
   878 
       
   879     /**
       
   880      * Removes class names representative of the widget's loading state from
       
   881      * the boundingBox.
       
   882      *
       
   883      * @method _removeLoadingClassNames
       
   884      * @protected
       
   885      */
       
   886     _removeLoadingClassNames: function () {
       
   887 
       
   888         var boundingBox = this.get(BOUNDING_BOX),
       
   889             contentBox = this.get(CONTENT_BOX),
       
   890             instClass = this.getClassName(LOADING),
       
   891             widgetClass = _getWidgetClassName(LOADING);
       
   892 
       
   893         boundingBox.removeClass(widgetClass)
       
   894                    .removeClass(instClass);
       
   895 
       
   896         contentBox.removeClass(widgetClass)
       
   897                   .removeClass(instClass);
       
   898     },
       
   899 
       
   900     /**
       
   901      * Sets up DOM and CustomEvent listeners for the widget.
       
   902      *
       
   903      * @method _bindUI
       
   904      * @protected
       
   905      */
       
   906     _bindUI: function() {
       
   907         this._bindAttrUI(this._UI_ATTRS.BIND);
       
   908         this._bindDOM();
       
   909     },
       
   910 
       
   911     /**
       
   912      * @method _unbindUI
       
   913      * @protected
       
   914      */
       
   915     _unbindUI : function(boundingBox) {
       
   916         this._unbindDOM(boundingBox);
       
   917     },
       
   918 
       
   919     /**
       
   920      * Sets up DOM listeners, on elements rendered by the widget.
       
   921      *
       
   922      * @method _bindDOM
       
   923      * @protected
       
   924      */
       
   925     _bindDOM : function() {
       
   926         var oDocument = this.get(BOUNDING_BOX).get(OWNER_DOCUMENT),
       
   927             focusHandle = Widget._hDocFocus;
       
   928 
       
   929         // Shared listener across all Widgets.
       
   930         if (!focusHandle) {
       
   931             focusHandle = Widget._hDocFocus = oDocument.on("focus", this._onDocFocus, this);
       
   932             focusHandle.listeners = {
       
   933                 count: 0
       
   934             };
       
   935         }
       
   936 
       
   937         focusHandle.listeners[Y.stamp(this, true)] = true;
       
   938         focusHandle.listeners.count++;
       
   939 
       
   940         //	Fix for Webkit:
       
   941         //	Document doesn't receive focus in Webkit when the user mouses
       
   942         //	down on it, so the "focused" attribute won't get set to the
       
   943         //	correct value. Keeping this instance based for now, potential better performance.
       
   944         //  Otherwise we'll end up looking up widgets from the DOM on every mousedown.
       
   945         if (WEBKIT){
       
   946             this._hDocMouseDown = oDocument.on("mousedown", this._onDocMouseDown, this);
       
   947         }
       
   948     },
       
   949 
       
   950     /**
       
   951      * @method _unbindDOM
       
   952      * @protected
       
   953      */
       
   954     _unbindDOM : function(boundingBox) {
       
   955 
       
   956         var focusHandle = Widget._hDocFocus,
       
   957             yuid = Y.stamp(this, true),
       
   958             focusListeners,
       
   959             mouseHandle = this._hDocMouseDown;
       
   960 
       
   961         if (focusHandle) {
       
   962 
       
   963             focusListeners = focusHandle.listeners;
       
   964 
       
   965             if (focusListeners[yuid]) {
       
   966                 delete focusListeners[yuid];
       
   967                 focusListeners.count--;
       
   968             }
       
   969 
       
   970             if (focusListeners.count === 0) {
       
   971                 focusHandle.detach();
       
   972                 Widget._hDocFocus = null;
       
   973             }
       
   974         }
       
   975 
       
   976         if (WEBKIT && mouseHandle) {
       
   977             mouseHandle.detach();
       
   978         }
       
   979     },
       
   980 
       
   981     /**
       
   982      * Updates the widget UI to reflect the attribute state.
       
   983      *
       
   984      * @method _syncUI
       
   985      * @protected
       
   986      */
       
   987     _syncUI: function() {
       
   988         this._syncAttrUI(this._UI_ATTRS.SYNC);
       
   989     },
       
   990 
       
   991     /**
       
   992      * Sets the height on the widget's bounding box element
       
   993      *
       
   994      * @method _uiSetHeight
       
   995      * @protected
       
   996      * @param {String | Number} val
       
   997      */
       
   998     _uiSetHeight: function(val) {
       
   999         this._uiSetDim(HEIGHT, val);
       
  1000         this._uiSizeCB((val !== EMPTY_STR && val !== AUTO));
       
  1001     },
       
  1002 
       
  1003     /**
       
  1004      * Sets the width on the widget's bounding box element
       
  1005      *
       
  1006      * @method _uiSetWidth
       
  1007      * @protected
       
  1008      * @param {String | Number} val
       
  1009      */
       
  1010     _uiSetWidth: function(val) {
       
  1011         this._uiSetDim(WIDTH, val);
       
  1012     },
       
  1013 
       
  1014     /**
       
  1015      * @method _uiSetDim
       
  1016      * @private
       
  1017      * @param {String} dim The dimension - "width" or "height"
       
  1018      * @param {Number | String} val The value to set
       
  1019      */
       
  1020     _uiSetDim: function(dimension, val) {
       
  1021         this.get(BOUNDING_BOX).setStyle(dimension, L.isNumber(val) ? val + this.DEF_UNIT : val);
       
  1022     },
       
  1023 
       
  1024     /**
       
  1025      * Sets the visible state for the UI
       
  1026      *
       
  1027      * @method _uiSetVisible
       
  1028      * @protected
       
  1029      * @param {boolean} val
       
  1030      */
       
  1031     _uiSetVisible: function(val) {
       
  1032         this.get(BOUNDING_BOX).toggleClass(this.getClassName(HIDDEN), !val);
       
  1033     },
       
  1034 
       
  1035     /**
       
  1036      * Sets the disabled state for the UI
       
  1037      *
       
  1038      * @method _uiSetDisabled
       
  1039      * @protected
       
  1040      * @param {boolean} val
       
  1041      */
       
  1042     _uiSetDisabled: function(val) {
       
  1043         this.get(BOUNDING_BOX).toggleClass(this.getClassName(DISABLED), val);
       
  1044     },
       
  1045 
       
  1046     /**
       
  1047      * Sets the focused state for the UI
       
  1048      *
       
  1049      * @method _uiSetFocused
       
  1050      * @protected
       
  1051      * @param {boolean} val
       
  1052      * @param {string} src String representing the source that triggered an update to
       
  1053      * the UI.
       
  1054      */
       
  1055     _uiSetFocused: function(val, src) {
       
  1056          var boundingBox = this.get(BOUNDING_BOX);
       
  1057          boundingBox.toggleClass(this.getClassName(FOCUSED), val);
       
  1058 
       
  1059          if (src !== UI) {
       
  1060             if (val) {
       
  1061                 boundingBox.focus();
       
  1062             } else {
       
  1063                 boundingBox.blur();
       
  1064             }
       
  1065          }
       
  1066     },
       
  1067 
       
  1068     /**
       
  1069      * Set the tabIndex on the widget's rendered UI
       
  1070      *
       
  1071      * @method _uiSetTabIndex
       
  1072      * @protected
       
  1073      * @param Number
       
  1074      */
       
  1075     _uiSetTabIndex: function(index) {
       
  1076         var boundingBox = this.get(BOUNDING_BOX);
       
  1077 
       
  1078         if (L.isNumber(index)) {
       
  1079             boundingBox.set(TAB_INDEX, index);
       
  1080         } else {
       
  1081             boundingBox.removeAttribute(TAB_INDEX);
       
  1082         }
       
  1083     },
       
  1084 
       
  1085     /**
       
  1086      * @method _onDocMouseDown
       
  1087      * @description "mousedown" event handler for the owner document of the
       
  1088      * widget's bounding box.
       
  1089      * @protected
       
  1090      * @param {EventFacade} evt The event facade for the DOM focus event
       
  1091      */
       
  1092     _onDocMouseDown: function (evt) {
       
  1093         if (this._domFocus) {
       
  1094             this._onDocFocus(evt);
       
  1095         }
       
  1096     },
       
  1097 
       
  1098     /**
       
  1099      * DOM focus event handler, used to sync the state of the Widget with the DOM
       
  1100      *
       
  1101      * @method _onDocFocus
       
  1102      * @protected
       
  1103      * @param {EventFacade} evt The event facade for the DOM focus event
       
  1104      */
       
  1105     _onDocFocus: function (evt) {
       
  1106         var widget = Widget.getByNode(evt.target),
       
  1107             activeWidget = Widget._active;
       
  1108 
       
  1109         if (activeWidget && (activeWidget !== widget)) {
       
  1110             activeWidget._domFocus = false;
       
  1111             activeWidget._set(FOCUSED, false, {src:UI});
       
  1112 
       
  1113             Widget._active = null;
       
  1114         }
       
  1115 
       
  1116         if (widget) {
       
  1117             widget._domFocus = true;
       
  1118             widget._set(FOCUSED, true, {src:UI});
       
  1119 
       
  1120             Widget._active = widget;
       
  1121         }
       
  1122     },
       
  1123 
       
  1124     /**
       
  1125      * Generic toString implementation for all widgets.
       
  1126      *
       
  1127      * @method toString
       
  1128      * @return {String} The default string value for the widget [ displays the NAME of the instance, and the unique id ]
       
  1129      */
       
  1130     toString: function() {
       
  1131         // Using deprecated name prop for kweight squeeze.
       
  1132         return this.name + "[" + this.get(ID) + "]";
       
  1133     },
       
  1134 
       
  1135     /**
       
  1136      * Default unit to use for dimension values
       
  1137      *
       
  1138      * @property DEF_UNIT
       
  1139      * @type String
       
  1140      */
       
  1141     DEF_UNIT : "px",
       
  1142 
       
  1143     /**
       
  1144      * Default node to render the bounding box to. If not set,
       
  1145      * will default to the current document body.
       
  1146      *
       
  1147      * @property DEF_PARENT_NODE
       
  1148      * @type String | Node
       
  1149      */
       
  1150     DEF_PARENT_NODE : null,
       
  1151 
       
  1152     /**
       
  1153      * Property defining the markup template for content box. If your Widget doesn't
       
  1154      * need the dual boundingBox/contentBox structure, set CONTENT_TEMPLATE to null,
       
  1155      * and contentBox and boundingBox will both point to the same Node.
       
  1156      *
       
  1157      * @property CONTENT_TEMPLATE
       
  1158      * @type String
       
  1159      */
       
  1160     CONTENT_TEMPLATE : DIV,
       
  1161 
       
  1162     /**
       
  1163      * Property defining the markup template for bounding box.
       
  1164      *
       
  1165      * @property BOUNDING_TEMPLATE
       
  1166      * @type String
       
  1167      */
       
  1168     BOUNDING_TEMPLATE : DIV,
       
  1169 
       
  1170     /**
       
  1171      * @method _guid
       
  1172      * @protected
       
  1173      */
       
  1174     _guid : function() {
       
  1175         return Y.guid();
       
  1176     },
       
  1177 
       
  1178     /**
       
  1179      * @method _validTabIndex
       
  1180      * @protected
       
  1181      * @param {Number} tabIndex
       
  1182      */
       
  1183     _validTabIndex : function (tabIndex) {
       
  1184         return (L.isNumber(tabIndex) || L.isNull(tabIndex));
       
  1185     },
       
  1186 
       
  1187     /**
       
  1188      * Binds after listeners for the list of attributes provided
       
  1189      *
       
  1190      * @method _bindAttrUI
       
  1191      * @private
       
  1192      * @param {Array} attrs
       
  1193      */
       
  1194     _bindAttrUI : function(attrs) {
       
  1195         var i,
       
  1196             l = attrs.length;
       
  1197 
       
  1198         for (i = 0; i < l; i++) {
       
  1199             this.after(attrs[i] + CHANGE, this._setAttrUI);
       
  1200         }
       
  1201     },
       
  1202 
       
  1203     /**
       
  1204      * Invokes the _uiSet&#61;ATTR NAME&#62; method for the list of attributes provided
       
  1205      *
       
  1206      * @method _syncAttrUI
       
  1207      * @private
       
  1208      * @param {Array} attrs
       
  1209      */
       
  1210     _syncAttrUI : function(attrs) {
       
  1211         var i, l = attrs.length, attr;
       
  1212         for (i = 0; i < l; i++) {
       
  1213             attr = attrs[i];
       
  1214             this[_UISET + _toInitialCap(attr)](this.get(attr));
       
  1215         }
       
  1216     },
       
  1217 
       
  1218     /**
       
  1219      * @method _setAttrUI
       
  1220      * @private
       
  1221      * @param {EventFacade} e
       
  1222      */
       
  1223     _setAttrUI : function(e) {
       
  1224         if (e.target === this) {
       
  1225             this[_UISET + _toInitialCap(e.attrName)](e.newVal, e.src);
       
  1226         }
       
  1227     },
       
  1228 
       
  1229     /**
       
  1230      * The default setter for the strings attribute. Merges partial sets
       
  1231      * into the full string set, to allow users to partial sets of strings
       
  1232      *
       
  1233      * @method _strSetter
       
  1234      * @protected
       
  1235      * @param {Object} strings
       
  1236      * @return {String} The full set of strings to set
       
  1237      */
       
  1238     _strSetter : function(strings) {
       
  1239         return Y.merge(this.get(STRINGS), strings);
       
  1240     },
       
  1241 
       
  1242     /**
       
  1243      * Helper method to get a specific string value
       
  1244      *
       
  1245      * @deprecated Used by deprecated WidgetLocale implementations.
       
  1246      * @method getString
       
  1247      * @param {String} key
       
  1248      * @return {String} The string
       
  1249      */
       
  1250     getString : function(key) {
       
  1251         return this.get(STRINGS)[key];
       
  1252     },
       
  1253 
       
  1254     /**
       
  1255      * Helper method to get the complete set of strings for the widget
       
  1256      *
       
  1257      * @deprecated  Used by deprecated WidgetLocale implementations.
       
  1258      * @method getStrings
       
  1259      * @param {String} key
       
  1260      * @return {String} The strings
       
  1261      */
       
  1262     getStrings : function() {
       
  1263         return this.get(STRINGS);
       
  1264     },
       
  1265 
       
  1266     /**
       
  1267      * The lists of UI attributes to bind and sync for widget's _bindUI and _syncUI implementations
       
  1268      *
       
  1269      * @property _UI_ATTRS
       
  1270      * @type Object
       
  1271      * @private
       
  1272      */
       
  1273     _UI_ATTRS : {
       
  1274         BIND: UI_ATTRS,
       
  1275         SYNC: UI_ATTRS
       
  1276     }
       
  1277 });
       
  1278 
       
  1279 Y.Widget = Widget;
       
  1280 
       
  1281 
       
  1282 }, '@VERSION@', {
       
  1283     "requires": [
       
  1284         "attribute",
       
  1285         "base-base",
       
  1286         "base-pluginhost",
       
  1287         "classnamemanager",
       
  1288         "event-focus",
       
  1289         "node-base",
       
  1290         "node-style"
       
  1291     ],
       
  1292     "skinnable": true
       
  1293 });