Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

781 lines (669 sloc) 27.882 kB
<html>
<head>
<script type="text/javascript" src="commands.js"></script>
<script type="text/javascript" src="lib/clipboard.js"></script>
<script type="text/javascript" src="lib/utils.js"></script>
<script type="text/javascript" charset="utf-8">
// Chromium #15242 will make this XHR request to access the manifest unnecessary.
var manifestRequest = new XMLHttpRequest();
manifestRequest.open("GET", chrome.extension.getURL("manifest.json"), false);
manifestRequest.send(null);
var currentVersion = JSON.parse(manifestRequest.responseText).version;
var tabQueue = {}; // windowId -> Array
var openTabs = {}; // tabId -> object with various tab properties
var keyQueue = ""; // Queue of keys typed
var validFirstKeys = {};
var singleKeyCommands = [];
var focusedFrame = null;
var framesForTab = {};
var lastActiveTabIds = {};
// Keys are either literal characters, or "named" - for example <a-b> (alt+b), <left> (left arrow) or <f12>
// This regular expression captures two groups: the first is a named key, the second is the remainder of
// the string.
var namedKeyRegex = /^(<(?:[amc]-.|(?:[amc]-)?[a-z0-9]{2,5})>)(.*)$/;
var defaultSettings = {
scrollStepSize: 60,
linkHintCharacters: "sadfjklewcmpgh",
filterLinkHints: false,
userDefinedLinkHintCss:
"#vimiumHintMarkerContainer .vimiumHintMarker \n/* linkhint boxes */ " +
"{\nbackground-color:yellow;\nborder:1px solid #E3BE23;\n}\n\n" +
"#vimiumHintMarkerContainer .vimiumHintMarker span \n/* linkhint text */ " +
"{\ncolor: black;\nfont-weight: bold;\nfont-size: 12px;\n}\n\n" +
"#vimiumHintMarkerContainer .vimiumHintMarker > .matchingCharacter {\n\n}",
excludedUrls: "http*://mail.google.com/*\n" +
"http*://www.google.com/reader/*\n",
// NOTE : If a page contains both a single angle-bracket link and a double angle-bracket link, then in
// most cases the single bracket link will be "prev/next page" and the double bracket link will be
// "first/last page", so we put the single bracket first in the pattern string so that it gets searched
// for first.
// "\bprev\b,\bprevious\b,\bback\b,<,←,«,≪,<<"
previousPatterns: "prev,previous,back,<,\u2190,\xab,\u226a,<<",
// "\bnext\b,\bmore\b,>,→,»,≫,>>"
nextPatterns: "next,more,>,\u2192,\xbb,\u226b,>>",
};
// Port handler mapping
var portHandlers = {
keyDown: handleKeyDown,
returnScrollPosition: handleReturnScrollPosition,
getCurrentTabUrl: getCurrentTabUrl,
getSetting: getSetting,
getBookmarks: getBookmarks
};
var sendRequestHandlers = {
getCompletionKeys: getCompletionKeysRequest,
getLinkHintCss: getLinkHintCss,
openUrlInNewTab: openUrlInNewTab,
openUrlInCurrentTab: openUrlInCurrentTab,
openOptionsPageInNewTab: openOptionsPageInNewTab,
registerFrame: registerFrame,
frameFocused: handleFrameFocused,
upgradeNotificationClosed: upgradeNotificationClosed,
updateScrollPosition: handleUpdateScrollPosition,
copyToClipboard: copyToClipboard,
copyLinkUrl: copyLinkUrl,
isEnabledForUrl: isEnabledForUrl,
saveHelpDialogSettings: saveHelpDialogSettings
};
// Event handlers
var selectionChangedHandlers = [];
var getScrollPositionHandlers = {}; // tabId -> function(tab, scrollX, scrollY);
var tabLoadedHandlers = {}; // tabId -> function()
chrome.extension.onConnect.addListener(function(port, name) {
var senderTabId = port.sender.tab ? port.sender.tab.id : null;
// If this is a tab we've been waiting to open, execute any "tab loaded" handlers, e.g. to restore
// the tab's scroll position. Wait until domReady before doing this; otherwise operations like restoring
// the scroll position will not be possible.
if (port.name == "domReady" && senderTabId != null) {
if (tabLoadedHandlers[senderTabId]) {
var toCall = tabLoadedHandlers[senderTabId];
// Delete first to be sure there's no circular events.
delete tabLoadedHandlers[senderTabId];
toCall.call();
}
// domReady is the appropriate time to show the "vimium has been upgraded" message.
// TODO: This might be broken on pages with frames.
if (shouldShowUpgradeMessage())
chrome.tabs.sendRequest(senderTabId, { name: "showUpgradeNotification", version: currentVersion });
}
if (portHandlers[port.name])
port.onMessage.addListener(portHandlers[port.name]);
});
chrome.extension.onRequest.addListener(function (request, sender, sendResponse) {
var senderTabId = sender.tab ? sender.tab.id : null;
if (sendRequestHandlers[request.handler])
sendResponse(sendRequestHandlers[request.handler](request, sender));
});
function handleReturnScrollPosition(args) {
if (getScrollPositionHandlers[args.currentTab.id]) {
// Delete first to be sure there's no circular events.
var toCall = getScrollPositionHandlers[args.currentTab.id];
delete getScrollPositionHandlers[args.currentTab.id];
toCall(args.currentTab, args.scrollX, args.scrollY);
}
}
/*
* Used by the content scripts to get their full URL. This is needed for URLs like "view-source:http:// .."
* because window.location doesn't know anything about the Chrome-specific "view-source:".
*/
function getCurrentTabUrl(args, port) {
var returnPort = chrome.tabs.connect(port.tab.id, { name: "returnCurrentTabUrl" });
returnPort.postMessage({ url: port.tab.url });
}
/*
* Checks the user's preferences in local storage to determine if Vimium is enabled for the given URL.
*/
function isEnabledForUrl(request) {
// excludedUrls are stored as a series of URL expressions separated by newlines.
var excludedUrls = getSettingFromLocalStorage("excludedUrls").split("\n");
var isEnabled = true;
for (var i = 0; i < excludedUrls.length; i++) {
// The user can add "*" to the URL which means ".*"
var regexp = new RegExp("^" + excludedUrls[i].replace(/\*/g, ".*") + "$");
if (request.url.match(regexp))
isEnabled = false;
}
return { isEnabledForUrl: isEnabled };
}
function saveHelpDialogSettings(request) {
localStorage["helpDialog_showAdvancedCommands"] = request.showAdvancedCommands;
}
function showHelp(callback, frameId) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id,
{ name: "showHelpDialog", dialogHtml: helpDialogHtml(), frameId:frameId });
});
}
/*
* Retrieves the help dialog HTML template from a file, and populates it with the latest keybindings.
*/
function helpDialogHtml(showUnboundCommands, showCommandNames, customTitle) {
var commandsToKey = {};
for (var key in keyToCommandRegistry) {
var command = keyToCommandRegistry[key].command;
commandsToKey[command] = (commandsToKey[command] || []).concat(key);
}
var dialogHtml = fetchFileContents("helpDialog.html");
for (var group in commandGroups)
dialogHtml = dialogHtml.replace("{{" + group + "}}",
helpDialogHtmlForCommandGroup(group, commandsToKey, availableCommands,
showUnboundCommands, showCommandNames));
dialogHtml = dialogHtml.replace("{{version}}", currentVersion);
dialogHtml = dialogHtml.replace("{{title}}", customTitle || "Help");
dialogHtml = dialogHtml.replace("{{showAdvancedCommands}}",
localStorage["helpDialog_showAdvancedCommands"] == "true");
return dialogHtml;
}
/*
* Generates HTML for a given set of commands. commandGroups are defined in commands.js
*/
function helpDialogHtmlForCommandGroup(group, commandsToKey, availableCommands,
showUnboundCommands, showCommandNames) {
var html = [];
for (var i = 0; i < commandGroups[group].length; i++) {
var command = commandGroups[group][i];
bindings = (commandsToKey[command] || [""]).join(", ");
if (showUnboundCommands || commandsToKey[command]) {
html.push(
"<tr class='vimiumReset " + (advancedCommands.indexOf(command) >= 0 ? "advanced" : "") + "'>",
"<td class='vimiumReset'>", 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");
}
function escapeHtml(string) { return string.replace(/</g, "&lt;").replace(/>/g, "&gt;"); }
/*
* Fetches the contents of a file bundled with this extension.
*/
function fetchFileContents(extensionFileName) {
var req = new XMLHttpRequest();
req.open("GET", chrome.extension.getURL(extensionFileName), false); // false => synchronous
req.send();
return req.responseText;
}
/**
* Returns the keys that can complete a valid command given the current key queue.
*/
function getCompletionKeysRequest(request) {
return { name: "refreshCompletionKeys",
completionKeys: generateCompletionKeys(),
validFirstKeys: validFirstKeys
};
}
/**
* Opens the url in the current tab.
*/
function openUrlInCurrentTab(request) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.update(tab.id, {url: request.url});
});
}
/**
* Opens request.url in new tab and switches to it if request.selected is true.
*
*/
function openUrlInNewTab(request) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.create({ url: request.url, index: tab.index + 1, selected: request.selected });
});
}
/**
* Copies url of selected link to the clipboard (wget ftw)
*/
function copyLinkUrl(request) {
Clipboard.copy(request.data);
}
function openCopiedUrlInCurrentTab(request) {
openUrlInCurrentTab({ url: utils.ensureUrl(Clipboard.paste()) });
}
function openCopiedUrlInNewTab(request) {
openUrlInNewTab({ url: utils.ensureUrl(Clipboard.paste()) });
}
/*
* Returns the user-provided CSS overrides.
*/
function getLinkHintCss(request) {
return { linkHintCss: (localStorage['userDefinedLinkHintCss'] || "") };
}
/*
* Called when the user has clicked the close icon on the "Vimium has been updated" message.
* We should now dismiss that message in all tabs.
*/
function upgradeNotificationClosed(request) {
localStorage.previousVersion = currentVersion;
sendRequestToAllTabs({ name: "hideUpgradeNotification" });
}
/*
* Copies some data (request.data) to the clipboard.
*/
function copyToClipboard(request) {
Clipboard.copy(request.data);
}
/*
* Used by the content scripts to get settings from the local storage.
*/
function getSetting(args, port) {
var value = getSettingFromLocalStorage(args.key);
var returnPort = chrome.tabs.connect(port.tab.id, { name: "returnSetting" });
returnPort.postMessage({ key: args.key, value: value });
}
function getBookmarks(args, port) {
chrome.bookmarks.search(args.query, function(bookmarks) {
port.postMessage({bookmarks:bookmarks})
})
}
/*
* Used by everyone to get settings from local storage.
*/
function getSettingFromLocalStorage(setting) {
if (localStorage[setting] != "" && !localStorage[setting]) {
return defaultSettings[setting];
} else {
return localStorage[setting];
}
}
function getCurrentTimeInSeconds() { Math.floor((new Date()).getTime() / 1000); }
chrome.tabs.onSelectionChanged.addListener(function(tabId, selectionInfo) {
if (selectionChangedHandlers.length > 0) { selectionChangedHandlers.pop().call(); }
if (lastActiveTabIds[selectionInfo.windowId] !== undefined)
chrome.tabs.sendRequest(lastActiveTabIds[selectionInfo.windowId], { name: "exitImplicitInsertMode" });
lastActiveTabIds[selectionInfo.windowId] = tabId;
});
function repeatFunction(func, totalCount, currentCount, frameId) {
if (currentCount < totalCount)
func(function() { repeatFunction(func, totalCount, currentCount + 1, frameId); }, frameId);
}
// Returns the scroll coordinates of the given tab. Pass in a callback of the form:
// function(tab, scrollX, scrollY) { .. }
function getScrollPosition(tab, callback) {
getScrollPositionHandlers[tab.id] = callback;
var scrollPort = chrome.tabs.connect(tab.id, { name: "getScrollPosition" });
scrollPort.postMessage({currentTab: tab});
}
// Start action functions
function createTab(callback) {
chrome.tabs.create({}, function(tab) { callback(); });
}
function nextTab(callback) { selectTab(callback, "next"); }
function previousTab(callback) { selectTab(callback, "previous"); }
function firstTab(callback) { selectTab(callback, "first"); }
function lastTab(callback) { selectTab(callback, "last"); }
/*
* Selects a tab before or after the currently selected tab. Direction is either "next", "previous", "first" or "last".
*/
function selectTab(callback, direction) {
chrome.tabs.getAllInWindow(null, function(tabs) {
if (tabs.length <= 1)
return;
chrome.tabs.getSelected(null, function(currentTab) {
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];
break;
}
selectionChangedHandlers.push(callback);
chrome.tabs.update(toSelect.id, { selected: true });
});
});
}
function removeTab(callback) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.remove(tab.id);
// We can't just call the callback here because we actually need to wait
// for the selection to change to consider this action done.
selectionChangedHandlers.push(callback);
});
}
function updateOpenTabs(tab) {
openTabs[tab.id] = { url: tab.url, positionIndex: tab.index, windowId: tab.windowId };
// Frames are recreated on refresh
delete framesForTab[tab.id];
}
function handleUpdateScrollPosition(request, sender) {
updateScrollPosition(sender.tab, request.scrollX, request.scrollY);
}
function updateScrollPosition(tab, scrollX, scrollY) {
openTabs[tab.id].scrollX = scrollX;
openTabs[tab.id].scrollY = scrollY;
}
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (changeInfo.status != "loading") { return; } // only do this once per URL change
updateOpenTabs(tab);
});
chrome.tabs.onAttached.addListener(function(tabId, attachedInfo) {
// We should update all the tabs in the old window and the new window.
if (openTabs[tabId]) {
updatePositionsAndWindowsForAllTabsInWindow(openTabs[tabId].windowId);
}
updatePositionsAndWindowsForAllTabsInWindow(attachedInfo.newWindowId);
});
chrome.tabs.onMoved.addListener(function(tabId, moveInfo) {
updatePositionsAndWindowsForAllTabsInWindow(moveInfo.windowId);
});
chrome.tabs.onRemoved.addListener(function(tabId) {
var openTabInfo = openTabs[tabId];
updatePositionsAndWindowsForAllTabsInWindow(openTabInfo.windowId);
// If we restore chrome:// pages, they'll ignore Vimium keystrokes when they reappear.
// Pretend they never existed and adjust tab indices accordingly.
// Could possibly expand this into a blacklist in the future
if (/^chrome[^:]*:\/\/.*/.test(openTabInfo.url)) {
for (var 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];
delete openTabs[tabId];
delete framesForTab[tabId];
});
chrome.windows.onRemoved.addListener(function(windowId) {
delete tabQueue[windowId];
delete lastActiveTabIds[windowId];
});
function restoreTab(callback) {
// TODO(ilya): Should this be getLastFocused instead?
chrome.windows.getCurrent(function(window) {
if (tabQueue[window.id] && tabQueue[window.id].length > 0)
{
var tabQueueEntry = tabQueue[window.id].pop();
// Clean out the tabQueue so we don't have unused windows laying about.
if (tabQueue[window.id].length == 0)
delete tabQueue[window.id];
// We have to chain a few callbacks to set the appropriate scroll position. We can't just wait until the
// tab is created because the content script is not available during the "loading" state. We need to
// wait until that's over before we can call setScrollPosition.
chrome.tabs.create({ url: tabQueueEntry.url, index: tabQueueEntry.positionIndex }, function(tab) {
tabLoadedHandlers[tab.id] = function() {
var scrollPort = chrome.tabs.connect(tab.id, {name: "setScrollPosition"});
scrollPort.postMessage({ scrollX: tabQueueEntry.scrollX, scrollY: tabQueueEntry.scrollY });
};
callback();
});
}
});
}
// End action functions
function updatePositionsAndWindowsForAllTabsInWindow(windowId) {
chrome.tabs.getAllInWindow(windowId, function (tabs) {
for (var i = 0; i < tabs.length; i++) {
var tab = tabs[i];
var openTabInfo = openTabs[tab.id];
if (openTabInfo) {
openTabInfo.positionIndex = tab.index;
openTabInfo.windowId = tab.windowId;
}
}
});
}
function splitKeyIntoFirstAndSecond(key) {
if (key.search(namedKeyRegex) == 0)
return { first: RegExp.$1, second: RegExp.$2 };
else
return { first: key[0], second: key.slice(1) };
}
function getActualKeyStrokeLength(key) {
if (key.search(namedKeyRegex) == 0)
return 1 + getActualKeyStrokeLength(RegExp.$2);
else
return key.length;
}
function populateValidFirstKeys() {
for (var key in keyToCommandRegistry)
{
if (getActualKeyStrokeLength(key) == 2)
validFirstKeys[splitKeyIntoFirstAndSecond(key).first] = true;
}
}
function populateSingleKeyCommands() {
for (var key in keyToCommandRegistry)
{
if (getActualKeyStrokeLength(key) == 1)
singleKeyCommands.push(key);
}
}
function refreshCompletionKeysAfterMappingSave() {
validFirstKeys = {};
singleKeyCommands = [];
populateValidFirstKeys();
populateSingleKeyCommands();
sendRequestToAllTabs(getCompletionKeysRequest());
}
/*
* Generates a list of keys that can complete a valid command given the current key queue or the one passed
* in.
*/
function generateCompletionKeys(keysToCheck) {
var splitHash = splitKeyQueue(keysToCheck || keyQueue);
command = splitHash.command;
count = splitHash.count;
var completionKeys = singleKeyCommands.slice(0);
if (getActualKeyStrokeLength(command) == 1)
{
for (var key in keyToCommandRegistry)
{
var splitKey = splitKeyIntoFirstAndSecond(key);
if (splitKey.first == command)
completionKeys.push(splitKey.second);
}
}
return completionKeys;
}
function splitKeyQueue(queue) {
var match = /([0-9]*)(.*)/.exec(queue);
var count = parseInt(match[1]);
var command = match[2];
return {count: count, command: command};
}
function handleKeyDown(request, port) {
var key = request.keyChar;
if (key == "<ESC>") {
console.log("clearing keyQueue");
keyQueue = ""
}
else {
console.log("checking keyQueue: [", keyQueue + key, "]");
keyQueue = checkKeyQueue(keyQueue + key, port.tab.id, request.frameId);
console.log("new KeyQueue: " + keyQueue);
}
}
function checkKeyQueue(keysToCheck, tabId, frameId) {
var refreshedCompletionKeys = false;
var splitHash = splitKeyQueue(keysToCheck);
command = splitHash.command;
count = splitHash.count;
if (command.length == 0) { return keysToCheck; }
if (isNaN(count)) { count = 1; }
if (keyToCommandRegistry[command]) {
registryEntry = keyToCommandRegistry[command];
console.log("command found for [", keysToCheck, "],", registryEntry.command);
if (!registryEntry.isBackgroundCommand) {
var port = chrome.tabs.connect(tabId, { name: "executePageCommand" });
port.postMessage({ command: registryEntry.command,
frameId: frameId,
count: count,
passCountToFunction: registryEntry.passCountToFunction,
completionKeys: generateCompletionKeys("")
});
refreshedCompletionKeys = true;
} else {
if(registryEntry.passCountToFunction){
this[registryEntry.command](count);
} else {
repeatFunction(this[registryEntry.command], count, 0, frameId);
}
}
newKeyQueue = "";
} else if (getActualKeyStrokeLength(command) > 1) {
var splitKey = splitKeyIntoFirstAndSecond(command);
// The second key might be a valid command by its self.
if (keyToCommandRegistry[splitKey.second])
newKeyQueue = checkKeyQueue(splitKey.second, tabId, frameId);
else
newKeyQueue = (validFirstKeys[splitKey.second] ? splitKey.second : "");
} else {
newKeyQueue = (validFirstKeys[command] ? count.toString() + command : "");
}
// If we haven't sent the completion keys piggybacked on executePageCommand,
// send them by themselves.
if (!refreshedCompletionKeys) {
chrome.tabs.sendRequest(tabId, getCompletionKeysRequest(), null);
}
return newKeyQueue;
}
/*
* Message all tabs. Args should be the arguments hash used by the Chrome sendRequest API.
*/
function sendRequestToAllTabs(args) {
chrome.windows.getAll({ populate: true }, function(windows) {
for (var i = 0; i < windows.length; i++)
for (var j = 0; j < windows[i].tabs.length; j++)
chrome.tabs.sendRequest(windows[i].tabs[j].id, args, null);
});
}
// Compares two version strings (e.g. "1.1" and "1.5") and returns
// -1 if versionA is < versionB, 0 if they're equal, and 1 if versionA is > versionB.
function compareVersions(versionA, versionB) {
versionA = versionA.split(".");
versionB = versionB.split(".");
for (var i = 0; i < Math.max(versionA.length, versionB.length); i++) {
var a = parseInt(versionA[i] || 0);
var b = parseInt(versionB[i] || 0);
if (a < b) return -1;
else if (a > b) return 1;
}
return 0;
}
/*
* Returns true if the current extension version is greater than the previously recorded version in
* localStorage, and false otherwise.
*/
function shouldShowUpgradeMessage() {
// Avoid showing the upgrade notification when localStorage.previousVersion is undefined, which is the
// case for new installs.
if (!localStorage.previousVersion)
localStorage.previousVersion = currentVersion;
return compareVersions(currentVersion, localStorage.previousVersion) == 1;
}
function openOptionsPageInNewTab() {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.create({ url: chrome.extension.getURL("options.html"), index: tab.index + 1 });
});
}
function registerFrame(request, sender) {
if (!framesForTab[sender.tab.id])
framesForTab[sender.tab.id] = { frames: [] };
if (request.is_top) {
focusedFrame = request.frameId;
framesForTab[sender.tab.id].total = request.total;
}
framesForTab[sender.tab.id].frames.push({ id: request.frameId, area: request.area });
// We've seen all the frames. Time to focus the largest one.
// NOTE: Disabled because it's buggy with iframes.
// if (framesForTab[sender.tab.id].frames.length >= framesForTab[sender.tab.id].total)
// focusLargestFrame(sender.tab.id);
}
function focusLargestFrame(tabId) {
var mainFrameId = null;
var mainFrameArea = 0;
for (var i = 0; i < framesForTab[tabId].frames.length; i++) {
var currentFrame = framesForTab[tabId].frames[i];
if (currentFrame.area > mainFrameArea) {
mainFrameId = currentFrame.id;
mainFrameArea = currentFrame.area;
}
}
chrome.tabs.sendRequest(tabId, { name: "focusFrame", frameId: mainFrameId, highlight: false });
}
function handleFrameFocused(request, sender) {
focusedFrame = request.frameId;
}
function nextFrame(count) {
chrome.tabs.getSelected(null, function(tab) {
var frames = framesForTab[tab.id].frames;
var curr_index = getCurrFrameIndex(frames);
// TODO: Skip the "top" frame (which doesn't actually have a <frame> tag),
// since it exists only to contain the other frames.
var new_index = (curr_index + count) % frames.length;
chrome.tabs.sendRequest(tab.id, { name: "focusFrame", frameId: frames[new_index].id, highlight: true });
});
}
function getCurrFrameIndex(frames) {
var index;
for (index=0; index < frames.length; index++) {
if (frames[index].id == focusedFrame)
break;
}
return index;
}
function init() {
clearKeyMappingsAndSetDefaults();
if (localStorage["keyMappings"])
parseCustomKeyMappings(localStorage["keyMappings"]);
// In version 1.22, we changed the mapping for "d" and "u" to be scroll page down/up instead of close
// and restore tab. For existing users, we want to preserve existing behavior for them by adding some
// custom key mappings on their behalf.
if (localStorage.previousVersion == "1.21") {
var customKeyMappings = localStorage["keyMappings"] || "";
if ((keyToCommandRegistry["d"] || {}).command == "scrollPageDown")
customKeyMappings += "\nmap d removeTab";
if ((keyToCommandRegistry["u"] || {}).command == "scrollPageUp")
customKeyMappings += "\nmap u restoreTab";
if (customKeyMappings != "") {
localStorage["keyMappings"] = customKeyMappings;
parseCustomKeyMappings(customKeyMappings);
}
}
populateValidFirstKeys();
populateSingleKeyCommands();
if (shouldShowUpgradeMessage())
sendRequestToAllTabs({ name: "showUpgradeNotification", version: currentVersion });
// Ensure that openTabs is populated when Vimium is installed.
chrome.windows.getAll({ populate: true }, function(windows) {
for (var i in windows) {
for (var j in windows[i].tabs) {
var tab = windows[i].tabs[j];
updateOpenTabs(tab);
getScrollPosition(tab, function(tab, scrollX, scrollY) {
// Not using the tab defined in the for loop because
// it might have changed by the time this callback is activated.
updateScrollPosition(tab, scrollX, scrollY);
});
}
}
});
}
init();
/**
* Convenience function for development use.
*/
function runTests() {
open(chrome.extension.getURL('test_harnesses/automated.html'));
}
/**
* Determines wether the heads up display (HUD) is enabled or not.
*
* This value is configurable via the options page.
*/
function hudEnabled() {
return localStorage['hideHud'] !== "true";
}
</script>
</head>
</html>
Jump to Line
Something went wrong with that request. Please try again.