2 * This script is derived (by Jason Woofenden) from an example script from
3 * uzbl (see uzbl.org) and is thus presumably licensed under the GNU GPLv3. In
4 * any case, Jason's changes are in the public domain.
8 // @name numbered links
9 // @namespace http://patcavit.com/greasemonkey
10 // @description make all links followable with the keyboard
18 // This is activated (and canceled) by pressing the `/~ key. To change it to a
19 // differet key, edit the ascii value on line 301
21 // Also, you can change the character set a few lines down. Don't use the "L"
22 // key unless you change the on line 235.
26 //Just some shortcuts and globals
27 var charset = 'thsnd-rcgmvwb/;789aefijkopquxyz234';
28 var uzblid = 'uzbl_link_hint';
29 var uzbldivid = uzblid + '_div_container';
32 var links = document.links;
33 var forms = document.forms;
34 //Make onlick-links "clickable"
36 HTMLElement.prototype.click = function() {
37 if (typeof this.onclick == 'function') {
44 //Calculate element position to draw the hint
45 //Pretty accurate but on fails in some very fancy cases
46 function elementPosition(el) {
47 var up = el.offsetTop;
48 var left = el.offsetLeft;
49 var width = el.offsetWidth;
50 var height = el.offsetHeight;
51 while (el.offsetParent) {
54 left += el.offsetLeft;
56 return [up, left, width, height];
58 //Calculate if an element is visible
59 function isVisible(el) {
70 if (el.style.display == 'none') {
73 if (el.style.visibility == 'hidden') {
77 return isVisible(el.parentNode);
79 //Calculate if an element is on the viewport.
80 function elementInViewport(el) {
81 offset = elementPosition(el);
84 var width = offset[2];
85 var height = offset[3];
86 return up < window.pageYOffset + window.innerHeight && left < window.pageXOffset + window.innerWidth && (up + height) > window.pageYOffset && (left + width) > window.pageXOffset;
88 //Removes all hints/leftovers that might be generated
90 function removeAllHints() {
91 var elements = doc.getElementById(uzbldivid);
93 elements.parentNode.removeChild(elements);
96 //Generate a hint for an element with the given label
97 //Here you can play around with the style of the hints!
98 function generateHint(el, label) {
99 var pos = elementPosition(el);
100 var hint = doc.createElement('div');
101 hint.setAttribute('name', uzblid);
102 hint.innerText = label;
103 hint.style.display = 'inline';
104 hint.style.backgroundColor = '#B9FF00';
105 hint.style.border = '2px solid #4A6600';
106 hint.style.color = 'black';
107 hint.style.fontSize = '9px';
108 hint.style.fontWeight = 'bold';
109 hint.style.lineHeight = '9px';
110 hint.style.margin = '0px';
111 hint.style.width = 'auto'; // fix broken rendering on w3schools.com
112 hint.style.padding = '1px';
113 hint.style.position = 'absolute';
114 hint.style.zIndex = '1000';
115 hint.style.textTransform = 'uppercase';
116 hint.style.left = Math.max(-1, (pos[1] - (7 + label.length * 9))) + 'px';
117 hint.style.top = (pos[0] + 1) + 'px';
118 var img = el.getElementsByTagName('img');
119 //if (img.length > 0) {
120 //hint.style.top = pos[1] + img[0].height / 2 - 6 + 'px';
122 hint.style.textDecoration = 'none';
123 // hint.style.webkitBorderRadius = '6px'; // slow
126 //Here we choose what to do with an element if we
127 //want to "follow" it. On form elements we "select"
128 //or pass the focus, on links we try to perform a click,
129 //but at least set the href of the link. (needs some improvements)
130 function clickElem(item) {
133 var name = item.tagName;
136 window.location = item.href;
137 } else if (name == 'INPUT') {
138 var type = item.getAttribute('type').toUpperCase();
139 if (type == 'TEXT' || type == 'FILE' || type == 'PASSWORD') {
145 } else if (name == 'TEXTAREA' || name == 'SELECT') {
150 window.location = item.href;
154 //Returns a list of all links (in this version
155 //just the elements itself, but in other versions, we
156 //add the label here.
157 function addLinks() {
159 for (var l = 0; l < links.length; l++) {
161 if (isVisible(li) && elementInViewport(li)) {
167 //Same as above, just for the form elements
168 function addFormElems() {
170 for (var f = 0; f < forms.length; f++) {
171 for (var e = 0; e < forms[f].elements.length; e++) {
172 var el = forms[f].elements[e];
173 if (el && ['INPUT', 'TEXTAREA', 'SELECT'].indexOf(el.tagName) + 1 && isVisible(el) && elementInViewport(el)) {
180 //Draw all hints for all elements passed. "len" is for
181 //the number of chars we should use to avoid collisions
182 function reDrawHints(elems, chars) {
184 var hintdiv = doc.createElement('div');
185 hintdiv.setAttribute('id', uzbldivid);
186 for (var i = 0; i < elems[0].length; i++) {
188 var label = elems[1][i].substring(chars);
189 var h = generateHint(elems[0][i], label);
190 hintdiv.appendChild(h);
194 document.body.appendChild(hintdiv);
197 // pass: number of keys
198 // returns: key length
199 function labelLength(n) {
205 n -= 1; // our highest key will be n-1
208 n = Math.floor(n / charset.length);
214 function intToLabel(n) {
217 label = charset.charAt(n % charset.length) + label;
218 n = Math.floor(n / charset.length);
224 function labelToInt(label) {
227 for(i = 0; i < label.length; ++i) {
229 n += charset.indexOf(label[i]);
233 //Put it all together
234 function followLinks(follow) {
235 if(follow.charAt(0) == 'l') {
236 follow = follow.substr(1);
237 charset = 'thsnlrcgfdbmwvz-/';
239 var s = follow.split('');
240 var linknr = labelToInt(follow);
241 var linkelems = addLinks();
242 var formelems = addFormElems();
243 var elems = [linkelems[0].concat(formelems[0]), linkelems[1].concat(formelems[1])];
244 var len = labelLength(elems[0].length);
245 var oldDiv = doc.getElementById(uzbldivid);
246 var leftover = [[], []];
247 if (s.length == len && linknr < elems[0].length && linknr >= 0) {
248 clickElem(elems[0][linknr]);
252 for (var j = 0; j < elems[0].length; j++) {
254 var label = intToLabel(j);
255 var n = label.length;
256 for (n; n < len; n++) {
257 label = charset.charAt(0) + label;
259 for (var k = 0; k < s.length; k++) {
260 b = b && label.charAt(k) == s[k];
263 leftover[0].push(elems[0][j]);
264 leftover[1].push(label);
267 reDrawHints(leftover, s.length);
298 document.addEventListener(
301 if(e.keyCode == 96) { // change this if you want a different activation key
312 got += String.fromCharCode(e.keyCode);