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