connect.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojo._base.connect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojo._base.connect"] = true;
  8. dojo.provide("dojo._base.connect");
  9. dojo.require("dojo._base.lang");
  10. // this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
  11. // low-level delegation machinery
  12. dojo._listener = {
  13. // create a dispatcher function
  14. getDispatcher: function(){
  15. // following comments pulled out-of-line to prevent cloning them
  16. // in the returned function.
  17. // - indices (i) that are really in the array of listeners (ls) will
  18. // not be in Array.prototype. This is the 'sparse array' trick
  19. // that keeps us safe from libs that take liberties with built-in
  20. // objects
  21. // - listener is invoked with current scope (this)
  22. return function(){
  23. var ap = Array.prototype, c = arguments.callee, ls = c._listeners, t = c.target,
  24. // return value comes from original target function
  25. r = t && t.apply(this, arguments),
  26. // make local copy of listener array so it is immutable during processing
  27. i, lls = [].concat(ls)
  28. ;
  29. // invoke listeners after target function
  30. for(i in lls){
  31. if(!(i in ap)){
  32. lls[i].apply(this, arguments);
  33. }
  34. }
  35. // return value comes from original target function
  36. return r;
  37. };
  38. },
  39. // add a listener to an object
  40. add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){
  41. // Whenever 'method' is invoked, 'listener' will have the same scope.
  42. // Trying to supporting a context object for the listener led to
  43. // complexity.
  44. // Non trivial to provide 'once' functionality here
  45. // because listener could be the result of a dojo.hitch call,
  46. // in which case two references to the same hitch target would not
  47. // be equivalent.
  48. source = source || dojo.global;
  49. // The source method is either null, a dispatcher, or some other function
  50. var f = source[method];
  51. // Ensure a dispatcher
  52. if(!f || !f._listeners){
  53. var d = dojo._listener.getDispatcher();
  54. // original target function is special
  55. d.target = f;
  56. // dispatcher holds a list of listeners
  57. d._listeners = [];
  58. // redirect source to dispatcher
  59. f = source[method] = d;
  60. }
  61. // The contract is that a handle is returned that can
  62. // identify this listener for disconnect.
  63. //
  64. // The type of the handle is private. Here is it implemented as Integer.
  65. // DOM event code has this same contract but handle is Function
  66. // in non-IE browsers.
  67. //
  68. // We could have separate lists of before and after listeners.
  69. return f._listeners.push(listener); /*Handle*/
  70. },
  71. // remove a listener from an object
  72. remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){
  73. var f = (source || dojo.global)[method];
  74. // remember that handle is the index+1 (0 is not a valid handle)
  75. if(f && f._listeners && handle--){
  76. delete f._listeners[handle];
  77. }
  78. }
  79. };
  80. // Multiple delegation for arbitrary methods.
  81. // This unit knows nothing about DOM, but we include DOM aware documentation
  82. // and dontFix argument here to help the autodocs. Actual DOM aware code is in
  83. // event.js.
  84. dojo.connect = function(/*Object|null*/ obj,
  85. /*String*/ event,
  86. /*Object|null*/ context,
  87. /*String|Function*/ method,
  88. /*Boolean?*/ dontFix){
  89. // summary:
  90. // `dojo.connect` is the core event handling and delegation method in
  91. // Dojo. It allows one function to "listen in" on the execution of
  92. // any other, triggering the second whenever the first is called. Many
  93. // listeners may be attached to a function, and source functions may
  94. // be either regular function calls or DOM events.
  95. //
  96. // description:
  97. // Connects listeners to actions, so that after event fires, a
  98. // listener is called with the same arguments passed to the original
  99. // function.
  100. //
  101. // Since `dojo.connect` allows the source of events to be either a
  102. // "regular" JavaScript function or a DOM event, it provides a uniform
  103. // interface for listening to all the types of events that an
  104. // application is likely to deal with though a single, unified
  105. // interface. DOM programmers may want to think of it as
  106. // "addEventListener for everything and anything".
  107. //
  108. // When setting up a connection, the `event` parameter must be a
  109. // string that is the name of the method/event to be listened for. If
  110. // `obj` is null, `dojo.global` is assumed, meaning that connections
  111. // to global methods are supported but also that you may inadvertently
  112. // connect to a global by passing an incorrect object name or invalid
  113. // reference.
  114. //
  115. // `dojo.connect` generally is forgiving. If you pass the name of a
  116. // function or method that does not yet exist on `obj`, connect will
  117. // not fail, but will instead set up a stub method. Similarly, null
  118. // arguments may simply be omitted such that fewer than 4 arguments
  119. // may be required to set up a connection See the examples for details.
  120. //
  121. // The return value is a handle that is needed to
  122. // remove this connection with `dojo.disconnect`.
  123. //
  124. // obj:
  125. // The source object for the event function.
  126. // Defaults to `dojo.global` if null.
  127. // If obj is a DOM node, the connection is delegated
  128. // to the DOM event manager (unless dontFix is true).
  129. //
  130. // event:
  131. // String name of the event function in obj.
  132. // I.e. identifies a property `obj[event]`.
  133. //
  134. // context:
  135. // The object that method will receive as "this".
  136. //
  137. // If context is null and method is a function, then method
  138. // inherits the context of event.
  139. //
  140. // If method is a string then context must be the source
  141. // object object for method (context[method]). If context is null,
  142. // dojo.global is used.
  143. //
  144. // method:
  145. // A function reference, or name of a function in context.
  146. // The function identified by method fires after event does.
  147. // method receives the same arguments as the event.
  148. // See context argument comments for information on method's scope.
  149. //
  150. // dontFix:
  151. // If obj is a DOM node, set dontFix to true to prevent delegation
  152. // of this connection to the DOM event manager.
  153. //
  154. // example:
  155. // When obj.onchange(), do ui.update():
  156. // | dojo.connect(obj, "onchange", ui, "update");
  157. // | dojo.connect(obj, "onchange", ui, ui.update); // same
  158. //
  159. // example:
  160. // Using return value for disconnect:
  161. // | var link = dojo.connect(obj, "onchange", ui, "update");
  162. // | ...
  163. // | dojo.disconnect(link);
  164. //
  165. // example:
  166. // When onglobalevent executes, watcher.handler is invoked:
  167. // | dojo.connect(null, "onglobalevent", watcher, "handler");
  168. //
  169. // example:
  170. // When ob.onCustomEvent executes, customEventHandler is invoked:
  171. // | dojo.connect(ob, "onCustomEvent", null, "customEventHandler");
  172. // | dojo.connect(ob, "onCustomEvent", "customEventHandler"); // same
  173. //
  174. // example:
  175. // When ob.onCustomEvent executes, customEventHandler is invoked
  176. // with the same scope (this):
  177. // | dojo.connect(ob, "onCustomEvent", null, customEventHandler);
  178. // | dojo.connect(ob, "onCustomEvent", customEventHandler); // same
  179. //
  180. // example:
  181. // When globalEvent executes, globalHandler is invoked
  182. // with the same scope (this):
  183. // | dojo.connect(null, "globalEvent", null, globalHandler);
  184. // | dojo.connect("globalEvent", globalHandler); // same
  185. // normalize arguments
  186. var a=arguments, args=[], i=0;
  187. // if a[0] is a String, obj was omitted
  188. args.push(dojo.isString(a[0]) ? null : a[i++], a[i++]);
  189. // if the arg-after-next is a String or Function, context was NOT omitted
  190. var a1 = a[i+1];
  191. args.push(dojo.isString(a1)||dojo.isFunction(a1) ? a[i++] : null, a[i++]);
  192. // absorb any additional arguments
  193. for(var l=a.length; i<l; i++){ args.push(a[i]); }
  194. // do the actual work
  195. return dojo._connect.apply(this, args); /*Handle*/
  196. }
  197. // used by non-browser hostenvs. always overriden by event.js
  198. dojo._connect = function(obj, event, context, method){
  199. var l=dojo._listener, h=l.add(obj, event, dojo.hitch(context, method));
  200. return [obj, event, h, l]; // Handle
  201. };
  202. dojo.disconnect = function(/*Handle*/ handle){
  203. // summary:
  204. // Remove a link created by dojo.connect.
  205. // description:
  206. // Removes the connection between event and the method referenced by handle.
  207. // handle:
  208. // the return value of the dojo.connect call that created the connection.
  209. if(handle && handle[0] !== undefined){
  210. dojo._disconnect.apply(this, handle);
  211. // let's not keep this reference
  212. delete handle[0];
  213. }
  214. };
  215. dojo._disconnect = function(obj, event, handle, listener){
  216. listener.remove(obj, event, handle);
  217. };
  218. // topic publish/subscribe
  219. dojo._topics = {};
  220. dojo.subscribe = function(/*String*/ topic, /*Object|null*/ context, /*String|Function*/ method){
  221. // summary:
  222. // Attach a listener to a named topic. The listener function is invoked whenever the
  223. // named topic is published (see: dojo.publish).
  224. // Returns a handle which is needed to unsubscribe this listener.
  225. // context:
  226. // Scope in which method will be invoked, or null for default scope.
  227. // method:
  228. // The name of a function in context, or a function reference. This is the function that
  229. // is invoked when topic is published.
  230. // example:
  231. // | dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); });
  232. // | dojo.publish("alerts", [ "read this", "hello world" ]);
  233. // support for 2 argument invocation (omitting context) depends on hitch
  234. return [topic, dojo._listener.add(dojo._topics, topic, dojo.hitch(context, method))]; /*Handle*/
  235. };
  236. dojo.unsubscribe = function(/*Handle*/ handle){
  237. // summary:
  238. // Remove a topic listener.
  239. // handle:
  240. // The handle returned from a call to subscribe.
  241. // example:
  242. // | var alerter = dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };
  243. // | ...
  244. // | dojo.unsubscribe(alerter);
  245. if(handle){
  246. dojo._listener.remove(dojo._topics, handle[0], handle[1]);
  247. }
  248. };
  249. dojo.publish = function(/*String*/ topic, /*Array*/ args){
  250. // summary:
  251. // Invoke all listener method subscribed to topic.
  252. // topic:
  253. // The name of the topic to publish.
  254. // args:
  255. // An array of arguments. The arguments will be applied
  256. // to each topic subscriber (as first class parameters, via apply).
  257. // example:
  258. // | dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };
  259. // | dojo.publish("alerts", [ "read this", "hello world" ]);
  260. // Note that args is an array, which is more efficient vs variable length
  261. // argument list. Ideally, var args would be implemented via Array
  262. // throughout the APIs.
  263. var f = dojo._topics[topic];
  264. if(f){
  265. f.apply(this, args||[]);
  266. }
  267. };
  268. dojo.connectPublisher = function( /*String*/ topic,
  269. /*Object|null*/ obj,
  270. /*String*/ event){
  271. // summary:
  272. // Ensure that every time obj.event() is called, a message is published
  273. // on the topic. Returns a handle which can be passed to
  274. // dojo.disconnect() to disable subsequent automatic publication on
  275. // the topic.
  276. // topic:
  277. // The name of the topic to publish.
  278. // obj:
  279. // The source object for the event function. Defaults to dojo.global
  280. // if null.
  281. // event:
  282. // The name of the event function in obj.
  283. // I.e. identifies a property obj[event].
  284. // example:
  285. // | dojo.connectPublisher("/ajax/start", dojo, "xhrGet");
  286. var pf = function(){ dojo.publish(topic, arguments); }
  287. return event ? dojo.connect(obj, event, pf) : dojo.connect(obj, pf); //Handle
  288. };
  289. }