1 // link follower for uzbl
2 // requires http://github.com/DuClare/uzbl/commit/6c11777067bdb8aac09bba78d54caea04f85e059
4 // first, it needs to be loaded before every time it is used.
5 // One way would be to use something like load_start_handler to send
6 // "act script /usr/share/examples/scripts/linkfollow.js"
7 // (currently, it is recommended to use load_finish_handler since the JS code seems to get
8 // flushed. Using a load_start_handler with a 1s delay works but not always)
10 // when script is loaded, it can be invoked with
11 // bind f* = js hints.set("%s")
12 // bind f_ = js hints.follow("%s")
14 // At the moment, it may be useful to have way of forcing uzbl to load the script
15 // bind :lf = script /usr/share/examples/scripts/linkfollow.js
17 // To enable hint highlighting, add:
18 // set stylesheet_uri = /usr/share/uzbl/examples/data/style.css
20 // based on follow_Numbers.js
22 // TODO: fix styling for the first element
23 // TODO: load the script as soon as the DOM is ready
27 var uzblid = 'uzbl_hint';
28 var uzblclass = 'uzbl_highlight';
29 var uzblclassfirst = 'uzbl_h_first';
36 this.keyPressHandler = keyPressHandler;
38 function elementPosition(el) {
39 var up = el.offsetTop;
40 var left = el.offsetLeft; var width = el.offsetWidth;
41 var height = el.offsetHeight;
43 while (el.offsetParent) {
46 left += el.offsetLeft;
48 return [up, left, width, height];
51 function elementInViewport(offset) {
54 var width = offset[2];
55 var height = offset[3];
56 return (up < window.pageYOffset + window.innerHeight &&
57 left < window.pageXOffset + window.innerWidth &&
58 (up + height) > window.pageYOffset &&
59 (left + width) > window.pageXOffset);
62 function isVisible(el) {
63 if (el == doc) { return true; }
64 if (!el) { return false; }
65 if (!el.parentNode) { return false; }
67 if (el.style.display == 'none') {
70 if (el.style.visibility == 'hidden') {
74 return isVisible(el.parentNode);
77 var hintable = "//a[@href] | //img | //input";
79 function Matcher(str){
80 var numbers = str.replace(/[^\d]/g,"");
81 var words = str.replace(/\d/g,"").split(/\s+/).map(function (n) { return new RegExp(n,"i")});
83 this.toString = toString;
84 this.numbers = numbers;
85 function test(element) {
86 // test all the regexp
87 return words.every(function (regex) { return element.node.textContent.match(regex)});
91 function HintElement(node,pos){
94 this.isHinted = false;
97 this.addHint = function (labelNum) {
99 this.node.className += " " + uzblclass;
101 this.isHinted = true;
104 var hintNode = doc.createElement('div');
105 hintNode.name = uzblid;
106 hintNode.innerText = labelNum;
107 hintNode.style.left = this.position[1] + 'px';
108 hintNode.style.top = this.position[0] + 'px';
109 hintNode.style.position = "absolute";
110 doc.body.firstChild.appendChild(hintNode);
113 this.removeHint = function(){
115 var s = (this.num)?uzblclassfirst:uzblclass;
116 this.node.className = this.node.className.replace(new RegExp(" "+s,"g"),"");
117 this.isHinted = false;
122 function createHintDiv(){
123 var hintdiv = doc.getElementById(uzblid);
125 hintdiv.parentNode.removeChild(hintdiv);
127 hintdiv = doc.createElement("div");
128 hintdiv.setAttribute('id',uzblid);
129 doc.body.insertBefore(hintdiv,doc.body.firstChild);
135 doc.body.setAttribute("onkeyup","hints.keyPressHandler(event)");
136 hintdiv = createHintDiv();
139 var items = doc.evaluate(hintable,doc,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
140 for (var i = 0;i<items.snapshotLength;i++){
141 var item = items.snapshotItem(i);
142 var pos = elementPosition(item);
143 if(isVisible && elementInViewport(elementPosition(item))){
144 visible.push(new HintElement(item,pos));
151 visible.forEach(function (n) { n.removeHint(); } );
152 hintdiv = doc.getElementById(uzblid);
154 hintdiv.parentNode.removeChild(hintdiv);
155 hintdiv = doc.getElementById(uzblid);
159 function update(str) {
160 var match = new Matcher(str);
161 hintdiv = createHintDiv();
163 visible.forEach(function (n) {
173 if(str.length == 0) init();
177 function keyPressHandler(e) {
178 var kC = window.event ? event.keyCode: e.keyCode;
179 var Esc = window.event ? 27 : e.DOM_VK_ESCAPE;
182 doc.body.removeAttribute("onkeyup");
186 function follow(str){
187 var m = new Matcher(str);
189 var items = visible.filter(function (n) { return n.isHinted });
191 var num = parseInt(m.numbers,10);
193 var item = items[num-1].node;
195 var item = items[0].node;
198 item.style.borderStyle = "dotted";
199 item.style.borderWidth = "thin";
201 var name = item.tagName;
203 if(item.click) {item.click()};
204 window.location = item.href;
205 } else if (name == 'INPUT') {
206 var type = item.getAttribute('type').toUpperCase();
207 if (type == 'TEXT' || type == 'FILE' || type == 'PASSWORD') {
213 } else if (name == 'TEXTAREA' || name == 'SELECT') {
218 window.location = item.href;
224 var hints = new Hints();
225 //document.attachEvent("onKeyUp",hints.keyPressHandler);