Merge branch 'MDL-32509' of git://github.com/danpoltawski/moodle
[moodle.git] / lib / yui / 3.5.0 / build / widget-base / widget-base-debug.js
blob2d9077bdc0762d74c178ca70b78f506b53a3993a
1 /*
2 YUI 3.5.0 (build 5089)
3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
7 YUI.add('widget-base', function(Y) {
9 /**
10  * Provides the base Widget class, with HTML Parser support
11  *
12  * @module widget
13  * @main widget
14  */
16 /**
17  * Provides the base Widget class
18  *
19  * @module widget
20  * @submodule widget-base
21  */
22 var L = Y.Lang,
23     Node = Y.Node,
25     ClassNameManager = Y.ClassNameManager,
27     _getClassName = ClassNameManager.getClassName,
28     _getWidgetClassName,
30     _toInitialCap = Y.cached(function(str) {
31         return str.substring(0, 1).toUpperCase() + str.substring(1);
32     }),
34     // K-Weight, IE GC optimizations
35     CONTENT = "content",
36     VISIBLE = "visible",
37     HIDDEN = "hidden",
38     DISABLED = "disabled",
39     FOCUSED = "focused",
40     WIDTH = "width",
41     HEIGHT = "height",
42     BOUNDING_BOX = "boundingBox",
43     CONTENT_BOX = "contentBox",
44     PARENT_NODE = "parentNode",
45     OWNER_DOCUMENT = "ownerDocument",
46     AUTO = "auto",
47     SRC_NODE = "srcNode",
48     BODY = "body",
49     TAB_INDEX = "tabIndex",
50     ID = "id",
51     RENDER = "render",
52     RENDERED = "rendered",
53     DESTROYED = "destroyed",
54     STRINGS = "strings",
55     DIV = "<div></div>",
56     CHANGE = "Change",
57     LOADING = "loading",
59     _UISET = "_uiSet",
61     EMPTY_STR = "",
62     EMPTY_FN = function() {},
64     TRUE = true,
65     FALSE = false,
67     UI,
68     ATTRS = {},
69     UI_ATTRS = [VISIBLE, DISABLED, HEIGHT, WIDTH, FOCUSED, TAB_INDEX],
71     WEBKIT = Y.UA.webkit,
73     // Widget nodeid-to-instance map.
74     _instances = {};
76 /**
77  * A base class for widgets, providing:
78  * <ul>
79  *    <li>The render lifecycle method, in addition to the init and destroy 
80  *        lifecycle methods provide by Base</li>
81  *    <li>Abstract methods to support consistent MVC structure across 
82  *        widgets: renderer, renderUI, bindUI, syncUI</li>
83  *    <li>Support for common widget attributes, such as boundingBox, contentBox, visible, 
84  *        disabled, focused, strings</li>
85  * </ul>
86  *
87  * @param config {Object} Object literal specifying widget configuration properties.
88  *
89  * @class Widget
90  * @constructor
91  * @extends Base
92  */
93 function Widget(config) {
94     Y.log('constructor called', 'life', 'widget');
96     // kweight
97     var widget = this,
98         parentNode,
99         render, 
100         constructor = widget.constructor; 
102     widget._strs = {};
103     widget._cssPrefix = constructor.CSS_PREFIX || _getClassName(constructor.NAME.toLowerCase());
105     // We need a config for HTML_PARSER to work.
106     config = config || {};
108     Widget.superclass.constructor.call(widget, config);
110     render = widget.get(RENDER);
112     if (render) {
113         // Render could be a node or boolean
114         if (render !== TRUE) {
115             parentNode = render;
116         }
117         widget.render(parentNode);
118     }
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>
128  * @property NAME
129  * @type String
130  * @static
131  */
132 Widget.NAME = "widget";
135  * Constant used to identify state changes originating from
136  * the DOM (as opposed to the JavaScript model).
138  * @property UI_SRC
139  * @type String
140  * @static
141  * @final
142  */
143 UI = Widget.UI_SRC = "ui";
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;
155 // Trying to optimize kweight by setting up attrs this way saves about 0.4K min'd
158  * @attribute id
159  * @writeOnce
160  * @default Generated using guid()
161  * @type String
162  */
164 ATTRS[ID] = {
165     valueFn: "_guid",
166     writeOnce: TRUE
170  * Flag indicating whether or not this Widget
171  * has been through the render lifecycle phase.
173  * @attribute rendered
174  * @readOnly
175  * @default false
176  * @type boolean
177  */
178 ATTRS[RENDERED] = {
179     value:FALSE,
180     readOnly: TRUE
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
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
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"
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
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
251  * @attribute visible
252  * @description Boolean indicating weather or not the Widget is visible.
253  * @default TRUE
254  * @type boolean
255  */
256 ATTRS[VISIBLE] = {
257     value: TRUE
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
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
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"
295  * Whether or not to render the widget automatically after init, and optionally, to which parent node.
297  * @attribute render
298  * @type boolean | Node
299  * @writeOnce
300  */
301 ATTRS[RENDER] = {
302     value:FALSE,
303     writeOnce:TRUE
307  * The css prefix which the static Widget.getClassName method should use when constructing class names
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());
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));
335 _getWidgetClassName = Widget.getClassName;
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         nodeid,
352         widgetMarker = _getWidgetClassName();
354     node = Node.one(node);
355     if (node) {
356         node = node.ancestor("." + widgetMarker, true);
357         if (node) {
358             nodeid = node.get(ID);
359             widget = _instances[nodeid];
360         }
361     }
363     return widget || null;
366 Y.extend(Widget, Y.Base, {
368     /**
369      * Returns a class name prefixed with the the value of the 
370      * <code>YUI.config.classNamePrefix</code> attribute + the instances <code>NAME</code> property.
371      * Uses <code>YUI.config.classNameDelimiter</code> attribute to delimit the provided strings.
372      * e.g. 
373      * <code>
374      * <pre>
375      *    // returns "yui-slider-foo-bar", for a slider instance
376      *    var scn = slider.getClassName('foo','bar');
377      *
378      *    // returns "yui-overlay-foo-bar", for an overlay instance
379      *    var ocn = overlay.getClassName('foo','bar');
380      * </pre>
381      * </code>
382      *
383      * @method getClassName
384      * @param {String}+ One or more classname bits to be joined and prefixed
385      */
386     getClassName: function () {
387         return _getClassName.apply(ClassNameManager, [this._cssPrefix].concat(Y.Array(arguments), true));
388     },
390     /**
391      * Initializer lifecycle implementation for the Widget class. Registers the 
392      * widget instance, and runs through the Widget's HTML_PARSER definition. 
393      *
394      * @method initializer
395      * @protected
396      * @param  config {Object} Configuration object literal for the widget
397      */
398     initializer: function(config) {
399         Y.log('initializer called', 'life', 'widget');
401         var bb = this.get(BOUNDING_BOX);
402         if (bb instanceof Node) {
403             this._mapInstance(bb.get(ID));
404         }
406         /**
407          * Notification event, which widget implementations can fire, when
408          * they change the content of the widget. This event has no default
409          * behavior and cannot be prevented, so the "on" or "after"
410          * moments are effectively equivalent (with on listeners being invoked before 
411          * after listeners).
412          *
413          * @event widget:contentUpdate
414          * @preventable false
415          * @param {EventFacade} e The Event Facade
416          */
418         if (this._applyParser) {
419             this._applyParser(config);
420         }
421     },
423     /**
424      * Utility method used to add an entry to the boundingBox id to instance map. 
425      *
426      * This method can be used to populate the instance with lazily created boundingBox Node references. 
427      *
428      * @method _mapInstance
429      * @param {String} The boundingBox id
430      * @protected
431      */
432     _mapInstance : function(id) {
433         if (!(_instances[id])) {
434             _instances[id] = this;
435         }
436     },
438     /**
439      * Destructor lifecycle implementation for the Widget class. Purges events attached
440      * to the bounding box and content box, removes them from the DOM and removes 
441      * the Widget from the list of registered widgets.
442      *
443      * @method destructor
444      * @protected
445      */
446     destructor: function() {
447         Y.log('destructor called', 'life', 'widget');
449         var boundingBox = this.get(BOUNDING_BOX),
450             bbid;
452         if (boundingBox instanceof Node) {
453             bbid = boundingBox.get(ID);
455             if (bbid in _instances) {
456                 delete _instances[bbid];
457             }
459             this._destroyBox();
460         }
461     },
463     /**
464      * <p>
465      * Destroy lifecycle method. Fires the destroy
466      * event, prior to invoking destructors for the
467      * class hierarchy.
468      *
469      * Overrides Base's implementation, to support arguments to destroy
470      * </p>
471      * <p>
472      * Subscribers to the destroy
473      * event can invoke preventDefault on the event object, to prevent destruction
474      * from proceeding.
475      * </p>
476      * @method destroy
477      * @param destroyAllNodes {Boolean} If true, all nodes contained within the Widget are removed and destroyed. Defaults to false due to potentially high run-time cost. 
478      * @return {Widget} A reference to this object
479      * @chainable
480      */
481     destroy: function(destroyAllNodes) {
482         this._destroyAllNodes = destroyAllNodes;
483         return Widget.superclass.destroy.apply(this);
484     },
486     /**
487      * Removes and destroys the widgets rendered boundingBox, contentBox,
488      * and detaches bound UI events.
489      *
490      * @method _destroyBox
491      * @protected 
492      */
493     _destroyBox : function() {
495         var boundingBox = this.get(BOUNDING_BOX),
496             contentBox = this.get(CONTENT_BOX),
497             deep = this._destroyAllNodes,
498             same;
500         same = boundingBox && boundingBox.compareTo(contentBox);
502         if (this.UI_EVENTS) {
503             this._destroyUIEvents();
504         }
506         this._unbindUI(boundingBox);
508         if (deep) {
509             // Removes and destroys all child nodes.
510             boundingBox.empty();
511             boundingBox.remove(TRUE);
512         } else {
513             if (contentBox) {
514                 contentBox.remove(TRUE);
515             }
516             if (!same) {
517                 boundingBox.remove(TRUE);
518             }
519         }
520     },
522     /**
523      * Establishes the initial DOM for the widget. Invoking this
524      * method will lead to the creating of all DOM elements for
525      * the widget (or the manipulation of existing DOM elements 
526      * for the progressive enhancement use case).
527      * <p>
528      * This method should only be invoked once for an initialized
529      * widget.
530      * </p>
531      * <p>
532      * It delegates to the widget specific renderer method to do
533      * the actual work.
534      * </p>
535      *
536      * @method render
537      * @chainable
538      * @final 
539      * @param  parentNode {Object | String} Optional. The Node under which the 
540      * Widget is to be rendered. This can be a Node instance or a CSS selector string. 
541      * <p>
542      * If the selector string returns more than one Node, the first node will be used 
543      * as the parentNode. NOTE: This argument is required if both the boundingBox and contentBox
544      * are not currently in the document. If it's not provided, the Widget will be rendered
545      * to the body of the current document in this case.
546      * </p>
547      */
548     render: function(parentNode) {
549         if (this.get(DESTROYED)) { Y.log("Render failed; widget has been destroyed", "error", "widget"); }
551         if (!this.get(DESTROYED) && !this.get(RENDERED)) {
552              /**
553               * Lifecycle event for the render phase, fired prior to rendering the UI 
554               * for the widget (prior to invoking the widget's renderer method).
555               * <p>
556               * Subscribers to the "on" moment of this event, will be notified 
557               * before the widget is rendered.
558               * </p>
559               * <p>
560               * Subscribers to the "after" moment of this event, will be notified
561               * after rendering is complete.
562               * </p>
563               *
564               * @event widget:render
565               * @preventable _defRenderFn
566               * @param {EventFacade} e The Event Facade
567               */
568             this.publish(RENDER, {
569                 queuable:FALSE,
570                 fireOnce:TRUE,
571                 defaultTargetOnly:TRUE,
572                 defaultFn: this._defRenderFn
573             });
575             this.fire(RENDER, {parentNode: (parentNode) ? Node.one(parentNode) : null});
576         }
577         return this;
578     },
580     /**
581      * Default render handler
582      *
583      * @method _defRenderFn
584      * @protected
585      * @param {EventFacade} e The Event object
586      * @param {Node} parentNode The parent node to render to, if passed in to the <code>render</code> method
587      */
588     _defRenderFn : function(e) {
589         this._parentNode = e.parentNode;
590          
591         this.renderer();
592         this._set(RENDERED, TRUE);
594         this._removeLoadingClassNames();
595     },
597     /**
598      * Creates DOM (or manipulates DOM for progressive enhancement)
599      * This method is invoked by render() and is not chained 
600      * automatically for the class hierarchy (unlike initializer, destructor) 
601      * so it should be chained manually for subclasses if required.
602      *
603      * @method renderer
604      * @protected
605      */
606     renderer: function() {
607         // kweight
608         var widget = this;
610         widget._renderUI();
611         widget.renderUI();
613         widget._bindUI();
614         widget.bindUI();
616         widget._syncUI();
617         widget.syncUI();
618     },
620     /**
621      * Configures/Sets up listeners to bind Widget State to UI/DOM
622      * 
623      * This method is not called by framework and is not chained 
624      * automatically for the class hierarchy.
625      * 
626      * @method bindUI
627      * @protected
628      */
629     bindUI: EMPTY_FN,
631     /**
632      * Adds nodes to the DOM 
633      * 
634      * This method is not called by framework and is not chained 
635      * automatically for the class hierarchy.
636      * 
637      * @method renderUI
638      * @protected
639      */
640     renderUI: EMPTY_FN,
642     /**
643      * Refreshes the rendered UI, based on Widget State
644      * 
645      * This method is not called by framework and is not chained
646      * automatically for the class hierarchy.
647      *
648      * @method syncUI
649      * @protected
650      *
651      */
652     syncUI: EMPTY_FN,
654     /**
655      * @method hide
656      * @description Hides the Widget by setting the "visible" attribute to "false".
657      * @chainable
658      */
659     hide: function() {
660         return this.set(VISIBLE, FALSE);
661     },
663     /**
664      * @method show
665      * @description Shows the Widget by setting the "visible" attribute to "true".
666      * @chainable
667      */
668     show: function() {
669         return this.set(VISIBLE, TRUE);
670     },
672     /**
673      * @method focus
674      * @description Causes the Widget to receive the focus by setting the "focused" 
675      * attribute to "true".
676      * @chainable
677      */
678     focus: function () {
679         return this._set(FOCUSED, TRUE);
680     },
682     /**
683      * @method blur
684      * @description Causes the Widget to lose focus by setting the "focused" attribute 
685      * to "false"
686      * @chainable
687      */
688     blur: function () {
689         return this._set(FOCUSED, FALSE);
690     },
692     /**
693      * @method enable
694      * @description Set the Widget's "disabled" attribute to "false".
695      * @chainable
696      */
697     enable: function() {
698         return this.set(DISABLED, FALSE);
699     },
701     /**
702      * @method disable
703      * @description Set the Widget's "disabled" attribute to "true".
704      * @chainable
705      */
706     disable: function() {
707         return this.set(DISABLED, TRUE);
708     },
710     /**
711      * @method _uiSizeCB
712      * @protected
713      * @param {boolean} expand
714      */
715     _uiSizeCB : function(expand) {
716         this.get(CONTENT_BOX).toggleClass(_getWidgetClassName(CONTENT, "expanded"), expand);        
717     },
719     /**
720      * Helper method to collect the boundingBox and contentBox and append to the provided parentNode, if not
721      * already a child. The owner document of the boundingBox, or the owner document of the contentBox will be used 
722      * as the document into which the Widget is rendered if a parentNode is node is not provided. If both the boundingBox and
723      * the contentBox are not currently in the document, and no parentNode is provided, the widget will be rendered 
724      * to the current document's body.
725      *
726      * @method _renderBox
727      * @private
728      * @param {Node} parentNode The parentNode to render the widget to. If not provided, and both the boundingBox and
729      * the contentBox are not currently in the document, the widget will be rendered to the current document's body.
730      */
731     _renderBox: function(parentNode) {
733         // TODO: Performance Optimization [ More effective algo to reduce Node refs, compares, replaces? ]
735         var widget = this, // kweight
736             contentBox = widget.get(CONTENT_BOX),
737             boundingBox = widget.get(BOUNDING_BOX),
738             srcNode = widget.get(SRC_NODE),
739             defParentNode = widget.DEF_PARENT_NODE,
741             doc = (srcNode && srcNode.get(OWNER_DOCUMENT)) || boundingBox.get(OWNER_DOCUMENT) || contentBox.get(OWNER_DOCUMENT);
743         // If srcNode (assume it's always in doc), have contentBox take its place (widget render responsible for re-use of srcNode contents)
744         if (srcNode && !srcNode.compareTo(contentBox) && !contentBox.inDoc(doc)) {
745             srcNode.replace(contentBox);
746         }
748         if (!boundingBox.compareTo(contentBox.get(PARENT_NODE)) && !boundingBox.compareTo(contentBox)) {
749             // If contentBox box is already in the document, have boundingBox box take it's place
750             if (contentBox.inDoc(doc)) {
751                 contentBox.replace(boundingBox);
752             }
753             boundingBox.appendChild(contentBox);
754         }
756         parentNode = parentNode || (defParentNode && Node.one(defParentNode));
758         if (parentNode) {
759             parentNode.appendChild(boundingBox);
760         } else if (!boundingBox.inDoc(doc)) {
761             Node.one(BODY).insert(boundingBox, 0);
762         }
763     },
765     /**
766      * Setter for the boundingBox attribute
767      *
768      * @method _setBB
769      * @private
770      * @param Node/String
771      * @return Node
772      */
773     _setBB: function(node) {
774         return this._setBox(this.get(ID), node, this.BOUNDING_TEMPLATE);
775     },
777     /**
778      * Setter for the contentBox attribute
779      *
780      * @method _setCB
781      * @private
782      * @param {Node|String} node
783      * @return Node
784      */
785     _setCB: function(node) {
786         return (this.CONTENT_TEMPLATE === null) ? this.get(BOUNDING_BOX) : this._setBox(null, node, this.CONTENT_TEMPLATE);
787     },
789     /**
790      * Returns the default value for the contentBox attribute. 
791      *
792      * For the Widget class, this will be the srcNode if provided, otherwise null (resulting in
793      * a new contentBox node instance being created)
794      *
795      * @method _defaultCB
796      * @protected
797      */
798     _defaultCB : function(node) {
799         return this.get(SRC_NODE) || null;
800     },
802     /**
803      * Helper method to set the bounding/content box, or create it from
804      * the provided template if not found.
805      *
806      * @method _setBox
807      * @private
808      *
809      * @param {String} id The node's id attribute
810      * @param {Node|String} node The node reference
811      * @param {String} template HTML string template for the node
812      * @return {Node} The node
813      */
814     _setBox : function(id, node, template) {
815         node = Node.one(node) || Node.create(template);
816         if (!node.get(ID)) {
817             node.set(ID, id || Y.guid());
818         }
819         return node;
820     },
822     /**
823      * Initializes the UI state for the Widget's bounding/content boxes.
824      *
825      * @method _renderUI
826      * @protected
827      */
828     _renderUI: function() {
829         this._renderBoxClassNames();
830         this._renderBox(this._parentNode);
831     },
833     /**
834      * Applies standard class names to the boundingBox and contentBox
835      *
836      * @method _renderBoxClassNames
837      * @protected
838      */
839     _renderBoxClassNames : function() {
840         var classes = this._getClasses(),
841             cl,
842             boundingBox = this.get(BOUNDING_BOX),
843             i;
845         boundingBox.addClass(_getWidgetClassName());
847         // Start from Widget Sub Class
848         for (i = classes.length-3; i >= 0; i--) {
849             cl = classes[i];
850             boundingBox.addClass(cl.CSS_PREFIX || _getClassName(cl.NAME.toLowerCase()));
851         }
853         // Use instance based name for content box
854         this.get(CONTENT_BOX).addClass(this.getClassName(CONTENT));
855     },
857     /**
858      * Removes class names representative of the widget's loading state from 
859      * the boundingBox.
860      *
861      * @method _removeLoadingClassNames
862      * @protected
863      */
864     _removeLoadingClassNames: function () {
866         var boundingBox = this.get(BOUNDING_BOX),
867             contentBox = this.get(CONTENT_BOX),
868             instClass = this.getClassName(LOADING),
869             widgetClass = _getWidgetClassName(LOADING);
871         boundingBox.removeClass(widgetClass)
872                    .removeClass(instClass);
874         contentBox.removeClass(widgetClass)
875                   .removeClass(instClass);
876     },
878     /**
879      * Sets up DOM and CustomEvent listeners for the widget.
880      *
881      * @method _bindUI
882      * @protected
883      */
884     _bindUI: function() {
885         this._bindAttrUI(this._UI_ATTRS.BIND);
886         this._bindDOM();
887     },
889     /**
890      * @method _unbindUI
891      * @protected
892      */
893     _unbindUI : function(boundingBox) {
894         this._unbindDOM(boundingBox);
895     },
897     /**
898      * Sets up DOM listeners, on elements rendered by the widget.
899      * 
900      * @method _bindDOM
901      * @protected
902      */
903     _bindDOM : function() {
904         var oDocument = this.get(BOUNDING_BOX).get(OWNER_DOCUMENT),
905             focusHandle = Widget._hDocFocus;
907         // Shared listener across all Widgets.
908         if (!focusHandle) {
909             focusHandle = Widget._hDocFocus = oDocument.on("focus", this._onDocFocus, this);
910             focusHandle.listeners = 1;
911         } else {
912             focusHandle.listeners++; 
913         }
915         //      Fix for Webkit:
916         //      Document doesn't receive focus in Webkit when the user mouses 
917         //      down on it, so the "focused" attribute won't get set to the 
918         //      correct value. Keeping this instance based for now, potential better performance.
919         //  Otherwise we'll end up looking up widgets from the DOM on every mousedown.
920         if (WEBKIT){
921             this._hDocMouseDown = oDocument.on("mousedown", this._onDocMouseDown, this);
922         }
923     },
925     /**
926      * @method _unbindDOM
927      * @protected
928      */   
929     _unbindDOM : function(boundingBox) {
931         var focusHandle = Widget._hDocFocus,
932             mouseHandle = this._hDocMouseDown;
934         if (focusHandle) {
935             if (focusHandle.listeners > 0) {
936                 focusHandle.listeners--;
937             } else {
938                 focusHandle.detach();
939                 Widget._hDocFocus = null;
940             }
941         }
943         if (WEBKIT && mouseHandle) {
944             mouseHandle.detach();
945         }
946     },
948     /**
949      * Updates the widget UI to reflect the attribute state.
950      *
951      * @method _syncUI
952      * @protected
953      */
954     _syncUI: function() {
955         this._syncAttrUI(this._UI_ATTRS.SYNC);
956     },
958     /**
959      * Sets the height on the widget's bounding box element
960      *
961      * @method _uiSetHeight
962      * @protected
963      * @param {String | Number} val
964      */
965     _uiSetHeight: function(val) {
966         this._uiSetDim(HEIGHT, val);
967         this._uiSizeCB((val !== EMPTY_STR && val !== AUTO));
968     },
970     /**
971      * Sets the width on the widget's bounding box element
972      *
973      * @method _uiSetWidth
974      * @protected
975      * @param {String | Number} val
976      */
977     _uiSetWidth: function(val) {
978         this._uiSetDim(WIDTH, val);
979     },
981     /**
982      * @method _uiSetDim
983      * @private
984      * @param {String} dim The dimension - "width" or "height"
985      * @param {Number | String} val The value to set
986      */
987     _uiSetDim: function(dimension, val) {
988         this.get(BOUNDING_BOX).setStyle(dimension, L.isNumber(val) ? val + this.DEF_UNIT : val);
989     },
991     /**
992      * Sets the visible state for the UI
993      * 
994      * @method _uiSetVisible
995      * @protected
996      * @param {boolean} val
997      */
998     _uiSetVisible: function(val) {
999         this.get(BOUNDING_BOX).toggleClass(this.getClassName(HIDDEN), !val);
1000     },
1002     /**
1003      * Sets the disabled state for the UI
1004      *
1005      * @method _uiSetDisabled
1006      * @protected
1007      * @param {boolean} val
1008      */
1009     _uiSetDisabled: function(val) {
1010         this.get(BOUNDING_BOX).toggleClass(this.getClassName(DISABLED), val);
1011     },
1013     /**
1014      * Sets the focused state for the UI
1015      *
1016      * @method _uiSetFocused
1017      * @protected
1018      * @param {boolean} val
1019      * @param {string} src String representing the source that triggered an update to 
1020      * the UI.     
1021      */
1022     _uiSetFocused: function(val, src) {
1023          var boundingBox = this.get(BOUNDING_BOX);
1024          boundingBox.toggleClass(this.getClassName(FOCUSED), val);
1026          if (src !== UI) {
1027             if (val) {
1028                 boundingBox.focus();  
1029             } else {
1030                 boundingBox.blur();
1031             }
1032          }
1033     },
1035     /**
1036      * Set the tabIndex on the widget's rendered UI
1037      *
1038      * @method _uiSetTabIndex
1039      * @protected
1040      * @param Number
1041      */
1042     _uiSetTabIndex: function(index) {
1043         var boundingBox = this.get(BOUNDING_BOX);
1045         if (L.isNumber(index)) {
1046             boundingBox.set(TAB_INDEX, index);
1047         } else {
1048             boundingBox.removeAttribute(TAB_INDEX);
1049         }
1050     },
1052     /**
1053      * @method _onDocMouseDown
1054      * @description "mousedown" event handler for the owner document of the 
1055      * widget's bounding box.
1056      * @protected
1057      * @param {EventFacade} evt The event facade for the DOM focus event
1058      */
1059     _onDocMouseDown: function (evt) {
1060         if (this._domFocus) {
1061             this._onDocFocus(evt);
1062         }
1063     },
1065     /**
1066      * DOM focus event handler, used to sync the state of the Widget with the DOM
1067      * 
1068      * @method _onDocFocus
1069      * @protected
1070      * @param {EventFacade} evt The event facade for the DOM focus event
1071      */
1072     _onDocFocus: function (evt) {
1073         var widget = Widget.getByNode(evt.target),
1074             activeWidget = Widget._active;
1076         if (activeWidget && (activeWidget !== widget)) {
1077             activeWidget._domFocus = false;
1078             activeWidget._set(FOCUSED, false, {src:UI});
1080             Widget._active = null;
1081         }
1083         if (widget) {
1084             widget._domFocus = true;
1085             widget._set(FOCUSED, true, {src:UI});
1087             Widget._active = widget;
1088         }
1089     },
1091     /**
1092      * Generic toString implementation for all widgets.
1093      *
1094      * @method toString
1095      * @return {String} The default string value for the widget [ displays the NAME of the instance, and the unique id ]
1096      */
1097     toString: function() {
1098         // Using deprecated name prop for kweight squeeze.
1099         return this.name + "[" + this.get(ID) + "]";
1100     },
1102     /**
1103      * Default unit to use for dimension values
1104      * 
1105      * @property DEF_UNIT
1106      * @type String
1107      */
1108     DEF_UNIT : "px",
1110     /** 
1111      * Default node to render the bounding box to. If not set,
1112      * will default to the current document body.
1113      * 
1114      * @property DEF_PARENT_NODE
1115      * @type String | Node
1116      */ 
1117     DEF_PARENT_NODE : null,
1119     /**
1120      * Property defining the markup template for content box. If your Widget doesn't
1121      * need the dual boundingBox/contentBox structure, set CONTENT_TEMPLATE to null,
1122      * and contentBox and boundingBox will both point to the same Node. 
1123      *
1124      * @property CONTENT_TEMPLATE
1125      * @type String
1126      */
1127     CONTENT_TEMPLATE : DIV,
1129     /**
1130      * Property defining the markup template for bounding box.
1131      *
1132      * @property BOUNDING_TEMPLATE
1133      * @type String
1134      */
1135     BOUNDING_TEMPLATE : DIV,
1137     /**
1138      * @method _guid
1139      * @protected
1140      */
1141     _guid : function() {
1142         return Y.guid();
1143     },
1145     /**
1146      * @method _validTabIndex
1147      * @protected
1148      * @param {Number} tabIndex
1149      */
1150     _validTabIndex : function (tabIndex) {
1151         return (L.isNumber(tabIndex) || L.isNull(tabIndex));
1152     },
1154     /**
1155      * Binds after listeners for the list of attributes provided
1156      * 
1157      * @method _bindAttrUI
1158      * @private
1159      * @param {Array} attrs
1160      */
1161     _bindAttrUI : function(attrs) {
1162         var i, 
1163             l = attrs.length; 
1165         for (i = 0; i < l; i++) {
1166             this.after(attrs[i] + CHANGE, this._setAttrUI);
1167         }
1168     },
1170     /**
1171      * Invokes the _uiSet&#61;ATTR NAME&#62; method for the list of attributes provided  
1172      *
1173      * @method _syncAttrUI
1174      * @private
1175      * @param {Array} attrs
1176      */
1177     _syncAttrUI : function(attrs) {
1178         var i, l = attrs.length, attr;
1179         for (i = 0; i < l; i++) {
1180             attr = attrs[i];
1181             this[_UISET + _toInitialCap(attr)](this.get(attr));
1182         }
1183     },
1185     /**
1186      * @method _setAttrUI
1187      * @private
1188      * @param {EventFacade} e
1189      */
1190     _setAttrUI : function(e) {
1191         if (e.target === this) {
1192             this[_UISET + _toInitialCap(e.attrName)](e.newVal, e.src);
1193         }
1194     },
1196     /**
1197      * The default setter for the strings attribute. Merges partial sets
1198      * into the full string set, to allow users to partial sets of strings  
1199      *
1200      * @method _strSetter
1201      * @protected
1202      * @param {Object} strings
1203      * @return {String} The full set of strings to set
1204      */
1205     _strSetter : function(strings) {
1206         return Y.merge(this.get(STRINGS), strings);
1207     },
1209     /**
1210      * Helper method to get a specific string value
1211      *
1212      * @deprecated Used by deprecated WidgetLocale implementations. 
1213      * @method getString
1214      * @param {String} key
1215      * @return {String} The string
1216      */
1217     getString : function(key) {
1218         return this.get(STRINGS)[key];
1219     },
1221     /**
1222      * Helper method to get the complete set of strings for the widget
1223      *
1224      * @deprecated  Used by deprecated WidgetLocale implementations.
1225      * @method getStrings
1226      * @param {String} key
1227      * @return {String} The strings
1228      */
1229     getStrings : function() {
1230         return this.get(STRINGS);
1231     },
1233     /**
1234      * The lists of UI attributes to bind and sync for widget's _bindUI and _syncUI implementations
1235      *
1236      * @property _UI_ATTRS
1237      * @type Object
1238      * @private
1239      */
1240     _UI_ATTRS : {
1241         BIND: UI_ATTRS,
1242         SYNC: UI_ATTRS
1243     }
1246 Y.Widget = Widget;
1249 }, '3.5.0' ,{requires:['attribute', 'event-focus', 'base-base', 'base-pluginhost', 'node-base', 'node-style', 'classnamemanager'], skinnable:true});