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