aboutsummaryrefslogtreecommitdiffhomepage
path: root/examples
diff options
context:
space:
mode:
authorGravatar Brendan Taylor <whateley@gmail.com>2011-03-06 02:40:35 -0700
committerGravatar Brendan Taylor <whateley@gmail.com>2011-03-06 02:40:35 -0700
commit941b92e60d4f636e16ab0d3bd0814073b27a0642 (patch)
tree2cf4b2ce18426ae9a6fb56d55b27e5b649f8b97c /examples
parent1197e8ddbc8348e9437ada42b35c72f38d4698ce (diff)
frames-enabled follow.js
Diffstat (limited to 'examples')
-rw-r--r--examples/data/scripts/follow.js291
1 files changed, 153 insertions, 138 deletions
diff --git a/examples/data/scripts/follow.js b/examples/data/scripts/follow.js
index 2976834..1551f1c 100644
--- a/examples/data/scripts/follow.js
+++ b/examples/data/scripts/follow.js
@@ -12,81 +12,101 @@
*/
uzbl.follow = function() {
-
// Export
- charset = arguments[0]
- keypress = arguments[1]
- newwindow = arguments[2]
+ charset = arguments[0];
+ newwindow = arguments[2];
// Some shortcuts and globals
uzblid = 'uzbl_link_hint';
uzbldivid = uzblid + '_div_container';
- doc = document;
- win = window;
- links = doc.links;
- forms = doc.forms;
-
- // Make onclick-links "clickable"
- try {
- HTMLElement.prototype.click = function() {
- if (typeof this.onclick == 'function') {
- this.onclick({ type: 'click' });
- }
- }
- } catch(e) {}
+ var keypress = arguments[1];
return arguments.callee.followLinks(keypress);
}
+uzbl.follow.isFrame = function(el) {
+ return (el.tagName == "FRAME" || el.tagName == "IFRAME");
+}
+
+// find the document that the given element belongs to
+uzbl.follow.getDocument = function(el) {
+ if (this.isFrame(el))
+ return el.contentDocument;
+
+ var doc = el;
+ while (doc.parentNode !== null)
+ doc = doc.parentNode;
+ return doc;
+}
+
+// find all documents in the display, searching frames recursively
+uzbl.follow.documents = function() {
+ return this.windows().map(function(w) { return w.document; }).filter(function(d) { return d != null; });
+}
+
+// find all windows in the display, searching for frames recursively
+uzbl.follow.windows = function(w) {
+  w = (typeof w == 'undefined') ? window.top : w;
+
+ var wins = [w];
+ var frames = w.frames;
+ for(var i = 0; i < frames.length; i++)
+ wins = wins.concat(uzbl.follow.windows(frames[i]));
+ return wins;
+}
+
+// search all frames for elements matching the given CSS selector
+uzbl.follow.query = function(selector) {
+ var res = [];
+ this.documents().forEach(function (doc) {
+ var set = doc.body.querySelectorAll(selector);
+ // convert the NodeList to an Array
+ set = Array.prototype.slice.call(set);
+ res = res.concat(set);
+ });
+ return res;
+}
+
// Calculate element position to draw the hint
-// Pretty accurate but fails in some very fancy cases
uzbl.follow.elementPosition = function(el) {
- var up = el.offsetTop;
- var left = el.offsetLeft;
- var width = el.offsetWidth;
- var height = el.offsetHeight;
- while (el.offsetParent) {
- el = el.offsetParent;
- up += el.offsetTop;
- left += el.offsetLeft;
- }
- return [up, left, width, height];
-}
+ var rect = el.getBoundingClientRect();
-// Calculate if an element is visible
-uzbl.follow.isVisible = function(el) {
- if (el == doc) return true;
- if (!el) return false;
- if (!el.parentNode) return false;
+ var left, up;
- if (el.style) {
- if (el.style.display == 'none') return false;
- if (el.style.visibility == 'hidden') return false;
+ if (uzbl.follow.isFrame(el)) {
+ left = document.defaultView.scrollX;
+ up = document.defaultView.scrollY;
+ } else {
+ left = Math.max((rect.left + document.defaultView.scrollX), document.defaultView.scrollX);
+ up = Math.max((rect.top + document.defaultView.scrollY), document.defaultView.scrollY);
}
- return this.isVisible(el.parentNode);
+
+ return [up, left, rect.width, rect.height];
}
// Calculate if an element is on the viewport.
uzbl.follow.elementInViewport = function(el) {
- offset = this.elementPosition(el);
- var up = offset[0];
- var left = offset[1];
- var width = offset[2];
+ offset = uzbl.follow.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;
+ return up < window.pageYOffset + window.innerHeight &&
+ left < window.pageXOffset + window.innerWidth &&
+ (up + height) > window.pageYOffset &&
+ (left + width) > window.pageXOffset;
}
// Removes all hints/leftovers that might be generated
// by this script.
-uzbl.follow.removeAllHints = function() {
+uzbl.follow.removeAllHints = function(doc) {
var elements = doc.getElementById(uzbldivid);
if (elements) elements.parentNode.removeChild(elements);
}
// Generate a hint for an element with the given label
// Here you can play around with the style of the hints!
-uzbl.follow.generateHint = function(el, label) {
- var pos = this.elementPosition(el);
+uzbl.follow.generateHint = function(doc, el, label, top, left) {
var hint = doc.createElement('div');
hint.setAttribute('name', uzblid);
hint.innerText = label;
@@ -105,10 +125,10 @@ uzbl.follow.generateHint = function(el, label) {
hint.style.padding = '1px';
hint.style.position = 'absolute';
hint.style.zIndex = '1000';
- hint.style.left = pos[1] + 'px';
- hint.style.top = pos[0] + 'px';
hint.style.textDecoration = 'none';
hint.style.webkitTransform = 'translate(-5px,-5px)';
+ hint.style.top = top + 'px';
+ hint.style.left = left + 'px';
return hint;
}
@@ -117,79 +137,53 @@ uzbl.follow.generateHint = function(el, label) {
// or pass the focus, on links we try to perform a click,
// but at least set the href of the link. (needs some improvements)
uzbl.follow.clickElem = function(item) {
- this.removeAllHints();
- if (item) {
- if (newwindow && item.tagName == 'A') {
- window.open(item.href);
- return "XXXRESET_MODEXXX"
- }
- else {
- var name = item.tagName;
- if (name == 'A') {
- item.click();
- window.location = item.href;
- return "XXXRESET_MODEXXX";
- } else if (name == 'INPUT') {
- var type = item.getAttribute('type').toUpperCase();
- if (type == 'TEXT' || type == 'FILE' || type == 'PASSWORD') {
- item.focus();
- item.select();
- return "XXXEMIT_FORM_ACTIVEXXX";
- } else {
- item.click();
- return "XXXRESET_MODEXXX";
- }
- } else if (name == 'TEXTAREA' || name == 'SELECT') {
- item.focus();
- item.select();
- return "XXXEMIT_FORM_ACTIVEXXX";
- } else {
- item.click();
- window.location = item.href;
- return "XXXRESET_MODEXXX";
- }
- }
- }
-}
+ if(!item) return;
+ var name = item.tagName;
-// Returns a list of all links (in this version
-// just the elements itself, but in other versions, we
-// add the label here.
-uzbl.follow.addLinks = function() {
- res = [[], []];
- for (var l = 0; l < links.length; l++) {
- var li = links[l];
- if (this.isVisible(li) && this.elementInViewport(li)) res[0].push(li);
- }
- return res;
-}
-
-// Same as above, just for the form elements
-uzbl.follow.addFormElems = function() {
- 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];
- if (el && ['INPUT', 'TEXTAREA', 'SELECT'].indexOf(el.tagName) + 1 && this.isVisible(el) && this.elementInViewport(el)) res[0].push(el);
+ if (name == 'INPUT') {
+ var type = item.getAttribute('type').toUpperCase();
+ if (type == 'TEXT' || type == 'FILE' || type == 'PASSWORD') {
+ item.focus();
+ item.select();
+ return "XXXEMIT_FORM_ACTIVEXXX";
}
+ // otherwise fall through to a simulated mouseclick.
+ } else if (name == 'TEXTAREA' || name == 'SELECT') {
+ item.focus();
+ item.select();
+ return "XXXEMIT_FORM_ACTIVEXXX";
}
- return res;
+
+ // simulate a mouseclick to activate the element
+ var mouseEvent = document.createEvent("MouseEvent");
+ mouseEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ item.dispatchEvent(mouseEvent);
+ return "XXXRESET_MODEXXX";
}
-// Draw all hints for all elements passed. "len" is for
-// the number of chars we should use to avoid collisions
+// Draw all hints for all elements passed.
uzbl.follow.reDrawHints = function(elems, chars) {
- this.removeAllHints();
- var hintdiv = doc.createElement('div');
- hintdiv.setAttribute('id', uzbldivid);
- for (var i = 0; i < elems[0].length; i++) {
- if (elems[0][i]) {
- var label = elems[1][i].substring(chars);
- var h = this.generateHint(elems[0][i], label);
- hintdiv.appendChild(h);
- }
- }
- if (document.body) document.body.appendChild(hintdiv);
+ var elements = elems.map(function(pair) { return pair[0] });
+ var labels = elems.map(function(pair) { return pair[1].substring(chars) });
+ // we have to calculate element positions before we modify the DOM
+ // otherwise the elementPosition call slows way down.
+ var positions = elements.map(uzbl.follow.elementPosition);
+
+ this.documents().forEach(function(doc) {
+ uzbl.follow.removeAllHints(doc);
+ if (!doc.body) return;
+ doc.hintdiv = doc.createElement('div');
+ doc.hintdiv.setAttribute('id', uzbldivid);
+ doc.body.appendChild(doc.hintdiv);
+ });
+
+ elements.forEach(function(el, i) {
+ var label = labels[i];
+ var pos = positions[i];
+ var doc = uzbl.follow.getDocument(el);
+ var h = uzbl.follow.generateHint(doc, el, label, pos[0], pos[1]);
+ doc.hintdiv.appendChild(h);
+ });
}
// pass: number of keys
@@ -221,8 +215,7 @@ uzbl.follow.intToLabel = function(n) {
// returns: number
uzbl.follow.labelToInt = function(label) {
var n = 0;
- var i;
- for(i = 0; i < label.length; ++i) {
+ for(var i = 0; i < label.length; ++i) {
n *= charset.length;
n += charset.indexOf(label[i]);
}
@@ -231,32 +224,54 @@ uzbl.follow.labelToInt = function(label) {
// Put it all together
uzbl.follow.followLinks = function(follow) {
- //if(follow.charAt(0) == 'l') {
- // follow = follow.substr(1);
- // charset = 'thsnlrcgfdbmwvz-/';
- //}
var s = follow.split('');
var linknr = this.labelToInt(follow);
- if (document.body) document.body.setAttribute('onkeyup', 'keyPressHandler(event)');
- var linkelems = this.addLinks();
- var formelems = this.addFormElems();
- var elems = [linkelems[0].concat(formelems[0]), linkelems[1].concat(formelems[1])];
- var len = this.labelLength(elems[0].length);
- var oldDiv = doc.getElementById(uzbldivid);
- var leftover = [[], []];
- if (s.length == len && linknr < elems[0].length && linknr >= 0)
- return this.clickElem(elems[0][linknr]);
-
- for (var j = 0; j < elems[0].length; j++) {
+
+ var followable = 'a, area, textarea, select, input:not([type=hidden]), button';
+ var uri = 'a, area, frame, iframe';
+ //var focusable = 'a, area, textarea, select, input:not([type=hidden]), button, frame, iframe, applet, object';
+ //var desc = '*[title], img[alt], applet[alt], area[alt], input[alt]';
+ //var image = 'img, input[type=image]';
+
+ if(newwindow)
+ var res = this.query(uri);
+ else
+ var res = this.query(followable);
+
+ var elems = res.filter(uzbl.follow.elementInViewport);
+ var len = this.labelLength(elems.length);
+
+ if (s.length == len && linknr < elems.length && linknr >= 0) {
+ // an element has been selected!
+ var el = elems[linknr];
+
+ // clear all of our hints
+ this.documents().forEach(uzbl.follow.removeAllHints);
+
+ if (newwindow) {
+ // we're opening a new window using the URL attached to this element
+ var uri = el.src || el.href;
+ if(uri.match(/javascript:/)) return;
+ window.open(uri);
+ return "XXXRESET_MODEXXX"
+ }
+
+ // we're just going to click the element
+ return this.clickElem(el);
+ }
+
+ var leftover = [];
+ for (var j = 0; j < elems.length; j++) {
var b = true;
var label = this.intToLabel(j);
var n = label.length;
- for (n; n < len; n++) label = charset.charAt(0) + label;
- for (var k = 0; k < s.length; k++) b = b && label.charAt(k) == s[k];
- if (b) {
- leftover[0].push(elems[0][j]);
- leftover[1].push(label);
- }
+ for (n; n < len; n++)
+ label = charset.charAt(0) + label;
+ for (var k = 0; k < s.length; k++)
+ b = b && label.charAt(k) == s[k];
+ if (b)
+ leftover.push([elems[j], label]);
}
+
this.reDrawHints(leftover, s.length);
}