From f0cfe83e3725f9a3928da97a6e3085e79cb25309 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 18 Mar 2013 10:26:24 +0400 Subject: upgrade dojo to 1.8.3 (refs #570) --- lib/dijit/_WidgetBase.js.uncompressed.js | 1137 ++++++++++++++++++++++++++++++ 1 file changed, 1137 insertions(+) create mode 100644 lib/dijit/_WidgetBase.js.uncompressed.js (limited to 'lib/dijit/_WidgetBase.js.uncompressed.js') diff --git a/lib/dijit/_WidgetBase.js.uncompressed.js b/lib/dijit/_WidgetBase.js.uncompressed.js new file mode 100644 index 000000000..fc39d26b7 --- /dev/null +++ b/lib/dijit/_WidgetBase.js.uncompressed.js @@ -0,0 +1,1137 @@ +define("dijit/_WidgetBase", [ + "require", // require.toUrl + "dojo/_base/array", // array.forEach array.map + "dojo/aspect", + "dojo/_base/config", // config.blankGif + "dojo/_base/connect", // connect.connect + "dojo/_base/declare", // declare + "dojo/dom", // dom.byId + "dojo/dom-attr", // domAttr.set domAttr.remove + "dojo/dom-class", // domClass.add domClass.replace + "dojo/dom-construct", // domConstruct.destroy domConstruct.place + "dojo/dom-geometry", // isBodyLtr + "dojo/dom-style", // domStyle.set, domStyle.get + "dojo/has", + "dojo/_base/kernel", + "dojo/_base/lang", // mixin(), isArray(), etc. + "dojo/on", + "dojo/ready", + "dojo/Stateful", // Stateful + "dojo/topic", + "dojo/_base/window", // win.doc, win.body() + "./Destroyable", + "./registry" // registry.getUniqueId(), registry.findWidgets() +], function(require, array, aspect, config, connect, declare, + dom, domAttr, domClass, domConstruct, domGeometry, domStyle, has, kernel, + lang, on, ready, Stateful, topic, win, Destroyable, registry){ + +// module: +// dijit/_WidgetBase + +// Flag to make dijit load modules the app didn't explicitly request, for backwards compatibility +has.add("dijit-legacy-requires", !kernel.isAsync); + +// For back-compat, remove in 2.0. +if(has("dijit-legacy-requires")){ + ready(0, function(){ + var requires = ["dijit/_base/manager"]; + require(requires); // use indirection so modules not rolled into a build + }); +} + +// Nested hash listing attributes for each tag, all strings in lowercase. +// ex: {"div": {"style": true, "tabindex" true}, "form": { ... +var tagAttrs = {}; +function getAttrs(obj){ + var ret = {}; + for(var attr in obj){ + ret[attr.toLowerCase()] = true; + } + return ret; +} + +function nonEmptyAttrToDom(attr){ + // summary: + // Returns a setter function that copies the attribute to this.domNode, + // or removes the attribute from this.domNode, depending on whether the + // value is defined or not. + return function(val){ + domAttr[val ? "set" : "remove"](this.domNode, attr, val); + this._set(attr, val); + }; +} + +return declare("dijit._WidgetBase", [Stateful, Destroyable], { + // summary: + // Future base class for all Dijit widgets. + // description: + // Future base class for all Dijit widgets. + // _Widget extends this class adding support for various features needed by desktop. + // + // Provides stubs for widget lifecycle methods for subclasses to extend, like postMixInProperties(), buildRendering(), + // postCreate(), startup(), and destroy(), and also public API methods like set(), get(), and watch(). + // + // Widgets can provide custom setters/getters for widget attributes, which are called automatically by set(name, value). + // For an attribute XXX, define methods _setXXXAttr() and/or _getXXXAttr(). + // + // _setXXXAttr can also be a string/hash/array mapping from a widget attribute XXX to the widget's DOMNodes: + // + // - DOM node attribute + // | _setFocusAttr: {node: "focusNode", type: "attribute"} + // | _setFocusAttr: "focusNode" (shorthand) + // | _setFocusAttr: "" (shorthand, maps to this.domNode) + // Maps this.focus to this.focusNode.focus, or (last example) this.domNode.focus + // + // - DOM node innerHTML + // | _setTitleAttr: { node: "titleNode", type: "innerHTML" } + // Maps this.title to this.titleNode.innerHTML + // + // - DOM node innerText + // | _setTitleAttr: { node: "titleNode", type: "innerText" } + // Maps this.title to this.titleNode.innerText + // + // - DOM node CSS class + // | _setMyClassAttr: { node: "domNode", type: "class" } + // Maps this.myClass to this.domNode.className + // + // If the value of _setXXXAttr is an array, then each element in the array matches one of the + // formats of the above list. + // + // If the custom setter is null, no action is performed other than saving the new value + // in the widget (in this). + // + // If no custom setter is defined for an attribute, then it will be copied + // to this.focusNode (if the widget defines a focusNode), or this.domNode otherwise. + // That's only done though for attributes that match DOMNode attributes (title, + // alt, aria-labelledby, etc.) + + // id: [const] String + // A unique, opaque ID string that can be assigned by users or by the + // system. If the developer passes an ID which is known not to be + // unique, the specified ID is ignored and the system-generated ID is + // used instead. + id: "", + _setIdAttr: "domNode", // to copy to this.domNode even for auto-generated id's + + // lang: [const] String + // Rarely used. Overrides the default Dojo locale used to render this widget, + // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute. + // Value must be among the list of locales specified during by the Dojo bootstrap, + // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us). + lang: "", + // set on domNode even when there's a focus node. but don't set lang="", since that's invalid. + _setLangAttr: nonEmptyAttrToDom("lang"), + + // dir: [const] String + // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir) + // attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's + // default direction. + dir: "", + // set on domNode even when there's a focus node. but don't set dir="", since that's invalid. + _setDirAttr: nonEmptyAttrToDom("dir"), // to set on domNode even when there's a focus node + + // textDir: String + // Bi-directional support, the main variable which is responsible for the direction of the text. + // The text direction can be different than the GUI direction by using this parameter in creation + // of a widget. + // + // Allowed values: + // + // 1. "ltr" + // 2. "rtl" + // 3. "auto" - contextual the direction of a text defined by first strong letter. + // + // By default is as the page direction. + textDir: "", + + // class: String + // HTML class attribute + "class": "", + _setClassAttr: { node: "domNode", type: "class" }, + + // style: String||Object + // HTML style attributes as cssText string or name/value hash + style: "", + + // title: String + // HTML title attribute. + // + // For form widgets this specifies a tooltip to display when hovering over + // the widget (just like the native HTML title attribute). + // + // For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer, + // etc., it's used to specify the tab label, accordion pane title, etc. + title: "", + + // tooltip: String + // When this widget's title attribute is used to for a tab label, accordion pane title, etc., + // this specifies the tooltip to appear when the mouse is hovered over that text. + tooltip: "", + + // baseClass: [protected] String + // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate + // widget state. + baseClass: "", + + // srcNodeRef: [readonly] DomNode + // pointer to original DOM node + srcNodeRef: null, + + // domNode: [readonly] DomNode + // This is our visible representation of the widget! Other DOM + // Nodes may by assigned to other properties, usually through the + // template system's data-dojo-attach-point syntax, but the domNode + // property is the canonical "top level" node in widget UI. + domNode: null, + + // containerNode: [readonly] DomNode + // Designates where children of the source DOM node will be placed. + // "Children" in this case refers to both DOM nodes and widgets. + // For example, for myWidget: + // + // |
+ // | here's a plain DOM node + // | and a widget + // | and another plain DOM node + // |
+ // + // containerNode would point to: + // + // | here's a plain DOM node + // | and a widget + // | and another plain DOM node + // + // In templated widgets, "containerNode" is set via a + // data-dojo-attach-point assignment. + // + // containerNode must be defined for any widget that accepts innerHTML + // (like ContentPane or BorderContainer or even Button), and conversely + // is null for widgets that don't, like TextBox. + containerNode: null, + + // ownerDocument: [const] Document? + // The document this widget belongs to. If not specified to constructor, will default to + // srcNodeRef.ownerDocument, or if no sourceRef specified, then to dojo/_base/window::doc + ownerDocument: null, + _setOwnerDocumentAttr: function(val){ + // this setter is merely to avoid automatically trying to set this.domNode.ownerDocument + this._set("ownerDocument", val); + }, + +/*===== + // _started: [readonly] Boolean + // startup() has completed. + _started: false, +=====*/ + + // attributeMap: [protected] Object + // Deprecated. Instead of attributeMap, widget should have a _setXXXAttr attribute + // for each XXX attribute to be mapped to the DOM. + // + // attributeMap sets up a "binding" between attributes (aka properties) + // of the widget and the widget's DOM. + // Changes to widget attributes listed in attributeMap will be + // reflected into the DOM. + // + // For example, calling set('title', 'hello') + // on a TitlePane will automatically cause the TitlePane's DOM to update + // with the new title. + // + // attributeMap is a hash where the key is an attribute of the widget, + // and the value reflects a binding to a: + // + // - DOM node attribute + // | focus: {node: "focusNode", type: "attribute"} + // Maps this.focus to this.focusNode.focus + // + // - DOM node innerHTML + // | title: { node: "titleNode", type: "innerHTML" } + // Maps this.title to this.titleNode.innerHTML + // + // - DOM node innerText + // | title: { node: "titleNode", type: "innerText" } + // Maps this.title to this.titleNode.innerText + // + // - DOM node CSS class + // | myClass: { node: "domNode", type: "class" } + // Maps this.myClass to this.domNode.className + // + // If the value is an array, then each element in the array matches one of the + // formats of the above list. + // + // There are also some shorthands for backwards compatibility: + // + // - string --> { node: string, type: "attribute" }, for example: + // + // | "focusNode" ---> { node: "focusNode", type: "attribute" } + // + // - "" --> { node: "domNode", type: "attribute" } + attributeMap: {}, + + // _blankGif: [protected] String + // Path to a blank 1x1 image. + // Used by `` nodes in templates that really get their image via CSS background-image. + _blankGif: config.blankGif || require.toUrl("dojo/resources/blank.gif"), + + //////////// INITIALIZATION METHODS /////////////////////////////////////// + + /*===== + constructor: function(params, srcNodeRef){ + // summary: + // Create the widget. + // params: Object|null + // Hash of initialization parameters for widget, including scalar values (like title, duration etc.) + // and functions, typically callbacks like onClick. + // The hash can contain any of the widget's properties, excluding read-only properties. + // srcNodeRef: DOMNode|String? + // If a srcNodeRef (DOM node) is specified: + // + // - use srcNodeRef.innerHTML as my contents + // - if this is a behavioral widget then apply behavior to that srcNodeRef + // - otherwise, replace srcNodeRef with my generated DOM tree + }, + =====*/ + + postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){ + // summary: + // Kicks off widget instantiation. See create() for details. + // tags: + // private + this.create(params, srcNodeRef); + }, + + create: function(params, srcNodeRef){ + // summary: + // Kick off the life-cycle of a widget + // description: + // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate, + // etc.), some of which of you'll want to override. See http://dojotoolkit.org/reference-guide/dijit/_WidgetBase.html + // for a discussion of the widget creation lifecycle. + // + // Of course, adventurous developers could override create entirely, but this should + // only be done as a last resort. + // params: Object|null + // Hash of initialization parameters for widget, including scalar values (like title, duration etc.) + // and functions, typically callbacks like onClick. + // The hash can contain any of the widget's properties, excluding read-only properties. + // srcNodeRef: DOMNode|String? + // If a srcNodeRef (DOM node) is specified: + // + // - use srcNodeRef.innerHTML as my contents + // - if this is a behavioral widget then apply behavior to that srcNodeRef + // - otherwise, replace srcNodeRef with my generated DOM tree + // tags: + // private + + // store pointer to original DOM tree + this.srcNodeRef = dom.byId(srcNodeRef); + + // No longer used, remove for 2.0. + this._connects = []; + this._supportingWidgets = []; + + // this is here for back-compat, remove in 2.0 (but check NodeList-instantiate.html test) + if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; } + + // mix in our passed parameters + if(params){ + this.params = params; + lang.mixin(this, params); + } + this.postMixInProperties(); + + // Generate an id for the widget if one wasn't specified, or it was specified as id: undefined. + // Do this before buildRendering() because it might expect the id to be there. + if(!this.id){ + this.id = registry.getUniqueId(this.declaredClass.replace(/\./g,"_")); + if(this.params){ + // if params contains {id: undefined}, prevent _applyAttributes() from processing it + delete this.params.id; + } + } + + // The document and node this widget is associated with + this.ownerDocument = this.ownerDocument || (this.srcNodeRef ? this.srcNodeRef.ownerDocument : win.doc); + this.ownerDocumentBody = win.body(this.ownerDocument); + + registry.add(this); + + this.buildRendering(); + + var deleteSrcNodeRef; + + if(this.domNode){ + // Copy attributes listed in attributeMap into the [newly created] DOM for the widget. + // Also calls custom setters for all attributes with custom setters. + this._applyAttributes(); + + // If srcNodeRef was specified, then swap out original srcNode for this widget's DOM tree. + // For 2.0, move this after postCreate(). postCreate() shouldn't depend on the + // widget being attached to the DOM since it isn't when a widget is created programmatically like + // new MyWidget({}). See #11635. + var source = this.srcNodeRef; + if(source && source.parentNode && this.domNode !== source){ + source.parentNode.replaceChild(this.domNode, source); + deleteSrcNodeRef = true; + } + + // Note: for 2.0 may want to rename widgetId to dojo._scopeName + "_widgetId", + // assuming that dojo._scopeName even exists in 2.0 + this.domNode.setAttribute("widgetId", this.id); + } + this.postCreate(); + + // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC. + // I think for back-compatibility it isn't deleting srcNodeRef until after postCreate() has run. + if(deleteSrcNodeRef){ + delete this.srcNodeRef; + } + + this._created = true; + }, + + _applyAttributes: function(){ + // summary: + // Step during widget creation to copy widget attributes to the + // DOM according to attributeMap and _setXXXAttr objects, and also to call + // custom _setXXXAttr() methods. + // + // Skips over blank/false attribute values, unless they were explicitly specified + // as parameters to the widget, since those are the default anyway, + // and setting tabIndex="" is different than not setting tabIndex at all. + // + // For backwards-compatibility reasons attributeMap overrides _setXXXAttr when + // _setXXXAttr is a hash/string/array, but _setXXXAttr as a functions override attributeMap. + // tags: + // private + + // Get list of attributes where this.set(name, value) will do something beyond + // setting this[name] = value. Specifically, attributes that have: + // - associated _setXXXAttr() method/hash/string/array + // - entries in attributeMap (remove this for 2.0); + var ctor = this.constructor, + list = ctor._setterAttrs; + if(!list){ + list = (ctor._setterAttrs = []); + for(var attr in this.attributeMap){ + list.push(attr); + } + + var proto = ctor.prototype; + for(var fxName in proto){ + if(fxName in this.attributeMap){ continue; } + var setterName = "_set" + fxName.replace(/^[a-z]|-[a-zA-Z]/g, function(c){ return c.charAt(c.length-1).toUpperCase(); }) + "Attr"; + if(setterName in proto){ + list.push(fxName); + } + } + } + + // Call this.set() for each property that was either specified as parameter to constructor, + // or is in the list found above. For correlated properties like value and displayedValue, the one + // specified as a parameter should take precedence. + // Particularly important for new DateTextBox({displayedValue: ...}) since DateTextBox's default value is + // NaN and thus is not ignored like a default value of "". + + // Step 1: Save the current values of the widget properties that were specified as parameters to the constructor. + // Generally this.foo == this.params.foo, except if postMixInProperties() changed the value of this.foo. + var params = {}; + for(var key in this.params || {}){ + params[key] = this[key]; + } + + // Step 2: Call set() for each property that wasn't passed as a parameter to the constructor + array.forEach(list, function(attr){ + if(attr in params){ + // skip this one, do it below + }else if(this[attr]){ + this.set(attr, this[attr]); + } + }, this); + + // Step 3: Call set() for each property that was specified as parameter to constructor. + // Use params hash created above to ignore side effects from step #2 above. + for(key in params){ + this.set(key, params[key]); + } + }, + + postMixInProperties: function(){ + // summary: + // Called after the parameters to the widget have been read-in, + // but before the widget template is instantiated. Especially + // useful to set properties that are referenced in the widget + // template. + // tags: + // protected + }, + + buildRendering: function(){ + // summary: + // Construct the UI for this widget, setting this.domNode. + // Most widgets will mixin `dijit._TemplatedMixin`, which implements this method. + // tags: + // protected + + if(!this.domNode){ + // Create root node if it wasn't created by _Templated + this.domNode = this.srcNodeRef || this.ownerDocument.createElement("div"); + } + + // baseClass is a single class name or occasionally a space-separated list of names. + // Add those classes to the DOMNode. If RTL mode then also add with Rtl suffix. + // TODO: make baseClass custom setter + if(this.baseClass){ + var classes = this.baseClass.split(" "); + if(!this.isLeftToRight()){ + classes = classes.concat( array.map(classes, function(name){ return name+"Rtl"; })); + } + domClass.add(this.domNode, classes); + } + }, + + postCreate: function(){ + // summary: + // Processing after the DOM fragment is created + // description: + // Called after the DOM fragment has been created, but not necessarily + // added to the document. Do not include any operations which rely on + // node dimensions or placement. + // tags: + // protected + }, + + startup: function(){ + // summary: + // Processing after the DOM fragment is added to the document + // description: + // Called after a widget and its children have been created and added to the page, + // and all related widgets have finished their create() cycle, up through postCreate(). + // This is useful for composite widgets that need to control or layout sub-widgets. + // Many layout widgets can use this as a wiring phase. + if(this._started){ return; } + this._started = true; + array.forEach(this.getChildren(), function(obj){ + if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){ + obj.startup(); + obj._started = true; + } + }); + }, + + //////////// DESTROY FUNCTIONS //////////////////////////////// + + destroyRecursive: function(/*Boolean?*/ preserveDom){ + // summary: + // Destroy this widget and its descendants + // description: + // This is the generic "destructor" function that all widget users + // should call to cleanly discard with a widget. Once a widget is + // destroyed, it is removed from the manager object. + // preserveDom: + // If true, this method will leave the original DOM structure + // alone of descendant Widgets. Note: This will NOT work with + // dijit._Templated widgets. + + this._beingDestroyed = true; + this.destroyDescendants(preserveDom); + this.destroy(preserveDom); + }, + + destroy: function(/*Boolean*/ preserveDom){ + // summary: + // Destroy this widget, but not its descendants. + // This method will, however, destroy internal widgets such as those used within a template. + // preserveDom: Boolean + // If true, this method will leave the original DOM structure alone. + // Note: This will not yet work with _Templated widgets + + this._beingDestroyed = true; + this.uninitialize(); + + function destroy(w){ + if(w.destroyRecursive){ + w.destroyRecursive(preserveDom); + }else if(w.destroy){ + w.destroy(preserveDom); + } + } + + // Back-compat, remove for 2.0 + array.forEach(this._connects, lang.hitch(this, "disconnect")); + array.forEach(this._supportingWidgets, destroy); + + // Destroy supporting widgets, but not child widgets under this.containerNode (for 2.0, destroy child widgets + // here too). if() statement is to guard against exception if destroy() called multiple times (see #15815). + if(this.domNode){ + array.forEach(registry.findWidgets(this.domNode, this.containerNode), destroy); + } + + this.destroyRendering(preserveDom); + registry.remove(this.id); + this._destroyed = true; + }, + + destroyRendering: function(/*Boolean?*/ preserveDom){ + // summary: + // Destroys the DOM nodes associated with this widget + // preserveDom: + // If true, this method will leave the original DOM structure alone + // during tear-down. Note: this will not work with _Templated + // widgets yet. + // tags: + // protected + + if(this.bgIframe){ + this.bgIframe.destroy(preserveDom); + delete this.bgIframe; + } + + if(this.domNode){ + if(preserveDom){ + domAttr.remove(this.domNode, "widgetId"); + }else{ + domConstruct.destroy(this.domNode); + } + delete this.domNode; + } + + if(this.srcNodeRef){ + if(!preserveDom){ + domConstruct.destroy(this.srcNodeRef); + } + delete this.srcNodeRef; + } + }, + + destroyDescendants: function(/*Boolean?*/ preserveDom){ + // summary: + // Recursively destroy the children of this widget and their + // descendants. + // preserveDom: + // If true, the preserveDom attribute is passed to all descendant + // widget's .destroy() method. Not for use with _Templated + // widgets. + + // get all direct descendants and destroy them recursively + array.forEach(this.getChildren(), function(widget){ + if(widget.destroyRecursive){ + widget.destroyRecursive(preserveDom); + } + }); + }, + + uninitialize: function(){ + // summary: + // Deprecated. Override destroy() instead to implement custom widget tear-down + // behavior. + // tags: + // protected + return false; + }, + + ////////////////// GET/SET, CUSTOM SETTERS, ETC. /////////////////// + + _setStyleAttr: function(/*String||Object*/ value){ + // summary: + // Sets the style attribute of the widget according to value, + // which is either a hash like {height: "5px", width: "3px"} + // or a plain string + // description: + // Determines which node to set the style on based on style setting + // in attributeMap. + // tags: + // protected + + var mapNode = this.domNode; + + // Note: technically we should revert any style setting made in a previous call + // to his method, but that's difficult to keep track of. + + if(lang.isObject(value)){ + domStyle.set(mapNode, value); + }else{ + if(mapNode.style.cssText){ + mapNode.style.cssText += "; " + value; + }else{ + mapNode.style.cssText = value; + } + } + + this._set("style", value); + }, + + _attrToDom: function(/*String*/ attr, /*String*/ value, /*Object?*/ commands){ + // summary: + // Reflect a widget attribute (title, tabIndex, duration etc.) to + // the widget DOM, as specified by commands parameter. + // If commands isn't specified then it's looked up from attributeMap. + // Note some attributes like "type" + // cannot be processed this way as they are not mutable. + // attr: + // Name of member variable (ex: "focusNode" maps to this.focusNode) pointing + // to DOMNode inside the widget, or alternately pointing to a subwidget + // tags: + // private + + commands = arguments.length >= 3 ? commands : this.attributeMap[attr]; + + array.forEach(lang.isArray(commands) ? commands : [commands], function(command){ + + // Get target node and what we are doing to that node + var mapNode = this[command.node || command || "domNode"]; // DOM node + var type = command.type || "attribute"; // class, innerHTML, innerText, or attribute + + switch(type){ + case "attribute": + if(lang.isFunction(value)){ // functions execute in the context of the widget + value = lang.hitch(this, value); + } + + // Get the name of the DOM node attribute; usually it's the same + // as the name of the attribute in the widget (attr), but can be overridden. + // Also maps handler names to lowercase, like onSubmit --> onsubmit + var attrName = command.attribute ? command.attribute : + (/^on[A-Z][a-zA-Z]*$/.test(attr) ? attr.toLowerCase() : attr); + + if(mapNode.tagName){ + // Normal case, mapping to a DOMNode. Note that modern browsers will have a mapNode.set() + // method, but for consistency we still call domAttr + domAttr.set(mapNode, attrName, value); + }else{ + // mapping to a sub-widget + mapNode.set(attrName, value); + } + break; + case "innerText": + mapNode.innerHTML = ""; + mapNode.appendChild(this.ownerDocument.createTextNode(value)); + break; + case "innerHTML": + mapNode.innerHTML = value; + break; + case "class": + domClass.replace(mapNode, value, this[attr]); + break; + } + }, this); + }, + + get: function(name){ + // summary: + // Get a property from a widget. + // name: + // The property to get. + // description: + // Get a named property from a widget. The property may + // potentially be retrieved via a getter method. If no getter is defined, this + // just retrieves the object's property. + // + // For example, if the widget has properties `foo` and `bar` + // and a method named `_getFooAttr()`, calling: + // `myWidget.get("foo")` would be equivalent to calling + // `widget._getFooAttr()` and `myWidget.get("bar")` + // would be equivalent to the expression + // `widget.bar2` + var names = this._getAttrNames(name); + return this[names.g] ? this[names.g]() : this[name]; + }, + + set: function(name, value){ + // summary: + // Set a property on a widget + // name: + // The property to set. + // value: + // The value to set in the property. + // description: + // Sets named properties on a widget which may potentially be handled by a + // setter in the widget. + // + // For example, if the widget has properties `foo` and `bar` + // and a method named `_setFooAttr()`, calling + // `myWidget.set("foo", "Howdy!")` would be equivalent to calling + // `widget._setFooAttr("Howdy!")` and `myWidget.set("bar", 3)` + // would be equivalent to the statement `widget.bar = 3;` + // + // set() may also be called with a hash of name/value pairs, ex: + // + // | myWidget.set({ + // | foo: "Howdy", + // | bar: 3 + // | }); + // + // This is equivalent to calling `set(foo, "Howdy")` and `set(bar, 3)` + + if(typeof name === "object"){ + for(var x in name){ + this.set(x, name[x]); + } + return this; + } + var names = this._getAttrNames(name), + setter = this[names.s]; + if(lang.isFunction(setter)){ + // use the explicit setter + var result = setter.apply(this, Array.prototype.slice.call(arguments, 1)); + }else{ + // Mapping from widget attribute to DOMNode/subwidget attribute/value/etc. + // Map according to: + // 1. attributeMap setting, if one exists (TODO: attributeMap deprecated, remove in 2.0) + // 2. _setFooAttr: {...} type attribute in the widget (if one exists) + // 3. apply to focusNode or domNode if standard attribute name, excluding funcs like onClick. + // Checks if an attribute is a "standard attribute" by whether the DOMNode JS object has a similar + // attribute name (ex: accept-charset attribute matches jsObject.acceptCharset). + // Note also that Tree.focusNode() is a function not a DOMNode, so test for that. + var defaultNode = this.focusNode && !lang.isFunction(this.focusNode) ? "focusNode" : "domNode", + tag = this[defaultNode].tagName, + attrsForTag = tagAttrs[tag] || (tagAttrs[tag] = getAttrs(this[defaultNode])), + map = name in this.attributeMap ? this.attributeMap[name] : + names.s in this ? this[names.s] : + ((names.l in attrsForTag && typeof value != "function") || + /^aria-|^data-|^role$/.test(name)) ? defaultNode : null; + if(map != null){ + this._attrToDom(name, value, map); + } + this._set(name, value); + } + return result || this; + }, + + _attrPairNames: {}, // shared between all widgets + _getAttrNames: function(name){ + // summary: + // Helper function for get() and set(). + // Caches attribute name values so we don't do the string ops every time. + // tags: + // private + + var apn = this._attrPairNames; + if(apn[name]){ return apn[name]; } + var uc = name.replace(/^[a-z]|-[a-zA-Z]/g, function(c){ return c.charAt(c.length-1).toUpperCase(); }); + return (apn[name] = { + n: name+"Node", + s: "_set"+uc+"Attr", // converts dashes to camel case, ex: accept-charset --> _setAcceptCharsetAttr + g: "_get"+uc+"Attr", + l: uc.toLowerCase() // lowercase name w/out dashes, ex: acceptcharset + }); + }, + + _set: function(/*String*/ name, /*anything*/ value){ + // summary: + // Helper function to set new value for specified attribute, and call handlers + // registered with watch() if the value has changed. + var oldValue = this[name]; + this[name] = value; + if(this._created && value !== oldValue){ + if(this._watchCallbacks){ + this._watchCallbacks(name, oldValue, value); + } + this.emit("attrmodified-" + name, { + detail: { + prevValue: oldValue, + newValue: value + } + }); + } + }, + + emit: function(/*String*/ type, /*Object?*/ eventObj, /*Array?*/ callbackArgs){ + // summary: + // Used by widgets to signal that a synthetic event occurred, ex: + // | myWidget.emit("attrmodified-selectedChildWidget", {}). + // + // Emits an event on this.domNode named type.toLowerCase(), based on eventObj. + // Also calls onType() method, if present, and returns value from that method. + // By default passes eventObj to callback, but will pass callbackArgs instead, if specified. + // Modifies eventObj by adding missing parameters (bubbles, cancelable, widget). + // tags: + // protected + + // Specify fallback values for bubbles, cancelable in case they are not set in eventObj. + // Also set pointer to widget, although since we can't add a pointer to the widget for native events + // (see #14729), maybe we shouldn't do it here? + eventObj = eventObj || {}; + if(eventObj.bubbles === undefined){ eventObj.bubbles = true; } + if(eventObj.cancelable === undefined){ eventObj.cancelable = true; } + if(!eventObj.detail){ eventObj.detail = {}; } + eventObj.detail.widget = this; + + var ret, callback = this["on"+type]; + if(callback){ + ret = callback.apply(this, callbackArgs ? callbackArgs : [eventObj]); + } + + // Emit event, but avoid spurious emit()'s as parent sets properties on child during startup/destroy + if(this._started && !this._beingDestroyed){ + on.emit(this.domNode, type.toLowerCase(), eventObj); + } + + return ret; + }, + + on: function(/*String|Function*/ type, /*Function*/ func){ + // summary: + // Call specified function when event occurs, ex: myWidget.on("click", function(){ ... }). + // type: + // Name of event (ex: "click") or extension event like touch.press. + // description: + // Call specified function when event `type` occurs, ex: `myWidget.on("click", function(){ ... })`. + // Note that the function is not run in any particular scope, so if (for example) you want it to run in the + // widget's scope you must do `myWidget.on("click", lang.hitch(myWidget, func))`. + + // For backwards compatibility, if there's an onType() method in the widget then connect to that. + // Remove in 2.0. + var widgetMethod = this._onMap(type); + if(widgetMethod){ + return aspect.after(this, widgetMethod, func, true); + } + + // Otherwise, just listen for the event on this.domNode. + return this.own(on(this.domNode, type, func))[0]; + }, + + _onMap: function(/*String|Function*/ type){ + // summary: + // Maps on() type parameter (ex: "mousemove") to method name (ex: "onMouseMove"). + // If type is a synthetic event like touch.press then returns undefined. + var ctor = this.constructor, map = ctor._onMap; + if(!map){ + map = (ctor._onMap = {}); + for(var attr in ctor.prototype){ + if(/^on/.test(attr)){ + map[attr.replace(/^on/, "").toLowerCase()] = attr; + } + } + } + return map[typeof type == "string" && type.toLowerCase()]; // String + }, + + toString: function(){ + // summary: + // Returns a string that represents the widget + // description: + // When a widget is cast to a string, this method will be used to generate the + // output. Currently, it does not implement any sort of reversible + // serialization. + return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String + }, + + getChildren: function(){ + // summary: + // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode. + // Does not return nested widgets, nor widgets that are part of this widget's template. + return this.containerNode ? registry.findWidgets(this.containerNode) : []; // dijit/_WidgetBase[] + }, + + getParent: function(){ + // summary: + // Returns the parent widget of this widget + return registry.getEnclosingWidget(this.domNode.parentNode); + }, + + connect: function( + /*Object|null*/ obj, + /*String|Function*/ event, + /*String|Function*/ method){ + // summary: + // Deprecated, will be removed in 2.0, use this.own(on(...)) or this.own(aspect.after(...)) instead. + // + // Connects specified obj/event to specified method of this object + // and registers for disconnect() on widget destroy. + // + // Provide widget-specific analog to dojo.connect, except with the + // implicit use of this widget as the target object. + // Events connected with `this.connect` are disconnected upon + // destruction. + // returns: + // A handle that can be passed to `disconnect` in order to disconnect before + // the widget is destroyed. + // example: + // | var btn = new Button(); + // | // when foo.bar() is called, call the listener we're going to + // | // provide in the scope of btn + // | btn.connect(foo, "bar", function(){ + // | console.debug(this.toString()); + // | }); + // tags: + // protected + + return this.own(connect.connect(obj, event, this, method))[0]; // handle + }, + + disconnect: function(handle){ + // summary: + // Deprecated, will be removed in 2.0, use handle.remove() instead. + // + // Disconnects handle created by `connect`. + // tags: + // protected + + handle.remove(); + }, + + subscribe: function(t, method){ + // summary: + // Deprecated, will be removed in 2.0, use this.own(topic.subscribe()) instead. + // + // Subscribes to the specified topic and calls the specified method + // of this object and registers for unsubscribe() on widget destroy. + // + // Provide widget-specific analog to dojo.subscribe, except with the + // implicit use of this widget as the target object. + // t: String + // The topic + // method: Function + // The callback + // example: + // | var btn = new Button(); + // | // when /my/topic is published, this button changes its label to + // | // be the parameter of the topic. + // | btn.subscribe("/my/topic", function(v){ + // | this.set("label", v); + // | }); + // tags: + // protected + return this.own(topic.subscribe(t, lang.hitch(this, method)))[0]; // handle + }, + + unsubscribe: function(/*Object*/ handle){ + // summary: + // Deprecated, will be removed in 2.0, use handle.remove() instead. + // + // Unsubscribes handle created by this.subscribe. + // Also removes handle from this widget's list of subscriptions + // tags: + // protected + + handle.remove(); + }, + + isLeftToRight: function(){ + // summary: + // Return this widget's explicit or implicit orientation (true for LTR, false for RTL) + // tags: + // protected + return this.dir ? (this.dir == "ltr") : domGeometry.isBodyLtr(this.ownerDocument); //Boolean + }, + + isFocusable: function(){ + // summary: + // Return true if this widget can currently be focused + // and false if not + return this.focus && (domStyle.get(this.domNode, "display") != "none"); + }, + + placeAt: function(/* String|DomNode|_Widget */ reference, /* String|Int? */ position){ + // summary: + // Place this widget somewhere in the DOM based + // on standard domConstruct.place() conventions. + // description: + // A convenience function provided in all _Widgets, providing a simple + // shorthand mechanism to put an existing (or newly created) Widget + // somewhere in the dom, and allow chaining. + // reference: + // Widget, DOMNode, or id of widget or DOMNode + // position: + // If reference is a widget (or id of widget), and that widget has an ".addChild" method, + // it will be called passing this widget instance into that method, supplying the optional + // position index passed. In this case position (if specified) should be an integer. + // + // If reference is a DOMNode (or id matching a DOMNode but not a widget), + // the position argument can be a numeric index or a string + // "first", "last", "before", or "after", same as dojo/dom-construct::place(). + // returns: dijit/_WidgetBase + // Provides a useful return of the newly created dijit._Widget instance so you + // can "chain" this function by instantiating, placing, then saving the return value + // to a variable. + // example: + // | // create a Button with no srcNodeRef, and place it in the body: + // | var button = new Button({ label:"click" }).placeAt(win.body()); + // | // now, 'button' is still the widget reference to the newly created button + // | button.on("click", function(e){ console.log('click'); })); + // example: + // | // create a button out of a node with id="src" and append it to id="wrapper": + // | var button = new Button({},"src").placeAt("wrapper"); + // example: + // | // place a new button as the first element of some div + // | var button = new Button({ label:"click" }).placeAt("wrapper","first"); + // example: + // | // create a contentpane and add it to a TabContainer + // | var tc = dijit.byId("myTabs"); + // | new ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc) + + var refWidget = !reference.tagName && registry.byId(reference); + if(refWidget && refWidget.addChild && (!position || typeof position === "number")){ + // Adding this to refWidget and can use refWidget.addChild() to handle everything. + refWidget.addChild(this, position); + }else{ + // "reference" is a plain DOMNode, or we can't use refWidget.addChild(). Use domConstruct.place() and + // target refWidget.containerNode for nested placement (position==number, "first", "last", "only"), and + // refWidget.domNode otherwise ("after"/"before"/"replace"). (But not supported officially, see #14946.) + var ref = refWidget ? + (refWidget.containerNode && !/after|before|replace/.test(position||"") ? + refWidget.containerNode : refWidget.domNode) : dom.byId(reference, this.ownerDocument); + domConstruct.place(this.domNode, ref, position); + + // Start this iff it has a parent widget that's already started. + if(!this._started && (this.getParent() || {})._started){ + this.startup(); + } + } + return this; + }, + + getTextDir: function(/*String*/ text,/*String*/ originalDir){ + // summary: + // Return direction of the text. + // The function overridden in the _BidiSupport module, + // its main purpose is to calculate the direction of the + // text, if was defined by the programmer through textDir. + // tags: + // protected. + return originalDir; + }, + + applyTextDir: function(/*===== element, text =====*/){ + // summary: + // The function overridden in the _BidiSupport module, + // originally used for setting element.dir according to this.textDir. + // In this case does nothing. + // element: DOMNode + // text: String + // tags: + // protected. + }, + + defer: function(fcn, delay){ + // summary: + // Wrapper to setTimeout to avoid deferred functions executing + // after the originating widget has been destroyed. + // Returns an object handle with a remove method (that returns null) (replaces clearTimeout). + // fcn: function reference + // delay: Optional number (defaults to 0) + // tags: + // protected. + var timer = setTimeout(lang.hitch(this, + function(){ + timer = null; + if(!this._destroyed){ + lang.hitch(this, fcn)(); + } + }), + delay || 0 + ); + return { + remove: function(){ + if(timer){ + clearTimeout(timer); + timer = null; + } + return null; // so this works well: handle = handle.remove(); + } + }; + } +}); + +}); -- cgit v1.2.3