a2091737de6fdd6a6e65112baccd9ad56690584f.svn-base 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. if (!dojo._hasResource["dijit._Widget"]) { // _hasResource checks added by
  2. // build. Do not use _hasResource
  3. // directly in your code.
  4. dojo._hasResource["dijit._Widget"] = true;
  5. dojo.provide("dijit._Widget");
  6. dojo.require("dijit._base");
  7. dojo.declare("dijit._Widget", null, {
  8. // summary:
  9. // The foundation of dijit widgets.
  10. //
  11. // id: String
  12. // a unique, opaque ID string that can be assigned by users or by the
  13. // system. If the developer passes an ID which is known not to be
  14. // unique, the specified ID is ignored and the system-generated ID is
  15. // used instead.
  16. id : "",
  17. // lang: String
  18. // Language to display this widget in (like en-us).
  19. // Defaults to brower's specified preferred language (typically the
  20. // language of the OS)
  21. lang : "",
  22. // dir: String
  23. // Bi-directional support, as defined by the HTML DIR attribute. Either
  24. // left-to-right "ltr" or right-to-left "rtl".
  25. dir : "",
  26. // class: String
  27. // HTML class attribute
  28. "class" : "",
  29. // style: String
  30. // HTML style attribute
  31. style : "",
  32. // title: String
  33. // HTML title attribute
  34. title : "",
  35. // srcNodeRef: DomNode
  36. // pointer to original dom node
  37. srcNodeRef : null,
  38. // domNode: DomNode
  39. // this is our visible representation of the widget! Other DOM
  40. // Nodes may by assigned to other properties, usually through the
  41. // template system's dojoAttachPonit syntax, but the domNode
  42. // property is the canonical "top level" node in widget UI.
  43. domNode : null,
  44. // attributeMap: Object
  45. // A map of attributes and attachpoints -- typically standard HTML
  46. // attributes -- to set
  47. // on the widget's dom, at the "domNode" attach point, by default.
  48. // Other node references can be specified as properties of 'this'
  49. attributeMap : {
  50. id : "",
  51. dir : "",
  52. lang : "",
  53. "class" : "",
  54. style : "",
  55. title : ""
  56. }, // TODO: add on* handlers?
  57. // ////////// INITIALIZATION METHODS
  58. // ///////////////////////////////////////
  59. postscript : function(params, srcNodeRef) {
  60. this.create(params, srcNodeRef);
  61. },
  62. create : function(params, srcNodeRef) {
  63. // summary:
  64. // To understand the process by which widgets are instantiated, it
  65. // is critical to understand what other methods create calls and
  66. // which of them you'll want to override. Of course, adventurous
  67. // developers could override create entirely, but this should
  68. // only be done as a last resort.
  69. //
  70. // Below is a list of the methods that are called, in the order
  71. // they are fired, along with notes about what they do and if/when
  72. // you should over-ride them in your widget:
  73. //
  74. // postMixInProperties:
  75. // a stub function that you can over-ride to modify
  76. // variables that may have been naively assigned by
  77. // mixInProperties
  78. // # widget is added to manager object here
  79. // buildRendering
  80. // Subclasses use this method to handle all UI initialization
  81. // Sets this.domNode. Templated widgets do this automatically
  82. // and otherwise it just uses the source dom node.
  83. // postCreate
  84. // a stub function that you can over-ride to modify take
  85. // actions once the widget has been placed in the UI
  86. // store pointer to original dom tree
  87. this.srcNodeRef = dojo.byId(srcNodeRef);
  88. // For garbage collection. An array of handles returned by
  89. // Widget.connect()
  90. // Each handle returned from Widget.connect() is an array of handles
  91. // from dojo.connect()
  92. this._connects = [];
  93. // _attaches: String[]
  94. // names of all our dojoAttachPoint variables
  95. this._attaches = [];
  96. // mixin our passed parameters
  97. if (this.srcNodeRef && (typeof this.srcNodeRef.id == "string")) {
  98. this.id = this.srcNodeRef.id;
  99. }
  100. if (params) {
  101. dojo.mixin(this, params);
  102. }
  103. this.postMixInProperties();
  104. // generate an id for the widget if one wasn't specified
  105. // (be sure to do this before buildRendering() because that function
  106. // might
  107. // expect the id to be there.
  108. if (!this.id) {
  109. this.id = dijit.getUniqueId(this.declaredClass.replace(/\./g,
  110. "_"));
  111. }
  112. dijit.registry.add(this);
  113. this.buildRendering();
  114. // Copy attributes listed in attributeMap into the [newly created]
  115. // DOM for the widget.
  116. // The placement of these attributes is according to the property
  117. // mapping in attributeMap.
  118. // Note special handling for 'style' and 'class' attributes which
  119. // are lists and can
  120. // have elements from both old and new structures, and some
  121. // attributes like "type"
  122. // cannot be processed this way as they are not mutable.
  123. if (this.domNode) {
  124. for (var attr in this.attributeMap) {
  125. var mapNode = this[this.attributeMap[attr] || "domNode"];
  126. var value = this[attr];
  127. if (typeof value != "object"
  128. && (value !== "" || (params && params[attr]))) {
  129. switch (attr) {
  130. case "class" :
  131. dojo.addClass(mapNode, value);
  132. break;
  133. case "style" :
  134. if (mapNode.style.cssText) {
  135. mapNode.style.cssText += "; " + value;// FIXME:
  136. // Opera
  137. } else {
  138. mapNode.style.cssText = value;
  139. }
  140. break;
  141. default :
  142. mapNode.setAttribute(attr, value);
  143. }
  144. }
  145. }
  146. }
  147. if (this.domNode) {
  148. this.domNode.setAttribute("widgetId", this.id);
  149. }
  150. this.postCreate();
  151. // If srcNodeRef has been processed and removed from the DOM (e.g.
  152. // TemplatedWidget) then delete it to allow GC.
  153. if (this.srcNodeRef && !this.srcNodeRef.parentNode) {
  154. delete this.srcNodeRef;
  155. }
  156. },
  157. postMixInProperties : function() {
  158. // summary
  159. // Called after the parameters to the widget have been read-in,
  160. // but before the widget template is instantiated.
  161. // Especially useful to set properties that are referenced in the
  162. // widget template.
  163. },
  164. buildRendering : function() {
  165. // summary:
  166. // Construct the UI for this widget, setting this.domNode.
  167. // Most widgets will mixin TemplatedWidget, which overrides this
  168. // method.
  169. this.domNode = this.srcNodeRef || dojo.doc.createElement('div');
  170. },
  171. postCreate : function() {
  172. // summary:
  173. // Called after a widget's dom has been setup
  174. },
  175. startup : function() {
  176. // summary:
  177. // Called after a widget's children, and other widgets on the page,
  178. // have been created.
  179. // Provides an opportunity to manipulate any children before they
  180. // are displayed
  181. // This is useful for composite widgets that need to control or
  182. // layout sub-widgets
  183. // Many layout widgets can use this as a wiring phase
  184. },
  185. // ////////// DESTROY FUNCTIONS ////////////////////////////////
  186. destroyRecursive : function(/* Boolean */finalize) {
  187. // summary:
  188. // Destroy this widget and it's descendants. This is the generic
  189. // "destructor" function that all widget users should call to
  190. // cleanly discard with a widget. Once a widget is destroyed, it's
  191. // removed from the manager object.
  192. // finalize: Boolean
  193. // is this function being called part of global environment
  194. // tear-down?
  195. this.destroyDescendants();
  196. this.destroy();
  197. },
  198. destroy : function(/* Boolean */finalize) {
  199. // summary:
  200. // Destroy this widget, but not its descendants
  201. // finalize: Boolean
  202. // is this function being called part of global environment
  203. // tear-down?
  204. this.uninitialize();
  205. dojo.forEach(this._connects, function(array) {
  206. dojo.forEach(array, dojo.disconnect);
  207. });
  208. this.destroyRendering(finalize);
  209. dijit.registry.remove(this.id);
  210. },
  211. destroyRendering : function(/* Boolean */finalize) {
  212. // summary:
  213. // Destroys the DOM nodes associated with this widget
  214. // finalize: Boolean
  215. // is this function being called part of global environment
  216. // tear-down?
  217. if (this.bgIframe) {
  218. this.bgIframe.destroy();
  219. delete this.bgIframe;
  220. }
  221. if (this.domNode) {
  222. dojo._destroyElement(this.domNode);
  223. delete this.domNode;
  224. }
  225. if (this.srcNodeRef) {
  226. dojo._destroyElement(this.srcNodeRef);
  227. delete this.srcNodeRef;
  228. }
  229. },
  230. destroyDescendants : function() {
  231. // summary:
  232. // Recursively destroy the children of this widget and their
  233. // descendants.
  234. // TODO: should I destroy in the reverse order, to go bottom up?
  235. dojo.forEach(this.getDescendants(), function(widget) {
  236. widget.destroy();
  237. });
  238. },
  239. uninitialize : function() {
  240. // summary:
  241. // stub function. Over-ride to implement custom widget tear-down
  242. // behavior.
  243. return false;
  244. },
  245. // //////////////// MISCELLANEOUS METHODS ///////////////////
  246. toString : function() {
  247. // summary:
  248. // returns a string that represents the widget. When a widget is
  249. // cast to a string, this method will be used to generate the
  250. // output. Currently, it does not implement any sort of reversable
  251. // serialization.
  252. return '[Widget ' + this.declaredClass + ', '
  253. + (this.id || 'NO ID') + ']'; // String
  254. },
  255. getDescendants : function() {
  256. // summary:
  257. // return all the descendant widgets
  258. var list = dojo.query('[widgetId]', this.domNode);
  259. return list.map(dijit.byNode); // Array
  260. },
  261. nodesWithKeyClick : ["input", "button"],
  262. connect : function(
  263. /* Object|null */obj,
  264. /* String */event,
  265. /* String|Function */method) {
  266. // summary:
  267. // Connects specified obj/event to specified method of this object
  268. // and registers for disconnect() on widget destroy.
  269. // Special event: "ondijitclick" triggers on a click or enter-down
  270. // or space-up
  271. // Similar to dojo.connect() but takes three arguments rather than
  272. // four.
  273. var handles = [];
  274. if (event == "ondijitclick") {
  275. var w = this;
  276. // add key based click activation for unsupported nodes.
  277. if (!this.nodesWithKeyClick[obj.nodeName]) {
  278. handles.push(dojo.connect(obj, "onkeydown", this, function(
  279. e) {
  280. if (e.keyCode == dojo.keys.ENTER) {
  281. return (dojo.isString(method))
  282. ? w[method](e)
  283. : method.call(w, e);
  284. } else if (e.keyCode == dojo.keys.SPACE) {
  285. // stop space down as it causes IE to scroll
  286. // the browser window
  287. dojo.stopEvent(e);
  288. }
  289. }));
  290. handles.push(dojo.connect(obj, "onkeyup", this,
  291. function(e) {
  292. if (e.keyCode == dojo.keys.SPACE) {
  293. return dojo.isString(method)
  294. ? w[method](e)
  295. : method.call(w, e);
  296. }
  297. }));
  298. }
  299. event = "onclick";
  300. }
  301. handles.push(dojo.connect(obj, event, this, method));
  302. // return handles for FormElement and ComboBox
  303. this._connects.push(handles);
  304. return handles;
  305. },
  306. disconnect : function(/* Object */handles) {
  307. // summary:
  308. // Disconnects handle created by this.connect.
  309. // Also removes handle from this widget's list of connects
  310. for (var i = 0; i < this._connects.length; i++) {
  311. if (this._connects[i] == handles) {
  312. dojo.forEach(handles, dojo.disconnect);
  313. this._connects.splice(i, 1);
  314. return;
  315. }
  316. }
  317. },
  318. isLeftToRight : function() {
  319. // summary:
  320. // Checks the DOM to for the text direction for bi-directional
  321. // support
  322. // description:
  323. // This method cannot be used during widget construction because the
  324. // widget
  325. // must first be connected to the DOM tree. Parent nodes are
  326. // searched for the
  327. // 'dir' attribute until one is found, otherwise left to right mode
  328. // is assumed.
  329. // See HTML spec, DIR attribute for more information.
  330. if (typeof this._ltr == "undefined") {
  331. this._ltr = dojo.getComputedStyle(this.domNode).direction != "rtl";
  332. }
  333. return this._ltr; // Boolean
  334. },
  335. isFocusable : function() {
  336. // summary:
  337. // Return true if this widget can currently be focused
  338. // and false if not
  339. return this.focus
  340. && (dojo.style(this.domNode, "display") != "none");
  341. }
  342. });
  343. }