added pages and keyboard navigation
This commit is contained in:
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "KeyboardJS",
|
||||
"homepage": "https://github.com/RobertWHurst/KeyboardJS",
|
||||
"version": "2.3.3",
|
||||
"_release": "2.3.3",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v2.3.3",
|
||||
"commit": "067e7b7d0b6d8519ac3ef14a714a9e605c6a694b"
|
||||
},
|
||||
"_source": "https://github.com/RobertWHurst/KeyboardJS.git",
|
||||
"_target": "^2.3.3",
|
||||
"_originalSource": "KeyboardJS",
|
||||
"_direct": true
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
# Directories
|
||||
node_modules
|
||||
|
||||
# Editors
|
||||
.c9
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
# Directories
|
||||
test
|
||||
|
||||
# Editors
|
||||
.c9
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"loadEagerly": [
|
||||
"index.js"
|
||||
],
|
||||
"plugins": {
|
||||
"node": true
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "stable"
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Robert Hurst
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
|
||||
KeyboardJS
|
||||
==========
|
||||
|
||||
[  ](https://travis-ci.org/RobertWHurst/KeyboardJS)
|
||||
[  ](https://www.npmjs.org/package/keyboardjs)
|
||||
[  ](https://www.npmjs.org/package/keyboardjs)
|
||||
[  ](https://www.npmjs.org/package/keyboardjs)
|
||||
|
||||
KeyboardJS is a library for use in the browser (node.js compatible). It Allows
|
||||
developers to easily setup key bindings. Use key combos to setup complex
|
||||
bindings. KeyboardJS also provides contexts. Contexts are great for single page
|
||||
applications. They allow you to scope your bindings to various parts of your
|
||||
application. Out of the box keyboardJS uses a US keyboard locale. If you need
|
||||
support for a different type of keyboard KeyboardJS provides custom locale
|
||||
support so you can create with a locale that better matches your needs.
|
||||
|
||||
KeyboardJS is available as a NPM module for use with
|
||||
[browserify](http://browserify.org/) (or in node.js). If you don't use
|
||||
browserify you can simply include
|
||||
[keyboard.js](https://github.com/RobertWHurst/KeyboardJS/blob/master/dist/keyboard.js)
|
||||
or
|
||||
[keyboard.min.js](https://github.com/RobertWHurst/KeyboardJS/blob/master/dist/keyboard.min.js)
|
||||
from the dist folder in this repo. These files are
|
||||
[UMD](https://github.com/umdjs/umd) wrapped so they can be used with or without
|
||||
a module loader such as [requireJS](http://requirejs.org/).
|
||||
|
||||
```shell
|
||||
npm install keyboardjs
|
||||
```
|
||||
|
||||
Note that all key names can be found in [./locales/us.js](https://github.com/RobertWHurst/KeyboardJS/blob/master/locales/us.js).
|
||||
|
||||
If you're looking for the previous v1.x.x release of KeyboardJS you can find it
|
||||
[here](https://github.com/RobertWHurst/KeyboardJS/tree/legacy).
|
||||
|
||||
__Setting up bindings is easy__
|
||||
|
||||
```javascript
|
||||
keyboardJS.bind('a', function(e) {
|
||||
console.log('a is pressed');
|
||||
});
|
||||
keyboardJS.bind('a + b', function(e) {
|
||||
console.log('a and b is pressed');
|
||||
});
|
||||
keyboardJS.bind('a + b > c', function(e) {
|
||||
console.log('a and b then c is pressed');
|
||||
});
|
||||
keyboardJS.bind(['a + b > c', 'z + y > z'], function(e) {
|
||||
console.log('a and b then c or z and y then z is pressed');
|
||||
});
|
||||
keyboardJS.bind('', function(e) {
|
||||
console.log('any key was pressed');
|
||||
});
|
||||
|
||||
// keyboardJS.bind === keyboardJS.on === keyboardJS.addListener
|
||||
```
|
||||
|
||||
|
||||
__keydown vs a keyup__
|
||||
|
||||
```javascript
|
||||
keyboardJS.bind('a', function(e) {
|
||||
console.log('a is pressed');
|
||||
}, function(e) {
|
||||
console.log('a is released');
|
||||
});
|
||||
keyboardJS.bind('a', null, function(e) {
|
||||
console.log('a is released');
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
__Prevent keydown repeat__
|
||||
|
||||
```javascript
|
||||
keyboardJS.bind('a', function(e) {
|
||||
// this function will once run once even if a is held
|
||||
e.preventRepeat();
|
||||
console.log('a is pressed');
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
__Unbind things__
|
||||
|
||||
```javascript
|
||||
keyboardJS.unbind('a', previouslyBoundHandler);
|
||||
// keyboardJS.unbind === keyboardJS.off === keyboardJS.removeListener
|
||||
```
|
||||
|
||||
|
||||
__Using contexts__
|
||||
|
||||
```javascript
|
||||
|
||||
// these will execute in all contexts
|
||||
keyboardJS.bind('a', function(e) {});
|
||||
keyboardJS.bind('b', function(e) {});
|
||||
keyboardJS.bind('c', function(e) {});
|
||||
|
||||
// these will execute in the index context
|
||||
keyboardJS.setContext('index');
|
||||
keyboardJS.bind('1', function(e) {});
|
||||
keyboardJS.bind('2', function(e) {});
|
||||
keyboardJS.bind('3', function(e) {});
|
||||
|
||||
// these will execute in the foo context
|
||||
keyboardJS.setContext('foo');
|
||||
keyboardJS.bind('x', function(e) {});
|
||||
keyboardJS.bind('y', function(e) {});
|
||||
keyboardJS.bind('z', function(e) {});
|
||||
|
||||
// if we have a router we can activate these contexts when appropriate
|
||||
myRouter.on('GET /', function(e) {
|
||||
keyboardJS.setContext('index');
|
||||
});
|
||||
myRouter.on('GET /foo', function(e) {
|
||||
keyboardJS.setContext('foo');
|
||||
});
|
||||
|
||||
// you can always figure out your context too
|
||||
var contextName = keyboardJS.getContext();
|
||||
|
||||
// you can also set up handlers for a context without losing the current context
|
||||
keyboardJS.withContext('bar', function() {
|
||||
// these will execute in the bar context
|
||||
keyboardJS.bind('7', function(e) {});
|
||||
keyboardJS.bind('8', function(e) {});
|
||||
keyboardJS.bind('9', function(e) {});
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
__pause, resume, and reset__
|
||||
|
||||
```javascript
|
||||
|
||||
// the keyboard will no longer trigger bindings
|
||||
keyboardJS.pause();
|
||||
|
||||
// the keyboard will once again trigger bindings
|
||||
keyboardJS.resume();
|
||||
|
||||
// all active bindings will released and unbound,
|
||||
// pressed keys will be cleared
|
||||
keyboardJS.reset();
|
||||
```
|
||||
|
||||
|
||||
__pressKey, releaseKey, and releaseAllKeys__
|
||||
|
||||
```javascript
|
||||
|
||||
// pressKey
|
||||
keyboardJS.pressKey('a');
|
||||
// or
|
||||
keyboardJS.pressKey(65);
|
||||
|
||||
// releaseKey
|
||||
keyboardJS.releaseKey('a');
|
||||
// or
|
||||
keyboardJS.releaseKey(65);
|
||||
|
||||
// releaseAllKeys
|
||||
keyboardJS.releaseAllKeys();
|
||||
```
|
||||
|
||||
|
||||
__watch and stop__
|
||||
|
||||
```javascript
|
||||
// bind to the window and document in the current window
|
||||
keyboardJS.watch();
|
||||
|
||||
// or pass your own window and document
|
||||
keyboardJS.watch(myDoc);
|
||||
keyboardJS.watch(myWin, myDoc);
|
||||
|
||||
// or scope to a specific element
|
||||
keyboardJS.watch(myForm);
|
||||
keyboardJS.watch(myWin, myForm);
|
||||
|
||||
// detach KeyboardJS from the window and document/element
|
||||
keyboardJS.stop();
|
||||
```
|
||||
+818
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
|
||||
var Keyboard = require('./lib/keyboard');
|
||||
var Locale = require('./lib/locale');
|
||||
var KeyCombo = require('./lib/key-combo');
|
||||
|
||||
var keyboard = new Keyboard();
|
||||
|
||||
keyboard.setLocale('us', require('./locales/us'));
|
||||
|
||||
exports = module.exports = keyboard;
|
||||
exports.Keyboard = Keyboard;
|
||||
exports.Locale = Locale;
|
||||
exports.KeyCombo = KeyCombo;
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
|
||||
function KeyCombo(keyComboStr) {
|
||||
this.sourceStr = keyComboStr;
|
||||
this.subCombos = KeyCombo.parseComboStr(keyComboStr);
|
||||
this.keyNames = this.subCombos.reduce(function(memo, nextSubCombo) {
|
||||
return memo.concat(nextSubCombo);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Add support for key combo sequences
|
||||
KeyCombo.sequenceDeliminator = '>>';
|
||||
KeyCombo.comboDeliminator = '>';
|
||||
KeyCombo.keyDeliminator = '+';
|
||||
|
||||
KeyCombo.parseComboStr = function(keyComboStr) {
|
||||
var subComboStrs = KeyCombo._splitStr(keyComboStr, KeyCombo.comboDeliminator);
|
||||
var combo = [];
|
||||
|
||||
for (var i = 0 ; i < subComboStrs.length; i += 1) {
|
||||
combo.push(KeyCombo._splitStr(subComboStrs[i], KeyCombo.keyDeliminator));
|
||||
}
|
||||
return combo;
|
||||
};
|
||||
|
||||
KeyCombo.prototype.check = function(pressedKeyNames) {
|
||||
var startingKeyNameIndex = 0;
|
||||
for (var i = 0; i < this.subCombos.length; i += 1) {
|
||||
startingKeyNameIndex = this._checkSubCombo(
|
||||
this.subCombos[i],
|
||||
startingKeyNameIndex,
|
||||
pressedKeyNames
|
||||
);
|
||||
if (startingKeyNameIndex === -1) { return false; }
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
KeyCombo.prototype.isEqual = function(otherKeyCombo) {
|
||||
if (
|
||||
!otherKeyCombo ||
|
||||
typeof otherKeyCombo !== 'string' &&
|
||||
typeof otherKeyCombo !== 'object'
|
||||
) { return false; }
|
||||
|
||||
if (typeof otherKeyCombo === 'string') {
|
||||
otherKeyCombo = new KeyCombo(otherKeyCombo);
|
||||
}
|
||||
|
||||
if (this.subCombos.length !== otherKeyCombo.subCombos.length) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < this.subCombos.length; i += 1) {
|
||||
if (this.subCombos[i].length !== otherKeyCombo.subCombos[i].length) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.subCombos.length; i += 1) {
|
||||
var subCombo = this.subCombos[i];
|
||||
var otherSubCombo = otherKeyCombo.subCombos[i].slice(0);
|
||||
|
||||
for (var j = 0; j < subCombo.length; j += 1) {
|
||||
var keyName = subCombo[j];
|
||||
var index = otherSubCombo.indexOf(keyName);
|
||||
|
||||
if (index > -1) {
|
||||
otherSubCombo.splice(index, 1);
|
||||
}
|
||||
}
|
||||
if (otherSubCombo.length !== 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
KeyCombo._splitStr = function(str, deliminator) {
|
||||
var s = str;
|
||||
var d = deliminator;
|
||||
var c = '';
|
||||
var ca = [];
|
||||
|
||||
for (var ci = 0; ci < s.length; ci += 1) {
|
||||
if (ci > 0 && s[ci] === d && s[ci - 1] !== '\\') {
|
||||
ca.push(c.trim());
|
||||
c = '';
|
||||
ci += 1;
|
||||
}
|
||||
c += s[ci];
|
||||
}
|
||||
if (c) { ca.push(c.trim()); }
|
||||
|
||||
return ca;
|
||||
};
|
||||
|
||||
KeyCombo.prototype._checkSubCombo = function(subCombo, startingKeyNameIndex, pressedKeyNames) {
|
||||
subCombo = subCombo.slice(0);
|
||||
pressedKeyNames = pressedKeyNames.slice(startingKeyNameIndex);
|
||||
|
||||
var endIndex = startingKeyNameIndex;
|
||||
for (var i = 0; i < subCombo.length; i += 1) {
|
||||
|
||||
var keyName = subCombo[i];
|
||||
if (keyName[0] === '\\') {
|
||||
var escapedKeyName = keyName.slice(1);
|
||||
if (
|
||||
escapedKeyName === KeyCombo.comboDeliminator ||
|
||||
escapedKeyName === KeyCombo.keyDeliminator
|
||||
) {
|
||||
keyName = escapedKeyName;
|
||||
}
|
||||
}
|
||||
|
||||
var index = pressedKeyNames.indexOf(keyName);
|
||||
if (index > -1) {
|
||||
subCombo.splice(i, 1);
|
||||
i -= 1;
|
||||
if (index > endIndex) {
|
||||
endIndex = index;
|
||||
}
|
||||
if (subCombo.length === 0) {
|
||||
return endIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
|
||||
module.exports = KeyCombo;
|
||||
+365
@@ -0,0 +1,365 @@
|
||||
|
||||
var Locale = require('./locale');
|
||||
var KeyCombo = require('./key-combo');
|
||||
|
||||
|
||||
function Keyboard(targetWindow, targetElement, platform, userAgent) {
|
||||
this._locale = null;
|
||||
this._currentContext = null;
|
||||
this._contexts = {};
|
||||
this._listeners = [];
|
||||
this._appliedListeners = [];
|
||||
this._locales = {};
|
||||
this._targetElement = null;
|
||||
this._targetWindow = null;
|
||||
this._targetPlatform = '';
|
||||
this._targetUserAgent = '';
|
||||
this._isModernBrowser = false;
|
||||
this._targetKeyDownBinding = null;
|
||||
this._targetKeyUpBinding = null;
|
||||
this._targetResetBinding = null;
|
||||
this._paused = false;
|
||||
|
||||
this.setContext('global');
|
||||
this.watch(targetWindow, targetElement, platform, userAgent);
|
||||
}
|
||||
|
||||
Keyboard.prototype.setLocale = function(localeName, localeBuilder) {
|
||||
var locale = null;
|
||||
if (typeof localeName === 'string') {
|
||||
|
||||
if (localeBuilder) {
|
||||
locale = new Locale(localeName);
|
||||
localeBuilder(locale, this._targetPlatform, this._targetUserAgent);
|
||||
} else {
|
||||
locale = this._locales[localeName] || null;
|
||||
}
|
||||
} else {
|
||||
locale = localeName;
|
||||
localeName = locale._localeName;
|
||||
}
|
||||
|
||||
this._locale = locale;
|
||||
this._locales[localeName] = locale;
|
||||
if (locale) {
|
||||
this._locale.pressedKeys = locale.pressedKeys;
|
||||
}
|
||||
};
|
||||
|
||||
Keyboard.prototype.getLocale = function(localName) {
|
||||
localName || (localName = this._locale.localeName);
|
||||
return this._locales[localName] || null;
|
||||
};
|
||||
|
||||
Keyboard.prototype.bind = function(keyComboStr, pressHandler, releaseHandler, preventRepeatByDefault) {
|
||||
if (keyComboStr === null || typeof keyComboStr === 'function') {
|
||||
preventRepeatByDefault = releaseHandler;
|
||||
releaseHandler = pressHandler;
|
||||
pressHandler = keyComboStr;
|
||||
keyComboStr = null;
|
||||
}
|
||||
|
||||
if (
|
||||
keyComboStr &&
|
||||
typeof keyComboStr === 'object' &&
|
||||
typeof keyComboStr.length === 'number'
|
||||
) {
|
||||
for (var i = 0; i < keyComboStr.length; i += 1) {
|
||||
this.bind(keyComboStr[i], pressHandler, releaseHandler);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._listeners.push({
|
||||
keyCombo : keyComboStr ? new KeyCombo(keyComboStr) : null,
|
||||
pressHandler : pressHandler || null,
|
||||
releaseHandler : releaseHandler || null,
|
||||
preventRepeat : preventRepeatByDefault || false,
|
||||
preventRepeatByDefault : preventRepeatByDefault || false
|
||||
});
|
||||
};
|
||||
Keyboard.prototype.addListener = Keyboard.prototype.bind;
|
||||
Keyboard.prototype.on = Keyboard.prototype.bind;
|
||||
|
||||
Keyboard.prototype.unbind = function(keyComboStr, pressHandler, releaseHandler) {
|
||||
if (keyComboStr === null || typeof keyComboStr === 'function') {
|
||||
releaseHandler = pressHandler;
|
||||
pressHandler = keyComboStr;
|
||||
keyComboStr = null;
|
||||
}
|
||||
|
||||
if (
|
||||
keyComboStr &&
|
||||
typeof keyComboStr === 'object' &&
|
||||
typeof keyComboStr.length === 'number'
|
||||
) {
|
||||
for (var i = 0; i < keyComboStr.length; i += 1) {
|
||||
this.unbind(keyComboStr[i], pressHandler, releaseHandler);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this._listeners.length; i += 1) {
|
||||
var listener = this._listeners[i];
|
||||
|
||||
var comboMatches = !keyComboStr && !listener.keyCombo ||
|
||||
listener.keyCombo && listener.keyCombo.isEqual(keyComboStr);
|
||||
var pressHandlerMatches = !pressHandler && !releaseHandler ||
|
||||
!pressHandler && !listener.pressHandler ||
|
||||
pressHandler === listener.pressHandler;
|
||||
var releaseHandlerMatches = !pressHandler && !releaseHandler ||
|
||||
!releaseHandler && !listener.releaseHandler ||
|
||||
releaseHandler === listener.releaseHandler;
|
||||
|
||||
if (comboMatches && pressHandlerMatches && releaseHandlerMatches) {
|
||||
this._listeners.splice(i, 1);
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
Keyboard.prototype.removeListener = Keyboard.prototype.unbind;
|
||||
Keyboard.prototype.off = Keyboard.prototype.unbind;
|
||||
|
||||
Keyboard.prototype.setContext = function(contextName) {
|
||||
if(this._locale) { this.releaseAllKeys(); }
|
||||
|
||||
if (!this._contexts[contextName]) {
|
||||
this._contexts[contextName] = [];
|
||||
}
|
||||
this._listeners = this._contexts[contextName];
|
||||
this._currentContext = contextName;
|
||||
};
|
||||
|
||||
Keyboard.prototype.getContext = function() {
|
||||
return this._currentContext;
|
||||
};
|
||||
|
||||
Keyboard.prototype.withContext = function(contextName, callback) {
|
||||
var previousContextName = this.getContext();
|
||||
this.setContext(contextName);
|
||||
|
||||
callback();
|
||||
|
||||
this.setContext(previousContextName);
|
||||
};
|
||||
|
||||
Keyboard.prototype.watch = function(targetWindow, targetElement, targetPlatform, targetUserAgent) {
|
||||
var _this = this;
|
||||
|
||||
this.stop();
|
||||
|
||||
if (!targetWindow) {
|
||||
if (!global.addEventListener && !global.attachEvent) {
|
||||
throw new Error('Cannot find global functions addEventListener or attachEvent.');
|
||||
}
|
||||
targetWindow = global;
|
||||
}
|
||||
|
||||
if (typeof targetWindow.nodeType === 'number') {
|
||||
targetUserAgent = targetPlatform;
|
||||
targetPlatform = targetElement;
|
||||
targetElement = targetWindow;
|
||||
targetWindow = global;
|
||||
}
|
||||
|
||||
if (!targetWindow.addEventListener && !targetWindow.attachEvent) {
|
||||
throw new Error('Cannot find addEventListener or attachEvent methods on targetWindow.');
|
||||
}
|
||||
|
||||
this._isModernBrowser = !!targetWindow.addEventListener;
|
||||
|
||||
var userAgent = targetWindow.navigator && targetWindow.navigator.userAgent || '';
|
||||
var platform = targetWindow.navigator && targetWindow.navigator.platform || '';
|
||||
|
||||
targetElement && targetElement !== null || (targetElement = targetWindow.document);
|
||||
targetPlatform && targetPlatform !== null || (targetPlatform = platform);
|
||||
targetUserAgent && targetUserAgent !== null || (targetUserAgent = userAgent);
|
||||
|
||||
this._targetKeyDownBinding = function(event) {
|
||||
_this.pressKey(event.keyCode, event);
|
||||
};
|
||||
this._targetKeyUpBinding = function(event) {
|
||||
_this.releaseKey(event.keyCode, event);
|
||||
};
|
||||
this._targetResetBinding = function(event) {
|
||||
_this.releaseAllKeys(event)
|
||||
};
|
||||
|
||||
this._bindEvent(targetElement, 'keydown', this._targetKeyDownBinding);
|
||||
this._bindEvent(targetElement, 'keyup', this._targetKeyUpBinding);
|
||||
this._bindEvent(targetWindow, 'focus', this._targetResetBinding);
|
||||
this._bindEvent(targetWindow, 'blur', this._targetResetBinding);
|
||||
|
||||
this._targetElement = targetElement;
|
||||
this._targetWindow = targetWindow;
|
||||
this._targetPlatform = targetPlatform;
|
||||
this._targetUserAgent = targetUserAgent;
|
||||
};
|
||||
|
||||
Keyboard.prototype.stop = function() {
|
||||
var _this = this;
|
||||
|
||||
if (!this._targetElement || !this._targetWindow) { return; }
|
||||
|
||||
this._unbindEvent(this._targetElement, 'keydown', this._targetKeyDownBinding);
|
||||
this._unbindEvent(this._targetElement, 'keyup', this._targetKeyUpBinding);
|
||||
this._unbindEvent(this._targetWindow, 'focus', this._targetResetBinding);
|
||||
this._unbindEvent(this._targetWindow, 'blur', this._targetResetBinding);
|
||||
|
||||
this._targetWindow = null;
|
||||
this._targetElement = null;
|
||||
};
|
||||
|
||||
Keyboard.prototype.pressKey = function(keyCode, event) {
|
||||
if (this._paused) { return; }
|
||||
if (!this._locale) { throw new Error('Locale not set'); }
|
||||
|
||||
this._locale.pressKey(keyCode);
|
||||
this._applyBindings(event);
|
||||
};
|
||||
|
||||
Keyboard.prototype.releaseKey = function(keyCode, event) {
|
||||
if (this._paused) { return; }
|
||||
if (!this._locale) { throw new Error('Locale not set'); }
|
||||
|
||||
this._locale.releaseKey(keyCode);
|
||||
this._clearBindings(event);
|
||||
};
|
||||
|
||||
Keyboard.prototype.releaseAllKeys = function(event) {
|
||||
if (this._paused) { return; }
|
||||
if (!this._locale) { throw new Error('Locale not set'); }
|
||||
|
||||
this._locale.pressedKeys.length = 0;
|
||||
this._clearBindings(event);
|
||||
};
|
||||
|
||||
Keyboard.prototype.pause = function() {
|
||||
if (this._paused) { return; }
|
||||
if (this._locale) { this.releaseAllKeys(); }
|
||||
this._paused = true;
|
||||
};
|
||||
|
||||
Keyboard.prototype.resume = function() {
|
||||
this._paused = false;
|
||||
};
|
||||
|
||||
Keyboard.prototype.reset = function() {
|
||||
this.releaseAllKeys();
|
||||
this._listeners.length = 0;
|
||||
};
|
||||
|
||||
Keyboard.prototype._bindEvent = function(targetElement, eventName, handler) {
|
||||
return this._isModernBrowser ?
|
||||
targetElement.addEventListener(eventName, handler, false) :
|
||||
targetElement.attachEvent('on' + eventName, handler);
|
||||
};
|
||||
|
||||
Keyboard.prototype._unbindEvent = function(targetElement, eventName, handler) {
|
||||
return this._isModernBrowser ?
|
||||
targetElement.removeEventListener(eventName, handler, false) :
|
||||
targetElement.detachEvent('on' + eventName, handler);
|
||||
};
|
||||
|
||||
Keyboard.prototype._getGroupedListeners = function() {
|
||||
var listenerGroups = [];
|
||||
var listenerGroupMap = [];
|
||||
|
||||
var listeners = this._listeners;
|
||||
if (this._currentContext !== 'global') {
|
||||
listeners = [].concat(listeners, this._contexts.global);
|
||||
}
|
||||
|
||||
listeners.sort(function(a, b) {
|
||||
return (b.keyCombo ? b.keyCombo.keyNames.length : 0) - (a.keyCombo ? a.keyCombo.keyNames.length : 0);
|
||||
}).forEach(function(l) {
|
||||
var mapIndex = -1;
|
||||
for (var i = 0; i < listenerGroupMap.length; i += 1) {
|
||||
if (listenerGroupMap[i] === null && l.keyCombo === null ||
|
||||
listenerGroupMap[i] !== null && listenerGroupMap[i].isEqual(l.keyCombo)) {
|
||||
mapIndex = i;
|
||||
}
|
||||
}
|
||||
if (mapIndex === -1) {
|
||||
mapIndex = listenerGroupMap.length;
|
||||
listenerGroupMap.push(l.keyCombo);
|
||||
}
|
||||
if (!listenerGroups[mapIndex]) {
|
||||
listenerGroups[mapIndex] = [];
|
||||
}
|
||||
listenerGroups[mapIndex].push(l);
|
||||
});
|
||||
return listenerGroups;
|
||||
};
|
||||
|
||||
Keyboard.prototype._applyBindings = function(event) {
|
||||
var preventRepeat = false;
|
||||
|
||||
event || (event = {});
|
||||
event.preventRepeat = function() { preventRepeat = true; };
|
||||
event.pressedKeys = this._locale.pressedKeys.slice(0);
|
||||
|
||||
var pressedKeys = this._locale.pressedKeys.slice(0);
|
||||
var listenerGroups = this._getGroupedListeners();
|
||||
|
||||
|
||||
for (var i = 0; i < listenerGroups.length; i += 1) {
|
||||
var listeners = listenerGroups[i];
|
||||
var keyCombo = listeners[0].keyCombo;
|
||||
|
||||
if (keyCombo === null || keyCombo.check(pressedKeys)) {
|
||||
for (var j = 0; j < listeners.length; j += 1) {
|
||||
var listener = listeners[j];
|
||||
|
||||
if (keyCombo === null) {
|
||||
listener = {
|
||||
keyCombo : new KeyCombo(pressedKeys.join('+')),
|
||||
pressHandler : listener.pressHandler,
|
||||
releaseHandler : listener.releaseHandler,
|
||||
preventRepeat : listener.preventRepeat,
|
||||
preventRepeatByDefault : listener.preventRepeatByDefault
|
||||
};
|
||||
}
|
||||
|
||||
if (listener.pressHandler && !listener.preventRepeat) {
|
||||
listener.pressHandler.call(this, event);
|
||||
if (preventRepeat) {
|
||||
listener.preventRepeat = preventRepeat;
|
||||
preventRepeat = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (listener.releaseHandler && this._appliedListeners.indexOf(listener) === -1) {
|
||||
this._appliedListeners.push(listener);
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCombo) {
|
||||
for (var j = 0; j < keyCombo.keyNames.length; j += 1) {
|
||||
var index = pressedKeys.indexOf(keyCombo.keyNames[j]);
|
||||
if (index !== -1) {
|
||||
pressedKeys.splice(index, 1);
|
||||
j -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Keyboard.prototype._clearBindings = function(event) {
|
||||
event || (event = {});
|
||||
|
||||
for (var i = 0; i < this._appliedListeners.length; i += 1) {
|
||||
var listener = this._appliedListeners[i];
|
||||
var keyCombo = listener.keyCombo;
|
||||
if (keyCombo === null || !keyCombo.check(this._locale.pressedKeys)) {
|
||||
listener.preventRepeat = listener.preventRepeatByDefault;
|
||||
listener.releaseHandler.call(this, event);
|
||||
this._appliedListeners.splice(i, 1);
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Keyboard;
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
|
||||
var KeyCombo = require('./key-combo');
|
||||
|
||||
|
||||
function Locale(name) {
|
||||
this.localeName = name;
|
||||
this.pressedKeys = [];
|
||||
this._appliedMacros = [];
|
||||
this._keyMap = {};
|
||||
this._killKeyCodes = [];
|
||||
this._macros = [];
|
||||
}
|
||||
|
||||
Locale.prototype.bindKeyCode = function(keyCode, keyNames) {
|
||||
if (typeof keyNames === 'string') {
|
||||
keyNames = [keyNames];
|
||||
}
|
||||
|
||||
this._keyMap[keyCode] = keyNames;
|
||||
};
|
||||
|
||||
Locale.prototype.bindMacro = function(keyComboStr, keyNames) {
|
||||
if (typeof keyNames === 'string') {
|
||||
keyNames = [ keyNames ];
|
||||
}
|
||||
|
||||
var handler = null;
|
||||
if (typeof keyNames === 'function') {
|
||||
handler = keyNames;
|
||||
keyNames = null;
|
||||
}
|
||||
|
||||
var macro = {
|
||||
keyCombo : new KeyCombo(keyComboStr),
|
||||
keyNames : keyNames,
|
||||
handler : handler
|
||||
};
|
||||
|
||||
this._macros.push(macro);
|
||||
};
|
||||
|
||||
Locale.prototype.getKeyCodes = function(keyName) {
|
||||
var keyCodes = [];
|
||||
for (var keyCode in this._keyMap) {
|
||||
var index = this._keyMap[keyCode].indexOf(keyName);
|
||||
if (index > -1) { keyCodes.push(keyCode|0); }
|
||||
}
|
||||
return keyCodes;
|
||||
};
|
||||
|
||||
Locale.prototype.getKeyNames = function(keyCode) {
|
||||
return this._keyMap[keyCode] || [];
|
||||
};
|
||||
|
||||
Locale.prototype.setKillKey = function(keyCode) {
|
||||
if (typeof keyCode === 'string') {
|
||||
var keyCodes = this.getKeyCodes(keyCode);
|
||||
for (var i = 0; i < keyCodes.length; i += 1) {
|
||||
this.setKillKey(keyCodes[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._killKeyCodes.push(keyCode);
|
||||
};
|
||||
|
||||
Locale.prototype.pressKey = function(keyCode) {
|
||||
if (typeof keyCode === 'string') {
|
||||
var keyCodes = this.getKeyCodes(keyCode);
|
||||
for (var i = 0; i < keyCodes.length; i += 1) {
|
||||
this.pressKey(keyCodes[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var keyNames = this.getKeyNames(keyCode);
|
||||
for (var i = 0; i < keyNames.length; i += 1) {
|
||||
if (this.pressedKeys.indexOf(keyNames[i]) === -1) {
|
||||
this.pressedKeys.push(keyNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this._applyMacros();
|
||||
};
|
||||
|
||||
Locale.prototype.releaseKey = function(keyCode) {
|
||||
if (typeof keyCode === 'string') {
|
||||
var keyCodes = this.getKeyCodes(keyCode);
|
||||
for (var i = 0; i < keyCodes.length; i += 1) {
|
||||
this.releaseKey(keyCodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
var keyNames = this.getKeyNames(keyCode);
|
||||
var killKeyCodeIndex = this._killKeyCodes.indexOf(keyCode);
|
||||
|
||||
if (killKeyCodeIndex > -1) {
|
||||
this.pressedKeys.length = 0;
|
||||
} else {
|
||||
for (var i = 0; i < keyNames.length; i += 1) {
|
||||
var index = this.pressedKeys.indexOf(keyNames[i]);
|
||||
if (index > -1) {
|
||||
this.pressedKeys.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._clearMacros();
|
||||
}
|
||||
};
|
||||
|
||||
Locale.prototype._applyMacros = function() {
|
||||
var macros = this._macros.slice(0);
|
||||
for (var i = 0; i < macros.length; i += 1) {
|
||||
var macro = macros[i];
|
||||
if (macro.keyCombo.check(this.pressedKeys)) {
|
||||
if (macro.handler) {
|
||||
macro.keyNames = macro.handler(this.pressedKeys);
|
||||
}
|
||||
for (var j = 0; j < macro.keyNames.length; j += 1) {
|
||||
if (this.pressedKeys.indexOf(macro.keyNames[j]) === -1) {
|
||||
this.pressedKeys.push(macro.keyNames[j]);
|
||||
}
|
||||
}
|
||||
this._appliedMacros.push(macro);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Locale.prototype._clearMacros = function() {
|
||||
for (var i = 0; i < this._appliedMacros.length; i += 1) {
|
||||
var macro = this._appliedMacros[i];
|
||||
if (!macro.keyCombo.check(this.pressedKeys)) {
|
||||
for (var j = 0; j < macro.keyNames.length; j += 1) {
|
||||
var index = this.pressedKeys.indexOf(macro.keyNames[j]);
|
||||
if (index > -1) {
|
||||
this.pressedKeys.splice(index, 1);
|
||||
}
|
||||
}
|
||||
if (macro.handler) {
|
||||
macro.keyNames = null;
|
||||
}
|
||||
this._appliedMacros.splice(i, 1);
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = Locale;
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
|
||||
module.exports = function(locale, platform, userAgent) {
|
||||
|
||||
// general
|
||||
locale.bindKeyCode(3, ['cancel']);
|
||||
locale.bindKeyCode(8, ['backspace']);
|
||||
locale.bindKeyCode(9, ['tab']);
|
||||
locale.bindKeyCode(12, ['clear']);
|
||||
locale.bindKeyCode(13, ['enter']);
|
||||
locale.bindKeyCode(16, ['shift']);
|
||||
locale.bindKeyCode(17, ['ctrl']);
|
||||
locale.bindKeyCode(18, ['alt', 'menu']);
|
||||
locale.bindKeyCode(19, ['pause', 'break']);
|
||||
locale.bindKeyCode(20, ['capslock']);
|
||||
locale.bindKeyCode(27, ['escape', 'esc']);
|
||||
locale.bindKeyCode(32, ['space', 'spacebar']);
|
||||
locale.bindKeyCode(33, ['pageup']);
|
||||
locale.bindKeyCode(34, ['pagedown']);
|
||||
locale.bindKeyCode(35, ['end']);
|
||||
locale.bindKeyCode(36, ['home']);
|
||||
locale.bindKeyCode(37, ['left']);
|
||||
locale.bindKeyCode(38, ['up']);
|
||||
locale.bindKeyCode(39, ['right']);
|
||||
locale.bindKeyCode(40, ['down']);
|
||||
locale.bindKeyCode(41, ['select']);
|
||||
locale.bindKeyCode(42, ['printscreen']);
|
||||
locale.bindKeyCode(43, ['execute']);
|
||||
locale.bindKeyCode(44, ['snapshot']);
|
||||
locale.bindKeyCode(45, ['insert', 'ins']);
|
||||
locale.bindKeyCode(46, ['delete', 'del']);
|
||||
locale.bindKeyCode(47, ['help']);
|
||||
locale.bindKeyCode(145, ['scrolllock', 'scroll']);
|
||||
locale.bindKeyCode(187, ['equal', 'equalsign', '=']);
|
||||
locale.bindKeyCode(188, ['comma', ',']);
|
||||
locale.bindKeyCode(190, ['period', '.']);
|
||||
locale.bindKeyCode(191, ['slash', 'forwardslash', '/']);
|
||||
locale.bindKeyCode(192, ['graveaccent', '`']);
|
||||
locale.bindKeyCode(219, ['openbracket', '[']);
|
||||
locale.bindKeyCode(220, ['backslash', '\\']);
|
||||
locale.bindKeyCode(221, ['closebracket', ']']);
|
||||
locale.bindKeyCode(222, ['apostrophe', '\'']);
|
||||
|
||||
// 0-9
|
||||
locale.bindKeyCode(48, ['zero', '0']);
|
||||
locale.bindKeyCode(49, ['one', '1']);
|
||||
locale.bindKeyCode(50, ['two', '2']);
|
||||
locale.bindKeyCode(51, ['three', '3']);
|
||||
locale.bindKeyCode(52, ['four', '4']);
|
||||
locale.bindKeyCode(53, ['five', '5']);
|
||||
locale.bindKeyCode(54, ['six', '6']);
|
||||
locale.bindKeyCode(55, ['seven', '7']);
|
||||
locale.bindKeyCode(56, ['eight', '8']);
|
||||
locale.bindKeyCode(57, ['nine', '9']);
|
||||
|
||||
// numpad
|
||||
locale.bindKeyCode(96, ['numzero', 'num0']);
|
||||
locale.bindKeyCode(97, ['numone', 'num1']);
|
||||
locale.bindKeyCode(98, ['numtwo', 'num2']);
|
||||
locale.bindKeyCode(99, ['numthree', 'num3']);
|
||||
locale.bindKeyCode(100, ['numfour', 'num4']);
|
||||
locale.bindKeyCode(101, ['numfive', 'num5']);
|
||||
locale.bindKeyCode(102, ['numsix', 'num6']);
|
||||
locale.bindKeyCode(103, ['numseven', 'num7']);
|
||||
locale.bindKeyCode(104, ['numeight', 'num8']);
|
||||
locale.bindKeyCode(105, ['numnine', 'num9']);
|
||||
locale.bindKeyCode(106, ['nummultiply', 'num*']);
|
||||
locale.bindKeyCode(107, ['numadd', 'num+']);
|
||||
locale.bindKeyCode(108, ['numenter']);
|
||||
locale.bindKeyCode(109, ['numsubtract', 'num-']);
|
||||
locale.bindKeyCode(110, ['numdecimal', 'num.']);
|
||||
locale.bindKeyCode(111, ['numdivide', 'num/']);
|
||||
locale.bindKeyCode(144, ['numlock', 'num']);
|
||||
|
||||
// function keys
|
||||
locale.bindKeyCode(112, ['f1']);
|
||||
locale.bindKeyCode(113, ['f2']);
|
||||
locale.bindKeyCode(114, ['f3']);
|
||||
locale.bindKeyCode(115, ['f4']);
|
||||
locale.bindKeyCode(116, ['f5']);
|
||||
locale.bindKeyCode(117, ['f6']);
|
||||
locale.bindKeyCode(118, ['f7']);
|
||||
locale.bindKeyCode(119, ['f8']);
|
||||
locale.bindKeyCode(120, ['f9']);
|
||||
locale.bindKeyCode(121, ['f10']);
|
||||
locale.bindKeyCode(122, ['f11']);
|
||||
locale.bindKeyCode(123, ['f12']);
|
||||
|
||||
// secondary key symbols
|
||||
locale.bindMacro('shift + `', ['tilde', '~']);
|
||||
locale.bindMacro('shift + 1', ['exclamation', 'exclamationpoint', '!']);
|
||||
locale.bindMacro('shift + 2', ['at', '@']);
|
||||
locale.bindMacro('shift + 3', ['number', '#']);
|
||||
locale.bindMacro('shift + 4', ['dollar', 'dollars', 'dollarsign', '$']);
|
||||
locale.bindMacro('shift + 5', ['percent', '%']);
|
||||
locale.bindMacro('shift + 6', ['caret', '^']);
|
||||
locale.bindMacro('shift + 7', ['ampersand', 'and', '&']);
|
||||
locale.bindMacro('shift + 8', ['asterisk', '*']);
|
||||
locale.bindMacro('shift + 9', ['openparen', '(']);
|
||||
locale.bindMacro('shift + 0', ['closeparen', ')']);
|
||||
locale.bindMacro('shift + -', ['underscore', '_']);
|
||||
locale.bindMacro('shift + =', ['plus', '+']);
|
||||
locale.bindMacro('shift + [', ['opencurlybrace', 'opencurlybracket', '{']);
|
||||
locale.bindMacro('shift + ]', ['closecurlybrace', 'closecurlybracket', '}']);
|
||||
locale.bindMacro('shift + \\', ['verticalbar', '|']);
|
||||
locale.bindMacro('shift + ;', ['colon', ':']);
|
||||
locale.bindMacro('shift + \'', ['quotationmark', '\'']);
|
||||
locale.bindMacro('shift + !,', ['openanglebracket', '<']);
|
||||
locale.bindMacro('shift + .', ['closeanglebracket', '>']);
|
||||
locale.bindMacro('shift + /', ['questionmark', '?']);
|
||||
|
||||
//a-z and A-Z
|
||||
for (var keyCode = 65; keyCode <= 90; keyCode += 1) {
|
||||
var keyName = String.fromCharCode(keyCode + 32);
|
||||
var capitalKeyName = String.fromCharCode(keyCode);
|
||||
locale.bindKeyCode(keyCode, keyName);
|
||||
locale.bindMacro('shift + ' + keyName, capitalKeyName);
|
||||
locale.bindMacro('capslock + ' + keyName, capitalKeyName);
|
||||
}
|
||||
|
||||
// browser caveats
|
||||
var semicolonKeyCode = userAgent.match('Firefox') ? 59 : 186;
|
||||
var dashKeyCode = userAgent.match('Firefox') ? 173 : 189;
|
||||
var leftCommandKeyCode;
|
||||
var rightCommandKeyCode;
|
||||
if (platform.match('Mac') && (userAgent.match('Safari') || userAgent.match('Chrome'))) {
|
||||
leftCommandKeyCode = 91;
|
||||
rightCommandKeyCode = 93;
|
||||
} else if(platform.match('Mac') && userAgent.match('Opera')) {
|
||||
leftCommandKeyCode = 17;
|
||||
rightCommandKeyCode = 17;
|
||||
} else if(platform.match('Mac') && userAgent.match('Firefox')) {
|
||||
leftCommandKeyCode = 224;
|
||||
rightCommandKeyCode = 224;
|
||||
}
|
||||
locale.bindKeyCode(semicolonKeyCode, ['semicolon', ';']);
|
||||
locale.bindKeyCode(dashKeyCode, ['dash', '-']);
|
||||
locale.bindKeyCode(leftCommandKeyCode, ['command', 'windows', 'win', 'super', 'leftcommand', 'leftwindows', 'leftwin', 'leftsuper']);
|
||||
locale.bindKeyCode(rightCommandKeyCode, ['command', 'windows', 'win', 'super', 'rightcommand', 'rightwindows', 'rightwin', 'rightsuper']);
|
||||
|
||||
// kill keys
|
||||
locale.setKillKey('command');
|
||||
};
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "keyboardjs",
|
||||
"description": "A library for binding to keys and key combos without the pain of key codes and key combo conflicts.",
|
||||
"version": "2.3.3",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "mocha test/**/*.spec.js",
|
||||
"build": "browserify -g uglifyify -s keyboardJS ./index.js > ./dist/keyboard.min.js & browserify --debug -s keyboardJS ./index.js > ./dist/keyboard.js"
|
||||
},
|
||||
"keywords": [
|
||||
"Key Binding, Keyboard, Key combos, Keyboard Shortcuts"
|
||||
],
|
||||
"author": "Robert Hurst <robertwhurst@gmail.com>",
|
||||
"bugs": {
|
||||
"url": "https://github.com/RobertWHurst/KeyboardJS/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:RobertWHurst/KeyboardJS.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"browserify": "^6.0.2",
|
||||
"mocha": "^2.2.5",
|
||||
"sinon": "^1.15.3",
|
||||
"uglifyify": "^2.5.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
var sinon = require('sinon');
|
||||
|
||||
|
||||
module.exports = {
|
||||
addEventListener: sinon.stub(),
|
||||
removeEventListener: sinon.stub(),
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
var sinon = require('sinon');
|
||||
|
||||
|
||||
module.exports = {
|
||||
addEventListener : sinon.stub(),
|
||||
removeEventListener : sinon.stub(),
|
||||
navigator: {
|
||||
platform : 'test-platform',
|
||||
userAgent : 'test-user-agent'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
var assert = require('assert');
|
||||
var KeyCombo = require('../lib/key-combo');
|
||||
|
||||
|
||||
describe('KeyCombo', function() {
|
||||
|
||||
|
||||
describe('.parseComboStr', function() {
|
||||
|
||||
it('can parse combo strings', function() {
|
||||
var comboArr = KeyCombo.parseComboStr('a + b');
|
||||
|
||||
assert.equal(comboArr[0][0], 'a');
|
||||
assert.equal(comboArr[0][1], 'b');
|
||||
});
|
||||
|
||||
it('can parse combo strings containing combo deliminators', function() {
|
||||
var comboArr = KeyCombo.parseComboStr('a + b > c + d');
|
||||
|
||||
assert.equal(comboArr[0][0], 'a');
|
||||
assert.equal(comboArr[0][1], 'b');
|
||||
assert.equal(comboArr[1][0], 'c');
|
||||
assert.equal(comboArr[1][1], 'd');
|
||||
});
|
||||
|
||||
it('can parse combo strings containing sequence deliminators');
|
||||
});
|
||||
|
||||
|
||||
describe('#check', function() {
|
||||
|
||||
it('can check the combo against an array of key names', function() {
|
||||
var keyCombo1 = new KeyCombo('a + b');
|
||||
var keyCombo2 = new KeyCombo('a + \\+');
|
||||
|
||||
assert.ok(keyCombo1.check(['a', 'b']));
|
||||
assert.ok(keyCombo1.check(['b', 'a']));
|
||||
assert.ok(keyCombo1.check(['a', 'b', 'c']));
|
||||
assert.ok(keyCombo1.check(['z', 'a', 'b']));
|
||||
assert.ok(keyCombo1.check(['z', 'a', 'b', 'c']));
|
||||
|
||||
assert.ok(keyCombo2.check(['a', '+']));
|
||||
});
|
||||
|
||||
it('can check the combo containing combo deliminators against an array of key names', function() {
|
||||
var keyCombo = new KeyCombo('a + b > c + d');
|
||||
|
||||
assert.ok(keyCombo.check(['a', 'b', 'c', 'd']));
|
||||
assert.ok(keyCombo.check(['b', 'a', 'd', 'c']));
|
||||
assert.ok(keyCombo.check(['a', 'b', 'e', 'c', 'd', 'f']));
|
||||
assert.ok(keyCombo.check(['z', 'a', 'b', 'y', 'c', 'd']));
|
||||
assert.ok(keyCombo.check(['z', 'a', 'b', 'y', 'x', 'c', 'd', 'w']));
|
||||
|
||||
assert.equal(keyCombo.check(['c', 'd', 'a', 'b']), false);
|
||||
assert.equal(keyCombo.check(['d', 'c', 'b', 'a']), false);
|
||||
});
|
||||
|
||||
it('can check the combo containing sequence deliminators against an array of key names');
|
||||
});
|
||||
describe('#isEqual', function() {
|
||||
|
||||
it('can correctly equate two the combo to a given one', function() {
|
||||
var keyCombo1 = new KeyCombo('a + b');
|
||||
var keyCombo2 = new KeyCombo('a + b');
|
||||
var keyCombo3 = new KeyCombo('b + a');
|
||||
var keyCombo4 = new KeyCombo('a + b + c');
|
||||
var keyCombo5 = new KeyCombo('a > b');
|
||||
|
||||
assert.ok(keyCombo1.isEqual(keyCombo2));
|
||||
assert.ok(keyCombo1.isEqual(keyCombo3));
|
||||
assert.equal(keyCombo1.isEqual(keyCombo4), false);
|
||||
assert.equal(keyCombo1.isEqual(keyCombo5), false);
|
||||
});
|
||||
});
|
||||
});
|
||||
+700
@@ -0,0 +1,700 @@
|
||||
|
||||
var sinon = require('sinon');
|
||||
var assert = require('assert');
|
||||
var Keyboard = require('../lib/keyboard');
|
||||
var KeyCombo = require('../lib/key-combo');
|
||||
var Locale = require('../lib/locale');
|
||||
var doc = require('./fixtures/document');
|
||||
var win = require('./fixtures/window');
|
||||
|
||||
|
||||
describe('Keyboard', function() {
|
||||
|
||||
var keyboard;
|
||||
beforeEach(function() {
|
||||
keyboard = new Keyboard(win, doc);
|
||||
});
|
||||
|
||||
|
||||
describe('#setLocale', function() {
|
||||
|
||||
it('creates and sets a locale', function() {
|
||||
keyboard.setLocale('testName', function(locale, platform, userAgent) {
|
||||
assert.equal(platform, 'test-platform');
|
||||
assert.equal(userAgent, 'test-user-agent');
|
||||
|
||||
locale.test = 1;
|
||||
});
|
||||
|
||||
assert.equal(keyboard._locale.test, 1);
|
||||
assert.equal(keyboard._locales.testName.test, 1);
|
||||
});
|
||||
|
||||
it('sets a locale', function() {
|
||||
keyboard._locales.testName = { test: 2 };
|
||||
|
||||
keyboard.setLocale('testName');
|
||||
|
||||
assert.equal(keyboard._locale.test, 2);
|
||||
});
|
||||
|
||||
it('accepts locale instance and sets it', function() {
|
||||
keyboard.setLocale({
|
||||
localeName: 'testName',
|
||||
test: 3
|
||||
});
|
||||
|
||||
assert.equal(keyboard._locale.test, 3);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#getLocale', function() {
|
||||
|
||||
it('returns the current locale', function() {
|
||||
keyboard._locales.testName = { test: 4, localeName: 'testName' };
|
||||
keyboard._locale = keyboard._locales.testName;
|
||||
|
||||
var locale = keyboard.getLocale();
|
||||
|
||||
assert.equal(locale.test, 4);
|
||||
});
|
||||
|
||||
it('returns the a locale by name', function() {
|
||||
keyboard._locales.testName = { test: 5 };
|
||||
keyboard._locale = keyboard._locales.testName;
|
||||
|
||||
var locale = keyboard.getLocale('testName');
|
||||
|
||||
assert.equal(locale.test, 5);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#bind', function() {
|
||||
|
||||
it('binds a combo to press handler and release handlers', function() {
|
||||
var pressHandler = function() {};
|
||||
var releaseHandler = function() {};
|
||||
|
||||
keyboard.bind('a', pressHandler, releaseHandler);
|
||||
|
||||
assert.equal(keyboard._listeners[0].keyCombo.sourceStr, 'a');
|
||||
assert.equal(keyboard._listeners[0].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[0].releaseHandler, releaseHandler);
|
||||
});
|
||||
|
||||
it('binds a combo to a press handler', function() {
|
||||
var pressHandler = function() {};
|
||||
|
||||
keyboard.bind('a', pressHandler);
|
||||
|
||||
assert.equal(keyboard._listeners[0].keyCombo.sourceStr, 'a');
|
||||
assert.equal(keyboard._listeners[0].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[0].releaseHandler, null);
|
||||
});
|
||||
|
||||
it('binds a combo to a release handler', function() {
|
||||
var releaseHandler = function() {};
|
||||
|
||||
keyboard.bind('a', null, releaseHandler);
|
||||
|
||||
assert.equal(keyboard._listeners[0].keyCombo.sourceStr, 'a');
|
||||
assert.equal(keyboard._listeners[0].pressHandler, null);
|
||||
assert.equal(keyboard._listeners[0].releaseHandler, releaseHandler);
|
||||
});
|
||||
|
||||
it('binds several combos to press and release handlers', function() {
|
||||
var pressHandler = function() {};
|
||||
var releaseHandler = function() {};
|
||||
|
||||
keyboard.bind(['a', 'b'], pressHandler, releaseHandler);
|
||||
|
||||
assert.equal(keyboard._listeners[0].keyCombo.sourceStr, 'a');
|
||||
assert.equal(keyboard._listeners[1].keyCombo.sourceStr, 'b');
|
||||
assert.equal(keyboard._listeners[0].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[1].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[0].releaseHandler, releaseHandler);
|
||||
assert.equal(keyboard._listeners[1].releaseHandler, releaseHandler);
|
||||
});
|
||||
|
||||
it('binds press and release handlers to any keypress', function() {
|
||||
var pressHandler = function() {};
|
||||
var releaseHandler = function() {};
|
||||
|
||||
keyboard.bind(pressHandler, releaseHandler);
|
||||
|
||||
assert.equal(keyboard._listeners[0].keyCombo, null);
|
||||
assert.equal(keyboard._listeners[0].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[0].releaseHandler, releaseHandler);
|
||||
});
|
||||
|
||||
it('accepts preventRepeat as a final argument', function() {
|
||||
var pressHandler = function() {};
|
||||
var releaseHandler = function() {};
|
||||
|
||||
keyboard.bind('a', pressHandler, releaseHandler, true);
|
||||
keyboard.bind(pressHandler, releaseHandler, true);
|
||||
|
||||
assert.equal(keyboard._listeners[0].keyCombo.sourceStr, 'a');
|
||||
assert.equal(keyboard._listeners[0].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[0].releaseHandler, releaseHandler);
|
||||
assert.equal(keyboard._listeners[0].preventRepeat, true);
|
||||
assert.equal(keyboard._listeners[1].keyCombo, null);
|
||||
assert.equal(keyboard._listeners[1].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[1].releaseHandler, releaseHandler);
|
||||
assert.equal(keyboard._listeners[1].preventRepeat, true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#addListener', function() {
|
||||
|
||||
it('is an alias for bind', function() {
|
||||
assert.equal(keyboard.addListener, keyboard.bind);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#on', function() {
|
||||
|
||||
it('is an alias for bind', function() {
|
||||
assert.equal(keyboard.on, keyboard.bind);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#unbind', function() {
|
||||
|
||||
var pressHandler = function() {};
|
||||
var releaseHandler = function() {};
|
||||
beforeEach(function() {
|
||||
keyboard._listeners.push({
|
||||
keyCombo: new KeyCombo('a'),
|
||||
pressHandler: pressHandler,
|
||||
releaseHandler: releaseHandler,
|
||||
preventRepeat: false
|
||||
});
|
||||
keyboard._listeners.push({
|
||||
keyCombo: new KeyCombo('a'),
|
||||
pressHandler: null,
|
||||
releaseHandler: releaseHandler,
|
||||
preventRepeat: false
|
||||
});
|
||||
keyboard._listeners.push({
|
||||
keyCombo: new KeyCombo('a'),
|
||||
pressHandler: pressHandler,
|
||||
releaseHandler: null,
|
||||
preventRepeat: false
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
keyboard._listeners.length = 0;
|
||||
});
|
||||
|
||||
it('unbinds a combo from press handler and release handlers', function() {
|
||||
keyboard.unbind('a', pressHandler, releaseHandler);
|
||||
|
||||
assert.equal(keyboard._listeners.length, 2);
|
||||
assert.equal(keyboard._listeners[0].pressHandler, null);
|
||||
assert.equal(keyboard._listeners[0].releaseHandler, releaseHandler);
|
||||
assert.equal(keyboard._listeners[1].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[1].releaseHandler, null);
|
||||
});
|
||||
|
||||
it('unbinds a combo from a press handler', function() {
|
||||
keyboard.unbind('a', pressHandler);
|
||||
|
||||
assert.equal(keyboard._listeners.length, 2);
|
||||
assert.equal(keyboard._listeners[0].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[0].releaseHandler, releaseHandler);
|
||||
assert.equal(keyboard._listeners[1].pressHandler, null);
|
||||
assert.equal(keyboard._listeners[1].releaseHandler, releaseHandler);
|
||||
});
|
||||
|
||||
it('unbinds a combo from a release handler', function() {
|
||||
keyboard.unbind('a', null, releaseHandler);
|
||||
|
||||
assert.equal(keyboard._listeners.length, 2);
|
||||
assert.equal(keyboard._listeners[0].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[0].releaseHandler, releaseHandler);
|
||||
assert.equal(keyboard._listeners[1].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[1].releaseHandler, null);
|
||||
});
|
||||
|
||||
it('unbinds a several combos from press and release handlers', function() {
|
||||
keyboard._listeners.push({
|
||||
keyCombo: new KeyCombo('b'),
|
||||
pressHandler: pressHandler,
|
||||
releaseHandler: releaseHandler,
|
||||
preventRepeat: false
|
||||
});
|
||||
|
||||
keyboard.unbind(['a', 'b'], pressHandler, releaseHandler);
|
||||
|
||||
assert.equal(keyboard._listeners.length, 2);
|
||||
assert.equal(keyboard._listeners[0].pressHandler, null);
|
||||
assert.equal(keyboard._listeners[0].releaseHandler, releaseHandler);
|
||||
assert.equal(keyboard._listeners[1].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[1].releaseHandler, null);
|
||||
});
|
||||
|
||||
it('unbinds press and release handlers bound to any key press', function() {
|
||||
keyboard._listeners.push({
|
||||
keyCombo: null,
|
||||
pressHandler: pressHandler,
|
||||
releaseHandler: releaseHandler,
|
||||
preventRepeat: false
|
||||
});
|
||||
|
||||
|
||||
keyboard.unbind(pressHandler, releaseHandler);
|
||||
|
||||
assert.equal(keyboard._listeners.length, 3);
|
||||
assert.equal(keyboard._listeners[0].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[0].releaseHandler, releaseHandler);
|
||||
assert.equal(keyboard._listeners[1].pressHandler, null);
|
||||
assert.equal(keyboard._listeners[1].releaseHandler, releaseHandler);
|
||||
assert.equal(keyboard._listeners[2].pressHandler, pressHandler);
|
||||
assert.equal(keyboard._listeners[2].releaseHandler, null);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#removeListener', function() {
|
||||
|
||||
it('is an alias for unbind', function() {
|
||||
assert.equal(keyboard.removeListener, keyboard.unbind);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#off', function() {
|
||||
|
||||
it('is an alias for unbind', function() {
|
||||
assert.equal(keyboard.off, keyboard.unbind);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#setContext', function() {
|
||||
|
||||
it('releases all keys before setting the context', function() {
|
||||
keyboard._locale = {};
|
||||
sinon.stub(keyboard, 'releaseAllKeys');
|
||||
|
||||
keyboard.setContext('myContext');
|
||||
|
||||
assert.ok(keyboard.releaseAllKeys.calledOnce);
|
||||
|
||||
keyboard.releaseAllKeys.restore();
|
||||
});
|
||||
|
||||
it('creates a new context with the given name if it doesn\'t exist.', function() {
|
||||
keyboard.setContext('myContext');
|
||||
|
||||
assert.ok(keyboard._contexts.myContext);
|
||||
assert.equal(keyboard._currentContext, 'myContext');
|
||||
});
|
||||
|
||||
it('applies an existing context if one with the given name already exists', function() {
|
||||
keyboard._contexts.myContext = [1];
|
||||
|
||||
keyboard.setContext('myContext');
|
||||
|
||||
assert.equal(keyboard._currentContext, 'myContext');
|
||||
assert.equal(keyboard._contexts[keyboard._currentContext][0], 1);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#getContext', function() {
|
||||
|
||||
it('returns the current context', function() {
|
||||
keyboard._currentContext = 'myContext';
|
||||
|
||||
assert.equal(keyboard.getContext(), 'myContext');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#watch', function() {
|
||||
|
||||
it('calls stop', function() {
|
||||
sinon.stub(keyboard, 'stop');
|
||||
|
||||
keyboard.watch(win, doc);
|
||||
|
||||
assert.ok(keyboard.stop.calledOnce);
|
||||
keyboard.stop.restore();
|
||||
});
|
||||
|
||||
it('attaches to a given window and document', function() {
|
||||
var win = { addEventListener: sinon.stub() };
|
||||
var doc = { addEventListener: sinon.stub() };
|
||||
|
||||
keyboard.watch(win, doc);
|
||||
|
||||
assert.equal(keyboard._isModernBrowser, true);
|
||||
assert.equal(keyboard._targetWindow, win);
|
||||
assert.equal(keyboard._targetElement, doc);
|
||||
assert.ok(win.addEventListener.firstCall.args[0], 'focus');
|
||||
assert.ok(win.addEventListener.secondCall.args[0], 'blur');
|
||||
assert.ok(doc.addEventListener.firstCall.args[0], 'keydown');
|
||||
assert.ok(doc.addEventListener.secondCall.args[0], 'keyup');
|
||||
});
|
||||
|
||||
it('attaches to a given window and document (Legacy IE)', function() {
|
||||
var win = { attachEvent: sinon.stub() };
|
||||
var doc = { attachEvent: sinon.stub() };
|
||||
|
||||
keyboard.watch(win, doc);
|
||||
|
||||
assert.equal(keyboard._isModernBrowser, false);
|
||||
assert.equal(keyboard._targetWindow, win);
|
||||
assert.equal(keyboard._targetElement, doc);
|
||||
assert.ok(win.attachEvent.firstCall.args[0], 'onfocus');
|
||||
assert.ok(win.attachEvent.secondCall.args[0], 'onblur');
|
||||
assert.ok(doc.attachEvent.firstCall.args[0], 'onkeydown');
|
||||
assert.ok(doc.attachEvent.secondCall.args[0], 'onkeyup');
|
||||
});
|
||||
|
||||
it('attaches to the global namespace if a window and document is not given', function() {
|
||||
global.addEventListener = sinon.stub();
|
||||
global.document = { addEventListener: sinon.stub() };
|
||||
|
||||
keyboard.watch();
|
||||
|
||||
assert.equal(keyboard._isModernBrowser, true);
|
||||
assert.equal(keyboard._targetWindow, global);
|
||||
assert.equal(keyboard._targetElement, global.document);
|
||||
assert.ok(global.addEventListener.firstCall.args[0], 'focus');
|
||||
assert.ok(global.addEventListener.secondCall.args[0], 'blur');
|
||||
assert.ok(global.document.addEventListener.firstCall.args[0], 'keydown');
|
||||
assert.ok(global.document.addEventListener.secondCall.args[0], 'keyup');
|
||||
|
||||
delete global.addEventListener;
|
||||
delete global.document;
|
||||
});
|
||||
|
||||
it('throws is error if the target window does not have the nessisary methods', function() {
|
||||
var win = {};
|
||||
var doc = {};
|
||||
|
||||
assert.throws(function() {
|
||||
keyboard.watch(win, doc);
|
||||
}, /^(?=.*targetWindow)(?=.*addEventListener)(?=.*attachEvent).*$/);
|
||||
});
|
||||
|
||||
it('throws is error a target window was not given and if the global does contain the nessisary functions', function() {
|
||||
assert.throws(function() {
|
||||
keyboard.watch();
|
||||
}, /^(?=.*global)(?=.*addEventListener)(?=.*attachEvent).*$/);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#stop', function() {
|
||||
|
||||
it('dettaches from the currently attached window and document', function() {
|
||||
var doc = keyboard._targetElement = { removeEventListener: sinon.stub() };
|
||||
var win = keyboard._targetWindow = { removeEventListener: sinon.stub() };
|
||||
|
||||
keyboard.stop();
|
||||
|
||||
assert.equal(keyboard._targetWindow, null);
|
||||
assert.equal(keyboard._targetElement, null);
|
||||
assert.ok(win.removeEventListener.firstCall.args[0], 'focus');
|
||||
assert.ok(win.removeEventListener.secondCall.args[0], 'blur');
|
||||
assert.ok(doc.removeEventListener.firstCall.args[0], 'keydown');
|
||||
assert.ok(doc.removeEventListener.secondCall.args[0], 'keyup');
|
||||
});
|
||||
|
||||
it('dettaches from the currently attached window and document (Legacy IE)', function() {
|
||||
var doc = { detachEvent: sinon.stub() };
|
||||
var win = { detachEvent: sinon.stub() };
|
||||
|
||||
keyboard._isModernBrowser = false;
|
||||
keyboard._targetElement = doc;
|
||||
keyboard._targetWindow = win;
|
||||
|
||||
keyboard.stop();
|
||||
|
||||
assert.equal(keyboard._targetWindow, null);
|
||||
assert.equal(keyboard._targetElement, null);
|
||||
assert.ok(win.detachEvent.firstCall.args[0], 'onfocus');
|
||||
assert.ok(win.detachEvent.secondCall.args[0], 'onblur');
|
||||
assert.ok(doc.detachEvent.firstCall.args[0], 'onkeydown');
|
||||
assert.ok(doc.detachEvent.secondCall.args[0], 'onkeyup');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#pressKey', function() {
|
||||
|
||||
var locale;
|
||||
beforeEach(function() {
|
||||
locale = new Locale('test');
|
||||
locale.bindKeyCode(0, 'a');
|
||||
locale.bindKeyCode(1, 'b');
|
||||
keyboard._locale = locale;
|
||||
});
|
||||
|
||||
it('calls pressKey on the locale', function() {
|
||||
sinon.stub(locale, 'pressKey');
|
||||
|
||||
keyboard.pressKey('a');
|
||||
|
||||
assert.equal(locale.pressKey.lastCall.args[0], 'a');
|
||||
|
||||
locale.pressKey.restore();
|
||||
});
|
||||
|
||||
it('executes bindings with a combo matching the pressed keys within the locale', function() {
|
||||
var pressHandler = sinon.stub();
|
||||
keyboard._listeners.push({
|
||||
keyCombo: new KeyCombo('a'),
|
||||
pressHandler: pressHandler,
|
||||
releaseHandler: null,
|
||||
preventRepeat: false
|
||||
});
|
||||
|
||||
keyboard.pressKey('a');
|
||||
|
||||
assert.ok(pressHandler.calledOnce);
|
||||
});
|
||||
|
||||
it('executes bindings without a combo', function() {
|
||||
var pressHandler = sinon.stub();
|
||||
keyboard._listeners.push({
|
||||
keyCombo: null,
|
||||
pressHandler: pressHandler,
|
||||
releaseHandler: null,
|
||||
preventRepeat: false
|
||||
});
|
||||
|
||||
keyboard.pressKey('a');
|
||||
keyboard.pressKey('b');
|
||||
|
||||
assert.ok(pressHandler.calledTwice);
|
||||
});
|
||||
|
||||
it('prevents combo overlap by marking off keys once they have been used by a combo', function() {
|
||||
var aPressHandler = sinon.stub();
|
||||
var aBPressHandler = sinon.stub();
|
||||
keyboard._listeners.push({
|
||||
keyCombo: new KeyCombo('a'),
|
||||
pressHandler: aPressHandler,
|
||||
releaseHandler: null,
|
||||
preventRepeat: false
|
||||
});
|
||||
keyboard._listeners.push({
|
||||
keyCombo: new KeyCombo('a + b'),
|
||||
pressHandler: aBPressHandler,
|
||||
releaseHandler: null,
|
||||
preventRepeat: false
|
||||
});
|
||||
|
||||
keyboard.pressKey('a'); // combo a fires
|
||||
keyboard.pressKey('b'); // combo a + b fires, but not combo a because the
|
||||
// a key has been consumed by combo a + b
|
||||
|
||||
assert.ok(aPressHandler.calledOnce);
|
||||
assert.ok(aBPressHandler.calledOnce);
|
||||
});
|
||||
|
||||
it('allows any number of identical bindings to fire without inhibiting each other', function() {
|
||||
var a1PressHandler = sinon.stub();
|
||||
var a2PressHandler = sinon.stub();
|
||||
keyboard._listeners.push({
|
||||
keyCombo: new KeyCombo('a'),
|
||||
pressHandler: a1PressHandler,
|
||||
releaseHandler: null,
|
||||
preventRepeat: false
|
||||
});
|
||||
keyboard._listeners.push({
|
||||
keyCombo: new KeyCombo('a'),
|
||||
pressHandler: a2PressHandler,
|
||||
releaseHandler: null,
|
||||
preventRepeat: false
|
||||
});
|
||||
|
||||
keyboard.pressKey('a'); // combo a1 and a2 fires
|
||||
|
||||
assert.ok(a1PressHandler.calledOnce);
|
||||
assert.ok(a2PressHandler.calledOnce);
|
||||
});
|
||||
|
||||
it('does nothing when paused', function() {
|
||||
sinon.stub(locale, 'pressKey');
|
||||
keyboard._paused = true;
|
||||
|
||||
keyboard.pressKey('a');
|
||||
|
||||
assert.equal(locale.pressKey.called, false);
|
||||
assert.equal(locale.pressedKeys.length, 0);
|
||||
|
||||
locale.pressKey.restore();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#releaseKey', function() {
|
||||
|
||||
var locale;
|
||||
beforeEach(function() {
|
||||
locale = new Locale('test');
|
||||
locale.bindKeyCode(0, 'a');
|
||||
keyboard._locale = locale;
|
||||
});
|
||||
|
||||
it('calls releaseKey on the locale', function() {
|
||||
sinon.stub(locale, 'releaseKey');
|
||||
|
||||
keyboard.releaseKey('a');
|
||||
|
||||
assert.equal(locale.releaseKey.lastCall.args[0], 'a');
|
||||
|
||||
locale.releaseKey.restore();
|
||||
});
|
||||
|
||||
it('will not execute a binding\'s releaseHandler unless it was triggered first by a press', function() {
|
||||
var releaseHandler = sinon.stub();
|
||||
keyboard.pressKey('a');
|
||||
keyboard._listeners.push({
|
||||
keyCombo: new KeyCombo('a'),
|
||||
pressHandler: null,
|
||||
releaseHandler: releaseHandler,
|
||||
preventRepeat: false
|
||||
});
|
||||
|
||||
keyboard.releaseKey('a');
|
||||
|
||||
assert.equal(releaseHandler.calledOnce, false);
|
||||
});
|
||||
|
||||
it('executes the releaseHandler of active bindings that no longer match the pressed keys', function() {
|
||||
var releaseHandler = sinon.stub();
|
||||
keyboard._listeners.push({
|
||||
keyCombo: new KeyCombo('a'),
|
||||
pressHandler: null,
|
||||
releaseHandler: releaseHandler,
|
||||
preventRepeat: false
|
||||
});
|
||||
keyboard.pressKey('a');
|
||||
|
||||
keyboard.releaseKey('a');
|
||||
|
||||
assert.ok(releaseHandler.calledOnce);
|
||||
});
|
||||
|
||||
it('executes the releaseHandler without a combo', function() {
|
||||
var releaseHandler = sinon.stub();
|
||||
keyboard._listeners.push({
|
||||
keyCombo: null,
|
||||
pressHandler: null,
|
||||
releaseHandler: releaseHandler,
|
||||
preventRepeat: false
|
||||
});
|
||||
keyboard.pressKey('a');
|
||||
keyboard.pressKey('b');
|
||||
|
||||
keyboard.releaseKey('a');
|
||||
keyboard.releaseKey('b');
|
||||
|
||||
assert.ok(releaseHandler.calledTwice);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#releaseAllKeys', function() {
|
||||
|
||||
var locale;
|
||||
beforeEach(function() {
|
||||
locale = new Locale('test');
|
||||
locale.bindKeyCode(0, 'a');
|
||||
keyboard._locale = locale;
|
||||
});
|
||||
|
||||
it('clears pressedKeys on the locale', function() {
|
||||
keyboard.releaseAllKeys();
|
||||
|
||||
assert.equal(locale.pressedKeys.length, 0);
|
||||
});
|
||||
|
||||
it('will not execute a binding\'s releaseHandler unless it was triggered first by a press', function() {
|
||||
var releaseHandler = sinon.stub();
|
||||
keyboard.pressKey('a');
|
||||
keyboard._listeners.push({
|
||||
keyCombo: new KeyCombo('a'),
|
||||
pressHandler: null,
|
||||
releaseHandler: releaseHandler,
|
||||
preventRepeat: false
|
||||
});
|
||||
|
||||
keyboard.releaseAllKeys();
|
||||
|
||||
assert.equal(releaseHandler.calledOnce, false);
|
||||
});
|
||||
|
||||
it('executes the releaseHandler of active bindings that no longer match the pressed keys', function() {
|
||||
var releaseHandler = sinon.stub();
|
||||
keyboard._listeners.push({
|
||||
keyCombo: new KeyCombo('a'),
|
||||
pressHandler: null,
|
||||
releaseHandler: releaseHandler,
|
||||
preventRepeat: false
|
||||
});
|
||||
keyboard.pressKey('a');
|
||||
|
||||
keyboard.releaseAllKeys();
|
||||
|
||||
assert.ok(releaseHandler.calledOnce);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#pause', function() {
|
||||
|
||||
it('pauses the instance', function() {
|
||||
keyboard.pause();
|
||||
|
||||
assert.ok(keyboard._paused);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#resume', function() {
|
||||
|
||||
it('resumes the instance', function() {
|
||||
keyboard.resume();
|
||||
|
||||
assert.equal(keyboard._paused, false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#reset', function() {
|
||||
|
||||
it('calls releaseAllKeys', function() {
|
||||
sinon.stub(keyboard, 'releaseAllKeys');
|
||||
|
||||
keyboard.reset();
|
||||
|
||||
assert.ok(keyboard.releaseAllKeys.calledOnce);
|
||||
|
||||
keyboard.releaseAllKeys.reset();
|
||||
});
|
||||
|
||||
it('clears all listeners', function() {
|
||||
sinon.stub(keyboard, 'releaseAllKeys');
|
||||
keyboard._listeners = [1];
|
||||
|
||||
keyboard.reset();
|
||||
|
||||
assert.equal(keyboard._listeners.length, 0);
|
||||
|
||||
keyboard.releaseAllKeys.reset();
|
||||
});
|
||||
});
|
||||
});
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
|
||||
var assert = require('assert');
|
||||
var Locale = require('../lib/locale');
|
||||
var KeyCombo = require('../lib/key-combo');
|
||||
|
||||
|
||||
describe('Locale', function() {
|
||||
|
||||
var locale;
|
||||
beforeEach(function() {
|
||||
locale = new Locale('test');
|
||||
});
|
||||
|
||||
|
||||
describe('#bindKeyCode', function() {
|
||||
|
||||
it('binds a key code to a key name', function() {
|
||||
locale.bindKeyCode(0, 'a');
|
||||
|
||||
assert.equal(locale._keyMap[0][0], 'a');
|
||||
});
|
||||
|
||||
it('binds a key code to a set of key names', function() {
|
||||
locale.bindKeyCode(0, ['a', 'b', 'c']);
|
||||
|
||||
assert.equal(locale._keyMap[0][0], 'a');
|
||||
assert.equal(locale._keyMap[0][1], 'b');
|
||||
assert.equal(locale._keyMap[0][2], 'c');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#bindMacro', function() {
|
||||
|
||||
it('binds a key combo to a key name', function() {
|
||||
locale.bindMacro('a', 'b');
|
||||
|
||||
assert.equal(locale._macros[0].keyCombo.sourceStr, 'a');
|
||||
assert.equal(locale._macros[0].keyNames[0], 'b');
|
||||
});
|
||||
|
||||
it('binds a key combo to a set of key names', function() {
|
||||
locale.bindMacro('a', ['b', 'c', 'd']);
|
||||
|
||||
assert.equal(locale._macros[0].keyCombo.sourceStr, 'a');
|
||||
assert.equal(locale._macros[0].keyNames[0], 'b');
|
||||
assert.equal(locale._macros[0].keyNames[1], 'c');
|
||||
assert.equal(locale._macros[0].keyNames[2], 'd');
|
||||
});
|
||||
|
||||
it('binds a key combo to a macro handler', function() {
|
||||
var macroHandler = function() {};
|
||||
locale.bindMacro('a', macroHandler);
|
||||
|
||||
assert.equal(locale._macros[0].keyCombo.sourceStr, 'a');
|
||||
assert.equal(locale._macros[0].handler, macroHandler);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#getKeyCodes', function() {
|
||||
|
||||
it('gets all key codes associated with a key name', function() {
|
||||
locale._keyMap[0] = ['a'];
|
||||
locale._keyMap[1] = ['b'];
|
||||
locale._keyMap[2] = ['a'];
|
||||
|
||||
var keyCodes = locale.getKeyCodes('a');
|
||||
|
||||
assert.equal(keyCodes[0], 0);
|
||||
assert.equal(keyCodes[1], 2);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#getKeyNames', function() {
|
||||
|
||||
it('gets all key names associated with a key code', function() {
|
||||
locale._keyMap[0] = ['a', 'b'];
|
||||
|
||||
var keyNames = locale.getKeyNames(0);
|
||||
|
||||
assert.equal(keyNames[0], 'a');
|
||||
assert.equal(keyNames[1], 'b');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#setKillKey', function() {
|
||||
|
||||
it('marks a key code as a kill key', function() {
|
||||
locale.setKillKey(0);
|
||||
|
||||
assert.equal(locale._killKeyCodes[0], 0);
|
||||
});
|
||||
|
||||
it('marks all key codes matching a key name as a kill key', function() {
|
||||
locale._keyMap[0] = ['a'];
|
||||
locale._keyMap[1] = ['a'];
|
||||
|
||||
locale.setKillKey('a');
|
||||
|
||||
assert.equal(locale._killKeyCodes[0], 0);
|
||||
assert.equal(locale._killKeyCodes[1], 1);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#pressKey', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
locale._keyMap = {
|
||||
0: ['a', 'b'],
|
||||
1: ['a', 'c'],
|
||||
};
|
||||
});
|
||||
|
||||
it('adds all key names associated with a given key code to pressedKeys', function() {
|
||||
locale.pressKey(0);
|
||||
|
||||
assert.equal(locale.pressedKeys[0], 'a');
|
||||
assert.equal(locale.pressedKeys[1], 'b');
|
||||
});
|
||||
|
||||
it('adds all other key names associated with the same key code of a given key name to pressedKeys', function() {
|
||||
locale.pressKey('a');
|
||||
|
||||
assert.equal(locale.pressedKeys[0], 'a');
|
||||
assert.equal(locale.pressedKeys[1], 'b');
|
||||
assert.equal(locale.pressedKeys[2], 'c');
|
||||
});
|
||||
|
||||
it('applies macros with combos that match the pressed keys', function() {
|
||||
locale._keyMap = {
|
||||
0: ['a'],
|
||||
1: ['b']
|
||||
};
|
||||
locale._macros = [
|
||||
{
|
||||
keyCombo : new KeyCombo('a + b'),
|
||||
keyNames : ['c', 'd'],
|
||||
handler : null
|
||||
}, {
|
||||
keyCombo : new KeyCombo('a + b'),
|
||||
handler : function(pressedKeys) {
|
||||
|
||||
assert.equal(pressedKeys, locale.pressedKeys);
|
||||
|
||||
return ['e', 'f'];
|
||||
},
|
||||
keyNames : null
|
||||
}
|
||||
];
|
||||
|
||||
locale.pressKey('a');
|
||||
locale.pressKey('b');
|
||||
|
||||
assert.equal(locale.pressedKeys[0], 'a');
|
||||
assert.equal(locale.pressedKeys[1], 'b');
|
||||
assert.equal(locale.pressedKeys[2], 'c');
|
||||
assert.equal(locale.pressedKeys[3], 'd');
|
||||
assert.equal(locale.pressedKeys[4], 'e');
|
||||
assert.equal(locale.pressedKeys[5], 'f');
|
||||
assert.equal(locale._appliedMacros[0], locale._macros[0]);
|
||||
assert.equal(locale._appliedMacros[1], locale._macros[1]);
|
||||
assert.equal(locale._appliedMacros[1].keyNames[0], 'e');
|
||||
assert.equal(locale._appliedMacros[1].keyNames[1], 'f');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#releaseKey', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
locale._keyMap = {
|
||||
0: ['a', 'b'],
|
||||
1: ['a', 'c'],
|
||||
};
|
||||
locale.pressedKeys = ['a', 'b', 'c'];
|
||||
});
|
||||
|
||||
it('removes all key names associated with a given key code from pressedKeys', function() {
|
||||
locale.releaseKey(0);
|
||||
|
||||
assert.equal(locale.pressedKeys[0], 'c');
|
||||
});
|
||||
|
||||
it('removes all other key names associated with the same key code of a given key name from pressedKeys', function() {
|
||||
locale.releaseKey('a');
|
||||
|
||||
assert.equal(locale.pressedKeys.length, 0);
|
||||
});
|
||||
|
||||
it('clears applied macros with combos that no longer match the pressed keys', function() {
|
||||
locale._keyMap = {
|
||||
0: ['a'],
|
||||
1: ['b'],
|
||||
};
|
||||
locale._macros = [
|
||||
{
|
||||
keyCombo : new KeyCombo('a + b'),
|
||||
keyNames : ['c', 'd'],
|
||||
handler : null
|
||||
}, {
|
||||
keyCombo : new KeyCombo('a + b'),
|
||||
keyNames : ['e', 'f'],
|
||||
handler : function(pressedKeys) {
|
||||
|
||||
assert.equal(pressedKeys, locale.pressedKeys);
|
||||
|
||||
return ['e', 'f'];
|
||||
}
|
||||
}
|
||||
];
|
||||
locale.pressedKeys = ['a', 'b', 'c', 'd', 'e', 'f'];
|
||||
locale._appliedMacros = locale._macros.slice(0);
|
||||
|
||||
locale.releaseKey('a');
|
||||
|
||||
assert.equal(locale.pressedKeys[0], 'b');
|
||||
assert.equal(locale.pressedKeys.length, 1);
|
||||
assert.equal(locale._appliedMacros.length, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user