From 12524bfbb0d641418d53010c09afda91b67efb69 Mon Sep 17 00:00:00 2001 From: bosgood Date: Mon, 17 Dec 2012 13:11:26 -0500 Subject: [PATCH 1/4] unbindKey patch from @captainclam (3a0e489) for unbinding support --- keymaster.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/keymaster.js b/keymaster.js index 0e617e5..77d0080 100644 --- a/keymaster.js +++ b/keymaster.js @@ -58,7 +58,7 @@ return; } - // see if we need to ignore the keypress (filter() can can be overridden) + // see if we need to ignore the keypress (filter() can be overridden) // by default ignore key presses if a select, textarea, or input is focused if(!assignKey.filter.call(this, event)) return; @@ -86,7 +86,7 @@ } } } - } + } }; // unset modifier keys on keyup @@ -104,7 +104,7 @@ _mods[key] = false; for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false; } - }; + } function resetModifiers() { for(k in _mods) _mods[k] = false; @@ -173,6 +173,24 @@ function setScope(scope){ _scope = scope || 'all' }; function getScope(){ return _scope || 'all' }; + // unbind all handlers for given key in current scope + function unbindKey(key, scope) { + key = _MAP[key] || key.toUpperCase().charCodeAt(0); + if (scope === undefined) { + scope = getScope(); + } + if (!_handlers[key]) { + return; + } + var i; + for (i in _handlers[key]) { + obj = _handlers[key][i]; + if (obj.scope === scope) { + _handlers[key][i] = {}; + } + } + } + // delete all handlers for a given scope function deleteScope(scope){ var key, handlers, i; @@ -217,6 +235,7 @@ global.key.getScope = getScope; global.key.deleteScope = deleteScope; global.key.filter = filter; + global.key.unbind = unbindKey; global.key.isPressed = isPressed; global.key.getPressedKeyCodes = getPressedKeyCodes; global.key.noConflict = noConflict; From 4b480122a685526df86052516173b8937a6acbaa Mon Sep 17 00:00:00 2001 From: bosgood Date: Mon, 17 Dec 2012 13:11:40 -0500 Subject: [PATCH 2/4] JSLint corrections --- keymaster.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/keymaster.js b/keymaster.js index 77d0080..63287a5 100644 --- a/keymaster.js +++ b/keymaster.js @@ -77,7 +77,7 @@ if((!_mods[k] && index(handler.mods, +k) > -1) || (_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false; // call the handler and stop the event if neccessary - if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){ + if((handler.mods.length === 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){ if(handler.method(event, handler)===false){ if(event.preventDefault) event.preventDefault(); else event.returnValue = false; @@ -86,8 +86,8 @@ } } } + } } - }; // unset modifier keys on keyup function clearModifier(event){ @@ -107,8 +107,8 @@ } function resetModifiers() { - for(k in _mods) _mods[k] = false; - for(k in _MODIFIERS) assignKey[k] = false; + for(var k in _mods) _mods[k] = false; + for(var j in _MODIFIERS) assignKey[k] = false; } // parse and assign shortcut @@ -121,7 +121,7 @@ key = key.replace(/\s/g,''); keys = key.split(','); - if((keys[keys.length-1])=='') + if((keys[keys.length-1])==='') keys[keys.length-2] += ','; // for each shortcut for (i = 0; i < keys.length; i++) { @@ -135,13 +135,13 @@ key = [key[key.length-1]]; } // convert to keycode and... - key = key[0] + key = key[0]; key = _MAP[key] || key.toUpperCase().charCodeAt(0); // ...store handler if (!(key in _handlers)) _handlers[key] = []; _handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods }); } - }; + } // Returns true if the key with code 'keyCode' is currently down // Converts strings into key codes. @@ -170,8 +170,8 @@ for(k in _MODIFIERS) assignKey[k] = false; // set current scope (default 'all') - function setScope(scope){ _scope = scope || 'all' }; - function getScope(){ return _scope || 'all' }; + function setScope(scope){ _scope = scope || 'all'; } + function getScope(){ return _scope || 'all'; } // unbind all handlers for given key in current scope function unbindKey(key, scope) { @@ -202,18 +202,18 @@ else i++; } } - }; + } // cross-browser events function addEvent(object, event, method) { if (object.addEventListener) object.addEventListener(event, method, false); else if(object.attachEvent) - object.attachEvent('on'+event, function(){ method(window.event) }); - }; + object.attachEvent('on'+event, function(){ method(window.event); }); + } // set the handlers globally on document - addEvent(document, 'keydown', function(event) { dispatch(event, _scope) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48 + addEvent(document, 'keydown', function(event) { dispatch(event, _scope); }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48 addEvent(document, 'keyup', clearModifier); // reset modifiers to false whenever the window is (re)focused. From 1e9494a4a4e027ac751d553aed3741cf3843a9ca Mon Sep 17 00:00:00 2001 From: bosgood Date: Mon, 17 Dec 2012 13:11:44 -0500 Subject: [PATCH 3/4] Unit tests for unbindKey (scoped + unscoped) --- test/keymaster.html | 46 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/test/keymaster.html b/test/keymaster.html index 02d6030..faf0ae4 100644 --- a/test/keymaster.html +++ b/test/keymaster.html @@ -186,6 +186,52 @@

