Skip to content

Commit

Permalink
Merge ef13bd1 into 201eb3a
Browse files Browse the repository at this point in the history
  • Loading branch information
rodneyrehm committed Dec 5, 2015
2 parents 201eb3a + ef13bd1 commit 22fdba9
Show file tree
Hide file tree
Showing 9 changed files with 627 additions and 49 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -26,6 +26,10 @@ The following lists show the changes to the library grouped by domain.
* changing [`ally.is.focusRelevant`][ally/is/focus-relevant] to properly handle `<object type="application/x-shockwave-flash">` in IE9 - [Issue #71](https://github.com/medialize/ally.js/issues/71)
* fixing [`ally.query.tabsequence`][ally/query/tabsequence] to return `<area>` elements at the correct position [issue #5](https://github.com/medialize/ally.js/issues/5)

#### Keyboard support

* changing [`ally.when.key`][ally/when/key] to handle modifier keys and respect `context` and `filter` options - [issue #59](https://github.com/medialize/ally.js/issues/59)
* changing [`ally.map.keycode`][ally/map/keycode] to provide alphanumeric keys and aliasing

#### Internals

Expand Down
2 changes: 2 additions & 0 deletions build/metalsmith/assets/website.css
Expand Up @@ -340,6 +340,8 @@ main .example h1 {
main table {
border: 1px solid #E5E5E5;
border-collapse: collapse;
width: 100%;
word-break: normal;
}
main th,
main td {
Expand Down
47 changes: 45 additions & 2 deletions docs/api/map/keycode.md
Expand Up @@ -5,7 +5,7 @@ tags: data

# ally.map.keycode

Map of control [`event.keyCode`](https://developer.mozilla.org/en-US/docs/Web/API/event.keyCode)s.
Map human readable names for [`event.keyCode`](https://developer.mozilla.org/en-US/docs/Web/API/event.keyCode)s.


## Description
Expand All @@ -26,19 +26,62 @@ var map = {
enter: 13,
escape: 27,
space: 32,

// Function keys
f1: 112,
// ...

// Numeric keys
0: 48,
1: 49,
// ...
"num-0": 96,
"num-1": 97,
// ...

// Latin characters
a: 65,
// ...
z: 90,
}
```

The map knows the following keys:

* `a` - `z`
* `0` - `9`
* `num-0` - `num-9` (number block)
* `f1` - `f25` (function keys)
* `down`, `left`, `right`, `up` (arrow keys)
* `end`, `home`, `pageDown`, `page-down`, `pageUp`, `page-up`
* `enter`, `escape`, `space`, `tab`
* `alt`, `capsLock`, `caps-lock`, `ctrl`, `meta`, `shift`
* `pause`, `insert`, `delete`, `backspace`


## Usage

```js
console.log(ally.map.keycode.enter);
console.log("keycode of enter", ally.map.keycode.enter);
```


## Changes

* `v#master` replaced the key `command` by `meta`.
* `v#master` added `a` - `z`, `0` - `9`, `num-0` - `num-9`, `f13` - `f25`, `page-down`, `page-up`, `caps-lock`.
* `v#master` added `_alias` to resolve multiple keyCodes for the same logical key.


## Notes

* **NOTE:** The key `meta` is known by different keyCodes: `91`, `92`, `93`, `224` - which `ally.map.keycodes.alias.91` helps to resolve. The same is true for numeric keys (0-9) and their counterparts on the numblock.


## Related resources

* [`KeyboardEvent.key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) may help deal with this mess in the future.


## Contributing

Expand Down
71 changes: 63 additions & 8 deletions docs/api/when/key.md
Expand Up @@ -10,7 +10,49 @@ Executes a callback when a given key has been pressed.

## Description

This is a convenience API to avoid adding and removing keyboard event handlers and having to filter for specific keys in application code. Callbacks are executed on `keydown`.
This is a convenience API to avoid adding and removing keyboard event handlers and having to filter for specific keys in application code. Callbacks are executed synchronously while handling [`keydown`](https://developer.mozilla.org/en-US/docs/Web/Events/keydown) events to maintain the ability to [`event.preventDefault()`](https://developer.mozilla.org/en/docs/Web/API/Event/preventDefault).

Keyboard events are dispatched to the currently focused element (`document.activeElement`). This allows us to handle keyboard events only when the user is engaged in a particular widget.

### Key binding syntax

In order to easily register keyboard events including modifier keys, `ally.when.key` understands the following `<key-binding>` syntax:

| `<key-binding>` | primary key | keyCode | alt | ctrl | meta | shift |
|-----------------|-------------|---------|-----|------|-------|------|
| `space` | <kbd>Space</kbd> | 32 | no | no | no | no |
| `*+space` | <kbd>Space</kbd> | 32 | ? | ? | ? | ? |
| `shift+space` | <kbd>Space</kbd> | 32 | no | no | no | yes |
| `shift+*+enter` | <kbd>Enter</kbd> | 13 | ? | ? | ? | yes |
| `!shift+*+enter` | <kbd>Enter</kbd> | 13 | ? | ? | ? | no |
| `?shift+ctrl+enter` | <kbd>Enter</kbd> | 13 | no | yes | no | ? |
| `enter shift+8` | <kbd>Enter</kbd> | 13 | no | no | no | no |
| *(continued)* | <kbd>Backspace</kbd> | 8 | no | no | no | yes |

Legend: `no` means the modifier key is not pressed, `yes` means the modifier key is pressed, `?` means the state of the modifier key is ignored.

The `<key-binding>` syntax is defined by the following [EBNF](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form) grammar, with `property of map/keycode` referring to the property names of [`ally.map.keycode`](../map/keycode.md):

```ebnf
token-list = token, { " ", token } ;
token = { modifier, "+" }, key ;
key = named-key | keycode ;
named-key = ? property of ally.map.keycode ? ;
keycode = ? /[0-9]+/ ? ;
modifier = ( [ "!" | "?" ], modifier-name ) | "*" ;
modifier-name = "alt" | "ctrl" | "meta" | "shift" ;
```

### Modifier keys

The modifier keys may have different names/symbols depending on operating system and physical keyboard:

| modifier key name | physical key on keyboard |
|-------------------|--------------------------|
| alt | <kbd>Alt</kbd>, <kbd>Option</kbd> or<kbd> ⌥</kbd> on Mac) |
| ctrl | <kbd>Ctrl</kbd>, <kbd>⌃</kbd> on Mac |
| meta | <kbd>Meta</kbd>, <kbd> ⌘</kbd> or <kbd></kbd> or <kbd>Command</kbd> on Mac, <kbd>⊞</kbd> or <kbd>Windows</kbd> on Windows |
| shift | <kbd>Shift</kbd>, <kbd>⇧</kbd> on Mac |


## Usage
Expand All @@ -33,19 +75,23 @@ handle.disengage();

### Arguments

The method accepts an object of `<key>: <callback>` mappings. See [Callback Signature](#Callback-Signature) for details

`<key>`s can be specified as a numeric code ([`keyCode`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode)) e.g. `13` for the <kbd>Enter</kbd> key, or as the string name `enter` which is resolved to the numeric code internally using [`ally.map.keycode`](../map/keycode.md). Multiple `<key>: <callback>` combinations can be passed.
| Name | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| context | [`<selector>`](../concepts.md#Selector) | [`documentElement`](https://developer.mozilla.org/en-US/docs/Web/API/Document/documentElement) | The scope of the DOM in which keyboard events will be processed. The first element of a collection is used. |
| filter | [`<selector>`](../concepts.md#Selector) | `null` | The elements and descendants to exclude when processing keyboard events. |
| [`<key-binding>`](#Key-binding-syntax) | function | *required* | Mapping of `<key-binding>` to callback function. See [Callback Signature](#Callback-Signature) for details. This argument is expected one or more times. |

### Returns

A [`<service>`](../concepts.md#Service) interface, providing the `handle.disengage()` method to stop the service.

### Throws

* [`TypeError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError) if `<ke>` is neither numeric, nor found in [`ally.map.keycode`](../map/keycode.md).
* [`TypeError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError) if no `<key-binding>: <callback>` combinations were passed.
* [`TypeError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError) if `<key-binding>` does not resolve to a keyCode.
* [`TypeError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError) if `<key-binding>` contains illegal modifier names.
* [`TypeError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError) if `<callback>` is not a `function`.
* [`TypeError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError) when no `<key>: <callback>` combinations were passed.


## Callback signature

Expand All @@ -54,27 +100,36 @@ A [`<service>`](../concepts.md#Service) interface, providing the `handle.disenga
| event | [`KeyboardEvent`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent) | *required* | The original `keydown` event. |
| disengage | function | *required* | The service's `handle.disengage()` method. |

The callback is executed in the context of `document.documentElement` (that's where `this` inside the callback points to). The callback is passed the `handle.disengage()` method to allow simplified "execute once" behaviors. The callback's return value is ignored.
The callback is executed in the context of `context` (that's where `this` inside the callback points to). The callback is passed the `handle.disengage()` method to allow simplified "execute once" behaviors. The callback's return value is ignored.


## Examples

* **EXAMPLE:** [`ally.when.key` Example](./key.example.html)


## Changes

* `v#master` introduced the extended `<key-binding>` syntax (thereby changing the default modifier key behavior).
* `v#master` introduced the options `context` and `filter`.


## Notes

* **NOTE:** Firefox has a long standing issue with keyboard events propagating to the document while browser UI like autocomplete is being interacted with [Gecko 286933](https://bugzilla.mozilla.org/show_bug.cgi?id=286933).
* **WARNING:** The callback for the `<key-binding>` `space` only executes if *no* modifier key was pressed. In order to make the callback execute regardless of modifier keys, use the `<key-binding>` `*+space`.
* **NOTE:** The modifiers `alt`, `ctrl`, `meta` usually engage system-level or browser-level functionality. Do not use these modifiers lightly. For example `alt+a` prints the letter `å` on a Mac with German keyboard layout, `meta+q` exits the application and `meta+space` engages Spotlight.


## Related resources

* [`ally.map.keycode`](../map/keycode.md) used for resolving named keys
* The `<key-binding>` syntax is inspired by [PolymerElements/iron-a11y-keys](https://github.com/PolymerElements/iron-a11y-keys#grammar)


## Contributing

* [module source](https://github.com/medialize/ally.js/blob/master/src/when/key.js)
* [document source](https://github.com/medialize/ally.js/blob/master/docs/api/when/key.md)
* [unit test](https://github.com/medialize/ally.js/blob/master/test/unit/when.key.test.js)

* uses [when/key.binding](https://github.com/medialize/ally.js/blob/master/src/when/key.binding.js) to parse `<key-binding>`
58 changes: 42 additions & 16 deletions src/map/keycode.js
@@ -1,7 +1,11 @@

// https://github.com/keithamus/jwerty/blob/master/jwerty.js
// codes mostly cloned from https://github.com/keithamus/jwerty/blob/master/jwerty.js
// deliberately not exposing characters like <,.-#* because they vary *wildly*
// across keyboard layouts and may cause various problems
// (e.g. "*" is "Shift +" on a German Mac keyboard)
// (e.g. "@" is "Alt L" on a German Mac keyboard)

export default {
const keycode = {
// Element Focus
tab: 9,

Expand All @@ -11,7 +15,9 @@ export default {
right: 39,
down: 40,
pageUp: 33,
'page-up': 33,
pageDown: 34,
'page-down': 34,
end: 35,
home: 36,

Expand All @@ -23,27 +29,47 @@ export default {
// Modifier
shift: 16,
capsLock: 20,
'caps-lock': 20,
ctrl: 17,
alt: 18,
command: 224,
meta: 91,
// in firefox: 224
// on mac (chrome): meta-left=91, meta-right=93
// on win (IE11): meta-left=91, meta-right=92
pause: 19,

// Content Manipulation
insert: 45,
'delete': 46,
backspace: 8,

// Special Function
f1: 112,
f2: 113,
f3: 114,
f4: 115,
f5: 116,
f6: 117,
f7: 118,
f8: 119,
f9: 120,
f10: 121,
f11: 122,
f12: 123,
// the same logical key may be identified through different keyCodes
_alias: {
91: [92, 93, 224],
},
};

// Function keys (112 - 137)
// NOTE: not every keyboard knows F13+
for (let n = 1; n < 26; n++) {
keycode['f' + n] = n + 111;
}

// Number keys (48-57, numpad 96-105)
// NOTE: not every keyboard knows num-0+
for (let n = 0; n < 10; n++) {
const code = n + 48;
const numCode = n + 96;
keycode[n] = code;
keycode['num-' + n] = numCode;
keycode._alias[code] = [numCode];
}

// Latin characters (65 - 90)
for (let n = 0; n < 26; n++) {
const code = n + 65;
const name = String.fromCharCode(code).toLowerCase();
keycode[name] = code;
}

export default keycode;

0 comments on commit 22fdba9

Please sign in to comment.