text: // Generated by CoffeeScript 1.9.1
  (function() {
    var BackgroundCommands, bgLog, checkKeyQueue, completers, completionSources, copyToClipboard, currentVersion, cycleToFrame, fetchFileContents, filterCompleter, focusedFrame, frameIdsForTab, generateCompletionKeys, getActualKeyStrokeLength, getCompletionKeysRequest, getCurrentTabUrl, handleFrameFocused, handleKeyDown, handleSettings, handleUpdateScrollPosition, helpDialogHtmlForCommandGroup, isEnabledForUrl, keyQueue, moveTab, namedKeyRegex, onURLChange, openOptionsPageInNewTab, openUrlInCurrentTab, openUrlInIncognito, openUrlInNewTab, pasteFromClipboard, populateSingleKeyCommands, populateValidFirstKeys, portHandlers, refreshCompleter, registerFrame, removeTabsRelative, repeatFunction, root, selectSpecificTab, selectTab, selectionChangedHandlers, sendMessageToFrames, sendRequestHandlers, sendRequestToAllTabs, setIcon, showUpgradeMessage, singleKeyCommands, splitKeyIntoFirstAndSecond, splitKeyQueue, tabInfoMap, tabLoadedHandlers, tabQueue, unregisterFrame, updateOpenTabs, updatePositionsAndWindowsForAllTabsInWindow, updateScrollPosition, validFirstKeys, whitespaceRegexp,
      hasProp = {}.hasOwnProperty,
      slice = [].slice;
  
    root = typeof exports !== "undefined" && exports !== null ? exports : window;
  
    chrome.runtime.onInstalled.addListener(function(arg) {
      var checkLastRuntimeError, contentScripts, jobs, manifest, reason;
      reason = arg.reason;
      if (reason === "chrome_update" || reason === "shared_module_update") {
        return;
      }
      manifest = chrome.runtime.getManifest();
      contentScripts = manifest.content_scripts[0];
      jobs = [[chrome.tabs.executeScript, contentScripts.js], [chrome.tabs.insertCSS, contentScripts.css]];
      checkLastRuntimeError = function() {
        return chrome.runtime.lastError;
      };
      return chrome.tabs.query({
        status: "complete"
      }, function(tabs) {
        var file, files, func, j, len, results1, tab;
        results1 = [];
        for (j = 0, len = tabs.length; j < len; j++) {
          tab = tabs[j];
          results1.push((function() {
            var k, len1, ref, results2;
            results2 = [];
            for (k = 0, len1 = jobs.length; k < len1; k++) {
              ref = jobs[k], func = ref[0], files = ref[1];
              results2.push((function() {
                var l, len2, results3;
                results3 = [];
                for (l = 0, len2 = files.length; l < len2; l++) {
                  file = files[l];
                  results3.push(func(tab.id, {
                    file: file,
                    allFrames: contentScripts.allFrames
                  }, checkLastRuntimeError));
                }
                return results3;
              })());
            }
            return results2;
          })());
        }
        return results1;
      });
    });
  
    currentVersion = Utils.getCurrentVersion();
  
    tabQueue = {};
  
    tabInfoMap = {};
  
    keyQueue = "";
  
    validFirstKeys = {};
  
    singleKeyCommands = [];
  
    focusedFrame = null;
  
    frameIdsForTab = {};
  
    root.urlForTab = {};
  
    namedKeyRegex = /^(<(?:[amc]-.|(?:[amc]-)?[a-z0-9]{2,5})>)(.*)$/;
  
    selectionChangedHandlers = [];
  
    tabLoadedHandlers = {};
  
    chrome.storage.local.set({
      vimiumSecret: Math.floor(Math.random() * 2000000000)
    });
  
    completionSources = {
      bookmarks: new BookmarkCompleter(),
      history: new HistoryCompleter(),
      domains: new DomainCompleter(),
      tabs: new TabCompleter(),
      seachEngines: new SearchEngineCompleter()
    };
  
    completers = {
      omni: new MultiCompleter([completionSources.seachEngines, completionSources.bookmarks, completionSources.history, completionSources.domains]),
      bookmarks: new MultiCompleter([completionSources.bookmarks]),
      tabs: new MultiCompleter([completionSources.tabs])
    };
  
    chrome.runtime.onConnect.addListener(function(port, name) {
      var senderTabId, toCall;
      senderTabId = port.sender.tab ? port.sender.tab.id : null;
      if (port.name === "domReady" && senderTabId !== null) {
        if (tabLoadedHandlers[senderTabId]) {
          toCall = tabLoadedHandlers[senderTabId];
          delete tabLoadedHandlers[senderTabId];
          toCall.call();
        }
      }
      if (portHandlers[port.name]) {
        return port.onMessage.addListener(portHandlers[port.name]);
      }
    });
  
    chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
      if (sendRequestHandlers[request.handler]) {
        sendResponse(sendRequestHandlers[request.handler](request, sender));
      }
      return false;
    });
  
    getCurrentTabUrl = function(request, sender) {
      return sender.tab.url;
    };
  
    root.isEnabledForUrl = isEnabledForUrl = function(request, sender) {
      var rule;
      if (request.frameIsFocused) {
        urlForTab[sender.tab.id] = request.url;
      }
      rule = Exclusions.getRule(request.url);
      return {
        isEnabledForUrl: !rule || rule.passKeys,
        passKeys: (rule != null ? rule.passKeys : void 0) || ""
      };
    };
  
    onURLChange = function(details) {
      return chrome.tabs.sendMessage(details.tabId, {
        name: "checkEnabledAfterURLChange"
      });
    };
  
    chrome.webNavigation.onHistoryStateUpdated.addListener(onURLChange);
  
    chrome.webNavigation.onReferenceFragmentUpdated.addListener(onURLChange);
  
    root.helpDialogHtml = function(showUnboundCommands, showCommandNames, customTitle) {
      var command, commandsToKey, dialogHtml, group, key;
      commandsToKey = {};
      for (key in Commands.keyToCommandRegistry) {
        command = Commands.keyToCommandRegistry[key].command;
        commandsToKey[command] = (commandsToKey[command] || []).concat(key);
      }
      dialogHtml = fetchFileContents("pages/help_dialog.html");
      for (group in Commands.commandGroups) {
        dialogHtml = dialogHtml.replace("{{" + group + "}}", helpDialogHtmlForCommandGroup(group, commandsToKey, Commands.availableCommands, showUnboundCommands, showCommandNames));
      }
      dialogHtml = dialogHtml.replace("{{version}}", currentVersion);
      dialogHtml = dialogHtml.replace("{{title}}", customTitle || "Help");
      return dialogHtml;
    };
  
    helpDialogHtmlForCommandGroup = function(group, commandsToKey, availableCommands, showUnboundCommands, showCommandNames) {
      var bindings, command, html, isAdvanced, j, len, ref;
      html = [];
      ref = Commands.commandGroups[group];
      for (j = 0, len = ref.length; j < len; j++) {
        command = ref[j];
        bindings = (commandsToKey[command] || [""]).join(", ");
        if (showUnboundCommands || commandsToKey[command]) {
          isAdvanced = Commands.advancedCommands.indexOf(command) >= 0;
          html.push("<tr class='vimiumReset " + (isAdvanced ? "advanced" : void 0) + "'>", "<td class='vimiumReset'>", Utils.escapeHtml(bindings), "</td>", "<td class='vimiumReset'>:</td><td class='vimiumReset'>", availableCommands[command].description);
          if (showCommandNames) {
            html.push("<span class='vimiumReset commandName'>(" + command + ")</span>");
          }
          html.push("</td></tr>");
        }
      }
      return html.join("\n");
    };
  
    fetchFileContents = function(extensionFileName) {
      var req;
      req = new XMLHttpRequest();
      req.open("GET", chrome.runtime.getURL(extensionFileName), false);
      req.send();
      return req.responseText;
    };
  
    getCompletionKeysRequest = function(request, keysToCheck) {
      if (keysToCheck == null) {
        keysToCheck = "";
      }
      return {
        name: "refreshCompletionKeys",
        completionKeys: generateCompletionKeys(keysToCheck),
        validFirstKeys: validFirstKeys
      };
    };
  
    openUrlInCurrentTab = function(request) {
      return chrome.tabs.getSelected(null, function(tab) {
        return chrome.tabs.update(tab.id, {
          url: Utils.convertToUrl(request.url)
        });
      });
    };
  
    openUrlInNewTab = function(request, callback) {
      return chrome.tabs.getSelected(null, function(tab) {
        var tabConfig;
        tabConfig = {
          url: Utils.convertToUrl(request.url),
          index: tab.index + 1,
          selected: true,
          windowId: tab.windowId
        };
        if (typeof callback !== "function") {
          callback = (function() {});
        }
        return chrome.tabs.create(tabConfig, callback);
      });
    };
  
    openUrlInIncognito = function(request) {
      return chrome.windows.create({
        url: Utils.convertToUrl(request.url),
        incognito: true
      });
    };
  
    copyToClipboard = function(request) {
      Clipboard.copy(request.data);
      return null;
    };
  
    pasteFromClipboard = function(request) {
      Clipboard.paste();
      return null;
    };
  
    selectSpecificTab = function(request) {
      return chrome.tabs.get(request.id, function(tab) {
        chrome.windows.update(tab.windowId, {
          focused: true
        });
        return chrome.tabs.update(request.id, {
          selected: true
        });
      });
    };
  
    handleSettings = function(request, port) {
      var key, values;
      switch (request.operation) {
        case "get":
          return port.postMessage({
            key: request.key,
            value: Settings.get(request.key)
          });
        case "set":
          return Settings.set(request.key, request.value);
        case "fetch":
          values = request.values;
          for (key in values) {
            if (!hasProp.call(values, key)) continue;
            values[key] = Settings.get(key);
          }
          return port.postMessage({
            values: values
          });
      }
    };
  
    refreshCompleter = function(request) {
      return completers[request.name].refresh();
    };
  
    whitespaceRegexp = /\s+/;
  
    filterCompleter = function(args, port) {
      var queryTerms;
      queryTerms = args.query === "" ? [] : args.query.split(whitespaceRegexp);
      return completers[args.name].filter(queryTerms, function(results) {
        return port.postMessage({
          id: args.id,
          results: results
        });
      });
    };
  
    chrome.tabs.onSelectionChanged.addListener(function(tabId, selectionInfo) {
      if (selectionChangedHandlers.length > 0) {
        return selectionChangedHandlers.pop().call();
      }
    });
  
    repeatFunction = function(func, totalCount, currentCount, frameId) {
      if (currentCount < totalCount) {
        return func(function() {
          return repeatFunction(func, totalCount, currentCount + 1, frameId);
        }, frameId);
      }
    };
  
    moveTab = function(callback, direction) {
      return chrome.tabs.getSelected(null, function(tab) {
        return chrome.tabs.move(tab.id, {
          index: Math.max(0, tab.index + direction)
        }, callback);
      });
    };
  
    BackgroundCommands = {
      createTab: function(callback) {
        return chrome.tabs.query({
          active: true,
          currentWindow: true
        }, function(tabs) {
          var tab, url;
          tab = tabs[0];
          url = Settings.get("newTabUrl");
          if (url === "pages/blank.html") {
            url = tab.incognito ? "chrome://newtab" : chrome.runtime.getURL(url);
          }
          return openUrlInNewTab({
            url: url
          }, callback);
        });
      },
      duplicateTab: function(callback) {
        return chrome.tabs.getSelected(null, function(tab) {
          chrome.tabs.duplicate(tab.id);
          return selectionChangedHandlers.push(callback);
        });
      },
      moveTabToNewWindow: function(callback) {
        return chrome.tabs.query({
          active: true,
          currentWindow: true
        }, function(tabs) {
          var tab;
          tab = tabs[0];
          return chrome.windows.create({
            tabId: tab.id,
            incognito: tab.incognito
          });
        });
      },
      nextTab: function(callback) {
        return selectTab(callback, "next");
      },
      previousTab: function(callback) {
        return selectTab(callback, "previous");
      },
      firstTab: function(callback) {
        return selectTab(callback, "first");
      },
      lastTab: function(callback) {
        return selectTab(callback, "last");
      },
      removeTab: function(callback) {
        return chrome.tabs.getSelected(null, function(tab) {
          chrome.tabs.remove(tab.id);
          return selectionChangedHandlers.push(callback);
        });
      },
      restoreTab: function(callback) {
        if (chrome.sessions) {
          return chrome.sessions.restore(null, function(restoredSession) {
            if (!chrome.runtime.lastError) {
              return callback();
            }
          });
        } else {
          return chrome.windows.getCurrent(function(window) {
            var tabQueueEntry;
            if (!(tabQueue[window.id] && tabQueue[window.id].length > 0)) {
              return;
            }
            tabQueueEntry = tabQueue[window.id].pop();
            if (tabQueue[window.id].length === 0) {
              delete tabQueue[window.id];
            }
            return chrome.tabs.create({
              url: tabQueueEntry.url,
              index: tabQueueEntry.positionIndex
            }, function(tab) {
              tabLoadedHandlers[tab.id] = function() {
                return chrome.tabs.sendRequest(tab.id, {
                  name: "setScrollPosition",
                  scrollX: tabQueueEntry.scrollX,
                  scrollY: tabQueueEntry.scrollY
                });
              };
              return callback();
            });
          });
        }
      },
      openCopiedUrlInCurrentTab: function(request) {
        return openUrlInCurrentTab({
          url: Clipboard.paste()
        });
      },
      openCopiedUrlInNewTab: function(request) {
        return openUrlInNewTab({
          url: Clipboard.paste()
        });
      },
      togglePinTab: function(request) {
        return chrome.tabs.getSelected(null, function(tab) {
          return chrome.tabs.update(tab.id, {
            pinned: !tab.pinned
          });
        });
      },
      showHelp: function(callback, frameId) {
        return chrome.tabs.getSelected(null, function(tab) {
          return chrome.tabs.sendMessage(tab.id, {
            name: "toggleHelpDialog",
            dialogHtml: helpDialogHtml(),
            frameId: frameId
          });
        });
      },
      moveTabLeft: function(count) {
        return moveTab(null, -count);
      },
      moveTabRight: function(count) {
        return moveTab(null, count);
      },
      nextFrame: function(count, frameId) {
        return chrome.tabs.getSelected(null, function(tab) {
          frameIdsForTab[tab.id] = cycleToFrame(frameIdsForTab[tab.id], frameId, count);
          return chrome.tabs.sendMessage(tab.id, {
            name: "focusFrame",
            frameId: frameIdsForTab[tab.id][0],
            highlight: true
          });
        });
      },
      mainFrame: function() {
        return chrome.tabs.getSelected(null, function(tab) {
          return chrome.tabs.sendMessage(tab.id, {
            name: "focusFrame",
            frameId: 0,
            highlight: true
          });
        });
      },
      closeTabsOnLeft: function() {
        return removeTabsRelative("before");
      },
      closeTabsOnRight: function() {
        return removeTabsRelative("after");
      },
      closeOtherTabs: function() {
        return removeTabsRelative("both");
      }
    };
  
    removeTabsRelative = function(direction) {
      return chrome.tabs.query({
        currentWindow: true
      }, function(tabs) {
        return chrome.tabs.query({
          currentWindow: true,
          active: true
        }, function(activeTabs) {
          var activeTabIndex, j, len, shouldDelete, tab, toRemove;
          activeTabIndex = activeTabs[0].index;
          shouldDelete = (function() {
            switch (direction) {
              case "before":
                return function(index) {
                  return index < activeTabIndex;
                };
              case "after":
                return function(index) {
                  return index > activeTabIndex;
                };
              case "both":
                return function(index) {
                  return index !== activeTabIndex;
                };
            }
          })();
          toRemove = [];
          for (j = 0, len = tabs.length; j < len; j++) {
            tab = tabs[j];
            if (!tab.pinned && shouldDelete(tab.index)) {
              toRemove.push(tab.id);
            }
          }
          return chrome.tabs.remove(toRemove);
        });
      });
    };
  
    selectTab = function(callback, direction) {
      return chrome.tabs.getAllInWindow(null, function(tabs) {
        if (!(tabs.length > 1)) {
          return;
        }
        return chrome.tabs.getSelected(null, function(currentTab) {
          var toSelect;
          switch (direction) {
            case "next":
              toSelect = tabs[(currentTab.index + 1 + tabs.length) % tabs.length];
              break;
            case "previous":
              toSelect = tabs[(currentTab.index - 1 + tabs.length) % tabs.length];
              break;
            case "first":
              toSelect = tabs[0];
              break;
            case "last":
              toSelect = tabs[tabs.length - 1];
          }
          selectionChangedHandlers.push(callback);
          return chrome.tabs.update(toSelect.id, {
            selected: true
          });
        });
      });
    };
  
    updateOpenTabs = function(tab, deleteFrames) {
      var ref;
      if (deleteFrames == null) {
        deleteFrames = false;
      }
      if ((ref = tabInfoMap[tab.id]) != null ? ref.deletor : void 0) {
        clearTimeout(tabInfoMap[tab.id].deletor);
      }
      tabInfoMap[tab.id] = {
        url: tab.url,
        positionIndex: tab.index,
        windowId: tab.windowId,
        scrollX: null,
        scrollY: null,
        deletor: null
      };
      if (deleteFrames) {
        return delete frameIdsForTab[tab.id];
      }
    };
  
    setIcon = function(request, sender) {
      var path;
      path = (function() {
        switch (request.icon) {
          case "enabled":
            return "icons/browser_action_enabled.png";
          case "partial":
            return "icons/browser_action_partial.png";
          case "disabled":
            return "icons/browser_action_disabled.png";
        }
      })();
      return chrome.browserAction.setIcon({
        tabId: sender.tab.id,
        path: path
      });
    };
  
    handleUpdateScrollPosition = function(request, sender) {
      return updateScrollPosition(sender.tab, request.scrollX, request.scrollY);
    };
  
    updateScrollPosition = function(tab, scrollX, scrollY) {
      tabInfoMap[tab.id].scrollX = scrollX;
      return tabInfoMap[tab.id].scrollY = scrollY;
    };
  
    chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
      var cssConf;
      if (changeInfo.status !== "loading") {
        return;
      }
      cssConf = {
        allFrames: true,
        code: Settings.get("userDefinedLinkHintCss"),
        runAt: "document_start"
      };
      chrome.tabs.insertCSS(tabId, cssConf, function() {
        return chrome.runtime.lastError;
      });
      if (changeInfo.url != null) {
        return updateOpenTabs(tab);
      }
    });
  
    chrome.tabs.onAttached.addListener(function(tabId, attachedInfo) {
      if (tabInfoMap[tabId]) {
        updatePositionsAndWindowsForAllTabsInWindow(tabInfoMap[tabId].windowId);
      }
      return updatePositionsAndWindowsForAllTabsInWindow(attachedInfo.newWindowId);
    });
  
    chrome.tabs.onMoved.addListener(function(tabId, moveInfo) {
      return updatePositionsAndWindowsForAllTabsInWindow(moveInfo.windowId);
    });
  
    chrome.tabs.onRemoved.addListener(function(tabId) {
      var i, openTabInfo;
      openTabInfo = tabInfoMap[tabId];
      updatePositionsAndWindowsForAllTabsInWindow(openTabInfo.windowId);
      if (!chrome.sessions) {
        if (/^(chrome|view-source:)[^:]*:\/\/.*/.test(openTabInfo.url)) {
          for (i in tabQueue[openTabInfo.windowId]) {
            if (tabQueue[openTabInfo.windowId][i].positionIndex > openTabInfo.positionIndex) {
              tabQueue[openTabInfo.windowId][i].positionIndex--;
            }
          }
          return;
        }
        if (tabQueue[openTabInfo.windowId]) {
          tabQueue[openTabInfo.windowId].push(openTabInfo);
        } else {
          tabQueue[openTabInfo.windowId] = [openTabInfo];
        }
      }
      tabInfoMap.deletor = function() {
        return delete tabInfoMap[tabId];
      };
      setTimeout(tabInfoMap.deletor, 1000);
      delete frameIdsForTab[tabId];
      return delete urlForTab[tabId];
    });
  
    if (!chrome.sessions) {
      chrome.windows.onRemoved.addListener(function(windowId) {
        return delete tabQueue[windowId];
      });
    }
  
    updatePositionsAndWindowsForAllTabsInWindow = function(windowId) {
      return chrome.tabs.getAllInWindow(windowId, function(tabs) {
        var j, len, openTabInfo, results1, tab;
        results1 = [];
        for (j = 0, len = tabs.length; j < len; j++) {
          tab = tabs[j];
          openTabInfo = tabInfoMap[tab.id];
          if (openTabInfo) {
            openTabInfo.positionIndex = tab.index;
            results1.push(openTabInfo.windowId = tab.windowId);
          } else {
            results1.push(void 0);
          }
        }
        return results1;
      });
    };
  
    splitKeyIntoFirstAndSecond = function(key) {
      if (key.search(namedKeyRegex) === 0) {
        return {
          first: RegExp.$1,
          second: RegExp.$2
        };
      } else {
        return {
          first: key[0],
          second: key.slice(1)
        };
      }
    };
  
    getActualKeyStrokeLength = function(key) {
      if (key.search(namedKeyRegex) === 0) {
        return 1 + getActualKeyStrokeLength(RegExp.$2);
      } else {
        return key.length;
      }
    };
  
    populateValidFirstKeys = function() {
      var key, results1;
      results1 = [];
      for (key in Commands.keyToCommandRegistry) {
        if (getActualKeyStrokeLength(key) === 2) {
          results1.push(validFirstKeys[splitKeyIntoFirstAndSecond(key).first] = true);
        } else {
          results1.push(void 0);
        }
      }
      return results1;
    };
  
    populateSingleKeyCommands = function() {
      var key, results1;
      results1 = [];
      for (key in Commands.keyToCommandRegistry) {
        if (getActualKeyStrokeLength(key) === 1) {
          results1.push(singleKeyCommands.push(key));
        } else {
          results1.push(void 0);
        }
      }
      return results1;
    };
  
    root.refreshCompletionKeysAfterMappingSave = function() {
      validFirstKeys = {};
      singleKeyCommands = [];
      populateValidFirstKeys();
      populateSingleKeyCommands();
      return sendRequestToAllTabs(getCompletionKeysRequest());
    };
  
    generateCompletionKeys = function(keysToCheck) {
      var command, completionKeys, count, key, splitHash, splitKey;
      splitHash = splitKeyQueue(keysToCheck || keyQueue);
      command = splitHash.command;
      count = splitHash.count;
      completionKeys = singleKeyCommands.slice(0);
      if (getActualKeyStrokeLength(command) === 1) {
        for (key in Commands.keyToCommandRegistry) {
          splitKey = splitKeyIntoFirstAndSecond(key);
          if (splitKey.first === command) {
            completionKeys.push(splitKey.second);
          }
        }
      }
      return completionKeys;
    };
  
    splitKeyQueue = function(queue) {
      var command, count, match;
      match = /([1-9][0-9]*)?(.*)/.exec(queue);
      count = parseInt(match[1], 10);
      command = match[2];
      return {
        count: count,
        command: command
      };
    };
  
    handleKeyDown = function(request, port) {
      var key;
      key = request.keyChar;
      if (key === "<ESC>") {
        console.log("clearing keyQueue");
        keyQueue = "";
      } else {
        console.log("checking keyQueue: [", keyQueue + key, "]");
        keyQueue = checkKeyQueue(keyQueue + key, port.sender.tab.id, request.frameId);
        console.log("new KeyQueue: " + keyQueue);
      }
      return chrome.tabs.sendMessage(port.sender.tab.id, {
        name: "currentKeyQueue",
        keyQueue: keyQueue
      });
    };
  
    checkKeyQueue = function(keysToCheck, tabId, frameId) {
      var command, count, newKeyQueue, refreshedCompletionKeys, registryEntry, runCommand, splitHash, splitKey;
      refreshedCompletionKeys = false;
      splitHash = splitKeyQueue(keysToCheck);
      command = splitHash.command;
      count = splitHash.count;
      if (command.length === 0) {
        return keysToCheck;
      }
      if (isNaN(count)) {
        count = 1;
      }
      if (Commands.keyToCommandRegistry[command]) {
        registryEntry = Commands.keyToCommandRegistry[command];
        runCommand = true;
        if (registryEntry.noRepeat) {
          count = 1;
        } else if (registryEntry.repeatLimit && count > registryEntry.repeatLimit) {
          runCommand = confirm("You have asked Vimium to perform " + count + " repeats of the command:\n" + Commands.availableCommands[registryEntry.command].description + "\n\nAre you sure you want to continue?");
        }
        if (runCommand) {
          if (!registryEntry.isBackgroundCommand) {
            chrome.tabs.sendMessage(tabId, {
              name: "executePageCommand",
              command: registryEntry.command,
              frameId: frameId,
              count: count,
              passCountToFunction: registryEntry.passCountToFunction,
              completionKeys: generateCompletionKeys("")
            });
            refreshedCompletionKeys = true;
          } else {
            if (registryEntry.passCountToFunction) {
              BackgroundCommands[registryEntry.command](count, frameId);
            } else if (registryEntry.noRepeat) {
              BackgroundCommands[registryEntry.command](frameId);
            } else {
              repeatFunction(BackgroundCommands[registryEntry.command], count, 0, frameId);
            }
          }
        }
        newKeyQueue = "";
      } else if (getActualKeyStrokeLength(command) > 1) {
        splitKey = splitKeyIntoFirstAndSecond(command);
        if (Commands.keyToCommandRegistry[splitKey.second]) {
          newKeyQueue = checkKeyQueue(splitKey.second, tabId, frameId);
        } else {
          newKeyQueue = (validFirstKeys[splitKey.second] ? splitKey.second : "");
        }
      } else {
        newKeyQueue = (validFirstKeys[command] ? count.toString() + command : "");
      }
      if (!refreshedCompletionKeys) {
        chrome.tabs.sendMessage(tabId, getCompletionKeysRequest(null, newKeyQueue), null);
      }
      return newKeyQueue;
    };
  
    sendRequestToAllTabs = function(args) {
      return chrome.windows.getAll({
        populate: true
      }, function(windows) {
        var j, len, results1, tab, window;
        results1 = [];
        for (j = 0, len = windows.length; j < len; j++) {
          window = windows[j];
          results1.push((function() {
            var k, len1, ref, results2;
            ref = window.tabs;
            results2 = [];
            for (k = 0, len1 = ref.length; k < len1; k++) {
              tab = ref[k];
              results2.push(chrome.tabs.sendMessage(tab.id, args, null));
            }
            return results2;
          })());
        }
        return results1;
      });
    };
  
    openOptionsPageInNewTab = function() {
      return chrome.tabs.getSelected(null, function(tab) {
        return chrome.tabs.create({
          url: chrome.runtime.getURL("pages/options.html"),
          index: tab.index + 1
        });
      });
    };
  
    registerFrame = function(request, sender) {
      var name1;
      return (frameIdsForTab[name1 = sender.tab.id] != null ? frameIdsForTab[name1] : frameIdsForTab[name1] = []).push(request.frameId);
    };
  
    unregisterFrame = function(request, sender) {
      var tabId;
      tabId = sender.tab.id;
      if (frameIdsForTab[tabId] != null) {
        if (request.tab_is_closing) {
          return updateOpenTabs(sender.tab, true);
        } else {
          return frameIdsForTab[tabId] = frameIdsForTab[tabId].filter(function(id) {
            return id !== request.frameId;
          });
        }
      }
    };
  
    handleFrameFocused = function(request, sender) {
      var tabId;
      tabId = sender.tab.id;
      if (frameIdsForTab[tabId] != null) {
        frameIdsForTab[tabId] = cycleToFrame(frameIdsForTab[tabId], request.frameId);
      }
      return chrome.tabs.sendMessage(sender.tab.id, {
        name: "frameFocused",
        focusFrameId: request.frameId
      });
    };
  
    cycleToFrame = function(frames, frameId, count) {
      if (count == null) {
        count = 0;
      }
      frames || (frames = []);
      count = (count + Math.max(0, frames.indexOf(frameId))) % frames.length;
      return slice.call(frames.slice(count)).concat(slice.call(frames.slice(0, count)));
    };
  
    sendMessageToFrames = function(request, sender) {
      return chrome.tabs.sendMessage(sender.tab.id, request.message);
    };
  
    bgLog = function(request, sender) {
      return console.log(sender.tab.id + "/" + request.frameId, request.message);
    };
  
    portHandlers = {
      keyDown: handleKeyDown,
      settings: handleSettings,
      filterCompleter: filterCompleter
    };
  
    sendRequestHandlers = {
      getCompletionKeys: getCompletionKeysRequest,
      getCurrentTabUrl: getCurrentTabUrl,
      openUrlInNewTab: openUrlInNewTab,
      openUrlInIncognito: openUrlInIncognito,
      openUrlInCurrentTab: openUrlInCurrentTab,
      openOptionsPageInNewTab: openOptionsPageInNewTab,
      registerFrame: registerFrame,
      unregisterFrame: unregisterFrame,
      frameFocused: handleFrameFocused,
      nextFrame: function(request) {
        return BackgroundCommands.nextFrame(1, request.frameId);
      },
      updateScrollPosition: handleUpdateScrollPosition,
      copyToClipboard: copyToClipboard,
      pasteFromClipboard: pasteFromClipboard,
      isEnabledForUrl: isEnabledForUrl,
      selectSpecificTab: selectSpecificTab,
      refreshCompleter: refreshCompleter,
      createMark: Marks.create.bind(Marks),
      gotoMark: Marks.goto.bind(Marks),
      setIcon: setIcon,
      sendMessageToFrames: sendMessageToFrames,
      log: bgLog
    };
  
    chrome.storage.local.remove("findModeRawQueryListIncognito");
  
    chrome.tabs.onRemoved.addListener(function(tabId) {
      return chrome.storage.local.get("findModeRawQueryListIncognito", function(items) {
        if (items.findModeRawQueryListIncognito) {
          return chrome.windows.getAll(null, function(windows) {
            var j, len, window;
            for (j = 0, len = windows.length; j < len; j++) {
              window = windows[j];
              if (window.incognito) {
                return;
              }
            }
            return chrome.storage.local.remove("findModeRawQueryListIncognito");
          });
        }
      });
    });
  
    window.runTests = function() {
      return open(chrome.runtime.getURL('tests/dom_tests/dom_tests.html'));
    };
  
    Commands.clearKeyMappingsAndSetDefaults();
  
    if (Settings.has("keyMappings")) {
      Commands.parseCustomKeyMappings(Settings.get("keyMappings"));
    }
  
    populateValidFirstKeys();
  
    populateSingleKeyCommands();
  
    showUpgradeMessage = function() {
      var notification, notificationId, ref;
      if (!Settings.get("previousVersion")) {
        Settings.set("previousVersion", currentVersion);
      }
      if (Utils.compareVersions(currentVersion, Settings.get("previousVersion")) === 1) {
        notificationId = "VimiumUpgradeNotification";
        notification = {
          type: "basic",
          iconUrl: chrome.runtime.getURL("icons/vimium.png"),
          title: "Vimium Upgrade",
          message: "Vimium has been upgraded to version " + currentVersion + ". Click here for more information.",
          isClickable: true
        };
        if (((ref = chrome.notifications) != null ? ref.create : void 0) != null) {
          return chrome.notifications.create(notificationId, notification, function() {
            if (!chrome.runtime.lastError) {
              Settings.set("previousVersion", currentVersion);
              return chrome.notifications.onClicked.addListener(function(id) {
                if (id === notificationId) {
                  return openUrlInNewTab({
                    url: "https://github.com/philc/vimium#release-notes"
                  });
                }
              });
            }
          });
        } else {
          return chrome.permissions.onAdded.addListener(showUpgradeMessage);
        }
      }
    };
  
    chrome.windows.getAll({
      populate: true
    }, function(windows) {
      var createScrollPositionHandler, j, len, results1, tab, window;
      results1 = [];
      for (j = 0, len = windows.length; j < len; j++) {
        window = windows[j];
        results1.push((function() {
          var k, len1, ref, results2;
          ref = window.tabs;
          results2 = [];
          for (k = 0, len1 = ref.length; k < len1; k++) {
            tab = ref[k];
            updateOpenTabs(tab);
            createScrollPositionHandler = function() {
              return function(response) {
                if (response != null) {
                  return updateScrollPosition(tab, response.scrollX, response.scrollY);
                }
              };
            };
            results2.push(chrome.tabs.sendMessage(tab.id, {
              name: "getScrollPosition"
            }, createScrollPositionHandler()));
          }
          return results2;
        })());
      }
      return results1;
    });
  
    Sync.init();
  
    showUpgradeMessage();
  
  }).call(this);
  