1 /* This is the basic linkfollowing script.
2 * Its pretty stable, only using numbers to navigate.
4 * TODO: Some pages mess around a lot with the zIndex which
5 * lets some hints in the background.
6 * TODO: Some positions are not calculated correctly (mostly
7 * because of uber-fancy-designed-webpages. Basic HTML and CSS
9 * TODO: Still some links can't be followed/unexpected things
10 * happen. Blame some freaky webdesigners ;)
13 //Just some shortcuts and globals
14 var uzblid = 'uzbl_link_hint';
15 var uzbldivid = uzblid + '_div_container';
18 var links = document.links;
19 var forms = document.forms;
20 //Make onlick-links "clickable"
22 HTMLElement.prototype.click = function() {
23 if (typeof this.onclick == 'function') {
30 //Catch the ESC keypress to stop linkfollowing
31 function keyPressHandler(e) {
32 var kC = window.event ? event.keyCode: e.keyCode;
33 var Esc = window.event ? 27 : e.DOM_VK_ESCAPE;
38 //Calculate element position to draw the hint
39 //Pretty accurate but on fails in some very fancy cases
40 function elementPosition(el) {
41 var up = el.offsetTop;
42 var left = el.offsetLeft;
43 var width = el.offsetWidth;
44 var height = el.offsetHeight;
45 while (el.offsetParent) {
48 left += el.offsetLeft;
50 return [up, left, width, height];
52 //Calculate if an element is visible
53 function isVisible(el) {
64 if (el.style.display == 'none') {
67 if (el.style.visibility == 'hidden') {
71 return isVisible(el.parentNode);
73 //Calculate if an element is on the viewport.
74 function elementInViewport(el) {
75 offset = elementPosition(el);
78 var width = offset[2];
79 var height = offset[3];
80 return up < window.pageYOffset + window.innerHeight && left < window.pageXOffset + window.innerWidth && (up + height) > window.pageYOffset && (left + width) > window.pageXOffset;
82 //Removes all hints/leftovers that might be generated
84 function removeAllHints() {
85 var elements = doc.getElementById(uzbldivid);
87 elements.parentNode.removeChild(elements);
90 //Generate a hint for an element with the given label
91 //Here you can play around with the style of the hints!
92 function generateHint(el, label) {
93 var pos = elementPosition(el);
94 var hint = doc.createElement('div');
95 hint.setAttribute('name', uzblid);
96 hint.innerText = label;
97 hint.style.display = 'inline';
98 hint.style.backgroundColor = '#B9FF00';
99 hint.style.border = '2px solid #4A6600';
100 hint.style.color = 'black';
101 hint.style.fontSize = '9px';
102 hint.style.fontWeight = 'bold';
103 hint.style.lineHeight = '9px';
104 hint.style.margin = '0px';
105 hint.style.padding = '1px';
106 hint.style.position = 'absolute';
107 hint.style.zIndex = '1000';
108 hint.style.left = pos[1] + 'px';
109 hint.style.top = pos[0] + 'px';
110 var img = el.getElementsByTagName('img');
111 if (img.length > 0) {
112 hint.style.left = pos[1] + img[0].width / 2 + 'px';
114 hint.style.textDecoration = 'none';
115 hint.style.webkitBorderRadius = '6px';
116 // Play around with this, pretty funny things to do :)
117 hint.style.webkitTransform = 'scale(1) rotate(0deg) translate(-6px,-5px)';
120 //Here we choose what to do with an element if we
121 //want to "follow" it. On form elements we "select"
122 //or pass the focus, on links we try to perform a click,
123 //but at least set the href of the link. (needs some improvements)
124 function clickElem(item) {
127 var name = item.tagName;
130 window.location = item.href;
131 } else if (name == 'INPUT') {
132 var type = item.getAttribute('type').toUpperCase();
133 if (type == 'TEXT' || type == 'FILE' || type == 'PASSWORD') {
139 } else if (name == 'TEXTAREA' || name == 'SELECT') {
144 window.location = item.href;
148 //Returns a list of all links (in this version
149 //just the elements itself, but in other versions, we
150 //add the label here.
151 function addLinks() {
153 for (var l = 0; l < links.length; l++) {
155 if (isVisible(li) && elementInViewport(li)) {
161 //Same as above, just for the form elements
162 function addFormElems() {
164 for (var f = 0; f < forms.length; f++) {
165 for (var e = 0; e < forms[f].elements.length; e++) {
166 var el = forms[f].elements[e];
167 if (el && ['INPUT', 'TEXTAREA', 'SELECT'].indexOf(el.tagName) + 1 && isVisible(el) && elementInViewport(el)) {
174 //Draw all hints for all elements passed. "len" is for
175 //the number of chars we should use to avoid collisions
176 function reDrawHints(elems, chars) {
178 var hintdiv = doc.createElement('div');
179 hintdiv.setAttribute('id', uzbldivid);
180 for (var i = 0; i < elems[0].length; i++) {
182 var label = elems[1][i].substring(chars);
183 var h = generateHint(elems[0][i], label);
184 hintdiv.appendChild(h);
188 document.body.appendChild(hintdiv);
191 //Put it all together
192 function followLinks(follow) {
193 var s = follow.split('');
194 var linknr = parseInt(follow, 10);
195 if (document.body) document.body.setAttribute('onkeyup', 'keyPressHandler(event)');
196 var linkelems = addLinks();
197 var formelems = addFormElems();
198 var elems = [linkelems[0].concat(formelems[0]), linkelems[1].concat(formelems[1])];
199 var len = (elems[0].length + '').length;
200 var oldDiv = doc.getElementById(uzbldivid);
201 var leftover = [[], []];
202 if (linknr + 1 && s.length == len && linknr < elems[0].length && linknr >= 0) {
203 clickElem(elems[0][linknr]);
205 for (var j = 0; j < elems[0].length; j++) {
208 var n = label.length;
209 for (n; n < len; n++) {
212 for (var k = 0; k < s.length; k++) {
213 b = b && label.charAt(k) == s[k];
216 leftover[0].push(elems[0][j]);
217 leftover[1].push(label);
220 reDrawHints(leftover, s.length);