Changeset 8424

Show
Ignore:
Timestamp:
05/04/07 12:25:13 (19 months ago)
Author:
sjmiles
Message:

Fine tuning in connect. Redo IE keyboard magic in event.js so it actually works. Update docs. Refs #2641.

Location:
dojo/trunk/_base
Files:
3 modified

Legend:

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

    r8402 r8424  
    1010        // create a dispatcher function 
    1111        getDispatcher: function(){ 
    12                 // return a dispatcher function 
     12                // following comments pulled out-of-line to prevent cloning them  
     13                // in the returned function. 
     14                // - indices (i) that are really in the array of listeners (ls) will  
     15                //   not be in Array.prototype. This is the 'sparse array' trick 
     16                //   that keeps us safe from libs that take liberties with built-in  
     17                //   objects 
     18                // - listener is invoked with current scope (this) 
    1319                return function(){ 
    14                         // iterate over our listeners 
    1520                        var ls = arguments.callee.listeners; 
    1621                        for(var i in ls){ 
    17                                 // indices that are really in the array will not be in Array.prototype 
    18                                 // this 'sparse array' trick keeps us safe from libs that take 
    19                                 // liberties with built-in objects 
    2022                                if(!(i in Array.prototype)){ 
    21                                         // invoke the listener with our current scope 
    2223                                        ls[i].apply(this, arguments); 
    2324                                } 
     
    6566}; 
    6667 
    67 // arbitrary method delegation (knows nothing about DOM) 
     68// Multiple delegation for arbitrary methods. 
     69 
     70// This unit knows nothing about DOM,  
     71// but we include DOM aware  
     72// documentation and dontFix 
     73// argument here to help the autodocs. 
     74// Actual DOM aware code is in event.js. 
    6875 
    6976dojo.connect = function(/*Object|null*/ obj,  
    7077                                                /*String*/ event,  
    7178                                                /*Object|null*/ context,  
    72                                                 /*String|Function*/ method){ 
     79                                                /*String|Function*/ method, 
     80                                                /*Boolean*/ dontFix){ 
    7381        // summary: 
    7482        //              Create a link that calls one function when another executes.  
     
    8997        //              The source object for the event function.  
    9098        //              Defaults to dojo.global if null. 
     99        //              If obj is a DOM node, the connection is delegated  
     100        //              to the DOM event manager (unless dontFix is true). 
    91101        // event: 
    92102        //              String name of the event function in obj.  
     
    106116        //              method receives the same arguments as the event. 
    107117        //              See context argument comments for information on method's scope. 
     118        // dontFix: 
     119        //              If obj is a DOM node, set dontFix to true to  prevent delegation  
     120        //              of this connection to the DOM event manager.  
    108121        // usage: 
    109122        //              // when obj.onchange(), do ui.update() 
     
    133146        //              dojo.connect("globalEvent", globalHandler); // same 
    134147 
    135         // normalize arguments into args 
     148        // normalize arguments 
    136149        var a=arguments, args=[], i=0; 
    137         if(dojo.isString(a[0])){ 
    138                 args.push(null, a[i++]);  
    139         }else{ 
    140                 args.push(a[i++], a[i++]); 
    141         } 
    142         if(dojo.isString(a[i+1])||(dojo.isFunction(a[i+1]))){ 
    143                 args.push(a[i++], a[i++]); 
    144         }else{ 
    145                 args.push(null, a[i++]); 
    146         } 
    147         for (; i<arguments.length; i++){ 
    148                 args.push(arguments[i]); 
    149         } 
     150        // if a[0] is a String, obj was ommited 
     151        args.push(dojo.isString(a[0]) ? null : a[i++], a[i++]); 
     152        // if the arg-after-next is a String or Function, context was NOT omitted 
     153        var a1 = a[i+1]; 
     154        args.push(dojo.isString(a1)||dojo.isFunction(a1) ? a[i++] : null, a[i++]); 
     155        // absorb any additional arguments 
     156        for (var l=a.length; i<l; i++){ args.push(a[i]); } 
     157        // do the actual work 
    150158        return dojo._connect.apply(this, args); /*Handle*/ 
    151159} 
     
    162170        //              Removes the connection between event and the method referenced by handle. 
    163171        // handle: 
    164         //              the return value of the dojo.connect call that created the connection. 
     172        //              the return value of the dojo.connect call that created the connection. 
    165173        dojo._disconnect.apply(this, handle); 
     174        if (handle && handle[0]!=undefined){ 
     175                dojo._disconnect.apply(this, handle); 
     176                // let's not keep this reference 
     177                delete handle[0]; 
     178        } 
    166179} 
    167180 
  • dojo/trunk/_base/event.js

    r8402 r8424  
    88        var de = { 
    99                addListener: function(/*DOMNode*/node, /*String*/event, /*Function*/fp){ 
    10                         if(!node){ return; }  
     10                        if(!node){return;}  
    1111                        event = de._normalizeEventName(event) 
    1212                        fp = de._fixCallback(event, fp); 
     
    2727                _normalizeEventName: function(/*String*/name){ 
    2828                        // Generally, name should be lower case, unless it is special 
    29                         // somehow (e.g. a Mozilla event) 
    30                         // Remove 'on' 
     29                        // somehow (e.g. a Mozilla DOM event). 
     30                        // Remove 'on'. 
    3131                        return (name.slice(0,2)=="on" ? name.slice(2) : name); 
    3232                }, 
    3333                _fixCallback: function(/*String*/name, fp){ 
    34                         //return function(e){ return fp(de._fixEvent(e, this)); };       
    35                         return (name!="keypress" ? fp : function(e){ return fp(de._fixEvent(e, this)); });       
     34                        // By default, we only invoke _fixEvent for 'keypress' 
     35                        // If code is added to _fixEvent for other events, we have 
     36                        // to revisit this optimization. 
     37                        // This also applies to _fixEvent overrides for Safari and Opera 
     38                        // below. 
     39                        return (name!="keypress" ? fp : function(e){ return fp.call(this, de._fixEvent(e, this)); });    
    3640                }, 
    3741                _fixEvent: function(evt, sender){ 
    38                         // fixCallback only attaches us to keypress, but we switch on 
    39                         // evt.type because we might be called from dojo.fixEvent 
     42                        // _fixCallback only attaches us to keypress. 
     43                        // Switch on evt.type anyway because we might  
     44                        // be called directly from dojo.fixEvent. 
    4045                        switch(evt.type){ 
    4146                                case "keypress": 
    42                                         de._fixKey(evt); 
     47                                        de._setKeyChar(evt); 
    4348                                        break; 
    4449                        } 
    4550                        return evt; 
    4651                }, 
    47                 _fixKey: function(evt, charCode){ 
    48                         var c = (arguments.length > 1 ? charCode : evt.charCode); 
    49                         evt.keyChar = (c ? String.fromCharCode(c) : ''); 
     52                _setKeyChar: function(evt){ 
     53                        evt.keyChar = (evt.charCode ? String.fromCharCode(evt.charCode) : ''); 
    5054                } 
    5155        }; 
     
    5357        // DOM events 
    5458         
     59        // FIXME: no reason to make this public, use connect 
    5560        dojo.addListener = function(node, event, context, method){ 
    5661                return de.addListener(node, event, dojo.hitch(context, method)); // Handle 
    5762        } 
    5863 
     64        // FIXME: no reason to make this public, use disconnect 
    5965        dojo.removeListener = function(node, event, handle){ 
    6066                de.removeListener(node, event, handle); 
     
    6874                // sender: node to treat as "currentTarget" 
    6975                return de._fixEvent(evt, sender); 
    70                 //return (de._fixEvent ? de._fixEvent(evt, sender) : evt); 
    7176        } 
    7277 
     
    8893         
    8994        dojo._connect = function(obj, event, context, method, dontFix){ 
    90                 dontFix = !obj || !(obj.nodeType||obj.attachEvent||obj.addEventListener) || dontFix; 
     95                // use listener code (event fixing) for nodes that look like objects, unless told not to 
     96                dontFix = Boolean(!obj || !(obj.nodeType||obj.attachEvent||obj.addEventListener) || dontFix); 
     97                // grab up the result of baseline disconnect, or construct one using addListener 
    9198                var h = (dontFix ? dc.apply(this, arguments) : [obj, event, dojo.addListener.apply(this, arguments)]); 
     99                // append flag to the result identifying the kind of listener  
    92100                h.push(dontFix); 
    93101                return h; 
     
    95103 
    96104        dojo._disconnect = function(obj, event, handle, dontFix){ 
     105                // dispatch this disconnect either to the baseline code or to removeListener 
    97106                (dontFix ? dd : dojo.removeListener).apply(this, arguments); 
    98107        }                                                                                        
     
    166175        // IE event normalization 
    167176        if(dojo.isIE){  
     177                _trySetKeyCode = function(e, code){ 
     178                        try{ 
     179                                // squelch errors when keyCode is read-only 
     180                                // (e.g. if keyCode is ctrl or shift) 
     181                                return e.keyCode = code; 
     182                        }catch(e){ 
     183                                return 0; 
     184                        } 
     185                } 
     186 
    168187                var ap = Array.prototype; 
    169188                // by default, use the standard listener 
     
    205224                                if(!node){return;} // undefined 
    206225                                event = de._normalizeEventName(event); 
    207                                 var handle = iel.add(node, event, de._fixCallback(fp)); 
    208226                                if(event=="onkeypress"){ 
    209                                         // FIXME: we are using the knowledge that handle 
    210                                         // is an Integer, which is supposed to be private. 
    211                                         // Perhaps the listener could natively return a  
    212                                         // Number object instead of a concrete value. 
    213                                         handle = new Number(handle); 
    214                                         handle.keydown = de.addListener(node, "onkeydown", de._nop); 
    215                                 } 
    216                                 return handle; 
     227                                        // we need to listen to onkeydown to synthesize  
     228                                        // keypress events that otherwise won't fire 
     229                                        // on IE 
     230                                        var kd = node.onkeydown; 
     231                                        if(!kd||!kd.listeners||!kd.listeners._stealthKeydown){ 
     232                                                // we simply ignore this connection when disconnecting 
     233                                                // because it's harmless  
     234                                                de.addListener(node, "onkeydown", de._stealthKeyDown); 
     235                                                // we only want one stealth listener per node 
     236                                                kd.listeners._stealthKeydown = true; 
     237                                        }  
     238                                } 
     239                                return iel.add(node, event, de._fixCallback(fp, node)); 
    217240                        }, 
    218241                        removeListener: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){ 
    219242                                iel.remove(node, de._normalizeEventName(event), handle);  
    220                                 if(handle.keydown){iel.remove(node, "onkeydown", handle.keydown);}  
    221243                        }, 
    222244                        _normalizeEventName: function(/*String*/eventName){ 
     
    226248                                return (eventName.slice(0,2)!="on" ? "on"+eventName : eventName); 
    227249                        }, 
    228                         _nop: function(){ }, 
    229                         _fixCallback: function(fp){ 
     250                        _nop: function(){}, 
     251                        _fixCallback: function(fp, sender){ 
    230252                                return function(e){  
    231                                         e = de._fixEvent(e, this) 
    232                                         var r = fp(e); 
    233                                         de._postFixEvent(e); 
    234                                         return r; 
     253                                        return fp.call(this, de._fixEvent(e, sender)); 
    235254                                }; 
    236255                        }, 
     
    277296                                                        c=99; // Mozilla maps CTRL-BREAK to CTRL-c 
    278297                                                } 
    279                                                 try{ 
    280                                                         // squelch errors when keyCode is read-only 
    281                                                         // (e.g. if keyCode is ctrl or shift) 
    282                                                         evt.keyCode = (c ? 0 : evt.keyCode); 
    283                                                 }catch(e){ 
    284                                                         c=0; 
    285                                                 } 
     298                                                // if we have a charCode, try to 0 keycode 
     299                                                // if that fails, our charCode is bogus and is set to 0 
     300                                                if(c){c = _trySetKeyCode(evt, 0);} 
    286301                                                evt.charCode = c; 
    287                                                 de._fixKey(evt); 
     302                                                de._setKeyChar(evt); 
    288303                                                break; 
    289304                                } 
     
    308323                                222:39  
    309324                        }, 
    310                         _postFixEvent: function(evt){ 
    311                                 switch(evt.type){ 
    312                                         // IE doesn't fire keypress for most non-printable characters. 
    313                                         // other browsers do, we simulate it here. 
    314                                         case "keydown": 
    315                                                 var c = evt.keyCode; 
    316                                                 // These are Windows Virtual Key Codes 
    317                                                 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp 
    318                                                 var unprintable = (c!=13)&&(c!=32)&&(c!=27)&&(c<48||c>90)&&(c<96||c>111)&&(c<186||c>192)&&(c<219||c>222); 
    319                                                 if(unprintable||evt.ctrlKey){ 
    320                                                         c = (unprintable ? 0 : c); 
    321                                                         if(evt.ctrlKey){ 
    322                                                                 if(evt.keyCode==3){ 
    323                                                                         break; // IE will post CTRL-BREAK as keypress natively                                                                   
    324                                                                 }else if(c>95 && c<106){  
    325                                                                         c -= 48; // map CTRL-[numpad 0-9] to ASCII 
    326                                                                 }else if((!evt.shiftKey)&&(c>=65&&c<=90)){  
    327                                                                         c += 32; // map CTRL-[A-Z] to lowercase 
    328                                                                 }else{  
    329                                                                         c = de._punctMap[c] || c; // map other problematic CTRL combinations to ASCII 
    330                                                                 } 
    331                                                         } 
    332                                                         var faux = document.createEventObject(evt); 
    333                                                         faux.charCode = c; 
    334                                                         de._fixKey(faux); 
    335                                                         faux.faux = true; // just for debugging 
    336                                                         evt.target.fireEvent("onkeypress", faux); 
    337                                                         evt.cancelBubble = faux.cancelBubble; 
    338                                                         evt.returnValue = faux.returnValue; 
    339                                                         try{ 
    340                                                                 // squelch errors when keyCode is read-only 
    341                                                                 // (e.g. if keyCode is ctrl or shift) 
    342                                                                 evt.keyCode = faux.keyCode; 
    343                                                         }catch(e){};  
     325                        _stealthKeyDown: function(evt){ 
     326                                // IE doesn't fire keypress for most non-printable characters. 
     327                                // other browsers do, we simulate it here. 
     328                                var kp = evt.currentTarget.onkeypress; 
     329                                // only works if kp exists and is a dispatcher 
     330                                if(!kp||!kp.listeners)return; 
     331                                // munge key/charCode 
     332                                var c = evt.keyCode; 
     333                                // These are Windows Virtual Key Codes 
     334                                // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp 
     335                                var unprintable = (c!=13)&&(c!=32)&&(c!=27)&&(c<48||c>90)&&(c<96||c>111)&&(c<186||c>192)&&(c<219||c>222); 
     336                                if(unprintable||evt.ctrlKey){ 
     337                                        c = (unprintable ? 0 : c); 
     338                                        if(evt.ctrlKey){ 
     339                                                if(evt.keyCode==3){ 
     340                                                        return; // IE will post CTRL-BREAK as keypress natively                                                                          
     341                                                }else if(c>95 && c<106){  
     342                                                        c -= 48; // map CTRL-[numpad 0-9] to ASCII 
     343                                                }else if((!evt.shiftKey)&&(c>=65&&c<=90)){  
     344                                                        c += 32; // map CTRL-[A-Z] to lowercase 
     345                                                }else{  
     346                                                        c = de._punctMap[c] || c; // map other problematic CTRL combinations to ASCII 
    344347                                                } 
    345                                                 break; 
     348                                        } 
     349                                        // simulate a keypress event 
     350                                        var faux = de._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c}); 
     351                                        kp.call(evt.currentTarget, faux); 
     352                                        evt.cancelBubble = faux.cancelBubble; 
     353                                        evt.returnValue = faux.returnValue; 
     354                                        _trySetKeyCode(evt, faux.keyCode); 
    346355                                } 
    347356                        }, 
     
    351360                        }, 
    352361                        _preventDefault: function(){ 
    353                                 try{evt.keyCode = 0;}catch(e){}; // squelch errors when keyCode is read-only (e.g. if keyCode is ctrl or shift) 
     362                                _trySetKeyCode(this, 0); 
    354363                                this.returnValue = false; 
    355364                        } 
    356365                }); 
    357366                                 
    358                 // override stopEvent 
     367                // override stopEvent for IE 
    359368                dojo.stopEvent = function(evt){ 
    360369                        evt = evt || window.event; 
     
    364373        } 
    365374 
    366         de._synthesizeEvent = function(evt, keyCode, charCode, shiftKey) { 
    367                         var faux = {  
    368                                 type: evt.type,  
    369                                 shiftKey: shiftKey,  
    370                                 ctrlKey: evt.ctrlKey,  
    371                                 altKey: evt.altKey,  
    372                                 keyCode: (charCode ? 0 : keyCode),  
    373                                 charCode: charCode 
    374                         }; 
    375                         de._fixKey(faux, charCode); 
     375        de._synthesizeEvent = function(evt, props) { 
     376                        var faux = dojo.mixin({}, evt, props); 
     377                        if(faux.charCode){faux.keyCode = 0;} 
     378                        de._setKeyChar(faux); 
    376379                        // FIXME: would prefer to use dojo.hitch: dojo.hitch(evt, evt.preventDefault);  
    377380                        // but it throws an error when preventDefault is invoked on Safari 
     
    399402                                                        c += 32; 
    400403                                                } 
    401                                                 return de._synthesizeEvent(evt, evt.keyCode, c, evt.shiftKey); 
     404                                                return de._synthesizeEvent(evt, { charCode: c }); 
    402405                                } 
    403406                                return evt; 
     
    423426                                                        c = (c>=32 && c<63232 ? c : 0); // avoid generating keyChar for non-printables 
    424427                                                } 
    425                                                 return de._synthesizeEvent(evt, keyCode, c, s); 
     428                                                return de._synthesizeEvent(evt, {charCode: c, shiftKey: s}); 
    426429                                } 
    427430                                return evt; 
     
    462465if(dojo.isIE<7){ 
    463466        // keep this out of the closure 
    464         // closing over 'iel' or 'ieh' borks 
    465         // leak prevention 
     467        // closing over 'iel' or 'ieh' borks leak prevention 
     468        // ls[i] is an index into the master handler array 
    466469        dojo._getIeDispatcher = function(){ 
    467470                return function(){ 
    468471                        var ap=Array.prototype, ls=arguments.callee.listeners, h=dojo._ie_listener.handlers; 
    469472                        for(var i in ls){ 
    470                                 // real indices won't be in Array.prototype 
    471473                                if(!(i in ap)){ 
    472                                         // listener value is a hash into the master handler array 
    473474                                        h[ls[i]].apply(this, arguments); 
    474475                                } 
  • dojo/trunk/_base/lang.js

    r8299 r8424  
    115115        // summary:  
    116116        //              Returns a function that will only ever execute in the a given scope.  
    117         //              This allows for easy use of object member functions 
     117        //              This allows for easy use of object member functions 
    118118        //              in callbacks and other places in which the "this" keyword may 
    119119        //              otherwise not reference the expected scope.  
     
    123123        //              for the hitched function.  
    124124        // scope:  
    125         //              The scope to run the method in 
     125        //              The scope to use when method executes. If method is a string,  
     126        //              scope is also the object containing method. 
    126127        // method: 
    127128        //              A function to be hitched to scope, or the name of the method in