added pages and keyboard navigation

This commit is contained in:
Bachir Soussi Chiadmi
2016-11-20 11:43:52 +01:00
parent 5a3856e75b
commit 6850fa4d50
191 changed files with 34413 additions and 0 deletions
+15
View File
@@ -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
}
+6
View File
@@ -0,0 +1,6 @@
# Directories
node_modules
# Editors
.c9
+6
View File
@@ -0,0 +1,6 @@
# Directories
test
# Editors
.c9
+8
View File
@@ -0,0 +1,8 @@
{
"loadEagerly": [
"index.js"
],
"plugins": {
"node": true
}
}
+3
View File
@@ -0,0 +1,3 @@
language: node_js
node_js:
- "stable"
+21
View File
@@ -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
View File
@@ -0,0 +1,186 @@
KeyboardJS
==========
[ ![Build Status](https://travis-ci.org/RobertWHurst/KeyboardJS.svg?branch=master) ](https://travis-ci.org/RobertWHurst/KeyboardJS)
[ ![NPM Version](http://img.shields.io/npm/v/keyboardjs.svg?style=flat) ](https://www.npmjs.org/package/keyboardjs)
[ ![Downloads This Week](http://img.shields.io/npm/dm/keyboardjs.svg?style=flat) ](https://www.npmjs.org/package/keyboardjs)
[ ![License](http://img.shields.io/npm/l/keyboardjs.svg?style=flat) ](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();
```
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+13
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"
}
}
+8
View File
@@ -0,0 +1,8 @@
var sinon = require('sinon');
module.exports = {
addEventListener: sinon.stub(),
removeEventListener: sinon.stub(),
};
+12
View File
@@ -0,0 +1,12 @@
var sinon = require('sinon');
module.exports = {
addEventListener : sinon.stub(),
removeEventListener : sinon.stub(),
navigator: {
platform : 'test-platform',
userAgent : 'test-user-agent'
}
};
+76
View File
@@ -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
View File
@@ -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
View File
@@ -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);
});
});
});