Changeset 8235
- Timestamp:
- 04/21/07 18:25:14 (19 months ago)
- Location:
- dojo/trunk
- Files:
-
- 4 modified
-
tests/_base/connect.js (modified) (4 diffs)
-
_base/connect.js (modified) (3 diffs)
-
_base/event.js (modified) (12 diffs)
-
_base/lang.js (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
dojo/trunk/tests/_base/connect.js
r7988 r8235 1 1 dojo.provide("tests._base.connect"); 2 2 3 hub = function() { 4 } 5 6 bad = function() { 3 hub = function(){ 4 } 5 6 failures = 0; 7 bad = function(){ 7 8 failures++; 8 //logMe('BAD - I should have been disconnected'); 9 } 10 11 good = function() { 12 //logMe("good - I'm supposed to be here"); 13 } 14 15 markAndSweepTest = function(iterations) { 16 //console.group('markAndSweepTest(' + iterations + ');'); 9 } 10 11 good = function(){ 12 } 13 14 // make 'iterations' connections to hub 15 // roughly half of which will be to 'good' and 16 // half to 'bad' 17 // all connections to 'bad' are disconnected 18 // test can then be performed on the values 19 // 'failures' and 'successes' 20 markAndSweepTest = function(iterations){ 17 21 var marked = []; 18 22 // connections 19 //var t = new Date().getTime(); 20 for (var i=0;i<iterations;i++) { 21 if (Math.random() < 0.5) { 22 marked.push(dojo.connect(null, 'hub', null, bad)); 23 } else { 24 dojo.connect(null, 'hub', null, 'good'); 25 } 26 } 27 //t = new Date().getTime() - t; 28 //logMe('made ' + i + ' connections with ' + marked.length + ' marked for disconnection in ' + t + 'ms.'); 29 // Randomizing markers (takes a long time if the count is too high) 30 if (i < Math.pow(10, 4)) { 31 //var t = new Date().getTime(); 23 for(var i=0; i<iterations; i++){ 24 if(Math.random() < 0.5){ 25 marked.push(dojo.connect('hub', bad)); 26 }else{ 27 dojo.connect('hub', good); 28 } 29 } 30 // Randomize markers (only if the count isn't very high) 31 if(i < Math.pow(10, 4)){ 32 32 var rm = [ ]; 33 while (marked.length){33 while(marked.length){ 34 34 var m = Math.floor(Math.random() * marked.length); 35 35 rm.push(marked[m]); … … 37 37 } 38 38 marked = rm; 39 //t = new Date().getTime() - t;40 //logMe('randomized markers in ' + t + 'ms.');41 39 } 42 //else logMe('skipping marker randomization due to large set.'); 43 // disconnections 44 //var t = new Date().getTime(); 45 for (var m=0; m<marked.length; m++) { 46 dojo.disconnect(null, 'hub', marked[m]); 47 } 48 //t = new Date().getTime() - t; 49 //logMe('performed disconnects in ' + t + 'ms.'); 40 for(var m=0; m<marked.length; m++){ 41 dojo.disconnect('hub', marked[m]); 42 } 50 43 // test 51 44 failures = 0; 52 //var t = new Date().getTime();53 45 hub(); 54 //t = new Date().getTime() - t; 55 //logMe('invoked hub in ' + t + 'ms with ' + failures + ' failed disconnects over ' + i + ' iterations.'); 56 // done 57 //console.groupEnd(); 46 // return number of disconnected functions that fired (should be 0) 47 return failures; 48 } 49 50 markAndSweepSubscribersTest = function(iterations){ 51 var topic = "hubbins"; 52 var marked = []; 53 // connections 54 for(var i=0; i<iterations; i++){ 55 if(Math.random() < 0.5){ 56 marked.push(dojo.subscribe(topic, bad)); 57 }else{ 58 dojo.subscribe(topic, good); 59 } 60 } 61 // Randomize markers (only if the count isn't very high) 62 if(i < Math.pow(10, 4)){ 63 var rm = [ ]; 64 while(marked.length){ 65 var m = Math.floor(Math.random() * marked.length); 66 rm.push(marked[m]); 67 marked.splice(m, 1); 68 } 69 marked = rm; 70 } 71 for(var m=0; m<marked.length; m++){ 72 dojo.unsubscribe(topic, marked[m]); 73 } 74 // test 75 failures = 0; 76 dojo.publish(topic); 77 // return number of unsubscribed functions that fired (should be 0) 58 78 return failures; 59 79 } … … 62 82 [ 63 83 function smokeTest(t){ 64 var ok = false, foo = { foo: function(){} }; 84 // foo sets ok to false 85 var ok, foo = { foo: function(){ok=false} }; 86 // connected function sets ok to true 65 87 dojo.connect(foo, "foo", null, function(){ok=true}); 66 88 foo.foo(); 67 t.is(ok, true); 68 }, 69 function smokeTest2(t){ 70 var ok = false, foo = { foo: function(){}, ok: function(){ok=true} }; 71 dojo.connect(foo, "foo", foo, "ok"); 72 foo.foo(); 73 t.is(ok, true); 74 }, 75 function simpleTest(t) { 89 t.is(true, ok); 90 }, 91 function basicTest(t) { 76 92 var out = ''; 77 93 var obj = { … … 120 136 t.is(markAndSweepTest(1000), 0); 121 137 }, 122 function hubConnectDisconnect10000(t){ 123 t.is(markAndSweepTest(10000), 0); 138 function args4Test(t){ 139 // standard 4 args test 140 var ok, obj = { foo: function(){ok=false;}, bar: function(){ok=true} }; 141 dojo.connect(obj, "foo", obj, "bar"); 142 obj.foo(); 143 t.is(true, ok); 144 }, 145 function args3Test(t){ 146 // make some globals 147 var ok; 148 dojo.global["gFoo"] = function(){ok=false;}; 149 dojo.global["gOk"] = function(){ok=true;}; 150 // 3 arg shorthand for globals (a) 151 var link = dojo.connect("gFoo", null, "gOk"); 152 gFoo(); 153 dojo.disconnect("gFoo", link); 154 t.is(true, ok); 155 // 3 arg shorthand for globals (b) 156 link = dojo.connect(null, "gFoo", "gOk"); 157 gFoo(); 158 dojo.disconnect("gFoo", link); 159 t.is(true, ok); 160 // verify disconnections 161 gFoo(); 162 t.is(false, ok); 163 }, 164 function args2Test(t){ 165 // make some globals 166 var ok; 167 dojo.global["gFoo"] = function(){ok=false;}; 168 dojo.global["gOk"] = function(){ok=true;}; 169 // 2 arg shorthand for globals 170 var link = dojo.connect("gFoo", "gOk"); 171 gFoo(); 172 dojo.disconnect("gFoo", link); 173 console.log('argsTest2a: ', (ok ? "ok" : "not ok")); 174 // 2 arg shorthand for globals, alternate scoping 175 link = dojo.connect("gFoo", gOk); 176 gFoo(); 177 dojo.disconnect("gFoo", link); 178 console.log('argsTest2b: ', (ok ? "ok" : "not ok")); 179 }, 180 function scopeTest1(t){ 181 var foo = { ok: true, foo: function(){this.ok=false;} }; 182 var bar = { ok: false, bar: function(){this.ok=true} }; 183 // link foo.foo to bar.bar with natural scope 184 var link = dojo.connect(foo, "foo", bar, "bar"); 185 foo.foo(); 186 t.is(false, foo.ok); 187 t.is(true, bar.ok); 188 }, 189 function scopeTest2(t){ 190 var foo = { ok: true, foo: function(){this.ok=false;} }; 191 var bar = { ok: false, bar: function(){this.ok=true} }; 192 // link foo.foo to bar.bar such that scope is always 'foo' 193 var link = dojo.connect(foo, "foo", bar.bar); 194 foo.foo(); 195 t.is(true, foo.ok); 196 t.is(false, bar.ok); 197 }, 198 function publishSubscribe1000(t){ 199 t.is(markAndSweepSubscribersTest(1000), 0); 124 200 } 125 201 ] -
dojo/trunk/_base/connect.js
r7994 r8235 2 2 dojo.require("dojo._base.lang"); 3 3 4 // this file courtesy of the TurboAjax group, licensed under a Dojo CLA4 // this file courtesy of the TurboAjax Group, licensed under a Dojo CLA 5 5 6 6 // FIXME: needs in-code docs in the worst way!! 7 7 8 // low-level delegation machinery 8 9 dojo._listener = { 9 // low-level delegation machinery 10 dispatchers: [ ], 11 dispatcher: function(source, method, f) { 12 var op = Object.prototype; 13 var d = function(){ 14 var c=arguments.callee, list=c.listeners; 15 for (var i in list) { 16 !(i in op)&&(list[i].apply(this, arguments)); 10 // create a dispatcher function 11 dispatcher: function(f){ 12 // return a dispatcher function 13 return function(){ 14 // iterate over our listeners 15 var ls = arguments.callee.listeners; 16 for(var i in ls){ 17 // properties that are really listeners will not be in "a" 18 if(!(i in Array.prototype)){ 19 // invoke the listener with our current scope 20 ls[i].apply(this, arguments); 21 } 17 22 } 18 23 } 19 d.clean = function() {20 source[method] = null21 }22 d.listeners = (f ? [f] : []);23 source[method] = d;24 dojo._listener.dispatchers.push(d);25 return d;26 24 }, 25 // add a listener to an object 27 26 add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){ 28 27 // Whenever 'method' is invoked, 'listener' will have the same scope. 29 // Supporting a context object for the listener here leads to 30 // unwarranted complexity. 31 // 32 // This is an issue for providing 'once' functionality here 28 // Trying to supporting a context object for the listener led to 29 // complexity. 30 // Non trivial to provide 'once' functionality here 33 31 // because listener could be the result of a dojo.hitch call, 34 // in which case two references to the same functionwould not32 // in which case two references to the same hitch target would not 35 33 // be equivalent. 36 34 source = source || dojo.global; 37 35 // The source method is either null, a dispatcher, or some other function 38 var d= source[method];36 var f = source[method]; 39 37 // Ensure a dispatcher 40 if (!d||!d.listeners) { 41 d = dojo._listener.dispatcher(source, method, d); 38 if(!f||!f.listeners){ 39 var d = dojo._listener.dispatcher(f); 40 // dispatcher holds a list of handlers 41 d.listeners = (f ? [f] : []); 42 // put back in source 43 f = source[method] = d; 42 44 } 43 // The contract is that a 'handle' is returned that is suitable for44 // identify ing this listener.45 // The contract is that a handle is returned that can 46 // identify this listener for disconnect. 45 47 // 46 // The type of 'handle'is private. Here is it implemented as Integer.47 // DOM event code has this same contract but 'handle'is Function48 // The type of the handle is private. Here is it implemented as Integer. 49 // DOM event code has this same contract but handle is Function 48 50 // in non-IE browsers. 49 51 // 50 52 // Could implement 'before' with a flag and unshift. 51 return d.listeners.push(listener) - 1; /*Handle*/53 return f.listeners.push(listener) ; /*Handle*/ 52 54 }, 55 // remove a listener from an object 53 56 remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){ 54 57 var f = (source||dojo.global)[method]; 55 if(f && f.listeners && handle){ delete f.listeners[handle]; } 56 }, 57 clean: function(){ 58 for (var i=0, d; (d=this.dispatchers[i]); i++){ d.clean(); } 59 this.dispatchers = []; 58 // remember that handle is the index+1 (0 is not a valid handle) 59 if(f && f.listeners && handle--){ 60 delete f.listeners[handle]; 61 } 60 62 } 61 63 }; 64 62 65 // arbitrary method delegation (knows nothing about DOM) 63 66 … … 66 69 /*Object|null*/ context, 67 70 /*String|Function*/ method){ 68 // support for 3 argument invocation depends on hitch 69 return dojo._listener.add(obj, event, dojo.hitch(context, method)); /*Handle*/ 71 // summary: 72 // Create a link that calls one function when another executes. 73 // description: 74 // Connects method to event, so that after event fires, method 75 // does too. Connect as many methods to event as needed. 76 // 77 // event must be a string. If obj is null, dojo.global is used. 78 // 79 // null arguments may simply be omitted. 80 // 81 // obj[event] can resolve to a function or undefined (null). 82 // If obj[event] is null, it is assigned a function. 83 // 84 // The return value is a handle that is needed to 85 // remove this connection with dojo.disconnect. 86 // obj: 87 // The source object for the event function. 88 // Defaults to dojo.global if null. 89 // event: 90 // String name of the event function in obj. 91 // I.e. identifies a property obj[event]. 92 // context: 93 // The object that method will receive as "this". 94 // 95 // If context is null and method is a function, then method 96 // inherits the context of event. 97 // 98 // If method is a string then context must be the source 99 // object object for method (context[method]). If context is null, 100 // dojo.global is used. 101 // method: 102 // A function reference, or name of a function in context. 103 // The function identified by method fires after event does. 104 // method receives the same arguments as the event. 105 // See context argument comments for information on method's scope. 106 // usage: 107 // // when obj.onchange(), do ui.update() 108 // dojo.connect(obj, "onchange", ui, "update"); 109 // dojo.connect(obj, "onchange", ui, ui.update); // same 110 // 111 // // using return value for disconnect 112 // var link = dojo.connect(obj, "onchange", ui, "update"); 113 // ... 114 // dojo.disconnect(obj, "onchange", link); 115 // 116 // // when onglobalevent executes, watcher.handler is invoked 117 // dojo.connect(null, "onglobalevent", watcher, "handler"); 118 // 119 // // when ob.onCustomEvent executes, customEventHandler is invoked 120 // dojo.connect(ob, "onCustomEvent", null, "customEventHandler"); 121 // dojo.connect(ob, "onCustomEvent", "customEventHandler"); // same 122 // 123 // // when ob.onCustomEvent executes, customEventHandler is invoked 124 // // with the same scope (this) 125 // dojo.connect(ob, "onCustomEvent", null, customEventHandler); 126 // dojo.connect(ob, "onCustomEvent", customEventHandler); // same 127 // 128 // // when globalEvent executes, globalHandler is invoked 129 // // with the same scope (this) 130 // dojo.connect(null, "globalEvent", null, globalHandler); 131 // dojo.connect("globalEvent", globalHandler); // same 132 133 // support for omitting context argument depends on hitch 134 if(dojo.isString(obj)){ 135 return dojo._listener.add(null, obj, dojo.hitch(event, context)); /*Handle*/ 136 }else{ 137 return dojo._listener.add(obj, event, dojo.hitch(context, method)); /*Handle*/ 138 } 70 139 } 71 140 72 dojo.disconnect = function( /*Object|null*/ obj, 73 /*String*/ event, 74 /*Handle*/ handle){ 75 dojo._listener.remove(obj, event, handle); 141 dojo.disconnect = function(/*Object|null*/ obj, /*String*/ event, /*Handle*/ handle){ 142 // summary: 143 // Remove a link created by dojo.connect. 144 // description: 145 // Removes the connection between event and the method referenced by handle. 146 // obj: 147 // The source object for the event function. 148 // Defaults to dojo.global if null. May be omitted. 149 // event: 150 // String name of the event function in obj. 151 // I.e. identifies a property obj[event]. 152 // handle: 153 // the return value of the dojo.connect call that created the connection. 154 if(dojo.isString(obj)){ 155 dojo._listener.remove(null, obj, event); 156 }else{ 157 dojo._listener.remove(obj, event, handle); 158 } 76 159 } 77 160 … … 79 162 80 163 dojo._topics = {}; 81 dojo.subscribe = function( /*String*/ topic, 82 /*Object|null*/ context, 83 /*String|Function*/ method){ 164 165 dojo.subscribe = function(/*String*/ topic, /*Object|null*/ context, /*String|Function*/ method){ 84 166 // support for 3 argument invocation depends on hitch 85 167 return dojo._listener.add(dojo._topics, topic, dojo.hitch(context, method)); /*Handle*/ 86 168 } 169 87 170 dojo.unsubscribe = function(/*String*/ topic, /*Handle*/ handle){ 88 171 dojo._listener.remove(dojo._topics, topic, handle); 89 172 } 173 90 174 dojo.publish = function(/*String*/ topic, /*Array*/ args){ 91 175 // Note that args is an array. This is more efficient vs variable length argument list. -
dojo/trunk/_base/event.js
r8078 r8235 2 2 dojo.require("dojo._base.connect"); 3 3 4 // this file courtesy of the TurboAjax group, licensed under a Dojo CLA4 // this file courtesy of the TurboAjax Group, licensed under a Dojo CLA 5 5 6 6 (function(){ … … 10 10 addListener: function(/*DOMNode*/node, /*String*/event, /*Function*/fp){ 11 11 if(!node){ return; } 12 event = de.normalizeEventName(event); 13 fp = (!de._fixCallback ? fp : de._fixCallback(fp)); 14 if(node.addEventListener){ 15 node.addEventListener(event.slice(2), fp, false); 16 return fp; /*Handle*/ 17 }else{ 18 return dojo._listener.add(node, event, fp); /*Handle*/ 19 } 12 event = de._normalizeEventName(event) 13 node.addEventListener(event, de._fixCallback(event, fp), false); 14 return fp; /*Handle*/ 20 15 }, 21 16 removeListener: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){ … … 28 23 // fp: 29 24 // the function to register 30 event = de.normalizeEventName(event); 31 if(node.removeEventListener){ 32 node.removeEventListener(event.slice(2), handle, false); 33 }else{ 34 dojo.listener.remove(node, event, handle); 25 node.removeEventListener(de._normalizeEventName(event), handle, false); 26 }, 27 _normalizeEventName: function(/*String*/name){ 28 // Generally, name should be lower case, unless it is special 29 // somehow (e.g. a Mozilla event) 30 31 // Remove 'on' 32 return (name.slice(0,2)=="on" ? name.slice(2) : name); 33 }, 34 _fixCallback: function(/*String*/name, fp){ 35 //return function(e){ return fp(de._fixEvent(e, this)); }; 36 return (name!="keypress" ? fp : function(e){ return fp(de._fixEvent(e, this)); }); 37 }, 38 _fixEvent: function(evt, sender){ 39 // fixCallback only attaches us to keypress, but we switch on 40 // evt.type because we might be called from dojo.fixEvent 41 switch(evt.type){ 42 case "keypress": 43 de._fixKey(evt); 44 break; 35 45 } 46 return evt; 36 47 }, 37 normalizeEventName: function(/*String*/eventName){ 38 // Generally, eventName should be lower case, unless it is special somehow (e.g. a Mozilla event) 39 if(eventName.slice(0,2)!="on"){ eventName = "on"+eventName; } 40 if(eventName=="onkey"){ eventName = (dojo.isIE ? "onkeydown" : "onkeypress"); } 41 return eventName; 42 }, 43 // hosts can override to fix events as needed 44 _fixCallback: null, 45 // public fixEvent 46 fixEvent: function(/*Event*/evt, /*DOMNode*/sender){ 47 // summary: 48 // normalizes properties on the event object including event 49 // bubbling methods, keystroke normalization, and x/y positions 50 // evt: native event object 51 // sender: node to treat as "currentTarget" 52 var f = de._fixEvent; 53 return (f ? f(evt, sender) : evt); 54 }, 55 stopEvent: function(/*Event*/evt){ 56 // summary: 57 // prevents propagation and clobbers the default action of the 58 // passed event 59 // evt: Optional for IE. The native event object. 60 evt.preventDefault(); 61 evt.stopPropagation(); 48 _fixKey: function(evt, charCode){ 49 var c = (arguments.length > 1 ? charCode : evt.charCode); 50 evt.keyChar = (c ? String.fromCharCode(c) : ''); 62 51 } 63 52 }; 53 // FIXME: do we need this for debugging??? 54 // dojo.event = de; 64 55 65 56 // DOM events 66 dojo.addListener = function(node, event, context, method) {57 dojo.addListener = function(node, event, context, method){ 67 58 return de.addListener(node, event, dojo.hitch(context, method)); // Handle 68 59 } 69 60 70 dojo.removeListener = function(node, event, handle) {61 dojo.removeListener = function(node, event, handle){ 71 62 de.removeListener(node, event, handle); 72 63 } 73 64 74 // prefer these remain public, but could be confusing in top level docs 75 76 // dojo.addConnection = dojo.connect; 77 // dojo.removeConnection = dojo.disconnect; 65 dojo.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){ 66 // summary: 67 // normalizes properties on the event object including event 68 // bubbling methods, keystroke normalization, and x/y positions 69 // evt: native event object 70 // sender: node to treat as "currentTarget" 71 return de._fixEvent(evt, sender); 72 //return (de._fixEvent ? de._fixEvent(evt, sender) : evt); 73 } 74 75 dojo.stopEvent = function(/*Event*/evt){ 76 // summary: 77 // prevents propagation and clobbers the default action of the 78 // passed event 79 // evt: Optional for IE. The native event object. 80 evt.preventDefault(); 81 evt.stopPropagation(); 82 } 83 84 // ============================================= 85 // FIXME: I want to eliminate these and their 86 // IE<7 bretheren below 87 dojo.cleanEvents = function(){ 88 // self cleaning, except on IE 89 90 // FIXME: why public? 91 } 92 dojo.cleanNodeEvents = function(inNode){ 93 // self cleaning, except on IE 94 95 // FIXME: why public? 96 } 97 // ============================================= 98 <99 // cache raw implementations