diff options
author | Andrew Dolgov <[email protected]> | 2011-03-04 19:02:28 +0300 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2011-03-04 19:02:59 +0300 |
commit | a089699c8915636ba4f158d77dba9b012bc93208 (patch) | |
tree | b2d7d051f1f55d44a6be07d3ee137e5a7ccfcefb /lib/dojo/_base/connect.js | |
parent | cfad9259a6feacfa8194b1312770ae6db1ecce50 (diff) |
build custom layer of Dojo to speed up loading of tt-rss (refs #293)
Diffstat (limited to 'lib/dojo/_base/connect.js')
-rw-r--r-- | lib/dojo/_base/connect.js | 359 |
1 files changed, 292 insertions, 67 deletions
diff --git a/lib/dojo/_base/connect.js b/lib/dojo/_base/connect.js index 3905d92e7..f37af65b2 100644 --- a/lib/dojo/_base/connect.js +++ b/lib/dojo/_base/connect.js @@ -5,81 +5,306 @@ */ -if(!dojo._hasResource["dojo._base.connect"]){ -dojo._hasResource["dojo._base.connect"]=true; +if(!dojo._hasResource["dojo._base.connect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.connect"] = true; dojo.provide("dojo._base.connect"); dojo.require("dojo._base.lang"); -dojo._listener={getDispatcher:function(){ -return function(){ -var ap=Array.prototype,c=arguments.callee,ls=c._listeners,t=c.target; -var r=t&&t.apply(this,arguments); -var i,_1; -_1=[].concat(ls); -for(i in _1){ -if(!(i in ap)){ -_1[i].apply(this,arguments); -} -} -return r; + +// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA + +// low-level delegation machinery +dojo._listener = { + // create a dispatcher function + getDispatcher: function(){ + // following comments pulled out-of-line to prevent cloning them + // in the returned function. + // - indices (i) that are really in the array of listeners (ls) will + // not be in Array.prototype. This is the 'sparse array' trick + // that keeps us safe from libs that take liberties with built-in + // objects + // - listener is invoked with current scope (this) + return function(){ + var ap=Array.prototype, c=arguments.callee, ls=c._listeners, t=c.target; + // return value comes from original target function + var r = t && t.apply(this, arguments); + // make local copy of listener array so it is immutable during processing + var i, lls; + lls = [].concat(ls); + + // invoke listeners after target function + for(i in lls){ + if(!(i in ap)){ + lls[i].apply(this, arguments); + } + } + // return value comes from original target function + return r; + }; + }, + // add a listener to an object + add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){ + // Whenever 'method' is invoked, 'listener' will have the same scope. + // Trying to supporting a context object for the listener led to + // complexity. + // Non trivial to provide 'once' functionality here + // because listener could be the result of a dojo.hitch call, + // in which case two references to the same hitch target would not + // be equivalent. + source = source || dojo.global; + // The source method is either null, a dispatcher, or some other function + var f = source[method]; + // Ensure a dispatcher + if(!f || !f._listeners){ + var d = dojo._listener.getDispatcher(); + // original target function is special + d.target = f; + // dispatcher holds a list of listeners + d._listeners = []; + // redirect source to dispatcher + f = source[method] = d; + } + // The contract is that a handle is returned that can + // identify this listener for disconnect. + // + // The type of the handle is private. Here is it implemented as Integer. + // DOM event code has this same contract but handle is Function + // in non-IE browsers. + // + // We could have separate lists of before and after listeners. + return f._listeners.push(listener); /*Handle*/ + }, + // remove a listener from an object + remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){ + var f = (source || dojo.global)[method]; + // remember that handle is the index+1 (0 is not a valid handle) + if(f && f._listeners && handle--){ + delete f._listeners[handle]; + } + } }; -},add:function(_2,_3,_4){ -_2=_2||dojo.global; -var f=_2[_3]; -if(!f||!f._listeners){ -var d=dojo._listener.getDispatcher(); -d.target=f; -d._listeners=[]; -f=_2[_3]=d; + +// Multiple delegation for arbitrary methods. + +// This unit knows nothing about DOM, but we include DOM aware documentation +// and dontFix argument here to help the autodocs. Actual DOM aware code is in +// event.js. + +dojo.connect = function(/*Object|null*/ obj, + /*String*/ event, + /*Object|null*/ context, + /*String|Function*/ method, + /*Boolean?*/ dontFix){ + // summary: + // `dojo.connect` is the core event handling and delegation method in + // Dojo. It allows one function to "listen in" on the execution of + // any other, triggering the second whenever the first is called. Many + // listeners may be attached to a function, and source functions may + // be either regular function calls or DOM events. + // + // description: + // Connects listeners to actions, so that after event fires, a + // listener is called with the same arguments passed to the original + // function. + // + // Since `dojo.connect` allows the source of events to be either a + // "regular" JavaScript function or a DOM event, it provides a uniform + // interface for listening to all the types of events that an + // application is likely to deal with though a single, unified + // interface. DOM programmers may want to think of it as + // "addEventListener for everything and anything". + // + // When setting up a connection, the `event` parameter must be a + // string that is the name of the method/event to be listened for. If + // `obj` is null, `dojo.global` is assumed, meaning that connections + // to global methods are supported but also that you may inadvertently + // connect to a global by passing an incorrect object name or invalid + // reference. + // + // `dojo.connect` generally is forgiving. If you pass the name of a + // function or method that does not yet exist on `obj`, connect will + // not fail, but will instead set up a stub method. Similarly, null + // arguments may simply be omitted such that fewer than 4 arguments + // may be required to set up a connection See the examples for details. + // + // The return value is a handle that is needed to + // remove this connection with `dojo.disconnect`. + // + // obj: + // The source object for the event function. + // Defaults to `dojo.global` if null. + // If obj is a DOM node, the connection is delegated + // to the DOM event manager (unless dontFix is true). + // + // event: + // String name of the event function in obj. + // I.e. identifies a property `obj[event]`. + // + // context: + // The object that method will receive as "this". + // + // If context is null and method is a function, then method + // inherits the context of event. + // + // If method is a string then context must be the source + // object object for method (context[method]). If context is null, + // dojo.global is used. + // + // method: + // A function reference, or name of a function in context. + // The function identified by method fires after event does. + // method receives the same arguments as the event. + // See context argument comments for information on method's scope. + // + // dontFix: + // If obj is a DOM node, set dontFix to true to prevent delegation + // of this connection to the DOM event manager. + // + // example: + // When obj.onchange(), do ui.update(): + // | dojo.connect(obj, "onchange", ui, "update"); + // | dojo.connect(obj, "onchange", ui, ui.update); // same + // + // example: + // Using return value for disconnect: + // | var link = dojo.connect(obj, "onchange", ui, "update"); + // | ... + // | dojo.disconnect(link); + // + // example: + // When onglobalevent executes, watcher.handler is invoked: + // | dojo.connect(null, "onglobalevent", watcher, "handler"); + // + // example: + // When ob.onCustomEvent executes, customEventHandler is invoked: + // | dojo.connect(ob, "onCustomEvent", null, "customEventHandler"); + // | dojo.connect(ob, "onCustomEvent", "customEventHandler"); // same + // + // example: + // When ob.onCustomEvent executes, customEventHandler is invoked + // with the same scope (this): + // | dojo.connect(ob, "onCustomEvent", null, customEventHandler); + // | dojo.connect(ob, "onCustomEvent", customEventHandler); // same + // + // example: + // When globalEvent executes, globalHandler is invoked + // with the same scope (this): + // | dojo.connect(null, "globalEvent", null, globalHandler); + // | dojo.connect("globalEvent", globalHandler); // same + + // normalize arguments + var a=arguments, args=[], i=0; + // if a[0] is a String, obj was omitted + args.push(dojo.isString(a[0]) ? null : a[i++], a[i++]); + // if the arg-after-next is a String or Function, context was NOT omitted + var a1 = a[i+1]; + args.push(dojo.isString(a1)||dojo.isFunction(a1) ? a[i++] : null, a[i++]); + // absorb any additional arguments + for(var l=a.length; i<l; i++){ args.push(a[i]); } + // do the actual work + return dojo._connect.apply(this, args); /*Handle*/ } -return f._listeners.push(_4); -},remove:function(_5,_6,_7){ -var f=(_5||dojo.global)[_6]; -if(f&&f._listeners&&_7--){ -delete f._listeners[_7]; + +// used by non-browser hostenvs. always overriden by event.js +dojo._connect = function(obj, event, context, method){ + var l=dojo._listener, h=l.add(obj, event, dojo.hitch(context, method)); + return [obj, event, h, l]; // Handle } -}}; -dojo.connect=function(_8,_9,_a,_b,_c){ -var a=arguments,_d=[],i=0; -_d.push(dojo.isString(a[0])?null:a[i++],a[i++]); -var a1=a[i+1]; -_d.push(dojo.isString(a1)||dojo.isFunction(a1)?a[i++]:null,a[i++]); -for(var l=a.length;i<l;i++){ -_d.push(a[i]); + +dojo.disconnect = function(/*Handle*/ handle){ + // summary: + // Remove a link created by dojo.connect. + // description: + // Removes the connection between event and the method referenced by handle. + // handle: + // the return value of the dojo.connect call that created the connection. + if(handle && handle[0] !== undefined){ + dojo._disconnect.apply(this, handle); + // let's not keep this reference + delete handle[0]; + } } -return dojo._connect.apply(this,_d); -}; -dojo._connect=function(_e,_f,_10,_11){ -var l=dojo._listener,h=l.add(_e,_f,dojo.hitch(_10,_11)); -return [_e,_f,h,l]; -}; -dojo.disconnect=function(_12){ -if(_12&&_12[0]!==undefined){ -dojo._disconnect.apply(this,_12); -delete _12[0]; + +dojo._disconnect = function(obj, event, handle, listener){ + listener.remove(obj, event, handle); } -}; -dojo._disconnect=function(obj,_13,_14,_15){ -_15.remove(obj,_13,_14); -}; -dojo._topics={}; -dojo.subscribe=function(_16,_17,_18){ -return [_16,dojo._listener.add(dojo._topics,_16,dojo.hitch(_17,_18))]; -}; -dojo.unsubscribe=function(_19){ -if(_19){ -dojo._listener.remove(dojo._topics,_19[0],_19[1]); + +// topic publish/subscribe + +dojo._topics = {}; + +dojo.subscribe = function(/*String*/ topic, /*Object|null*/ context, /*String|Function*/ method){ + // summary: + // Attach a listener to a named topic. The listener function is invoked whenever the + // named topic is published (see: dojo.publish). + // Returns a handle which is needed to unsubscribe this listener. + // context: + // Scope in which method will be invoked, or null for default scope. + // method: + // The name of a function in context, or a function reference. This is the function that + // is invoked when topic is published. + // example: + // | dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); }); + // | dojo.publish("alerts", [ "read this", "hello world" ]); + + // support for 2 argument invocation (omitting context) depends on hitch + return [topic, dojo._listener.add(dojo._topics, topic, dojo.hitch(context, method))]; /*Handle*/ } -}; -dojo.publish=function(_1a,_1b){ -var f=dojo._topics[_1a]; -if(f){ -f.apply(this,_1b||[]); + +dojo.unsubscribe = function(/*Handle*/ handle){ + // summary: + // Remove a topic listener. + // handle: + // The handle returned from a call to subscribe. + // example: + // | var alerter = dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); }; + // | ... + // | dojo.unsubscribe(alerter); + if(handle){ + dojo._listener.remove(dojo._topics, handle[0], handle[1]); + } } + +dojo.publish = function(/*String*/ topic, /*Array*/ args){ + // summary: + // Invoke all listener method subscribed to topic. + // topic: + // The name of the topic to publish. + // args: + // An array of arguments. The arguments will be applied + // to each topic subscriber (as first class parameters, via apply). + // example: + // | dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); }; + // | dojo.publish("alerts", [ "read this", "hello world" ]); + + // Note that args is an array, which is more efficient vs variable length + // argument list. Ideally, var args would be implemented via Array + // throughout the APIs. + var f = dojo._topics[topic]; + if(f){ + f.apply(this, args||[]); + } +} + +dojo.connectPublisher = function( /*String*/ topic, + /*Object|null*/ obj, + /*String*/ event){ + // summary: + // Ensure that every time obj.event() is called, a message is published + // on the topic. Returns a handle which can be passed to + // dojo.disconnect() to disable subsequent automatic publication on + // the topic. + // topic: + // The name of the topic to publish. + // obj: + // The source object for the event function. Defaults to dojo.global + // if null. + // event: + // The name of the event function in obj. + // I.e. identifies a property obj[event]. + // example: + // | dojo.connectPublisher("/ajax/start", dojo, "xhrGet"); + var pf = function(){ dojo.publish(topic, arguments); } + return event ? dojo.connect(obj, event, pf) : dojo.connect(obj, pf); //Handle }; -dojo.connectPublisher=function(_1c,obj,_1d){ -var pf=function(){ -dojo.publish(_1c,arguments); -}; -return _1d?dojo.connect(obj,_1d,pf):dojo.connect(obj,pf); -}; + } |