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