aboutsummaryrefslogtreecommitdiffstats
path: root/linkHints.js
diff options
context:
space:
mode:
Diffstat (limited to 'linkHints.js')
-rw-r--r--linkHints.js740
1 files changed, 371 insertions, 369 deletions
diff --git a/linkHints.js b/linkHints.js
index 020f8fff..2b21f8c5 100644
--- a/linkHints.js
+++ b/linkHints.js
@@ -7,392 +7,394 @@
* The CSS which is used on the link hints is also a configurable option.
*/
-var hintMarkers = [];
-var hintMarkerContainingDiv = null;
-// The characters that were typed in while in "link hints" mode.
-var hintKeystrokeQueue = [];
-var linkTextKeystrokeQueue = [];
-var linkHintsModeActivated = false;
-var shouldOpenLinkHintInNewTab = false;
-var shouldOpenLinkHintWithQueue = false;
-// Whether link hint's "open in current/new tab" setting is currently toggled
-var openLinkModeToggle = false;
-// Whether we have added to the page the CSS needed to display link hints.
-var linkHintsCssAdded = false;
-
-/*
- * Generate an XPath describing what a clickable element is.
- * The final expression will be something like "//button | //xhtml:button | ..."
- */
-var clickableElementsXPath = (function() {
- var clickableElements = ["a", "textarea", "button", "select", "input[not(@type='hidden')]"];
- var xpath = [];
- for (var i in clickableElements)
- xpath.push("//" + clickableElements[i], "//xhtml:" + clickableElements[i]);
- xpath.push("//*[@onclick]");
- return xpath.join(" | ")
-})();
-
-function isNarrowMode() {
- return settings.narrowLinkHints == "true";
-}
-
-// We need this as a top-level function because our command system doesn't yet support arguments.
-function activateLinkHintsModeToOpenInNewTab() { activateLinkHintsMode(true, false); }
-
-function activateLinkHintsModeWithQueue() { activateLinkHintsMode(true, true); }
-
-function activateLinkHintsMode(openInNewTab, withQueue) {
- if (!linkHintsCssAdded)
- addCssToPage(linkHintCss); // linkHintCss is declared by vimiumFrontend.js
- linkHintCssAdded = true;
- linkHintsModeActivated = true;
- setOpenLinkMode(openInNewTab, withQueue);
- buildLinkHints();
- document.addEventListener("keydown", onKeyDownInLinkHintsMode, true);
- document.addEventListener("keyup", onKeyUpInLinkHintsMode, true);
-}
-
-function setOpenLinkMode(openInNewTab, withQueue) {
- shouldOpenLinkHintInNewTab = openInNewTab;
- shouldOpenLinkHintWithQueue = withQueue;
- if (shouldOpenLinkHintWithQueue) {
- HUD.show("Open multiple links in a new tab");
- } else {
- if (shouldOpenLinkHintInNewTab)
- HUD.show("Open link in new tab");
- else
- HUD.show("Open link in current tab");
- }
-}
-
-/*
- * Builds and displays link hints for every visible clickable item on the page.
- */
-function buildLinkHints() {
- var visibleElements = getVisibleClickableElements();
-
- // Initialize the number used to generate the character hints to be as many digits as we need to
- // highlight all the links on the page; we don't want some link hints to have more chars than others.
- var digitsNeeded = Math.ceil(logXOfBase(visibleElements.length, settings.linkHintCharacters.length));
- var linkHintNumber = 0;
- for (var i = 0; i < visibleElements.length; i++) {
- hintMarkers.push(createMarkerFor(visibleElements[i], linkHintNumber, digitsNeeded));
- linkHintNumber++;
- }
- // Note(philc): Append these markers as top level children instead of as child nodes to the link itself,
- // because some clickable elements cannot contain children, e.g. submit buttons. This has the caveat
- // that if you scroll the page and the link has position=fixed, the marker will not stay fixed.
- // Also note that adding these nodes to document.body all at once is significantly faster than one-by-one.
- hintMarkerContainingDiv = document.createElement("div");
- hintMarkerContainingDiv.className = "internalVimiumHintMarker";
- for (var i = 0; i < hintMarkers.length; i++)
- hintMarkerContainingDiv.appendChild(hintMarkers[i]);
- document.body.appendChild(hintMarkerContainingDiv);
-}
-
-function logXOfBase(x, base) { return Math.log(x) / Math.log(base); }
-
-/*
- * Returns all clickable elements that are not hidden and are in the current viewport.
- * We prune invisible elements partly for performance reasons, but moreso it's to decrease the number
- * of digits needed to enumerate all of the links on screen.
- */
-function getVisibleClickableElements() {
- var resultSet = document.evaluate(clickableElementsXPath, document.body,
- function (namespace) {
- return namespace == "xhtml" ? "http://www.w3.org/1999/xhtml" : null;
- },
- XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
-
-
- var visibleElements = [];
-
- // Find all visible clickable elements.
- for (var i = 0; i < resultSet.snapshotLength; i++) {
- var element = resultSet.snapshotItem(i);
- var clientRect = element.getClientRects()[0];
-
- if (isVisible(element, clientRect))
- visibleElements.push({element: element, rect: clientRect});
-
- // If the link has zero dimensions, it may be wrapping visible
- // but floated elements. Check for this.
- if (clientRect && (clientRect.width == 0 || clientRect.height == 0)) {
- for (var j = 0; j < element.children.length; j++) {
- if (window.getComputedStyle(element.children[j], null).getPropertyValue('float') != 'none') {
- var childClientRect = element.children[j].getClientRects()[0];
- if (isVisible(element.children[j], childClientRect)) {
- visibleElements.push({element: element.children[j], rect: childClientRect});
- break;
+var linkHints = {
+ hintMarkers: [],
+ hintMarkerContainingDiv: null,
+ // The characters that were typed in while in "link hints" mode.
+ hintKeystrokeQueue: [],
+ linkTextKeystrokeQueue: [],
+ linkHintsModeActivated: false,
+ shouldOpenLinkHintInNewTab: false,
+ shouldOpenLinkHintWithQueue: false,
+ // Whether link hint's "open in current/new tab" setting is currently toggled
+ openLinkModeToggle: false,
+ // Whether we have added to the page the CSS needed to display link hints.
+ linkHintsCssAdded: false,
+
+ /*
+ * Generate an XPath describing what a clickable element is.
+ * The final expression will be something like "//button | //xhtml:button | ..."
+ */
+ clickableElementsXPath: (function() {
+ var clickableElements = ["a", "textarea", "button", "select", "input[not(@type='hidden')]"];
+ var xpath = [];
+ for (var i in clickableElements)
+ xpath.push("//" + clickableElements[i], "//xhtml:" + clickableElements[i]);
+ xpath.push("//*[@onclick]");
+ return xpath.join(" | ")
+ })(),
+
+ isNarrowMode: function () {
+ return settings.narrowLinkHints == "true";
+ },
+
+ // We need this as a top-level function because our command system doesn't yet support arguments.
+ activateLinkHintsModeToOpenInNewTab: function() { linkHints.activateLinkHintsMode(true, false); },
+
+ activateLinkHintsModeWithQueue: function() { linkHints.activateLinkHintsMode(true, true); },
+
+ activateLinkHintsMode: function (openInNewTab, withQueue) {
+ if (!linkHints.linkHintsCssAdded)
+ addCssToPage(linkHintCss); // linkHintCss is declared by vimiumFrontend.js
+ linkHints.linkHintCssAdded = true;
+ linkHints.linkHintsModeActivated = true;
+ linkHints.setOpenLinkMode(openInNewTab, withQueue);
+ linkHints.buildLinkHints();
+ document.addEventListener("keydown", linkHints.onKeyDownInLinkHintsMode, true);
+ document.addEventListener("keyup", linkHints.onKeyUpInLinkHintsMode, true);
+ },
+
+ setOpenLinkMode: function(openInNewTab, withQueue) {
+ linkHints.shouldOpenLinkHintInNewTab = openInNewTab;
+ linkHints.shouldOpenLinkHintWithQueue = withQueue;
+ if (linkHints.shouldOpenLinkHintWithQueue) {
+ HUD.show("Open multiple links in a new tab");
+ } else {
+ if (linkHints.shouldOpenLinkHintInNewTab)
+ HUD.show("Open link in new tab");
+ else
+ HUD.show("Open link in current tab");
+ }
+ },
+
+ /*
+ * Builds and displays link hints for every visible clickable item on the page.
+ */
+ buildLinkHints: function() {
+ var visibleElements = linkHints.getVisibleClickableElements();
+
+ // Initialize the number used to generate the character hints to be as many digits as we need to
+ // highlight all the links on the page; we don't want some link hints to have more chars than others.
+ var digitsNeeded = Math.ceil(linkHints.logXOfBase(visibleElements.length, settings.linkHintCharacters.length));
+ var linkHintNumber = 0;
+ for (var i = 0; i < visibleElements.length; i++) {
+ linkHints.hintMarkers.push(linkHints.createMarkerFor(visibleElements[i], linkHintNumber, digitsNeeded));
+ linkHintNumber++;
+ }
+ // Note(philc): Append these markers as top level children instead of as child nodes to the link itself,
+ // because some clickable elements cannot contain children, e.g. submit buttons. This has the caveat
+ // that if you scroll the page and the link has position=fixed, the marker will not stay fixed.
+ // Also note that adding these nodes to document.body all at once is significantly faster than one-by-one.
+ linkHints.hintMarkerContainingDiv = document.createElement("div");
+ linkHints.hintMarkerContainingDiv.className = "internalVimiumHintMarker";
+ for (var i = 0; i < linkHints.hintMarkers.length; i++)
+ linkHints.hintMarkerContainingDiv.appendChild(linkHints.hintMarkers[i]);
+ document.body.appendChild(linkHints.hintMarkerContainingDiv);
+ },
+
+ logXOfBase: function(x, base) { return Math.log(x) / Math.log(base); },
+
+ /*
+ * Returns all clickable elements that are not hidden and are in the current viewport.
+ * We prune invisible elements partly for performance reasons, but moreso it's to decrease the number
+ * of digits needed to enumerate all of the links on screen.
+ */
+ getVisibleClickableElements: function() {
+ var resultSet = document.evaluate(linkHints.clickableElementsXPath, document.body,
+ function (namespace) {
+ return namespace == "xhtml" ? "http://www.w3.org/1999/xhtml" : null;
+ },
+ XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+
+
+ var visibleElements = [];
+
+ // Find all visible clickable elements.
+ for (var i = 0; i < resultSet.snapshotLength; i++) {
+ var element = resultSet.snapshotItem(i);
+ var clientRect = element.getClientRects()[0];
+
+ if (linkHints.isVisible(element, clientRect))
+ visibleElements.push({element: element, rect: clientRect});
+
+ // If the link has zero dimensions, it may be wrapping visible
+ // but floated elements. Check for this.
+ if (clientRect && (clientRect.width == 0 || clientRect.height == 0)) {
+ for (var j = 0; j < element.children.length; j++) {
+ if (window.getComputedStyle(element.children[j], null).getPropertyValue('float') != 'none') {
+ var childClientRect = element.children[j].getClientRects()[0];
+ if (linkHints.isVisible(element.children[j], childClientRect)) {
+ visibleElements.push({element: element.children[j], rect: childClientRect});
+ break;
+ }
}
}
}
}
- }
- return visibleElements;
-}
+ return visibleElements;
+ },
+
+ /*
+ * Returns true if element is visible.
+ */
+ isVisible: function(element, clientRect) {
+ // Exclude links which have just a few pixels on screen, because the link hints won't show for them anyway.
+ var zoomFactor = currentZoomLevel / 100.0;
+ if (!clientRect || clientRect.top < 0 || clientRect.top * zoomFactor >= window.innerHeight - 4 ||
+ clientRect.left < 0 || clientRect.left * zoomFactor >= window.innerWidth - 4)
+ return false;
+
+ if (clientRect.width < 3 || clientRect.height < 3)
+ return false;
+
+ // eliminate invisible elements (see test_harnesses/visibility_test.html)
+ var computedStyle = window.getComputedStyle(element, null);
+ if (computedStyle.getPropertyValue('visibility') != 'visible' ||
+ computedStyle.getPropertyValue('display') == 'none')
+ return false;
+
+ return true;
+ },
+
+ onKeyDownInLinkHintsMode: function(event) {
+ console.log("Key Down");
+ if (event.keyCode == keyCodes.shiftKey && !linkHints.openLinkModeToggle) {
+ // Toggle whether to open link in a new or current tab.
+ linkHints.setOpenLinkMode(!linkHints.shouldOpenLinkHintInNewTab, linkHints.shouldOpenLinkHintWithQueue);
+ linkHints.openLinkModeToggle = true;
+ }
-/*
- * Returns true if element is visible.
- */
-function isVisible(element, clientRect) {
- // Exclude links which have just a few pixels on screen, because the link hints won't show for them anyway.
- var zoomFactor = currentZoomLevel / 100.0;
- if (!clientRect || clientRect.top < 0 || clientRect.top * zoomFactor >= window.innerHeight - 4 ||
- clientRect.left < 0 || clientRect.left * zoomFactor >= window.innerWidth - 4)
- return false;
-
- if (clientRect.width < 3 || clientRect.height < 3)
- return false;
-
- // eliminate invisible elements (see test_harnesses/visibility_test.html)
- var computedStyle = window.getComputedStyle(element, null);
- if (computedStyle.getPropertyValue('visibility') != 'visible' ||
- computedStyle.getPropertyValue('display') == 'none')
- return false;
-
- return true;
-}
-
-function onKeyDownInLinkHintsMode(event) {
- console.log("Key Down");
- if (event.keyCode == keyCodes.shiftKey && !openLinkModeToggle) {
- // Toggle whether to open link in a new or current tab.
- setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue);
- openLinkModeToggle = true;
- }
-
- var keyChar = getKeyChar(event);
- if (!keyChar)
- return;
-
- // TODO(philc): Ignore keys that have modifiers.
- if (isEscape(event)) {
- deactivateLinkHintsMode();
- } else {
- if (isNarrowMode()) {
- if (event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey) {
- if (linkTextKeystrokeQueue.length == 0 && hintKeystrokeQueue.length == 0) {
- deactivateLinkHintsMode();
+ var keyChar = getKeyChar(event);
+ if (!keyChar)
+ return;
+
+ // TODO(philc): Ignore keys that have modifiers.
+ if (isEscape(event)) {
+ linkHints.deactivateLinkHintsMode();
+ } else {
+ if (linkHints.isNarrowMode()) {
+ if (event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey) {
+ if (linkHints.linkTextKeystrokeQueue.length == 0 && linkHints.hintKeystrokeQueue.length == 0) {
+ linkHints.deactivateLinkHintsMode();
+ } else {
+ // backspace clears hint key queue first, then acts on link text key queue
+ if (linkHints.hintKeystrokeQueue.pop() === undefined)
+ linkHints.linkTextKeystrokeQueue.pop();
+ linkHints.updateLinkHints();
+ }
+ } else if (/[0-9]/.test(keyChar)) {
+ linkHints.hintKeystrokeQueue.push(keyChar);
+ linkHints.updateLinkHints();
} else {
- // backspace clears hint key queue first, then acts on link text key queue
- if (hintKeystrokeQueue.pop() === undefined)
- linkTextKeystrokeQueue.pop();
- updateLinkHints();
+ linkHints.linkTextKeystrokeQueue.push(keyChar);
+ linkHints.updateLinkHints();
}
- } else if (/[0-9]/.test(keyChar)) {
- hintKeystrokeQueue.push(keyChar);
- updateLinkHints();
} else {
- linkTextKeystrokeQueue.push(keyChar);
- updateLinkHints();
- }
- } else {
- if (event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey) {
- if (hintKeystrokeQueue.length == 0) {
- deactivateLinkHintsMode();
- } else {
- hintKeystrokeQueue.pop();
- updateLinkHints();
+ if (event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey) {
+ if (linkHints.hintKeystrokeQueue.length == 0) {
+ linkHints.deactivateLinkHintsMode();
+ } else {
+ linkHints.hintKeystrokeQueue.pop();
+ linkHints.updateLinkHints();
+ }
+ } else if (settings.linkHintCharacters.indexOf(keyChar) >= 0) {
+ linkHints.hintKeystrokeQueue.push(keyChar);
+ linkHints.updateLinkHints();
}
- } else if (settings.linkHintCharacters.indexOf(keyChar) >= 0) {
- hintKeystrokeQueue.push(keyChar);
- updateLinkHints();
}
}
- }
-
- event.stopPropagation();
- event.preventDefault();
-}
-
-function onKeyUpInLinkHintsMode(event) {
- if (event.keyCode == keyCodes.shiftKey && openLinkModeToggle) {
- // Revert toggle on whether to open link in new or current tab.
- setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue);
- openLinkModeToggle = false;
- }
- event.stopPropagation();
- event.preventDefault();
-}
-/*
- * Updates the visibility of link hints on screen based on the keystrokes typed thus far. If only one
- * link hint remains, click on that link and exit link hints mode.
- */
-function updateLinkHints() {
- var matchString = hintKeystrokeQueue.join("");
- var linksMatched = highlightLinkMatches(matchString);
- if (linksMatched.length == 0)
- deactivateLinkHintsMode();
- else if (linksMatched.length == 1) {
- var matchedLink = linksMatched[0];
- if (isSelectable(matchedLink)) {
- matchedLink.focus();
- // When focusing a textbox, put the selection caret at the end of the textbox's contents.
- matchedLink.setSelectionRange(matchedLink.value.length, matchedLink.value.length);
- deactivateLinkHintsMode();
- } else {
- // When we're opening the link in the current tab, don't navigate to the selected link immediately;
- // we want to give the user some feedback depicting which link they've selected by focusing it.
- if (shouldOpenLinkHintWithQueue) {
- simulateClick(matchedLink);
- resetLinkHintsMode();
- } else if (shouldOpenLinkHintInNewTab) {
- simulateClick(matchedLink);
+ event.stopPropagation();
+ event.preventDefault();
+ },
+
+ onKeyUpInLinkHintsMode: function(event) {
+ if (event.keyCode == keyCodes.shiftKey && linkHints.openLinkModeToggle) {
+ // Revert toggle on whether to open link in new or current tab.
+ linkHints.setOpenLinkMode(!linkHints.shouldOpenLinkHintInNewTab, linkHints.shouldOpenLinkHintWithQueue);
+ linkHints.openLinkModeToggle = false;
+ }
+ event.stopPropagation();
+ event.preventDefault();
+ },
+
+ /*
+ * Updates the visibility of link hints on screen based on the keystrokes typed thus far. If only one
+ * link hint remains, click on that link and exit link hints mode.
+ */
+ updateLinkHints: function() {
+ var matchString = linkHints.hintKeystrokeQueue.join("");
+ var linksMatched = linkHints.highlightLinkMatches(matchString);
+ if (linksMatched.length == 0)
+ linkHints.deactivateLinkHintsMode();
+ else if (linksMatched.length == 1) {
+ var matchedLink = linksMatched[0];
+ if (linkHints.isSelectable(matchedLink)) {
matchedLink.focus();
- deactivateLinkHintsMode();
+ // When focusing a textbox, put the selection caret at the end of the textbox's contents.
+ matchedLink.setSelectionRange(matchedLink.value.length, matchedLink.value.length);
+ linkHints.deactivateLinkHintsMode();
} else {
- setTimeout(function() { simulateClick(matchedLink); }, 400);
- matchedLink.focus();
- deactivateLinkHintsMode();
+ // When we're opening the link in the current tab, don't navigate to the selected link immediately;
+ // we want to give the user some feedback depicting which link they've selected by focusing it.
+ if (linkHints.shouldOpenLinkHintWithQueue) {
+ linkHints.simulateClick(matchedLink);
+ linkHints.resetLinkHintsMode();
+ } else if (linkHints.shouldOpenLinkHintInNewTab) {
+ linkHints.simulateClick(matchedLink);
+ matchedLink.focus();
+ linkHints.deactivateLinkHintsMode();
+ } else {
+ setTimeout(function() { linkHints.simulateClick(matchedLink); }, 400);
+ matchedLink.focus();
+ linkHints.deactivateLinkHintsMode();
+ }
}
}
- }
-}
-
-/*
- * Selectable means the element has a text caret; this is not the same as "focusable".
- */
-function isSelectable(element) {
- var selectableTypes = ["search", "text", "password"];
- return (element.tagName == "INPUT" && selectableTypes.indexOf(element.type) >= 0) ||
- element.tagName == "TEXTAREA";
-}
+ },
+
+ /*
+ * Selectable means the element has a text caret; this is not the same as "focusable".
+ */
+ isSelectable: function(element) {
+ var selectableTypes = ["search", "text", "password"];
+ return (element.tagName == "INPUT" && selectableTypes.indexOf(element.type) >= 0) ||
+ element.tagName == "TEXTAREA";
+ },
+
+ /*
+ * Hides link hints which do not match the given search string. To allow the backspace key to work, this
+ * will also show link hints which do match but were previously hidden.
+ */
+ highlightLinkMatches: function(searchString) {
+ var linksMatched = [];
+ var linkSearchString = linkHints.linkTextKeystrokeQueue.join("");
+ var narrowMode = linkHints.isNarrowMode();
+ var hasSearchString = searchString.length != 0;
+ var hasLinkSearchString = linkSearchString.length != 0;
+ var matchedCount = 0;
+
+ for (var i = 0; i < linkHints.hintMarkers.length; i++) {
+ var linkMarker = linkHints.hintMarkers[i];
+ var matchedLink = linkMarker.getAttribute("linkText").toLowerCase().indexOf(linkSearchString.toLowerCase()) >= 0;
+ var matchedHintStart = linkMarker.getAttribute("hintString").indexOf(searchString) == 0;
+
+ var shouldRemoveMatch;
+ if (narrowMode) {
+ shouldRemoveMatch =
+ (!matchedLink && !matchedHintStart) ||
+ (!matchedLink && hasLinkSearchString) ||
+ (!matchedHintStart && hasSearchString)
+ } else {
+ shouldRemoveMatch = !matchedHintStart;
+ }
-/*
- * Hides link hints which do not match the given search string. To allow the backspace key to work, this
- * will also show link hints which do match but were previously hidden.
- */
-function highlightLinkMatches(searchString) {
- var linksMatched = [];
- var linkSearchString = linkTextKeystrokeQueue.join("");
- var narrowMode = isNarrowMode();
- var hasSearchString = searchString.length != 0;
- var hasLinkSearchString = linkSearchString.length != 0;
- var matchedCount = 0;
-
- for (var i = 0; i < hintMarkers.length; i++) {
- var linkMarker = hintMarkers[i];
- var matchedLink = linkMarker.getAttribute("linkText").toLowerCase().indexOf(linkSearchString.toLowerCase()) >= 0;
- var matchedHintStart = linkMarker.getAttribute("hintString").indexOf(searchString) == 0;
-
- var shouldRemoveMatch;
- if (narrowMode) {
- shouldRemoveMatch =
- (!matchedLink && !matchedHintStart) ||
- (!matchedLink && hasLinkSearchString) ||
- (!matchedHintStart && hasSearchString)
- } else {
- shouldRemoveMatch = !matchedHintStart;
- }
+ if (matchedHintStart) {
+ for (var j = 0; j < linkMarker.childNodes.length; j++)
+ linkMarker.childNodes[j].className = (j >= searchString.length) ? "" : "matchingCharacter";
+ }
- if (matchedHintStart) {
- for (var j = 0; j < linkMarker.childNodes.length; j++)
- linkMarker.childNodes[j].className = (j >= searchString.length) ? "" : "matchingCharacter";
- }
+ if (shouldRemoveMatch) {
+ linkMarker.style.display = "none";
+ } else {
+ if (linkMarker.style.display == "none")
+ linkMarker.style.display = "";
+ var newHint = matchedCount.toString();
+ linkMarker.innerHTML = linkHints.spanWrap(newHint);
+ linkMarker.setAttribute("hintString", newHint);
+ linksMatched.push(linkMarker.clickableItem);
+ matchedCount++;
+ }
- if (shouldRemoveMatch) {
- linkMarker.style.display = "none";
- } else {
- if (linkMarker.style.display == "none")
- linkMarker.style.display = "";
- var newHint = matchedCount.toString();
- linkMarker.innerHTML = spanWrap(newHint);
- linkMarker.setAttribute("hintString", newHint);
- linksMatched.push(linkMarker.clickableItem);
- matchedCount++;
}
-
- }
- return linksMatched;
-}
-
-/*
- * Converts a number like "8" into a hint string like "JK". This is used to sequentially generate all of
- * the hint text. The hint string will be "padded with zeroes" to ensure its length is equal to numHintDigits.
- */
-function numberToHintString(number, numHintDigits) {
- var base = settings.linkHintCharacters.length;
- var hintString = [];
- var remainder = 0;
- do {
- remainder = number % base;
- hintString.unshift(settings.linkHintCharacters[remainder]);
- number -= remainder;
- number /= Math.floor(base);
- } while (number > 0);
-
- // Pad the hint string we're returning so that it matches numHintDigits.
- var hintStringLength = hintString.length;
- for (var i = 0; i < numHintDigits - hintStringLength; i++)
- hintString.unshift(settings.linkHintCharacters[0]);
- return hintString.join("");
-}
-
-function simulateClick(link) {
- var event = document.createEvent("MouseEvents");
- // When "clicking" on a link, dispatch the event with the appropriate meta key (CMD on Mac, CTRL on windows)
- // to open it in a new tab if necessary.
- var metaKey = (platform == "Mac" && shouldOpenLinkHintInNewTab);
- var ctrlKey = (platform != "Mac" && shouldOpenLinkHintInNewTab);
- event.initMouseEvent("click", true, true, window, 1, 0, 0, 0, 0, ctrlKey, false, false, metaKey, 0, null);
-
- // Debugging note: Firefox will not execute the link's default action if we dispatch this click event,
- // but Webkit will. Dispatching a click on an input box does not seem to focus it; we do that separately
- link.dispatchEvent(event);
-}
-
-function deactivateLinkHintsMode() {
- if (hintMarkerContainingDiv)
- hintMarkerContainingDiv.parentNode.removeChild(hintMarkerContainingDiv);
- hintMarkerContainingDiv = null;
- hintMarkers = [];
- hintKeystrokeQueue = [];
- linkTextKeystrokeQueue = [];
- document.removeEventListener("keydown", onKeyDownInLinkHintsMode, true);
- document.removeEventListener("keyup", onKeyUpInLinkHintsMode, true);
- linkHintsModeActivated = false;
- HUD.hide();
-}
-
-function resetLinkHintsMode() {
- deactivateLinkHintsMode();
- activateLinkHintsModeWithQueue();
-}
-
-/*
- * Creates a link marker for the given link.
- */
-function createMarkerFor(link, linkHintNumber, linkHintDigits) {
- var hintString = isNarrowMode() ?
- linkHintNumber.toString() : numberToHintString(linkHintNumber, linkHintDigits);
- var linkText = link.element.innerHTML.toLowerCase();
- if (linkText == undefined)
- linkText = "";
- var marker = document.createElement("div");
- marker.className = "internalVimiumHintMarker vimiumHintMarker";
- marker.innerHTML = spanWrap(hintString);
- marker.setAttribute("hintString", hintString);
- marker.setAttribute("linkText", linkText);
-
- // Note: this call will be expensive if we modify the DOM in between calls.
- var clientRect = link.rect;
- // The coordinates given by the window do not have the zoom factor included since the zoom is set only on
- // the document node.
- var zoomFactor = currentZoomLevel / 100.0;
- marker.style.left = clientRect.left + window.scrollX / zoomFactor + "px";
- marker.style.top = clientRect.top + window.scrollY / zoomFactor + "px";
-
- marker.clickableItem = link.element;
- return marker;
-}
-
-// Make each hint character a span, so that we can highlight the typed characters as you type them.
-function spanWrap(hintString) {
- var innerHTML = [];
- for (var i = 0; i < hintString.length; i++)
- innerHTML.push("<span>" + hintString[i].toUpperCase() + "</span>");
- return innerHTML.join("");
-}
+ return linksMatched;
+ },
+
+ /*
+ * Converts a number like "8" into a hint string like "JK". This is used to sequentially generate all of
+ * the hint text. The hint string will be "padded with zeroes" to ensure its length is equal to numHintDigits.
+ */
+ numberToHintString: function(number, numHintDigits) {
+ var base = settings.linkHintCharacters.length;
+ var hintString = [];
+ var remainder = 0;
+ do {
+ remainder = number % base;
+ hintString.unshift(settings.linkHintCharacters[remainder]);
+ number -= remainder;
+ number /= Math.floor(base);
+ } while (number > 0);
+
+ // Pad the hint string we're returning so that it matches numHintDigits.
+ var hintStringLength = hintString.length;
+ for (var i = 0; i < numHintDigits - hintStringLength; i++)
+ hintString.unshift(settings.linkHintCharacters[0]);
+ return hintString.join("");
+ },
+
+ simulateClick: function(link) {
+ var event = document.createEvent("MouseEvents");
+ // When "clicking" on a link, dispatch the event with the appropriate meta key (CMD on Mac, CTRL on windows)
+ // to open it in a new tab if necessary.
+ var metaKey = (platform == "Mac" && linkHints.shouldOpenLinkHintInNewTab);
+ var ctrlKey = (platform != "Mac" && linkHints.shouldOpenLinkHintInNewTab);
+ event.initMouseEvent("click", true, true, window, 1, 0, 0, 0, 0, ctrlKey, false, false, metaKey, 0, null);
+
+ // Debugging note: Firefox will not execute the link's default action if we dispatch this click event,
+ // but Webkit will. Dispatching a click on an input box does not seem to focus it; we do that separately
+ link.dispatchEvent(event);
+ },
+
+ deactivateLinkHintsMode: function() {
+ if (linkHints.hintMarkerContainingDiv)
+ linkHints.hintMarkerContainingDiv.parentNode.removeChild(linkHints.hintMarkerContainingDiv);
+ linkHints.hintMarkerContainingDiv = null;
+ linkHints.hintMarkers = [];
+ linkHints.hintKeystrokeQueue = [];
+ linkHints.linkTextKeystrokeQueue = [];
+ document.removeEventListener("keydown", linkHints.onKeyDownInLinkHintsMode, true);
+ document.removeEventListener("keyup", linkHints.onKeyUpInLinkHintsMode, true);
+ linkHints.linkHintsModeActivated = false;
+ HUD.hide();
+ },
+
+ resetLinkHintsMode: function() {
+ linkHints.deactivateLinkHintsMode();
+ linkHints.activateLinkHintsModeWithQueue();
+ },
+
+ /*
+ * Creates a link marker for the given link.
+ */
+ createMarkerFor: function(link, linkHintNumber, linkHintDigits) {
+ var hintString = linkHints.isNarrowMode() ?
+ linkHintNumber.toString() : linkHints.numberToHintString(linkHintNumber, linkHintDigits);
+ var linkText = link.element.innerHTML.toLowerCase();
+ if (linkText == undefined)
+ linkText = "";
+ var marker = document.createElement("div");
+ marker.className = "internalVimiumHintMarker vimiumHintMarker";
+ marker.innerHTML = linkHints.spanWrap(hintString);
+ marker.setAttribute("hintString", hintString);
+ marker.setAttribute("linkText", linkText);
+
+ // Note: this call will be expensive if we modify the DOM in between calls.
+ var clientRect = link.rect;
+ // The coordinates given by the window do not have the zoom factor included since the zoom is set only on
+ // the document node.
+ var zoomFactor = currentZoomLevel / 100.0;
+ marker.style.left = clientRect.left + window.scrollX / zoomFactor + "px";
+ marker.style.top = clientRect.top + window.scrollY / zoomFactor + "px";
+
+ marker.clickableItem = link.element;
+ return marker;
+ },
+
+ // Make each hint character a span, so that we can highlight the typed characters as you type them.
+ spanWrap: function(hintString) {
+ var innerHTML = [];
+ for (var i = 0; i < hintString.length; i++)
+ innerHTML.push("<span>" + hintString[i].toUpperCase() + "</span>");
+ return innerHTML.join("");
+ },
+};