// @name numbered links
// @namespace http://patcavit.com/greasemonkey
// @description make all links followable with the keyboard
-// @include http://*
+// @include *
// ==/UserScript==
-(function()
+(function()
{
-
-// This is activated (and canceled) by pressing the `/~ key. To change it to a
-// differet key, edit the ascii value on line 301
-//
-// Also, you can change the character set a few lines down. Don't use the "L"
-// key unless you change the on line 235.
-
-
+var active = 0;
+var got = '';
+
+// This is activated (and canceled) by pressing the ^C
+// Press L (first) to switch to one-hand mode
+
+// I was getting some very funky return values from String.fromCharCode() for
+// punctuation keys so I made my own table. You might need to change this for
+// your computer/keyboard. You'll need to have every character in "charset" in
+// here.
+
+var key_to_char = {
+ '84': 't',
+ '72': 'h',
+ '83': 's',
+ '78': 'n',
+ '68': 'd',
+ '189': '-',
+ '82': 'r',
+ '67': 'c',
+ '71': 'g',
+ '77': 'm',
+ '86': 'v',
+ '87': 'w',
+ '66': 'b',
+ '191': '/',
+ '186': ';',
+ '55': '7',
+ '56': '8',
+ '57': '9',
+ '65': 'a',
+ '69': 'e',
+ '70': 'f',
+ '73': 'i',
+ '74': 'j',
+ '75': 'k',
+ '79': 'o',
+ '80': 'p',
+ '81': 'q',
+ '85': 'u',
+ '88': 'x',
+ '89': 'y',
+ '90': 'z',
+ '50': '2',
+ '51': '3',
+ '52': '4',
+ '222': "'",
+ '76': 'l' // switch to one-hand mode
+};
//Just some shortcuts and globals
-var charset = 'thsnd-rcgmvwb/;789aefijkopquxyz234';
+var charset = 'thsnd-rcgmvwb/;789aefijkopquxyz234'; // update key_to_char if you add to this
var uzblid = 'uzbl_link_hint';
var uzbldivid = uzblid + '_div_container';
var doc = document;
var win = window;
var links = document.links;
var forms = document.forms;
-//Make onlick-links "clickable"
-try {
- HTMLElement.prototype.click = function() {
- if (typeof this.onclick == 'function') {
- this.onclick({
- type: 'click'
- });
- }
- };
-} catch(e) {}
+
//Calculate element position to draw the hint
//Pretty accurate but on fails in some very fancy cases
-function elementPosition(el) {
- var up = el.offsetTop;
- var left = el.offsetLeft;
+function element_position(el) {
+ var x = el.offsetLeft;
+ var y = el.offsetTop;
var width = el.offsetWidth;
var height = el.offsetHeight;
while (el.offsetParent) {
el = el.offsetParent;
- up += el.offsetTop;
- left += el.offsetLeft;
+ y += el.offsetTop;
+ x += el.offsetLeft;
}
- return [up, left, width, height];
+ return { x: x, y: y, width: width, height: height };
}
+
+// Simulate a click on the element
+function click_element(target, options) {
+ var event = target.ownerDocument.createEvent('MouseEvents');
+ var pos = element_position(target);
+ options = options || {};
+
+
+ event.initMouseEvent(
+ options.type || 'click',
+ options.canBubble || true,
+ options.cancelable || true,
+ options.view || target.ownerDocument.defaultView,
+ options.detail || 1,
+ options.screenX || pos.x - window.pageXOffset,
+ options.screenY || pos.y - window.pageYOffset,
+ options.clientX || pos.x,
+ options.clientY || pos.y,
+ options.ctrlKey || false,
+ options.altKey || false,
+ options.shiftKey || false,
+ options.metaKey || false,
+ options.button || 0, //0 = left, 1 = middle, 2 = right
+ options.relatedTarget || null
+ );
+
+ target.dispatchEvent(event);
+}
+
//Calculate if an element is visible
function isVisible(el) {
if (el == doc) {
}
//Calculate if an element is on the viewport.
function elementInViewport(el) {
- offset = elementPosition(el);
- var up = offset[0];
- var left = offset[1];
- var width = offset[2];
- var height = offset[3];
- return up < window.pageYOffset + window.innerHeight && left < window.pageXOffset + window.innerWidth && (up + height) > window.pageYOffset && (left + width) > window.pageXOffset;
+ var pos = element_position(el);
+ return pos.y < window.pageYOffset + window.innerHeight && pos.x < window.pageXOffset + window.innerWidth && (pos.y + pos.height) > window.pageYOffset && (pos.x + pos.width) > window.pageXOffset;
}
//Removes all hints/leftovers that might be generated
//by this script.
//Generate a hint for an element with the given label
//Here you can play around with the style of the hints!
function generateHint(el, label) {
- var pos = elementPosition(el);
+ var pos = element_position(el);
var hint = doc.createElement('div');
hint.setAttribute('name', uzblid);
hint.innerText = label;
hint.style.position = 'absolute';
hint.style.zIndex = '1000';
hint.style.textTransform = 'uppercase';
- hint.style.left = Math.max(-1, (pos[1] - (7 + label.length * 9))) + 'px';
- hint.style.top = (pos[0] + 1) + 'px';
+ hint.style.left = Math.max(-1, (pos.x - (7 + label.length * 9))) + 'px';
+ hint.style.top = (pos.y + 1) + 'px';
var img = el.getElementsByTagName('img');
//if (img.length > 0) {
- //hint.style.top = pos[1] + img[0].height / 2 - 6 + 'px';
+ //hint.style.top = pos.x + img[0].height / 2 - 6 + 'px';
//}
hint.style.textDecoration = 'none';
// hint.style.webkitBorderRadius = '6px'; // slow
if (item) {
var name = item.tagName;
if (name == 'A') {
- item.click();
+ click_element(item);
window.location = item.href;
} else if (name == 'INPUT') {
var type = item.getAttribute('type').toUpperCase();
item.focus();
item.select();
} else {
- item.click();
+ click_element(item);
window.location = item.href;
}
}
//just the elements itself, but in other versions, we
//add the label here.
function addLinks() {
- res = [[], []];
+ var res = [[], []];
for (var l = 0; l < links.length; l++) {
var li = links[l];
if (isVisible(li) && elementInViewport(li)) {
}
//Same as above, just for the form elements
function addFormElems() {
- res = [[], []];
+ var res = [[], []];
for (var f = 0; f < forms.length; f++) {
for (var e = 0; e < forms[f].elements.length; e++) {
var el = forms[f].elements[e];
}
}
+// from your event handler you can: return stop_event(e)
+function stop_event(e) {
+ // try {
+ e.stopPropagation();
+ e.preventDefault();
+ // } catch (ex) {
+ // return false; // IE-compat
+ // }
+}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- var active = 0;
- var got = '';
+function init() {
document.addEventListener(
- 'keypress',
+ 'keydown',
function(e) {
- if(e.keyCode == 96) { // change this if you want a different activation key
+ // [de]activate on ^C
+ // deactivate on ESC
+ if(
+ (e.ctrlKey && e.keyCode == 67)
+ || (e.keyCode == 27 && active == 1)
+ ) {
if(active) {
got = '';
removeAllHints();
followLinks(got);
}
active = 1 - active;
- return;
+ return stop_event(e);
} else {
- if(active == 1) {
- got += String.fromCharCode(e.keyCode);
- followLinks(got);
+ if(active == 1 && !e.ctrlKey && !e.shiftKey && !e.altKey) {
+ if(key_to_char[e.keyCode]) {
+ got += key_to_char[e.keyCode];
+ followLinks(got);
+ return stop_event(e);
+ }
+ } else {
+ // general keybinding, unrelated to numbered links
+ if (document.activeElement == document.body) { // FIXME get more specific
+ var c = key_to_char[e.keyCode];
+ switch (c) {
+ case 'c':
+ window.scrollBy(0, -200);
+ stop_event(e);
+ break;
+ case 't':
+ window.scrollBy(0, 200);
+ stop_event(e);
+ break;
+ }
+ } else if (e.keyCode == 27) {
+ // unfocus on ESC
+ document.activeElement.blur();
+ stop_event(e);
+ }
}
}
},
true);
+}
+
+init();
+
})();