   #   so they can be removed later.
   # bindings - An {Object} whose top-level keys point at sub-objects mapping
   #   keystroke patterns to commands.
-
   addKeyBindings: (source, keyBindingsBySelector) ->
     for selector, keyBindings of keyBindingsBySelector
       @addKeyBindingsForSelector(source, selector, keyBindings)

  # For testing purposes
  getOtherPlatforms: -> OtherPlatforms

  # Public: Remove the key bindings added with {::addKeyBindings} or
  # {::loadKeyBindings}.
  #
  # source - A {String} representing the `source` in a previous call to
  #   {::addKeyBindings} or the path in {::loadKeyBindings}.
  removeKeyBindings: (source) ->
    @keyBindings = @keyBindings.filter (keyBinding) -> keyBinding.source isnt source

  addKeyBindingsForSelector: (source, selector, keyBindings) ->
    # Verify selector is valid before registering any bindings
    try
      document.body.webkitMatchesSelector(selector.replace(/!important/g, ''))
    catch e
      console.warn("Encountered an invalid selector adding key bindings from '#{source}': '#{selector}'")
      return

    for keystroke, command of keyBindings
      keyBinding = new KeyBinding(source, command, keystroke, selector)
      @keyBindings.push(keyBinding)

  handleKeyboardEvent: (event) ->
    @keystrokes.push(@keystrokeForKeyboardEvent(event))
    keystrokes = @keystrokes.join(' ')

    target = event.target
    target = @defaultTarget if event.target is document.body and @defaultTarget?
    while target? and target isnt document
      candidateBindings = @keyBindingsForKeystrokesAndTarget(keystrokes, target)
      if candidateBindings.length > 0
        @keystrokes = []
        return if @dispatchCommandEvent(event, target, candidateBindings[0].command)
      target = target.parentElement

  dispatchCommandEvent: (keyboardEvent, target, command) ->
    return true if command is 'native!'

class Keymap
  Emitter.includeInto(this)

  # Public:
  #
  # options - An {Object} with the following optional keys:
  #   :defaultTarget - This will be used as the target of events whose target
  #     is `document.body` to allow for a catch-all element when nothing is
  #     focused
  constructor: (options) ->
    @defaultTarget = options?.defaultTarget
    @keyBindings = []
    @keystrokes = []
    @watchSubscriptions = {}

  # Public: Unwatch all watched paths.
  destroy: ->
    for filePath, subscription of @watchSubscriptions
      subscription.off()
