add missing argument to scroll_horiz()
[luakit.git] / scripts / follow.js
blob6f57713021609d0bcc4bcd9e5bd7d4572225d090
1 var elements = [];
2 var active_arr = [];
3 var hints;
4 var overlays;
5 var active;
6 var lastpos = 0;
7 var last_input = "";
8 var last_strings = [];
10 focus_color = "#00ff00";
11 normal_color = "#ffff99";
12 opacity = 0.3;
13 border = "1px dotted #000000";
15 hint_foreground = "#ffffff";
16 hint_background = "#000088";
17 hint_border = "2px dashed #000000";
18 hint_opacity = 0.4;
19 hint_font = "11px monospace bold";
21 vertical_offset = 0;
22 horizontal_offset = -10;
24 function Hint(element) {
25 this.element = element;
26 this.rect = element.getBoundingClientRect();
28 function create_span(element, h, v) {
29 var span = document.createElement("span");
30 var leftpos = Math.max((element.rect.left + document.defaultView.scrollX), document.defaultView.scrollX) + h;
31 var toppos = Math.max((element.rect.top + document.defaultView.scrollY), document.defaultView.scrollY) + v;
32 span.style.position = "absolute";
33 span.style.left = leftpos + "px";
34 span.style.top = toppos + "px";
35 return span;
37 function create_hint(element) {
38 var hint = create_span(element, horizontal_offset, vertical_offset - element.rect.height/2);
39 hint.style.font = hint_font;
40 hint.style.color = hint_foreground;
41 hint.style.background = hint_background;
42 hint.style.opacity = hint_opacity;
43 hint.style.border = hint_border;
44 hint.style.zIndex = 10001;
45 hint.style.visibility = 'visible';
46 return hint;
48 function create_overlay(element) {
49 var overlay = create_span(element, 0, 0);
50 overlay.style.width = element.rect.width + "px";
51 overlay.style.height = element.rect.height + "px";
52 overlay.style.opacity = opacity;
53 overlay.style.backgroundColor = normal_color;
54 overlay.style.border = border;
55 overlay.style.zIndex = 10000;
56 overlay.style.visibility = 'visible';
57 overlay.addEventListener( 'click', function() { click_element(element); }, false );
58 return overlay;
61 this.hint = create_hint(this);
62 this.overlay = create_overlay(this);
64 function reload_hints(array, input, keep) {
65 var length = array.length;
66 var start = length < 10 ? 1 : length < 100 ? 10 : 100;
67 var bestposition = 37;
69 for (var i=0; i<length; i++) {
70 var e = array[i];
71 e.overlay.style.backgroundColor = normal_color;
72 if (!e.hint.parentNode && !e.hint.firstchild) {
73 var content = document.createTextNode(start + i);
74 e.hint.appendChild(content);
75 hints.appendChild(e.hint);
77 else if (!keep) {
78 e.hint.textContent = start + i;
80 if (!e.overlay.parentNode && !e.overlay.firstchild) {
81 overlays.appendChild(e.overlay);
83 if (input && bestposition != 0) {
84 // match word beginnings
85 var content = e.element.textContent.toLowerCase().split(" ");
86 for (var cl=0; cl<content.length; cl++) {
87 if (content[cl].toLowerCase().indexOf(input) == 0) {
88 if (cl < bestposition) {
89 lastpos = i;
90 bestposition = cl;
91 break;
97 active = array[lastpos];
98 active.overlay.style.backgroundColor = focus_color;
100 function click_element(e) {
101 var mouseEvent = document.createEvent("MouseEvent");
102 mouseEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
103 e.element.dispatchEvent(mouseEvent);
104 clear();
106 function show_hints() {
107 document.activeElement.blur();
108 if ( elements ) {
109 var res = document.body.querySelectorAll('a, area, textarea, select, link, input:not([type=hidden]), button, frame, iframe');
110 hints = document.createElement("div");
111 overlays = document.createElement("div");
112 for (var i=0; i<res.length; i++) {
113 var e = new Hint(res[i]);
114 var rects = e.element.getClientRects()[0];
115 var r = e.rect;
116 if (!r || r.top > window.innerHeight || r.bottom < 0 || r.left > window.innerWidth || r < 0 || !rects ) {
117 continue;
119 var style = document.defaultView.getComputedStyle(e.element, null);
120 if (style.getPropertyValue("visibility") != "visible" || style.getPropertyValue("display") == "none") {
121 continue;
123 elements.push(e);
125 elements.sort( function(a,b) { return a.rect.top - b.rect.top; });
126 active_arr = elements;
127 reload_hints(elements);
128 document.body.appendChild(hints);
129 document.body.appendChild(overlays);
132 function is_input(element) {
133 var e = element.element;
134 var type = e.type.toLowerCase();
135 if (e.tagName == "INPUT" || e.tagName == "TEXTAREA" ) {
136 if (type == "radio" || type == "checkbox") {
137 e.checked = !e.checked;
139 else if (type == "submit" || type == "reset" || type == "button") {
140 click_element(element);
142 else {
143 e.focus();
145 return true;
147 return false;
149 function update_hints(input) {
150 var array = [];
151 var text_content;
152 var keep = false;
153 if (input) {
154 input = input.toLowerCase();
156 for (var i=0; i<active_arr.length; i++) {
157 var e = active_arr[i];
158 if (parseInt(input) == input) {
159 text_content = e.hint.textContent;
160 keep = true;
162 else {
163 text_content = e.element.textContent.toLowerCase();
165 if (text_content.match(input)) {
166 array.push(e);
168 else {
169 e.hint.style.visibility = 'hidden';
170 e.overlay.style.visibility = 'hidden';
173 active_arr = array;
174 if (array.length == 0) {
175 clear();
176 return;
178 if (array.length == 1) {
179 if (evaluate(array[0])) {
180 return "__HINT_EVALUATED__"
183 reload_hints(array, input, keep);
185 function clear() {
186 if (overlays && overlays.parentNode) {
187 overlays.parentNode.removeChild(overlays);
189 if (hints && hints.parentNode) {
190 hints.parentNode.removeChild(hints);
192 elements = [];
193 active_arr = [];
194 active = undefined;
196 function evaluate(element) {
197 var ret = false;
198 var e = element.element;
199 if (!is_input(element) && e.href) {
200 if (e.href.match(/javascript:/) || (e.type.toLowerCase() == "button")) {
201 click_element(element);
202 ret = true;
204 else {
205 document.location = e.href;
206 ret = true;
209 clear();
210 return ret;
212 function get_active() {
213 return evaluate(active);
215 function focus(newpos) {
216 active_arr[lastpos].overlay.style.backgroundColor = normal_color;
217 active_arr[newpos].overlay.style.backgroundColor = focus_color;
218 active = active_arr[newpos];
219 lastpos = newpos;
221 function focus_next() {
222 var newpos = lastpos == active_arr.length-1 ? 0 : lastpos + 1;
223 focus(newpos);
225 function focus_prev() {
226 var newpos = lastpos == 0 ? active_arr.length-1 : lastpos - 1;
227 focus(newpos);
230 function update(input) {
231 input = input.replace(/(\d+)$/, " $1");
232 strings = input.split(" ");
233 if (input.length < last_input.length || strings.length < last_strings.length) {
234 // user removed a char
235 clear();
236 show_hints();
237 for (var i = 0; i < strings.length; i += 1) {
238 update_hints(strings[i]);
240 } else {
241 update_hints(strings[strings.length-1]);
243 last_input = input;
244 last_strings = strings;