summaryrefslogtreecommitdiff
path: root/lib/dijit/_WidgetBase.js.uncompressed.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dijit/_WidgetBase.js.uncompressed.js')
-rw-r--r--lib/dijit/_WidgetBase.js.uncompressed.js1137
1 files changed, 1137 insertions, 0 deletions
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:
+ //
+ // | <div data-dojo-type=myWidget>
+ // | <b> here's a plain DOM node
+ // | <span data-dojo-type=subWidget>and a widget</span>
+ // | <i> and another plain DOM node </i>
+ // | </div>
+ //
+ // containerNode would point to:
+ //
+ // | <b> here's a plain DOM node
+ // | <span data-dojo-type=subWidget>and a widget</span>
+ // | <i> and another plain DOM node </i>
+ //
+ // 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 `<img>` 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 <body> 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();
+ }
+ };
+ }
+});
+
+});