widget-base.js revision 787ab2d836a2419cedf3cc46b72c0522fbfd5e93
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Provides the base Widget class, with HTML Parser support
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @module widget
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Provides the base Widget class
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @module widget
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @submodule widget-base
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome return str.substring(0, 1).toUpperCase() + str.substring(1);
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome // K-Weight, IE GC optimizations
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome EMPTY_FN = function() {},
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome UI_ATTRS = [VISIBLE, DISABLED, HEIGHT, WIDTH, FOCUSED],
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome // Widget nodeguid-to-instance map.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * A base class for widgets, providing:
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * <li>The render lifecycle method, in addition to the init and destroy
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * lifecycle methods provide by Base</li>
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * <li>Abstract methods to support consistent MVC structure across
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * widgets: renderer, renderUI, bindUI, syncUI</li>
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * <li>Support for common widget attributes, such as boundingBox, contentBox, visible,
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * disabled, focused, strings</li>
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @param config {Object} Object literal specifying widget configuration properties.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @class Widget
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @constructor
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @extends Base
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome widget._cssPrefix = constructor.CSS_PREFIX || _getClassName(constructor.NAME.toLowerCase());
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome Widget.superclass.constructor.apply(widget, arguments);
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome // Render could be a node or boolean
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Static property provides a string to identify the class.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Currently used to apply class identifiers to the bounding box
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * and to classify events fired by the widget.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @property Widget.NAME
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type String
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Constant used to identify state changes originating from
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * the DOM (as opposed to the JavaScript model).
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @property Widget.UI_SRC
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type String
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Static property used to define the default attribute
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * configuration for the Widget.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @property Widget.ATTRS
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type Object
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome// Trying to optimize kweight by setting up attrs this way saves about 0.4K min'd
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @attribute id
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @writeOnce
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @default Generated using guid()
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type String
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Flag indicating whether or not this Widget
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * has been through the render lifecycle phase.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @attribute rendered
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @default false
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type boolean
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @attribute boundingBox
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @description The outermost DOM node for the Widget, used for sizing and positioning
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * of a Widget as well as a containing element for any decorator elements used
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * for skinning.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type String | Node
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @writeOnce
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @attribute contentBox
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @description A DOM node that is a direct descendant of a Widget's bounding box that
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * houses its content.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type String | Node
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @writeOnce
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @attribute tabIndex
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @description Number (between -32767 to 32767) indicating the widget's
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * position in the default tab flow. The value is used to set the
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * "tabIndex" attribute on the widget's bounding box. Negative values allow
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * the widget to receive DOM focus programmatically (by calling the focus
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * method), while being removed from the default tab flow. A value of
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * null removes the "tabIndex" attribute from the widget's bounding box.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type Number
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @default null
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @attribute focused
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @description Boolean indicating if the Widget, or one of its descendants,
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * has focus.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @default false
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type boolean
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @attribute disabled
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @description Boolean indicating if the Widget should be disabled. The disabled implementation
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * is left to the specific classes extending widget.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @default false
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type boolean
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @attribute visible
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @description Boolean indicating weather or not the Widget is visible.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @default TRUE
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type boolean
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @attribute height
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @description String with units, or number, representing the height of the Widget. If a number is provided,
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * the default unit, defined by the Widgets DEF_UNIT, property is used.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @default EMPTY_STR
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type {String | Number}
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @attribute width
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @description String with units, or number, representing the width of the Widget. If a number is provided,
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * the default unit, defined by the Widgets DEF_UNIT, property is used.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @default EMPTY_STR
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type {String | Number}
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @attribute strings
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @description Collection of strings used to label elements of the Widget's UI.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @default null
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type Object
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Whether or not to render the widget automatically after init, and optionally, to which parent node.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @attribute render
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type boolean | Node
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @writeOnce
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * The css prefix which the static Widget.getClassName method should use when constructing class names
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @property Widget.CSS_PREFIX
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @type String
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @default Widget.NAME.toLowerCase()
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas SoomeWidget.CSS_PREFIX = _getClassName(Widget.NAME.toLowerCase());
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Generate a standard prefixed classname for the Widget, prefixed by the default prefix defined
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * by the <code>Y.config.classNamePrefix</code> attribute used by <code>ClassNameManager</code> and
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * <code>Widget.NAME.toLowerCase()</code> (e.g. "yui-widget-xxxxx-yyyyy", based on default values for
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * the prefix and widget class name).
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * The instance based version of this method can be used to generate standard prefixed classnames,
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * based on the instances NAME, as opposed to Widget.NAME. This method should be used when you
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * need to use a constant class name across different types instances.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @method getClassName
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @param {String*} args* 0..n strings which should be concatenated, using the default separator defined by ClassNameManager, to create the class name
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome // arguments needs to be array'fied to concat
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome return _getClassName.apply(ClassNameManager, [Widget.CSS_PREFIX].concat(Y.Array(arguments), true));
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Returns the widget instance whose bounding box contains, or is, the given node.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * In the case of nested widgets, the nearest bounding box ancestor is used to
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * return the widget instance.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @method Widget.getByNode
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @param node {Node | String} The node for which to return a Widget instance. If a selector
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * string is passed in, which selects more than one node, the first node found is used.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @return {Widget} Widget instance, or null if not found.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome node = node.ancestor("." + widgetMarker, true);
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome return widget || null;
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Returns a class name prefixed with the the value of the
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * <code>YUI.config.classNamePrefix</code> attribute + the instances <code>NAME</code> property.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Uses <code>YUI.config.classNameDelimiter</code> attribute to delimit the provided strings.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * // returns "yui-slider-foo-bar", for a slider instance
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * var scn = slider.getClassName('foo','bar');
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * // returns "yui-overlay-foo-bar", for an overlay instance
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * var ocn = overlay.getClassName('foo','bar');
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @method getClassName
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @param {String}+ One or more classname bits to be joined and prefixed
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome getClassName: function () {
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome return _getClassName.apply(ClassNameManager, [this._cssPrefix].concat(Y.Array(arguments), true));
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Initializer lifecycle implementation for the Widget class. Registers the
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * widget instance, and runs through the Widget's HTML_PARSER definition.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @method initializer
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @protected
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @param config {Object} Configuration object literal for the widget
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome _instances[Y.stamp(this.get(BOUNDING_BOX))] = this;
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Notification event, which widget implementations can fire, when
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * they change the content of the widget. This event has no default
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * behavior and cannot be prevented, so the "on" or "after"
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * moments are effectively equivalent (with on listeners being invoked before
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * after listeners).
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @event widget:contentUpdate
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @preventable false
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @param {EventFacade} e The Event Facade
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Destructor lifecycle implementation for the Widget class. Purges events attached
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * to the bounding box (and all child nodes) and removes the Widget from the
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * list of registered widgets.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @method destructor
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @protected
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Removes and destroys the widgets rendered boundingBox, contentBox,
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * and detaches bound UI events.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @method _destroyBox
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @protected
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome _destroyBox : function() {
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome same = boundingBox && boundingBox.compareTo(contentBox);
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Establishes the initial DOM for the widget. Invoking this
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * method will lead to the creating of all DOM elements for
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * the widget (or the manipulation of existing DOM elements
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * for the progressive enhancement use case).
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * This method should only be invoked once for an initialized
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * It delegates to the widget specific renderer method to do
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * the actual work.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @method render
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @chainable
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * @param parentNode {Object | String} Optional. The Node under which the
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * Widget is to be rendered. This can be a Node instance or a CSS selector string.
afc2ba1deb75b323afde536f2dd18bcafdaa308dToomas Soome * If the selector string returns more than one Node, the first node will be used
* @param {Node} parentNode The parent node to render to, if passed in to the <code>render</code> method
_defRenderFn : function(e) {
this.renderer();
this._removeLoadingClassNames();
renderer: function() {
var widget = this;
* Configures/Sets up listeners to bind Widget State to UI/DOM
hide: function() {
show: function() {
focus: function () {
blur: function () {
enable: function() {
disable: function() {
* Helper method to collect the boundingBox and contentBox, set styles and append to the provided parentNode, if not
* already a child. The owner document of the boundingBox, or the owner document of the contentBox will be used
* as the document into which the Widget is rendered if a parentNode is node is not provided. If both the boundingBox and
* the contentBox are not currently in the document, and no parentNode is provided, the widget will be rendered
* @param {Node} parentNode The parentNode to render the widget to. If not provided, and both the boundingBox and
* the contentBox are not currently in the document, the widget will be rendered to the current document's body.
doc = (srcNode && srcNode.get(OWNER_DOCUMENT)) || boundingBox.get(OWNER_DOCUMENT) || contentBox.get(OWNER_DOCUMENT);
// If srcNode (assume it's always in doc), have contentBox take its place (widget render responsible for re-use of srcNode contents)
if (parentNode) {
return (this.CONTENT_TEMPLATE === null) ? this.get(BOUNDING_BOX) : this._setBox(null, node, this.CONTENT_TEMPLATE);
return node;
_renderUI: function() {
this._renderBoxClassNames();
_renderBoxClassNames : function() {
cl,
_removeLoadingClassNames: function () {
_bindUI: function() {
this._bindDOM();
_bindDOM : function() {
if (WEBKIT) {
if (this._hDocFocus) {
_syncUI: function() {
if (val) {
if (this._domFocus) {
this._domFocus = this.get(BOUNDING_BOX).contains(evt.target); // contains() checks invoking node also
* @return {String} The default string value for the widget [ displays the NAME of the instance, and the unique id ]
toString: function() {
DEF_PARENT_NODE : null,
* need the dual boundingBox/contentBox structure, set CONTENT_TEMPLATE to null,
_guid : function() {
return Y.guid();
_setAttrUI : function(e) {
getStrings : function() {
_UI_ATTRS : {
}, '@VERSION@' ,{requires:['attribute', 'event-focus', 'base-base', 'base-pluginhost', 'node-base', 'node-style', 'classnamemanager']});