diff --git a/spec/app/keymap-spec.coffee b/spec/app/keymap-spec.coffee index 22c56c337..21bc412dc 100644 --- a/spec/app/keymap-spec.coffee +++ b/spec/app/keymap-spec.coffee @@ -35,8 +35,8 @@ describe "Keymap", -> expect(event.keystrokes).toBe 'alt-meta-x' describe "when no binding matches the event's keystroke", -> - it "returns true, so the event continues to propagate", -> - expect(keymap.handleKeyEvent(keydownEvent('0', target: fragment[0]))).toBeTruthy() + it "does not return false so the event continues to propagate", -> + expect(keymap.handleKeyEvent(keydownEvent('0', target: fragment[0]))).not.toBe false describe "when at least one binding fully matches the event's keystroke", -> describe "when the event's target node matches a selector with a matching binding", -> @@ -176,8 +176,8 @@ describe "Keymap", -> fragment.on 'quit', quitHandler fragment.on 'close-other-windows', closeOtherWindowsHandler - it "only matches entire keystroke patters", -> - expect(keymap.handleKeyEvent(keydownEvent('c', target: fragment[0]))).toBeTruthy() + it "only matches entire keystroke patterns", -> + expect(keymap.handleKeyEvent(keydownEvent('c', target: fragment[0]))).not.toBe false describe "when the event's target node matches a selector with a partially matching multi-stroke binding", -> describe "when a second keystroke added to the first to match a multi-stroke binding completely", -> @@ -197,12 +197,12 @@ describe "Keymap", -> describe "when a second keystroke added to the first doesn't match any bindings", -> it "clears the queued keystrokes without triggering any events", -> - expect(keymap.handleKeyEvent(keydownEvent('x', target: fragment[0], ctrlKey: true))).toBeFalsy() - expect(keymap.handleKeyEvent(keydownEvent('c', target: fragment[0]))).toBeFalsy() + expect(keymap.handleKeyEvent(keydownEvent('x', target: fragment[0], ctrlKey: true))).toBe false + expect(keymap.handleKeyEvent(keydownEvent('c', target: fragment[0]))).toBe false expect(quitHandler).not.toHaveBeenCalled() expect(closeOtherWindowsHandler).not.toHaveBeenCalled() - expect(keymap.handleKeyEvent(keydownEvent('c', target: fragment[0]))).toBeTruthy() + expect(keymap.handleKeyEvent(keydownEvent('c', target: fragment[0]))).not.toBe false describe "when the event's target node descends from multiple nodes that match selectors with a partial binding match", -> it "allows any of the bindings to be triggered upon a second keystroke, favoring the most specific selector", -> @@ -230,6 +230,10 @@ describe "Keymap", -> describe "when there is a complete binding with a more specific selector", -> it "favors the more specific complete match", -> + describe "when a tab keystroke does not match any bindings", -> + it "returns false to prevent the browser from transferring focus", -> + expect(keymap.handleKeyEvent(keydownEvent('U+0009', target: fragment[0]))).toBe false + describe ".bindKeys(selector, hash)", -> it "normalizes the key patterns in the hash to put the modifiers in alphabetical order", -> fooHandler = jasmine.createSpy('fooHandler') diff --git a/src/app/keymap.coffee b/src/app/keymap.coffee index dd21eac85..676d33d44 100644 --- a/src/app/keymap.coffee +++ b/src/app/keymap.coffee @@ -70,26 +70,26 @@ class Keymap firstKeystroke = event.keystrokes.split(' ')[0] bindingSetsForFirstKeystroke = @bindingSetsByFirstKeystroke[firstKeystroke] - return true unless bindingSetsForFirstKeystroke? + if bindingSetsForFirstKeystroke? + currentNode = $(event.target) + currentNode = rootView if currentNode is $('body')[0] + while currentNode.length + candidateBindingSets = @bindingSetsForNode(currentNode, bindingSetsForFirstKeystroke) + for bindingSet in candidateBindingSets + command = bindingSet.commandForEvent(event) + if command + continue if @triggerCommandEvent(event, command) + return false + else if command == false + return false - currentNode = $(event.target) - currentNode = rootView if currentNode is $('body')[0] - while currentNode.length - candidateBindingSets = @bindingSetsForNode(currentNode, bindingSetsForFirstKeystroke) - for bindingSet in candidateBindingSets - command = bindingSet.commandForEvent(event) - if command - continue if @triggerCommandEvent(event, command) - return false - else if command == false - return false + if bindingSet.matchesKeystrokePrefix(event) + @queuedKeystrokes = event.keystrokes + return false + currentNode = currentNode.parent() - if bindingSet.matchesKeystrokePrefix(event) - @queuedKeystrokes = event.keystrokes - return false - currentNode = currentNode.parent() - - !isMultiKeystroke + return false if isMultiKeystroke + return false if firstKeystroke is 'tab' bindingSetsForNode: (node, candidateBindingSets = @bindingSets) -> bindingSets = candidateBindingSets.filter (set) -> node.is(set.selector)