diff --git a/spec/atom/key-event-handler-spec.coffee b/spec/atom/key-event-handler-spec.coffee
new file mode 100644
index 000000000..dea13b7cb
--- /dev/null
+++ b/spec/atom/key-event-handler-spec.coffee
@@ -0,0 +1,37 @@
+KeyEventHandler = require 'key-event-handler'
+$ = require 'jquery'
+
+describe "KeyEventHandler", ->
+ handler = null
+
+ beforeEach ->
+ handler = new KeyEventHandler
+
+ fdescribe "handleKeypress", ->
+ describe "when there is a mapping in a selector that matches the event's element", ->
+ fragment = null
+ deleteCharHandler = null
+ insertCharHandler = null
+
+ beforeEach ->
+ handler.bindKeys '.command-mode', 'x': 'deleteChar'
+ handler.bindKeys '.insert-mode', 'x': 'insertChar'
+
+ fragment = $('
')
+ deleteCharHandler = jasmine.createSpy 'deleteCharHandler'
+ insertCharHandler = jasmine.createSpy 'insertCharHandler'
+ fragment.on 'deleteChar', deleteCharHandler
+ fragment.on 'insertChar', insertCharHandler
+
+ it "only triggers an event based on the key-binding in that selector", ->
+ handler.handleKeypress(keypressEvent('x', target: fragment[0]))
+ expect(deleteCharHandler).toHaveBeenCalled()
+ expect(insertCharHandler).not.toHaveBeenCalled()
+
+ deleteCharHandler.reset()
+ fragment.removeClass('command-mode').addClass('insert-mode')
+
+ handler.handleKeypress(keypressEvent('x', target: fragment[0]))
+ expect(deleteCharHandler).not.toHaveBeenCalled()
+ expect(insertCharHandler).toHaveBeenCalled()
+
diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee
index 4d2fb0d41..8c792775f 100644
--- a/spec/spec-helper.coffee
+++ b/spec/spec-helper.coffee
@@ -15,6 +15,9 @@ window.keydown = (pattern) ->
window.createKeyEvent = (pattern) ->
$.Event "keydown", atom.keyBinder.parseKeyPattern(pattern)
+window.keypressEvent = (pattern, properties={}) ->
+ $.Event "keypress", _.extend(atom.keyBinder.parseKeyPattern(pattern), properties)
+
window.waitsForPromise = (fn) ->
window.waitsFor (moveOn) ->
fn().done(moveOn)
diff --git a/src/atom/binding-set.coffee b/src/atom/binding-set.coffee
new file mode 100644
index 000000000..13e68399e
--- /dev/null
+++ b/src/atom/binding-set.coffee
@@ -0,0 +1,46 @@
+$ = require 'jquery'
+
+module.exports =
+class BindingSet
+ namedKeys:
+ backspace: 8, tab: 9, clear: 12, enter: 13, 'return': 13,
+ esc: 27, escape: 27, space: 32, left: 37, up: 38, right: 39,
+ down: 40, del: 46, 'delete': 46, home: 36, end: 35, pageup: 33,
+ pagedown: 34, ',': 188, '.': 190, '/': 191, '`': 192, '-': 189,
+ '=': 187, ';': 186, '\'': 222, '[': 219, ']': 221, '\\': 220
+
+ selector: null
+ bindings: null
+
+ constructor: (@selector, @bindings) ->
+
+ commandForEvent: (event) ->
+ if $(event.target).is(@selector)
+ for pattern, command of @bindings
+ return command if @eventMatchesPattern(event, pattern)
+ null
+
+ eventMatchesPattern: (event, pattern) ->
+ pattern = @parseKeyPattern pattern
+ pattern.ctrlKey == event.ctrlKey and
+ pattern.altKey == event.altKey and
+ pattern.shiftKey == event.shiftKey and
+ pattern.metaKey == event.metaKey and
+ pattern.which == event.which
+
+ parseKeyPattern: (pattern) ->
+ [modifiers..., key] = pattern.split '+'
+
+ if @namedKeys[key]
+ charCode = @namedKeys[key]
+ key = null
+ else
+ charCode = key.toUpperCase().charCodeAt 0
+
+ ctrlKey: 'ctrl' in modifiers
+ altKey: 'alt' in modifiers
+ shiftKey: 'shift' in modifiers
+ metaKey: 'meta' in modifiers
+ which: charCode
+ key: key
+
diff --git a/src/atom/key-event-handler.coffee b/src/atom/key-event-handler.coffee
new file mode 100644
index 000000000..4ae8fb5ec
--- /dev/null
+++ b/src/atom/key-event-handler.coffee
@@ -0,0 +1,19 @@
+$ = require 'jquery'
+BindingSet = require 'binding-set'
+
+module.exports =
+class KeyEventHandler
+ bindingSetsBySelector: null
+
+ constructor: ->
+ @bindingSets = []
+
+ bindKeys: (selector, bindings) ->
+ @bindingSets.push(new BindingSet(selector, bindings))
+
+ handleKeypress: (event) ->
+ for bindingSet in @bindingSets
+ if command = bindingSet.commandForEvent(event)
+ $(event.target).trigger(command)
+ return
+