Keymaster unit tests

t.assertEqual('ab', sequence); }, + testUnbindKeyUnscoped: function(t){ + key.setScope('all'); + var cnt = 0; + key('a', function() { cnt++; }); + keydown(65); keyup(65); + + // verify key is bound + t.assertEqual(cnt, 1); + key.unbind('a'); + + // fire event again, verify count did not increment (key was unbound) + keydown(65); keyup(65); + t.assertEqual(cnt, 1); + }, + + testUnbindKeyScoped: function(t){ + var cntForScope1 = 0, + cntForScope2 = 0; + key('a', 'scope1', function() { cntForScope1++; }); + key('a', 'scope2', function() { cntForScope2++; }); + + // verify counter increments in scope 1 + key.setScope('scope1'); + keydown(65); keyup(65); + t.assertEqual(cntForScope1, 1); + t.assertEqual(cntForScope2, 0); + + // verify counter increments in scope 2 + key.setScope('scope2'); + keydown(65); keyup(65); + t.assertEqual(cntForScope1, 1); + t.assertEqual(cntForScope2, 1); + + // verify counter does not increment in scope2 after being unbound + key.unbind('a', 'scope2'); + keydown(65); keyup(65); + t.assertEqual(cntForScope1, 1); + t.assertEqual(cntForScope2, 1); + + // verify counter does not increment in scope1 after being unbound + key.unbind('a', 'scope1'); + key.setScope('scope1'); + t.assertEqual(cntForScope1, 1); + t.assertEqual(cntForScope2, 1); + }, + testDeleteScope: function(t){ var sequence = ''; From 2d0dd0cc5d9a5b55404dc8ebeac6b9431c95f22d Mon Sep 17 00:00:00 2001 From: Ben Firshman Date: Fri, 4 Jan 2013 11:45:09 +0000 Subject: [PATCH 4/4] Add method argument to unbind to specify exactly which handler to remove. --- keymaster.js | 9 +++++--- test/keymaster.html | 56 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/keymaster.js b/keymaster.js index 63287a5..9be53ad 100644 --- a/keymaster.js +++ b/keymaster.js @@ -174,9 +174,10 @@ function getScope(){ return _scope || 'all'; } // unbind all handlers for given key in current scope - function unbindKey(key, scope) { + function unbindKey(key, scope, method) { key = _MAP[key] || key.toUpperCase().charCodeAt(0); - if (scope === undefined) { + if (typeof scope !== 'string') { + method = scope; scope = getScope(); } if (!_handlers[key]) { @@ -186,7 +187,9 @@ for (i in _handlers[key]) { obj = _handlers[key][i]; if (obj.scope === scope) { - _handlers[key][i] = {}; + if (!method || obj.method === method) { + _handlers[key][i] = {}; + } } } } diff --git a/test/keymaster.html b/test/keymaster.html index faf0ae4..db20bf9 100644 --- a/test/keymaster.html +++ b/test/keymaster.html @@ -232,6 +232,62 @@

Keymaster unit tests

t.assertEqual(cntForScope2, 1); }, + testUnbindKeyUnscopedWithMethod: function(t) { + key.setScope('all'); + var cntForMethod1 = 0; + var cntForMethod2 = 0; + var method1 = function() { cntForMethod1++; } + var method2 = function() { cntForMethod2++; } + + key('a', method1); + key('a', method2); + + // verify key is bound + keydown(65); keyup(65); + + // verify key is bound + t.assertEqual(cntForMethod1, 1); + t.assertEqual(cntForMethod2, 1); + + // verify unbind just unbinds the correct method + key.unbind('a', method1); + keydown(65); keyup(65); + t.assertEqual(cntForMethod1, 1); + t.assertEqual(cntForMethod2, 2); + }, + + testUnbindKeyScopedWithMethod: function(t){ + key.setScope('all'); + var cntForMethod1 = 0; + var cntForMethod2 = 0; + var method1 = function() { cntForMethod1++; } + var method2 = function() { cntForMethod2++; } + + key('a', 'scope1', method1); + key('a', 'scope1', method2); + + // verify key is bound + key.setScope('scope1'); + keydown(65); keyup(65); + + // verify key is bound + t.assertEqual(cntForMethod1, 1); + t.assertEqual(cntForMethod2, 1); + + // verify scope is set + key.setScope('scope2'); + keydown(65); keyup(65); + t.assertEqual(cntForMethod1, 1); + t.assertEqual(cntForMethod2, 1); + + // verify unbind just unbinds the correct method + key.unbind('a', 'scope1', method1); + key.setScope('scope1') + keydown(65); keyup(65); + t.assertEqual(cntForMethod1, 1); + t.assertEqual(cntForMethod2, 2); + }, + testDeleteScope: function(t){ var sequence = '';