if (!dojo._hasResource["dijit._Widget"]) { // _hasResource checks added by // build. Do not use _hasResource // directly in your code. dojo._hasResource["dijit._Widget"] = true; dojo.provide("dijit._Widget"); dojo.require("dijit._base"); dojo.declare("dijit._Widget", null, { // summary: // The foundation of dijit widgets. // // id: 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 : "", // lang: String // Language to display this widget in (like en-us). // Defaults to brower's specified preferred language (typically the // language of the OS) lang : "", // dir: String // Bi-directional support, as defined by the HTML DIR attribute. Either // left-to-right "ltr" or right-to-left "rtl". dir : "", // class: String // HTML class attribute "class" : "", // style: String // HTML style attribute style : "", // title: String // HTML title attribute title : "", // srcNodeRef: DomNode // pointer to original dom node srcNodeRef : null, // domNode: DomNode // this is our visible representation of the widget! Other DOM // Nodes may by assigned to other properties, usually through the // template system's dojoAttachPonit syntax, but the domNode // property is the canonical "top level" node in widget UI. domNode : null, // attributeMap: Object // A map of attributes and attachpoints -- typically standard HTML // attributes -- to set // on the widget's dom, at the "domNode" attach point, by default. // Other node references can be specified as properties of 'this' attributeMap : { id : "", dir : "", lang : "", "class" : "", style : "", title : "" }, // TODO: add on* handlers? // ////////// INITIALIZATION METHODS // /////////////////////////////////////// postscript : function(params, srcNodeRef) { this.create(params, srcNodeRef); }, create : function(params, srcNodeRef) { // summary: // To understand the process by which widgets are instantiated, it // is critical to understand what other methods create calls and // which of them you'll want to override. Of course, adventurous // developers could override create entirely, but this should // only be done as a last resort. // // Below is a list of the methods that are called, in the order // they are fired, along with notes about what they do and if/when // you should over-ride them in your widget: // // postMixInProperties: // a stub function that you can over-ride to modify // variables that may have been naively assigned by // mixInProperties // # widget is added to manager object here // buildRendering // Subclasses use this method to handle all UI initialization // Sets this.domNode. Templated widgets do this automatically // and otherwise it just uses the source dom node. // postCreate // a stub function that you can over-ride to modify take // actions once the widget has been placed in the UI // store pointer to original dom tree this.srcNodeRef = dojo.byId(srcNodeRef); // For garbage collection. An array of handles returned by // Widget.connect() // Each handle returned from Widget.connect() is an array of handles // from dojo.connect() this._connects = []; // _attaches: String[] // names of all our dojoAttachPoint variables this._attaches = []; // mixin our passed parameters if (this.srcNodeRef && (typeof this.srcNodeRef.id == "string")) { this.id = this.srcNodeRef.id; } if (params) { dojo.mixin(this, params); } this.postMixInProperties(); // generate an id for the widget if one wasn't specified // (be sure to do this before buildRendering() because that function // might // expect the id to be there. if (!this.id) { this.id = dijit.getUniqueId(this.declaredClass.replace(/\./g, "_")); } dijit.registry.add(this); this.buildRendering(); // Copy attributes listed in attributeMap into the [newly created] // DOM for the widget. // The placement of these attributes is according to the property // mapping in attributeMap. // Note special handling for 'style' and 'class' attributes which // are lists and can // have elements from both old and new structures, and some // attributes like "type" // cannot be processed this way as they are not mutable. if (this.domNode) { for (var attr in this.attributeMap) { var mapNode = this[this.attributeMap[attr] || "domNode"]; var value = this[attr]; if (typeof value != "object" && (value !== "" || (params && params[attr]))) { switch (attr) { case "class" : dojo.addClass(mapNode, value); break; case "style" : if (mapNode.style.cssText) { mapNode.style.cssText += "; " + value;// FIXME: // Opera } else { mapNode.style.cssText = value; } break; default : mapNode.setAttribute(attr, value); } } } } if (this.domNode) { 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. if (this.srcNodeRef && !this.srcNodeRef.parentNode) { delete this.srcNodeRef; } }, 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. }, buildRendering : function() { // summary: // Construct the UI for this widget, setting this.domNode. // Most widgets will mixin TemplatedWidget, which overrides this // method. this.domNode = this.srcNodeRef || dojo.doc.createElement('div'); }, postCreate : function() { // summary: // Called after a widget's dom has been setup }, startup : function() { // summary: // Called after a widget's children, and other widgets on the page, // have been created. // Provides an opportunity to manipulate any children before they // are displayed // This is useful for composite widgets that need to control or // layout sub-widgets // Many layout widgets can use this as a wiring phase }, // ////////// DESTROY FUNCTIONS //////////////////////////////// destroyRecursive : function(/* Boolean */finalize) { // summary: // Destroy this widget and it's descendants. This is the generic // "destructor" function that all widget users should call to // cleanly discard with a widget. Once a widget is destroyed, it's // removed from the manager object. // finalize: Boolean // is this function being called part of global environment // tear-down? this.destroyDescendants(); this.destroy(); }, destroy : function(/* Boolean */finalize) { // summary: // Destroy this widget, but not its descendants // finalize: Boolean // is this function being called part of global environment // tear-down? this.uninitialize(); dojo.forEach(this._connects, function(array) { dojo.forEach(array, dojo.disconnect); }); this.destroyRendering(finalize); dijit.registry.remove(this.id); }, destroyRendering : function(/* Boolean */finalize) { // summary: // Destroys the DOM nodes associated with this widget // finalize: Boolean // is this function being called part of global environment // tear-down? if (this.bgIframe) { this.bgIframe.destroy(); delete this.bgIframe; } if (this.domNode) { dojo._destroyElement(this.domNode); delete this.domNode; } if (this.srcNodeRef) { dojo._destroyElement(this.srcNodeRef); delete this.srcNodeRef; } }, destroyDescendants : function() { // summary: // Recursively destroy the children of this widget and their // descendants. // TODO: should I destroy in the reverse order, to go bottom up? dojo.forEach(this.getDescendants(), function(widget) { widget.destroy(); }); }, uninitialize : function() { // summary: // stub function. Over-ride to implement custom widget tear-down // behavior. return false; }, // //////////////// MISCELLANEOUS METHODS /////////////////// toString : function() { // summary: // returns a string that represents the widget. 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 reversable // serialization. return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String }, getDescendants : function() { // summary: // return all the descendant widgets var list = dojo.query('[widgetId]', this.domNode); return list.map(dijit.byNode); // Array }, nodesWithKeyClick : ["input", "button"], connect : function( /* Object|null */obj, /* String */event, /* String|Function */method) { // summary: // Connects specified obj/event to specified method of this object // and registers for disconnect() on widget destroy. // Special event: "ondijitclick" triggers on a click or enter-down // or space-up // Similar to dojo.connect() but takes three arguments rather than // four. var handles = []; if (event == "ondijitclick") { var w = this; // add key based click activation for unsupported nodes. if (!this.nodesWithKeyClick[obj.nodeName]) { handles.push(dojo.connect(obj, "onkeydown", this, function( e) { if (e.keyCode == dojo.keys.ENTER) { return (dojo.isString(method)) ? w[method](e) : method.call(w, e); } else if (e.keyCode == dojo.keys.SPACE) { // stop space down as it causes IE to scroll // the browser window dojo.stopEvent(e); } })); handles.push(dojo.connect(obj, "onkeyup", this, function(e) { if (e.keyCode == dojo.keys.SPACE) { return dojo.isString(method) ? w[method](e) : method.call(w, e); } })); } event = "onclick"; } handles.push(dojo.connect(obj, event, this, method)); // return handles for FormElement and ComboBox this._connects.push(handles); return handles; }, disconnect : function(/* Object */handles) { // summary: // Disconnects handle created by this.connect. // Also removes handle from this widget's list of connects for (var i = 0; i < this._connects.length; i++) { if (this._connects[i] == handles) { dojo.forEach(handles, dojo.disconnect); this._connects.splice(i, 1); return; } } }, isLeftToRight : function() { // summary: // Checks the DOM to for the text direction for bi-directional // support // description: // This method cannot be used during widget construction because the // widget // must first be connected to the DOM tree. Parent nodes are // searched for the // 'dir' attribute until one is found, otherwise left to right mode // is assumed. // See HTML spec, DIR attribute for more information. if (typeof this._ltr == "undefined") { this._ltr = dojo.getComputedStyle(this.domNode).direction != "rtl"; } return this._ltr; // Boolean }, isFocusable : function() { // summary: // Return true if this widget can currently be focused // and false if not return this.focus && (dojo.style(this.domNode, "display") != "none"); } }); }