Changeset 8235

Show
Ignore:
Timestamp:
04/21/07 18:25:14 (19 months ago)
Author:
alex
Message:

committing Scott's new keyboard handling work + tests. Refs #2641. Still needs more unit tests.

Location:
dojo/trunk
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • dojo/trunk/tests/_base/connect.js

    r7988 r8235  
    11dojo.provide("tests._base.connect"); 
    22 
    3 hub = function() { 
    4 } 
    5  
    6 bad = function() { 
     3hub = function(){ 
     4} 
     5 
     6failures = 0; 
     7bad = function(){ 
    78        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 
     11good = 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' 
     20markAndSweepTest = function(iterations){ 
    1721        var marked = []; 
    1822        // 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)){ 
    3232                var rm = [ ]; 
    33                 while (marked.length) { 
     33                while(marked.length){ 
    3434                        var m = Math.floor(Math.random() * marked.length); 
    3535                        rm.push(marked[m]); 
     
    3737                } 
    3838                marked = rm;                             
    39                 //t = new Date().getTime() - t; 
    40                 //logMe('randomized markers in ' + t + 'ms.'); 
    4139        }  
    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        } 
    5043        // test 
    5144        failures = 0; 
    52         //var t = new Date().getTime(); 
    5345        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 
     50markAndSweepSubscribersTest = 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) 
    5878        return failures; 
    5979} 
     
    6282        [ 
    6383                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 
    6587                        dojo.connect(foo, "foo", null, function(){ok=true}); 
    6688                        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) { 
    7692                        var out = ''; 
    7793                        var obj = { 
     
    120136                        t.is(markAndSweepTest(1000), 0); 
    121137                }, 
    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); 
    124200                } 
    125201        ] 
  • dojo/trunk/_base/connect.js

    r7994 r8235  
    22dojo.require("dojo._base.lang"); 
    33 
    4 // this file courtesy of the TurboAjax group, licensed under a Dojo CLA 
     4// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA 
    55 
    66// FIXME: needs in-code docs in the worst way!! 
    77 
     8// low-level delegation machinery 
    89dojo._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                                } 
    1722                        } 
    1823                } 
    19                 d.clean = function() { 
    20                         source[method] = null  
    21                 } 
    22                 d.listeners = (f ? [f] : []); 
    23                 source[method] = d; 
    24                 dojo._listener.dispatchers.push(d); 
    25                 return d; 
    2624        }, 
     25        // add a listener to an object 
    2726        add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){ 
    2827                // 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 
    3331                // because listener could be the result of a dojo.hitch call, 
    34                 // in which case two references to the same function would not 
     32                // in which case two references to the same hitch target would not 
    3533                // be equivalent.  
    3634                source = source || dojo.global; 
    3735                // The source method is either null, a dispatcher, or some other function 
    38                 var d = source[method]; 
     36                var f = source[method]; 
    3937                // 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; 
    4244                } 
    43                 // The contract is that a 'handle' is returned that is suitable for  
    44                 // identifying this listener.  
     45                // The contract is that a handle is returned that can  
     46                // identify this listener for disconnect.  
    4547                // 
    46                 // The type of 'handle' is private. Here is it implemented as Integer.  
    47                 // DOM event code has this same contract but 'handle' is Function  
     48                // 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  
    4850                // in non-IE browsers. 
    4951                // 
    5052                // Could implement 'before' with a flag and unshift. 
    51                 return d.listeners.push(listener) - 1; /*Handle*/ 
     53                return f.listeners.push(listener) ; /*Handle*/ 
    5254        }, 
     55        // remove a listener from an object 
    5356        remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){ 
    5457                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                } 
    6062        } 
    6163}; 
     64 
    6265// arbitrary method delegation (knows nothing about DOM) 
    6366 
     
    6669                                                /*Object|null*/ context,  
    6770                                                /*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        } 
    70139} 
    71140 
    72 dojo.disconnect = function(     /*Object|null*/ obj, 
    73                                                         /*String*/ event,  
    74                                                         /*Handle*/ handle){ 
    75         dojo._listener.remove(obj, event, handle); 
     141dojo.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        } 
    76159} 
    77160 
     
    79162 
    80163dojo._topics = {}; 
    81 dojo.subscribe = function(      /*String*/ topic,  
    82                                                         /*Object|null*/ context,  
    83                                                         /*String|Function*/ method){ 
     164 
     165dojo.subscribe = function(/*String*/ topic, /*Object|null*/ context, /*String|Function*/ method){ 
    84166        // support for 3 argument invocation depends on hitch 
    85167        return dojo._listener.add(dojo._topics, topic, dojo.hitch(context, method)); /*Handle*/ 
    86168} 
     169 
    87170dojo.unsubscribe = function(/*String*/ topic, /*Handle*/ handle){ 
    88171        dojo._listener.remove(dojo._topics, topic, handle); 
    89172} 
     173 
    90174dojo.publish = function(/*String*/ topic, /*Array*/ args){ 
    91175        // Note that args is an array. This is more efficient vs variable length argument list. 
  • dojo/trunk/_base/event.js

    <
    r8078 r8235  
    22dojo.require("dojo._base.connect"); 
    33 
    4 // this file courtesy of the TurboAjax group, licensed under a Dojo CLA 
     4// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA 
    55 
    66(function(){ 
     
    1010                addListener: function(/*DOMNode*/node, /*String*/event, /*Function*/fp){ 
    1111                        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*/ 
    2015                }, 
    2116                removeListener: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){ 
     
    2823                        // fp: 
    2924                        //              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; 
    3545                        } 
     46                        return evt; 
    3647                }, 
    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) : ''); 
    6251                } 
    6352        }; 
     53        // FIXME: do we need this for debugging??? 
     54        // dojo.event = de; 
    6455 
    6556        // DOM events 
    66         dojo.addListener = function(node, event, context, method) { 
     57        dojo.addListener = function(node, event, context, method){ 
    6758                return de.addListener(node, event, dojo.hitch(context, method)); // Handle 
    6859        } 
    6960 
    70         dojo.removeListener = function(node, event, handle) { 
     61        dojo.removeListener = function(node, event, handle){ 
    7162                de.removeListener(node, event, handle); 
    7263        } 
    7364 
    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