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(); } }; } }); });