├── .bowerrc ├── .editorconfig ├── .ember-cli ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── .watchmanconfig ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── addon └── .gitkeep ├── app ├── .gitkeep ├── initializers │ └── key-responder.js ├── instance-initializers │ └── key-responder.js ├── key-responder.js └── views │ └── application.js ├── bower.json ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── package.json ├── scripts └── commit-version.js ├── testem.json ├── tests ├── .jshintrc ├── acceptance │ └── key-responder-test.js ├── dummy │ ├── .jshintrc │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ ├── .gitkeep │ │ │ └── component-example.js │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── application.js │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ ├── .gitkeep │ │ │ └── app.css │ │ ├── templates │ │ │ ├── .gitkeep │ │ │ ├── application.hbs │ │ │ └── components │ │ │ │ ├── .gitkeep │ │ │ │ ├── component-example.hbs │ │ │ │ └── key-responder-info.hbs │ │ └── views │ │ │ └── .gitkeep │ ├── config │ │ └── environment.js │ └── public │ │ ├── .gitkeep │ │ ├── crossdomain.xml │ │ └── robots.txt ├── helpers │ ├── resolver.js │ └── start-app.js ├── index.html ├── integration │ └── demo-test.js ├── test-helper.js └── unit │ └── .gitkeep └── vendor └── .gitkeep /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components", 3 | "analytics": false 4 | } 5 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp"] 3 | } 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | [![Build Status](https://travis-ci.org/yapplabs/ember-key-responder.svg?branch=master)](https://travis-ci.org/yapplabs/ember-key-responder) [![Ember Observer Score](http://emberobserver.com/badges/ember-key-responder.svg)](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 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/addon/.gitkeep -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/app/.gitkeep -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/views/application.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.View.extend({}); 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(/* environment, appConfig */) { 4 | return { }; 5 | }; 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | name: 'ember-key-responder' 6 | }; 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /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/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /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/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /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/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/styles/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/tests/dummy/app/styles/.gitkeep -------------------------------------------------------------------------------- /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/templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/tests/dummy/app/templates/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |

Key Responder Demo

2 |
3 | {{key-responder-info}} 4 |
5 | 6 |
7 | event stream ({{events.length}}) 8 | 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/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/tests/dummy/app/templates/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/component-example.hbs: -------------------------------------------------------------------------------- 1 | Component {{name}} 2 | {{yield}} 3 | -------------------------------------------------------------------------------- /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 |
  1. {{resp.name}} - {{resp}}
  2. 7 | {{/each}} 8 |
9 | -------------------------------------------------------------------------------- /tests/dummy/app/views/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/tests/dummy/app/views/.gitkeep -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/dummy/public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/tests/dummy/public/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import resolver from './helpers/resolver'; 2 | import { 3 | setResolver 4 | } from 'ember-qunit'; 5 | 6 | setResolver(resolver); 7 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/tests/unit/.gitkeep -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-key-responder/582f4173e4489b807087bac7a6cec36bb47a4aed/vendor/.gitkeep --------------------------------------------------------------------------------