├── app
├── .gitkeep
├── views
│ └── application.js
├── instance-initializers
│ └── key-responder.js
├── initializers
│ └── key-responder.js
└── key-responder.js
├── addon
└── .gitkeep
├── vendor
└── .gitkeep
├── tests
├── unit
│ └── .gitkeep
├── dummy
│ ├── public
│ │ ├── .gitkeep
│ │ ├── robots.txt
│ │ └── crossdomain.xml
│ ├── app
│ │ ├── helpers
│ │ │ └── .gitkeep
│ │ ├── models
│ │ │ └── .gitkeep
│ │ ├── routes
│ │ │ └── .gitkeep
│ │ ├── styles
│ │ │ ├── .gitkeep
│ │ │ └── app.css
│ │ ├── views
│ │ │ └── .gitkeep
│ │ ├── components
│ │ │ ├── .gitkeep
│ │ │ └── component-example.js
│ │ ├── controllers
│ │ │ ├── .gitkeep
│ │ │ └── application.js
│ │ ├── templates
│ │ │ ├── .gitkeep
│ │ │ ├── components
│ │ │ │ ├── .gitkeep
│ │ │ │ ├── component-example.hbs
│ │ │ │ └── key-responder-info.hbs
│ │ │ └── application.hbs
│ │ ├── router.js
│ │ ├── app.js
│ │ └── index.html
│ ├── .jshintrc
│ └── config
│ │ └── environment.js
├── test-helper.js
├── helpers
│ ├── resolver.js
│ └── start-app.js
├── acceptance
│ └── key-responder-test.js
├── .jshintrc
├── index.html
└── integration
│ └── demo-test.js
├── .watchmanconfig
├── .bowerrc
├── index.js
├── config
├── environment.js
└── ember-try.js
├── CHANGELOG.md
├── .npmignore
├── testem.json
├── .ember-cli
├── .gitignore
├── bower.json
├── ember-cli-build.js
├── .jshintrc
├── .editorconfig
├── .travis.yml
├── scripts
└── commit-version.js
├── LICENSE.md
├── package.json
└── README.md
/app/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/addon/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/public/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/helpers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/models/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/routes/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/styles/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/views/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/components/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {
2 | "ignore_dirs": ["tmp"]
3 | }
4 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components",
3 | "analytics": false
4 | }
5 |
--------------------------------------------------------------------------------
/tests/dummy/public/robots.txt:
--------------------------------------------------------------------------------
1 | # http://www.robotstxt.org
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/components/component-example.hbs:
--------------------------------------------------------------------------------
1 | Component {{name}}
2 | {{yield}}
3 |
--------------------------------------------------------------------------------
/app/views/application.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.View.extend({});
4 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 | 'use strict';
3 |
4 | module.exports = {
5 | name: 'ember-key-responder'
6 | };
7 |
--------------------------------------------------------------------------------
/config/environment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(/* environment, appConfig */) {
4 | return { };
5 | };
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 0.4.0
4 |
5 | Compatibility with multiple versions of Ember (1.10 - 2.0-beta). Thanks @truenorth!
6 |
--------------------------------------------------------------------------------
/tests/test-helper.js:
--------------------------------------------------------------------------------
1 | import resolver from './helpers/resolver';
2 | import {
3 | setResolver
4 | } from 'ember-qunit';
5 |
6 | setResolver(resolver);
7 |
--------------------------------------------------------------------------------
/tests/dummy/app/styles/app.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | margin: 20px;
3 | }
4 |
5 | .component {
6 | margin: 1em;
7 | padding: 1em;
8 | border: 1px solid steelblue;
9 | }
10 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/application.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Controller.extend({
4 | init: function() {
5 | this.set('events', Ember.A());
6 | }
7 | });
8 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | tests/
3 | tmp/
4 | dist/
5 |
6 | .bowerrc
7 | .editorconfig
8 | .ember-cli
9 | .travis.yml
10 | .npmignore
11 | **/.gitkeep
12 | bower.json
13 | Brocfile.js
14 | testem.json
15 |
--------------------------------------------------------------------------------
/testem.json:
--------------------------------------------------------------------------------
1 | {
2 | "framework": "qunit",
3 | "test_page": "tests/index.html?hidepassed",
4 | "disable_watching": true,
5 | "launch_in_ci": [
6 | "PhantomJS"
7 | ],
8 | "launch_in_dev": [
9 | "PhantomJS",
10 | "Chrome"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/tests/dummy/app/router.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import config from './config/environment';
3 |
4 | var Router = Ember.Router.extend({
5 | location: config.locationType
6 | });
7 |
8 | Router.map(function() {
9 | });
10 |
11 | export default Router;
12 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/components/key-responder-info.hbs:
--------------------------------------------------------------------------------
1 | KeyResponder stack active:
2 | {{keyResponder.isActive}}
3 | {{input type="checkbox" checked=keyResponder.isActive}}
4 |
5 | {{#each keyResponder.stack as |resp|}}
6 | - {{resp.name}} - {{resp}}
7 | {{/each}}
8 |
9 |
--------------------------------------------------------------------------------
/.ember-cli:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | Ember CLI sends analytics information by default. The data is completely
4 | anonymous, but there are times when you might want to disable this behavior.
5 |
6 | Setting `disableAnalytics` to true will prevent any data from being sent.
7 | */
8 | "disableAnalytics": false
9 | }
10 |
--------------------------------------------------------------------------------
/tests/helpers/resolver.js:
--------------------------------------------------------------------------------
1 | import Resolver from 'ember/resolver';
2 | import config from '../../config/environment';
3 |
4 | var resolver = Resolver.create();
5 |
6 | resolver.namespace = {
7 | modulePrefix: config.modulePrefix,
8 | podModulePrefix: config.podModulePrefix
9 | };
10 |
11 | export default resolver;
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 |
7 | # dependencies
8 | /node_modules
9 | /bower_components
10 |
11 | # misc
12 | /.sass-cache
13 | /connect.lock
14 | /coverage/*
15 | /libpeerconnection.log
16 | npm-debug.log
17 | testem.log
18 |
--------------------------------------------------------------------------------
/tests/dummy/app/app.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import Resolver from 'ember/resolver';
3 | import loadInitializers from 'ember/load-initializers';
4 | import config from './config/environment';
5 |
6 | Ember.MODEL_FACTORY_INJECTIONS = true;
7 |
8 | var App = Ember.Application.extend({
9 | modulePrefix: config.modulePrefix,
10 | podModulePrefix: config.podModulePrefix,
11 | Resolver: Resolver
12 | });
13 |
14 | loadInitializers(App, config.modulePrefix);
15 |
16 | export default App;
17 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ember-key-responder",
3 | "dependencies": {
4 | "ember": "1.10.0",
5 | "ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3",
6 | "ember-cli-test-loader": "ember-cli-test-loader#0.1.3",
7 | "ember-load-initializers": "ember-cli/ember-load-initializers#0.1.7",
8 | "ember-qunit": "0.4.9",
9 | "ember-qunit-notifications": "0.0.7",
10 | "ember-resolver": "~0.1.20",
11 | "jquery": "^1.11.3",
12 | "loader.js": "ember-cli/loader.js#3.2.1",
13 | "qunit": "~1.18.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ember-cli-build.js:
--------------------------------------------------------------------------------
1 | /* global require, module */
2 | var EmberApp = require('ember-cli/lib/broccoli/ember-addon');
3 |
4 | module.exports = function(defaults) {
5 | var app = new EmberApp(defaults, {
6 | // Add options here
7 | });
8 |
9 | /*
10 | This build file specifes the options for the dummy test app of this
11 | addon, located in `/tests/dummy`
12 | This build file does *not* influence how the addon or the app using it
13 | behave. You most likely want to be modifying `./index.js` or app's build file
14 | */
15 |
16 | return app.toTree();
17 | };
18 |
--------------------------------------------------------------------------------
/tests/helpers/start-app.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import Application from '../../app';
3 | import config from '../../config/environment';
4 |
5 | export default function startApp(attrs) {
6 | var application;
7 |
8 | var attributes = Ember.merge({}, config.APP);
9 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
10 |
11 | Ember.run(function() {
12 | application = Application.create(attributes);
13 | application.setupForTesting();
14 | application.injectTestHelpers();
15 | });
16 |
17 | return application;
18 | }
19 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "-Promise"
6 | ],
7 | "browser": true,
8 | "boss": true,
9 | "curly": true,
10 | "debug": false,
11 | "devel": true,
12 | "eqeqeq": true,
13 | "evil": true,
14 | "forin": false,
15 | "immed": false,
16 | "laxbreak": false,
17 | "newcap": true,
18 | "noarg": true,
19 | "noempty": false,
20 | "nonew": false,
21 | "nomen": false,
22 | "onevar": false,
23 | "plusplus": false,
24 | "regexp": false,
25 | "undef": true,
26 | "sub": true,
27 | "strict": false,
28 | "white": false,
29 | "eqnull": true,
30 | "esnext": true,
31 | "unused": true
32 | }
33 |
--------------------------------------------------------------------------------
/tests/dummy/public/crossdomain.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
15 |
16 |
--------------------------------------------------------------------------------
/tests/dummy/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "-Promise"
6 | ],
7 | "browser" : true,
8 | "boss" : true,
9 | "curly": true,
10 | "debug": false,
11 | "devel": true,
12 | "eqeqeq": true,
13 | "evil": true,
14 | "forin": false,
15 | "immed": false,
16 | "laxbreak": false,
17 | "newcap": true,
18 | "noarg": true,
19 | "noempty": false,
20 | "nonew": false,
21 | "nomen": false,
22 | "onevar": false,
23 | "plusplus": false,
24 | "regexp": false,
25 | "undef": true,
26 | "sub": true,
27 | "strict": false,
28 | "white": false,
29 | "eqnull": true,
30 | "esnext": true,
31 | "unused": true
32 | }
33 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 | indent_style = space
14 | indent_size = 2
15 |
16 | [*.js]
17 | indent_style = space
18 | indent_size = 2
19 |
20 | [*.hbs]
21 | insert_final_newline = false
22 | indent_style = space
23 | indent_size = 2
24 |
25 | [*.css]
26 | indent_style = space
27 | indent_size = 2
28 |
29 | [*.html]
30 | indent_style = space
31 | indent_size = 2
32 |
33 | [*.{diff,md}]
34 | trim_trailing_whitespace = false
35 |
--------------------------------------------------------------------------------
/tests/dummy/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dummy
7 |
8 |
9 |
10 | {{content-for 'head'}}
11 |
12 |
13 |
14 |
15 | {{content-for 'head-footer'}}
16 |
17 |
18 | {{content-for 'body'}}
19 |
20 |
21 |
22 |
23 | {{content-for 'body-footer'}}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/tests/acceptance/key-responder-test.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import { module, test } from 'qunit';
3 | import startApp from '../../tests/helpers/start-app';
4 |
5 | module('Acceptance | key responder', {
6 | beforeEach: function() {
7 | this.application = startApp();
8 | },
9 |
10 | afterEach: function() {
11 | Ember.run(this.application, 'destroy');
12 | }
13 | });
14 |
15 | test('visiting /', function(assert) {
16 | visit('/');
17 |
18 | const textInputSelector = '#text-input input';
19 | const textInput = Ember.$(textInputSelector);
20 |
21 | andThen(function() {
22 | textInput.focusin();
23 | });
24 |
25 | fillIn(textInputSelector, 'taco');
26 |
27 | andThen(function() {
28 | assert.equal(currentURL(), '/');
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | language: node_js
3 |
4 | sudo: false
5 |
6 | cache:
7 | directories:
8 | - node_modules
9 |
10 | env:
11 | - EMBER_TRY_SCENARIO=ember-1.10
12 | - EMBER_TRY_SCENARIO=ember-1.11
13 | - EMBER_TRY_SCENARIO=ember-1.12
14 | - EMBER_TRY_SCENARIO=ember-1.13
15 | - EMBER_TRY_SCENARIO=ember-release
16 | - EMBER_TRY_SCENARIO=ember-beta
17 | - ALLOW_DEPRECATIONS=true EMBER_TRY_SCENARIO=ember-canary
18 |
19 | matrix:
20 | fast_finish: true
21 | allow_failures:
22 | - env: ALLOW_DEPRECATIONS=true EMBER_TRY_SCENARIO=ember-canary
23 |
24 | before_install:
25 | - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH
26 | - "npm config set spin false"
27 | - "npm install -g npm@^2"
28 |
29 | install:
30 | - npm install -g bower
31 | - npm install
32 | - bower install
33 |
34 | script:
35 | - ember try $EMBER_TRY_SCENARIO test
36 |
--------------------------------------------------------------------------------
/scripts/commit-version.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var RSVP = require('RSVP');
3 | var exec = RSVP.denodeify(require('child_process').exec);
4 | var version = JSON.parse(fs.readFileSync('package.json')).version;
5 |
6 | add('package.json')()
7 | .then(commit(version))
8 | .then(tag(version))
9 | .then(push('origin', 'master'))
10 | .then(function() { console.log('version: ' + version); })
11 | .catch(console.error.bind(console));
12 |
13 | function add(file) {
14 | return function() {
15 | return exec('git add -f ' + file);
16 | };
17 | }
18 |
19 | function commit(version) {
20 | return function() {
21 | return exec('git commit -m "Release: ' + version + '"');
22 | };
23 | }
24 |
25 | function tag(version) {
26 | return function() {
27 | return exec('git tag v' + version);
28 | };
29 | }
30 |
31 | function push(origin, branch) {
32 | return function() {
33 | return exec('git push --tags ' + origin + ' ' + branch);
34 | };
35 | }
36 |
--------------------------------------------------------------------------------
/tests/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "location",
6 | "setTimeout",
7 | "$",
8 | "-Promise",
9 | "define",
10 | "console",
11 | "visit",
12 | "exists",
13 | "fillIn",
14 | "click",
15 | "keyEvent",
16 | "triggerEvent",
17 | "find",
18 | "findWithAssert",
19 | "wait",
20 | "DS",
21 | "andThen",
22 | "currentURL",
23 | "currentPath",
24 | "currentRouteName"
25 | ],
26 | "node": false,
27 | "browser": false,
28 | "boss": true,
29 | "curly": true,
30 | "debug": false,
31 | "devel": false,
32 | "eqeqeq": true,
33 | "evil": true,
34 | "forin": false,
35 | "immed": false,
36 | "laxbreak": false,
37 | "newcap": true,
38 | "noarg": true,
39 | "noempty": false,
40 | "nonew": false,
41 | "nomen": false,
42 | "onevar": false,
43 | "plusplus": false,
44 | "regexp": false,
45 | "undef": true,
46 | "sub": true,
47 | "strict": false,
48 | "white": false,
49 | "eqnull": true,
50 | "esnext": true,
51 | "unused": true
52 | }
53 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015
4 |
5 | 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:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | 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.
10 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/component-example.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Component.extend({
4 | acceptsKeyResponder: true,
5 | didInsertElement: function() {
6 | this.becomeKeyResponder(false /* true: replace | false: pushOnToStack*/);
7 | this._super(...arguments);
8 | },
9 |
10 | willDestroyElement: function() {
11 | this.resignKeyResponder();
12 | this._super(...arguments);
13 | },
14 |
15 | deleteBackward: log('deleteBackward'),
16 | insertTab: log('insertTab'),
17 | insertNewline: log('insertNewline'),
18 | cancel: log('cancel'),
19 | insertSpace: log('insertSpace'),
20 | moveLeft: log('moveLeft'),
21 | moveUp: log('moveUp'),
22 | moveRight: log('moveRight'),
23 | moveDown: log('moveDown'),
24 | deleteForward: log('deleteForward')
25 | });
26 |
27 | function log(eventName) {
28 | return function() {
29 | this.container.lookup('controller:application').get('events').unshiftObject({
30 | viewName: this.get('name'),
31 | eventName: eventName
32 | });
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dummy Tests
7 |
8 |
9 |
10 | {{content-for 'head'}}
11 | {{content-for 'test-head'}}
12 |
13 |
14 |
15 |
16 |
17 | {{content-for 'head-footer'}}
18 | {{content-for 'test-head-footer'}}
19 |
20 |
21 |
22 | {{content-for 'body'}}
23 | {{content-for 'test-body'}}
24 |
25 |
26 |
27 |
28 |
29 |
30 | {{content-for 'body-footer'}}
31 | {{content-for 'test-body-footer'}}
32 |
33 |
34 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/application.hbs:
--------------------------------------------------------------------------------
1 | Key Responder Demo
2 |
3 | {{key-responder-info}}
4 |
5 |
6 |
7 | event stream ({{events.length}})
8 |
9 | {{#each events as |event|}}
10 | - {{event.viewName}} - {{event.eventName}}
11 | {{/each}}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | {{#component-example name="a" class="component component-a"}}
20 | {{#if c}}
21 | {{#component-example name="c" class="component component-c"}}
22 | {{/component-example}}
23 | {{/if}}
24 |
25 | {{#component-example name="b" class="component component-b"}}
26 | {{#if d}}
27 | {{#component-example name="d" class="component component-d"}}
28 | {{/component-example}}
29 | {{/if}}
30 | {{/component-example}}
31 | {{/component-example}}
32 |
33 |
34 |
35 | {{input placeholder="Text Input"}}
36 |
37 |
--------------------------------------------------------------------------------
/tests/dummy/config/environment.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 |
3 | module.exports = function(environment) {
4 | var ENV = {
5 | modulePrefix: 'dummy',
6 | environment: environment,
7 | baseURL: '/',
8 | locationType: 'auto',
9 | EmberENV: {
10 | FEATURES: {
11 | // Here you can enable experimental features on an ember canary build
12 | // e.g. 'with-controller': true
13 | }
14 | },
15 | contentSecurityPolicy: {
16 | 'style-src': "'self' 'unsafe-inline'"
17 | },
18 | APP: {
19 | // Here you can pass flags/options to your application instance
20 | // when it is created
21 | }
22 | };
23 |
24 | if (environment === 'development') {
25 | // ENV.APP.LOG_RESOLVER = true;
26 | // ENV.APP.LOG_ACTIVE_GENERATION = true;
27 | // ENV.APP.LOG_TRANSITIONS = true;
28 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
29 | // ENV.APP.LOG_VIEW_LOOKUPS = true;
30 | }
31 | if (environment === 'test') {
32 | ENV.EmberENV.RAISE_ON_DEPRECATION = !process.env['ALLOW_DEPRECATIONS'];
33 |
34 | // Testem prefers this...
35 | ENV.baseURL = '/';
36 | ENV.locationType = 'none';
37 |
38 | // keep test console output quieter
39 | ENV.APP.LOG_ACTIVE_GENERATION = false;
40 | ENV.APP.LOG_VIEW_LOOKUPS = false;
41 |
42 | ENV.APP.rootElement = '#ember-testing';
43 | }
44 |
45 | if (environment === 'production') {
46 |
47 | }
48 |
49 | return ENV;
50 | };
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ember-key-responder",
3 | "description": "A component-oriented approach to keyboard shortcuts for Ember, inspired by Cocoa's KeyResponder.",
4 | "version": "0.4.2",
5 | "directories": {
6 | "doc": "doc",
7 | "test": "tests"
8 | },
9 | "scripts": {
10 | "start": "ember server",
11 | "build": "ember build",
12 | "test": "ember test",
13 | "commit-version": "node scripts/commit-version"
14 | },
15 | "repository": "https://github.com/yapplabs/ember-key-responder",
16 | "engines": {
17 | "node": ">= 0.10.0"
18 | },
19 | "author": "",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "broccoli-asset-rev": "^2.1.2",
23 | "ember-cli": "1.13.8",
24 | "ember-cli-app-version": "0.5.0",
25 | "ember-cli-content-security-policy": "0.4.0",
26 | "ember-cli-dependency-checker": "^1.0.1",
27 | "ember-cli-htmlbars": "0.7.9",
28 | "ember-cli-ic-ajax": "0.2.1",
29 | "ember-cli-inject-live-reload": "^1.3.1",
30 | "ember-cli-qunit": "^1.0.0",
31 | "ember-cli-release": "^0.2.5",
32 | "ember-cli-uglify": "^1.2.0",
33 | "ember-disable-prototype-extensions": "^1.0.0",
34 | "ember-disable-proxy-controllers": "^1.0.0",
35 | "ember-export-application-global": "^1.0.4",
36 | "ember-legacy-views": "0.2.0",
37 | "ember-try": "0.0.8",
38 | "rsvp": "^3.0.14"
39 | },
40 | "dependencies": {
41 | "ember-cli-babel": "^5.1.3"
42 | },
43 | "keywords": [
44 | "ember-addon"
45 | ],
46 | "ember-addon": {
47 | "configPath": "tests/dummy/config"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/integration/demo-test.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | import startApp from '../../tests/helpers/start-app';
4 | import { module, test } from 'qunit';
5 |
6 | var App;
7 |
8 | module('Acceptance - Demo page', {
9 | setup: function() {
10 | App = startApp();
11 | },
12 | teardown: function() {
13 | Ember.run(App, 'destroy');
14 | }
15 | });
16 |
17 | function pressTab() {
18 | let e = Ember.$.Event("keyup", {keyCode: 9, which: 9});
19 | Ember.$('body').trigger(e);
20 | }
21 |
22 | test('Load the demo page', function(assert) {
23 | visit('/');
24 |
25 | andThen(function () {
26 | let eventLiArray = find('#events ul li');
27 | assert.equal(eventLiArray.length, 0, 'No events fired yet');
28 |
29 | const componentC = find('.component-c');
30 | assert.equal(componentC.length, 0, 'Component c is initially inactive');
31 |
32 | pressTab();
33 |
34 | eventLiArray = find('#events ul li');
35 | assert.equal(eventLiArray.length, 1, '1 event fired');
36 | assert.equal(eventLiArray[0].innerText, 'a - insertTab', 'correct event name and key responder');
37 | });
38 |
39 | click('#example label:first-child .ember-checkbox');
40 |
41 | andThen(function () {
42 | const componentC = find('.component-c');
43 | assert.ok(componentC, 'Component c is active, after user activation');
44 |
45 | pressTab();
46 | pressTab();
47 |
48 | let eventLiArray = find('#events ul li');
49 | assert.equal(eventLiArray.length, 3, '3 events fired');
50 |
51 | eventLiArray = find('#events ul li');
52 | assert.equal(eventLiArray[1].innerText, 'c - insertTab', 'correct event name and key responder');
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/app/instance-initializers/key-responder.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | const { Mixin, on } = Ember;
4 |
5 | const ApplicationViewMixin = Mixin.create({
6 | delegateToKeyResponder: on('keyUp', function(event) {
7 | var currentKeyResponder = this.get('keyResponder.current');
8 | if (currentKeyResponder && currentKeyResponder.get('isVisible')) {
9 | // check to see if the event target is the keyResponder or the
10 | // keyResponders parents. if so, no need to dispatch as it has
11 | // already had a chance to handle this event.
12 | var id = '#' + currentKeyResponder.get('elementId');
13 | if (Ember.$(event.target).closest(id).length === 1) {
14 | return true;
15 | }
16 | return currentKeyResponder.respondToKeyEvent(event, currentKeyResponder);
17 | }
18 | return true;
19 | })
20 | });
21 |
22 | export default {
23 | name: 'ember-key-responder-instance',
24 |
25 | initialize() {
26 | // Handle 1.12.x case, where signature is
27 | // initialize(instance) {...}
28 | const instance = arguments[1] || arguments[0];
29 | const container = !!arguments[1] ? arguments[0] : instance.container;
30 |
31 | // Set up a handler on the ApplicationView for keyboard events that were
32 | // not handled by the current KeyResponder yet
33 | let ApplicationView = container.lookupFactory ?
34 | container.lookupFactory('view:application') :
35 | instance.resolveRegistration('view:application');
36 |
37 | ApplicationView = ApplicationView.extend(ApplicationViewMixin);
38 |
39 | //TextField/TextArea are currently uninjectable, so we're going to hack our
40 | //way in
41 | Ember.TextSupport.reopen({
42 | keyResponder: Ember.computed(function() {
43 | return container.lookup('key-responder:main');
44 | }).readOnly()
45 | });
46 | }
47 | };
48 |
--------------------------------------------------------------------------------
/config/ember-try.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | scenarios: [
3 | // // Not needed, since we explicitly test ~1.10.0
4 | // {
5 | // name: 'default',
6 | // dependencies: {}
7 | // },
8 | {
9 | name: 'ember-1.10',
10 | dependencies: {
11 | ember: '~1.10.0',
12 | 'ember-load-initializers': 'ember-cli/ember-load-initializers#0.0.2'
13 | },
14 | resolutions: {
15 | ember: '~1.10.0',
16 | 'ember-load-initializers': 'ember-cli/ember-load-initializers#0.0.2'
17 | }
18 | },
19 | {
20 | name: 'ember-1.11',
21 | dependencies: {
22 | ember: '~1.11.0',
23 | 'ember-load-initializers': 'ember-cli/ember-load-initializers#0.0.2'
24 | },
25 | resolutions: {
26 | ember: '~1.11.0',
27 | 'ember-load-initializers': 'ember-cli/ember-load-initializers#0.0.2'
28 | }
29 | },
30 | {
31 | name: 'ember-1.12',
32 | dependencies: {
33 | ember: '~1.12.0'
34 | },
35 | resolutions: {
36 | ember: '~1.12.0'
37 | }
38 | },
39 | {
40 | name: 'ember-1.13',
41 | dependencies: {
42 | ember: '~1.13.0'
43 | },
44 | resolutions: {
45 | ember: '~1.13.0'
46 | }
47 | },
48 | {
49 | name: 'ember-release',
50 | dependencies: {
51 | 'ember': 'components/ember#release'
52 | },
53 | resolutions: {
54 | 'ember': 'release'
55 | }
56 | },
57 | {
58 | name: 'ember-beta',
59 | dependencies: {
60 | 'ember': 'components/ember#beta'
61 | },
62 | resolutions: {
63 | 'ember': 'beta'
64 | }
65 | },
66 | {
67 | name: 'ember-canary',
68 | dependencies: {
69 | 'ember': 'components/ember#canary'
70 | },
71 | resolutions: {
72 | 'ember': 'canary'
73 | }
74 | }
75 | ]
76 | };
77 |
--------------------------------------------------------------------------------
/app/initializers/key-responder.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import keyResponderInstanceInitializer from '../instance-initializers/key-responder';
3 |
4 | var EMBER_VERSION_REGEX = /^([0-9]+)\.([0-9]+)\.([0-9]+)(?:(?:\-(alpha|beta)\.([0-9]+)(?:\.([0-9]+))?)?)?(?:\+(canary))?(?:\.([0-9abcdef]+))?(?:\-([A-Za-z0-9\.\-]+))?(?:\+([A-Za-z0-9\.\-]+))?$/;
5 |
6 | /**
7 | * VERSION_INFO[i] is as follows:
8 | *
9 | * 0 complete version string
10 | * 1 major version
11 | * 2 minor version
12 | * 3 trivial version
13 | * 4 pre-release type (optional: "alpha" or "beta" or undefined for stable releases)
14 | * 5 pre-release version (optional)
15 | * 6 pre-release sub-version (optional)
16 | * 7 canary (optional: "canary", or undefined for stable releases)
17 | * 8 SHA (optional)
18 | */
19 | var VERSION_INFO = EMBER_VERSION_REGEX.exec(Ember.VERSION);
20 |
21 |
22 | export default {
23 | name: 'ember-key-responder',
24 |
25 | initialize() {
26 | const application = arguments[1] || arguments[0];
27 | const registry = !!arguments[1] ? arguments[0] : application.registry;
28 | var isPre1dot12 = parseInt(VERSION_INFO[1], 10) < 2 && parseInt(VERSION_INFO[2], 10) < 12;
29 | const container = application.__container__;
30 |
31 | application.inject('view', 'keyResponder', 'key-responder:main');
32 | application.inject('component', 'keyResponder', 'key-responder:main');
33 |
34 | // Set up a handler on the document for keyboard events that are not
35 | // handled by Ember's event dispatcher.
36 | Ember.$(document).on('keyup.outside_ember_event_delegation', null, event => {
37 |
38 | if (Ember.$(event.target).closest('.ember-view').length === 0) {
39 | var keyResponder = container.lookup('key-responder:main');
40 | var currentKeyResponder = keyResponder.get('current');
41 | if (currentKeyResponder && currentKeyResponder.get('isVisible')) {
42 | return currentKeyResponder.respondToKeyEvent(event, currentKeyResponder);
43 | }
44 | }
45 |
46 | return true;
47 | });
48 |
49 | if (isPre1dot12) {
50 | // For versions before 1.12.0, we have to call the instanceInitializer
51 | keyResponderInstanceInitializer.initialize(registry, application);
52 | }
53 | }
54 | };
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DEPRECATED - ember-key-responder
2 |
3 | ---
4 |
5 | **NOTE:** This addon is no longer maintained. Please use [ember-keyboard](https://github.com/null-null-null/ember-keyboard) instead.
6 |
7 | ---
8 |
9 | [](https://travis-ci.org/yapplabs/ember-key-responder) [](http://emberobserver.com/addons/ember-key-responder)
10 |
11 | A component-oriented approach to keyboard shortcuts for Ember, inspired by Cocoa's KeyResponder.
12 |
13 | ember-key-responder will delegate keyEvents to the current keyResponder. Typically a keyResponder is a `view`, or a `component`. In complex applications, various keyResponders enter and leave the system. Since only 1 keyResponder can be active at any given point in time, a stack of them is maintained. The top of the stack is considered the current keyResponder.
14 |
15 | This allows for modals or other UI components to naturally become the default
16 | responder. As they enter they are pushed onto the stack. When they resign themselves they are dropped from the stack.
17 |
18 | ## Example
19 |
20 | Given the following components `component-a` and `component-b`
21 |
22 | ```js
23 | // component-a.js | component-b.js
24 | export default Ember.Component.extend({
25 | acceptsKeyResponder: true,
26 | didInsertElement: function() {
27 | this.becomeKeyResponder(false /* true: replace | false: pushOnToStack*/);
28 | this._super.apply(this, arguments);
29 | },
30 |
31 | willDestroyElement: function() {
32 | this.resignKeyResponder();
33 | this._super.apply(this, arguments);
34 | },
35 |
36 | moveUp: function() {
37 | // do something
38 | }
39 | });
40 | ```
41 |
42 | the template layout of
43 |
44 | ```hbs
45 | {{#component-a}
46 | {{#if showB}}
47 | {{#component-b}
48 | {{/component-b}
49 | {{/if}}
50 | {{/component-a}
51 | ```
52 |
53 | and `showB` is `true`
54 |
55 | the stack of key responders is
56 |
57 | ```
58 | component-b // <= current keyResponder
59 | component-a
60 | ```
61 |
62 | Key events captured will be delegated to `component-b`.
63 |
64 | If `showB` becomes `false` then `component-b` will be removed and the stack becomes
65 |
66 | ```
67 | component-a // <- current keyResponder
68 | ```
69 |
70 | At this point in time key events will be delegated to `component-a`.
71 |
72 | ## Further Usage
73 |
74 | `ember install:npm ember-key-responder`
75 |
76 | ```js
77 | // app/views/key-reponder-base.js
78 |
79 | export default Ember.View.extend({
80 | acceptsKeyResponder: true,
81 | didInsertElement: function() {
82 | this.becomeKeyResponder(false /* true: replace | false: pushOnToStack*/);
83 | this._super();
84 | },
85 |
86 | willDestroyElement: function() {
87 | this.resignKeyResponder();
88 | this._super();
89 | }
90 | });
91 | ```
92 |
93 | Events:
94 |
95 | ```js
96 | export var KEY_EVENTS = {
97 | 8: 'deleteBackward',
98 | 9: 'insertTab',
99 | 13: 'insertNewline',
100 | 27: 'cancel',
101 | 32: 'insertSpace',
102 | 37: 'moveLeft',
103 | 38: 'moveUp',
104 | 39: 'moveRight',
105 | 40: 'moveDown',
106 | 46: 'deleteForward'
107 | };
108 |
109 | export var MODIFIED_KEY_EVENTS = {
110 | 8: 'deleteForward',
111 | 9: 'insertBacktab',
112 | 37: 'moveLeftAndModifySelection',
113 | 38: 'moveUpAndModifySelection',
114 | 39: 'moveRightAndModifySelection',
115 | 40: 'moveDownAndModifySelection'
116 | };
117 | ```
118 |
119 | ## Additional
120 |
121 | To pause or resume the keyResponder:
122 |
123 | ```js
124 | keyResponder.pause();
125 | keyResponder.resume();
126 | ```
127 |
128 |
129 | ## Installation
130 |
131 | * `git clone` this repository
132 | * `npm install`
133 | * `bower install`
134 |
135 | ## Running
136 |
137 | * `ember server`
138 | * Visit your app at http://localhost:4200.
139 |
140 | ## Running Tests
141 |
142 | * `ember test`
143 | * `ember test --server`
144 |
145 | ## Building
146 |
147 | * `ember build`
148 |
149 | For more information on using ember-cli, visit [http://www.ember-cli.com/](http://www.ember-cli.com/).
150 |
--------------------------------------------------------------------------------
/app/key-responder.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | const { get, computed, isNone } = Ember;
4 |
5 | /*
6 | Holds a stack of key responder views. With this we can neatly handle
7 | restoring the previous key responder when some modal UI element is closed.
8 | There are a few simple rules that governs the usage of the stack:
9 | - mouse click does .replace (this should also be used for programmatically taking focus when not a modal element)
10 | - opening a modal UI element does .push
11 | - closing a modal element does .pop
12 |
13 | Also noteworthy is that a view will be signaled that it loses the key focus
14 | only when it's popped off the stack, not when something is pushed on top. The
15 | idea is that when a modal UI element is opened, we know that the previously
16 | focused view will re-gain the focus as soon as the modal element is closed.
17 | So if the previously focused view was e.g. in the middle of some edit
18 | operation, it shouldn't cancel that operation.
19 | */
20 | var KeyResponder = Ember.Object.extend({
21 | init: function() {
22 | this.set('isActive', true);
23 | this.set('stack', Ember.A());
24 | this._super.apply(this, arguments);
25 | },
26 |
27 | current: computed.readOnly('stack.lastObject'),
28 | pushView: function(view, wasTriggeredByFocus) {
29 | if (!isNone(view)) {
30 | view.trigger('willBecomeKeyResponder', wasTriggeredByFocus);
31 | const stack = get(this, 'stack');
32 | stack.pushObject(view);
33 | view.trigger('didBecomeKeyResponder', wasTriggeredByFocus);
34 | }
35 | return view;
36 | },
37 |
38 | resume: function() {
39 | this.set('isActive', true);
40 | },
41 |
42 | pause: function() {
43 | this.set('isActive', false);
44 | },
45 |
46 | popView: function(wasTriggeredByFocus) {
47 | const stack = get(this, 'stack');
48 | if (get(this, 'stack.length') > 0) {
49 | var view = get(this, 'current');
50 | if (view) {
51 | view.trigger('willLoseKeyResponder', wasTriggeredByFocus);
52 | }
53 | view = stack.popObject();
54 | if (view) {
55 | view.trigger('didLoseKeyResponder', wasTriggeredByFocus);
56 | }
57 | return view;
58 | } else {
59 | return undefined;
60 | }
61 | },
62 |
63 | replaceView: function(view, wasTriggeredByFocus) {
64 | if (get(this, 'current') !== view) {
65 | this.popView(wasTriggeredByFocus);
66 | return this.pushView(view, wasTriggeredByFocus);
67 | }
68 | }
69 | });
70 |
71 | export default KeyResponder;
72 |
73 | export var KEY_EVENTS = {
74 | 8: 'deleteBackward',
75 | 9: 'insertTab',
76 | 13: 'insertNewline',
77 | 27: 'cancel',
78 | 32: 'insertSpace',
79 | 37: 'moveLeft',
80 | 38: 'moveUp',
81 | 39: 'moveRight',
82 | 40: 'moveDown',
83 | 46: 'deleteForward'
84 | };
85 |
86 | export var MODIFIED_KEY_EVENTS = {
87 | 8: 'deleteForward',
88 | 9: 'insertBacktab',
89 | 37: 'moveLeftAndModifySelection',
90 | 38: 'moveUpAndModifySelection',
91 | 39: 'moveRightAndModifySelection',
92 | 40: 'moveDownAndModifySelection'
93 | };
94 |
95 | var KeyResponderSupportViewMixin = Ember.Mixin.create({
96 | // Set to true in your view if you want to accept key responder status (which
97 | // is needed for handling key events)
98 | acceptsKeyResponder: false,
99 | canBecomeKeyResponder: computed('acceptsKeyResponder',
100 | 'disabled',
101 | 'isVisible', function() {
102 | return get(this, 'acceptsKeyResponder') &&
103 | !get(this, 'disabled') &&
104 | get(this, 'isVisible');
105 | }).readOnly(),
106 |
107 | becomeKeyResponderViaMouseDown: Ember.on('mouseDown',function(evt) {
108 | var responder = this.get('keyResponder');
109 | if (responder === undefined) { return; }
110 |
111 | Ember.run.later(function() {
112 | responder._inEventBubblingPhase = undefined;
113 | }, 0);
114 |
115 | if (responder._inEventBubblingPhase === undefined) {
116 | responder._inEventBubblingPhase = true;
117 | this.becomeKeyResponder(false);
118 | }
119 | }),
120 |
121 | /*
122 | Sets this view as the target of key events. Call this if you need to make
123 | this happen programmatically. This gets also called on mouseDown if the
124 | view handles that, returns true and doesn't have property
125 | 'acceptsKeyResponder'
126 | set to false. If mouseDown returned true but 'acceptsKeyResponder' is
127 | false, this call is propagated to the parent view.
128 |
129 | If called with no parameters or with replace = true, the current key
130 | responder is first popped off the stack and this view is then pushed. See
131 | comments for Ember.KeyResponderStack above for more insight.
132 | */
133 | becomeKeyResponder: function(replace, wasTriggeredByFocus) {
134 | if (wasTriggeredByFocus === undefined) {
135 | wasTriggeredByFocus = false;
136 | }
137 |
138 | var keyResponder = get(this, 'keyResponder');
139 |
140 | if (!keyResponder) {
141 | return;
142 | }
143 |
144 | if (get(keyResponder, 'current') === this) {
145 | return;
146 | }
147 |
148 | if (get(this, 'canBecomeKeyResponder')) {
149 | if (replace === undefined || replace === true) {
150 | return keyResponder.replaceView(this, wasTriggeredByFocus);
151 | } else {
152 | return keyResponder.pushView(this, wasTriggeredByFocus);
153 | }
154 | } else {
155 | var parent = get(this, 'parentView');
156 |
157 | if (parent && parent.becomeKeyResponder) {
158 | return parent.becomeKeyResponder(replace, wasTriggeredByFocus);
159 | }
160 | }
161 | },
162 |
163 | becomeKeyResponderViaFocus: function() {
164 | return this.becomeKeyResponder(true, true);
165 | },
166 |
167 | /*
168 | Resign key responder status by popping the head off the stack. The head
169 | might or might not be this view, depending on whether user clicked anything
170 | since this view became the key responder. The new key responder
171 | will be the next view in the stack, if any.
172 | */
173 | resignKeyResponder: function(wasTriggeredByFocus) {
174 | if (wasTriggeredByFocus === undefined) {
175 | wasTriggeredByFocus = false;
176 | }
177 |
178 | var keyResponder = get(this, 'keyResponder');
179 |
180 | if (!keyResponder) {
181 | return;
182 | }
183 |
184 | keyResponder.popView(wasTriggeredByFocus);
185 | },
186 |
187 | resignKeyResponderViaFocus: function() {
188 | return this.resignKeyResponder(true);
189 | },
190 |
191 | respondToKeyEvent: function(event) {
192 | Ember.run(this, function() {
193 | if (get(this, 'keyResponder.isActive')) {
194 | if (get(this, 'keyResponder.current.canBecomeKeyResponder')) {
195 | get(this, 'keyResponder.current').interpretKeyEvents(event);
196 | }
197 | }
198 | });
199 | },
200 |
201 | interpretKeyEvents: function(event) {
202 | var mapping = event.shiftKey ? MODIFIED_KEY_EVENTS : KEY_EVENTS;
203 | var eventName = mapping[event.keyCode];
204 |
205 | if (eventName && this.has(eventName)) {
206 | return this.trigger(eventName, event);
207 | }
208 |
209 | return false;
210 | }
211 | });
212 |
213 | Ember.View.reopen(KeyResponderSupportViewMixin);
214 | Ember.Component.reopen(KeyResponderSupportViewMixin);
215 |
216 | export var KeyResponderInputSupport = Ember.Mixin.create({
217 | acceptsKeyResponder: true,
218 | init: function () {
219 | this._super.apply(this, arguments);
220 | this.on('focusIn', this, this.becomeKeyResponderViaFocus);
221 | this.on('focusOut', this, this.resignKeyResponderViaBlur);
222 | },
223 |
224 | didBecomeKeyResponder: function(wasTriggeredByFocus){
225 | if (!wasTriggeredByFocus && this._state === 'inDOM') {
226 | this.$().focus();
227 | }
228 | },
229 |
230 | didLoseKeyResponder: function(wasTriggeredByFocus){
231 | if (!wasTriggeredByFocus && this._state === 'inDOM') {
232 | this.$().blur();
233 | }
234 | }
235 | });
236 |
237 | Ember.TextSupport.reopen(KeyResponderInputSupport);
238 | Ember.Checkbox.reopen(KeyResponderInputSupport);
239 | Ember.Select.reopen(KeyResponderInputSupport);
240 |
--------------------------------------------------------------------------------