aboutsummaryrefslogtreecommitdiffhomepage
path: root/examples/data/scripts/follower.js
diff options
context:
space:
mode:
Diffstat (limited to 'examples/data/scripts/follower.js')
-rw-r--r--examples/data/scripts/follower.js420
1 files changed, 0 insertions, 420 deletions
diff --git a/examples/data/scripts/follower.js b/examples/data/scripts/follower.js
deleted file mode 100644
index 604b779..0000000
--- a/examples/data/scripts/follower.js
+++ /dev/null
@@ -1,420 +0,0 @@
-// A Link Follower for Uzbl.
-// P.C. Shyamshankar <sykora@lucentbeing.com>
-//
-// WARNING: this script depends on the Uzbl object which is now disabled for
-// WARNING security reasons. So the script currently doesn't work but it's
-// WARNING interesting nonetheless
-//
-// Based extensively (like copy-paste) on the follow_numbers.js and
-// linkfollow.js included with uzbl, but modified to be more customizable and
-// extensible.
-//
-// Usage
-// -----
-//
-// First, you'll need to make sure the script is loaded on each page. This can
-// be done with:
-//
-// @on_event LOAD_COMMIT script /path/to/follower.js
-//
-// Then you can bind it to a key:
-//
-// @bind f* = js follower.follow('%s', matchSpec, handler, hintStyler)
-//
-// where matchSpec, handler and hintStyler are parameters which control the
-// operation of follower. If you don't want to customize any further, you can
-// set these to follower.genericMatchSpec, follower.genericHandler and
-// follower.genericHintStyler respectively.
-//
-// For example,
-//
-// @bind f* = js follower.follow('%s', follower.genericMatchSpec, follower.genericHandler, follower.genericHintStyler)
-// @bind F* = js follower.follow('%s', follower.onlyLinksMatchSpec, follower.newPageHandler, follower.newPageHintStyler)
-//
-// In order to make hints disappear when pressing a key (the Escape key, for
-// example), you can do this:
-//
-// @bind <Escape> = js follower.clearHints()
-//
-// If your Escape is already bound to something like command mode, chain it.
-//
-// Alternatively, you can tell your <Escape> key to emit an event, and handle
-// that instead.
-//
-// @bind <Escape> = event ESCAPE
-// @on_event ESCAPE js follower.clearHints()
-//
-// Customization
-// -------------
-//
-// If however you do want to customize, 3 Aspects of the link follower can be
-// customized with minimal pain or alteration to the existing code base:
-//
-// * What elements are hinted.
-// * The style of the hints displayed.
-// * How the hints are handled.
-//
-// In order to customize behavior, write an alternative, and pass that in to
-// follower.follow invocation. You _will_ have to modify this script, but only
-// locally, it beats having to copy the entire script under a new name and
-// modify.
-//
-// TODO:
-// * Whatever all the other TODOs in the file say.
-// * Find out how to do default arguments in Javascript.
-// * Abstract out the hints into a Hint object, make hintables a list of hint
-// objects instead of two lists.
-
-// Helpers
-String.prototype.lpad = function(padding, length) {
- var padded = this;
- while (padded.length < length) {
- padded = padding + padded;
- }
-
- return padded;
-}
-
-function Follower() {
-
- // Globals
- var uzblID = 'uzbl-follow'; // ID to apply to each hint.
- var uzblContainerID = 'uzbl-follow-container'; // ID to apply to the div containing hints.
-
- // Translation table, used to display something other than numbers as hint
- // labels. Typically set to the ten keys of the home row.
- //
- // Must have exactly 10 elements.
- //
- // I haven't parameterized this, to make it customizable. Should I? Do
- // people really use more than one set of keys at a time?
- var translation = ["a", "r", "s", "t", "d", "h", "n", "e", "i", "o"];
-
- // MatchSpecs
- // These are XPath expressions which indicate which elements will be hinted.
- // Use multiple expressions for different situations, like hinting only form
- // elements, or only links, etc.
- //
- // TODO: Check that these XPath expressions are correct, and optimize/make
- // them more elegant. Preferably by someone who actually knows XPath, unlike
- // me.
-
- // Vimperator default (copy-pasted, I never used vimperator).
- this.genericMatchSpec = " //*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @role='link' or @href] | //input[not(@type='hidden')] | //a | //area | //iframe | //textarea | //button | //select";
-
- // Matches only links, suitable for opening in a new instance (I think).
- this.onlyLinksMatchSpec = " //*[@href] | //a | //area";
-
- // Follow Handlers
- // These decide how an element should be 'followed'. The handler is passed
- // the element in question.
-
- // Generic Handler, opens links in the same instance, emits the FORM_ACTIVE
- // event if a form element was chosen. Also clears the keycmd.
- this.genericHandler = function(node) {
- if (node) {
- if (window.itemClicker != undefined) {
- window.itemClicker(node);
- } else {
- var tag = node.tagName.toLowerCase();
- if (tag == 'a') {
- node.click();
- window.location = node.href;
- } else if (tag == 'input') {
- var inputType = node.getAttribute('type');
- if (inputType == undefined)
- inputType = 'text';
-
- inputType = inputType.toLowerCase();
-
- if (inputType == 'text' || inputType == 'file' || inputType == 'password') {
- node.focus();
- node.select();
- } else {
- node.click();
- }
- Uzbl.run("event FORM_ACTIVE");
- } else if (tag == 'textarea'|| tag == 'select') {
- node.focus();
- node.select();
- Uzbl.run("event FORM_ACTIVE");
- } else {
- node.click();
- if ((node.href != undefined) && node.href)
- window.location = node.href;
- }
- }
- }
- Uzbl.run("event SET_KEYCMD");
- }
-
- // Handler to open links in a new page. The rest is the same as before.
- this.newPageHandler = function(node) {
- if (node) {
- if (window.itemClicker != undefined) {
- window.itemClicker(node);
- } else {
- var tag = node.tagName.toLowerCase();
- if (tag == 'a') {
- node.click();
- Uzbl.run("@new_window " + node.href);
- } else if (tag == 'input') {
- var inputType = node.getAttribute('type');
- if (inputType == undefined)
- inputType = 'text';
-
- inputType = inputType.toLowerCase();
-
- if (inputType == 'text' || inputType == 'file' || inputType == 'password') {
- node.focus();
- node.select();
- } else {
- node.click();
- }
- Uzbl.run("event FORM_ACTIVE");
- } else if (tag == 'textarea'|| tag == 'select') {
- node.focus();
- node.select();
- Uzbl.run("event FORM_ACTIVE");
- } else {
- node.click();
- if ((node.href != undefined) && node.href)
- window.location = node.href;
- }
- }
- }
- Uzbl.run("event SET_KEYCMD");
- };
-
- // Hint styling.
- // Pretty much any attribute of the hint object can be modified here, but it
- // was meant to change the styling. Useful to differentiate between hints
- // with different handlers.
- //
- // Hint stylers are applied at the end of hint creation, so that they
- // override the defaults.
-
- this.genericHintStyler = function(hint) {
- hint.style.backgroundColor = '#AAAAAA';
- hint.style.border = '2px solid #4A6600';
- hint.style.color = 'black';
- hint.style.fontSize = '10px';
- hint.style.fontWeight = 'bold';
- hint.style.lineHeight = '12px';
- return hint;
- };
-
- this.newPageHintStyler = function(hint) {
- hint.style.backgroundColor = '#FFCC00';
- hint.style.border = '2px solid #4A6600';
- hint.style.color = 'black';
- hint.style.fontSize = '10px';
- hint.style.fontWeight = 'bold';
- hint.style.lineHeight = '12px';
- return hint;
- };
-
- // Beyond lies a jungle of pasta and verbosity.
-
- // Translate a numeric label using the translation table.
- function translate(digitLabel, translationTable) {
- translatedLabel = '';
- for (var i = 0; i < digitLabel.length; i++) {
- translatedLabel += translationTable[digitLabel.charAt(i)];
- }
-
- return translatedLabel;
- }
-
- function computeElementPosition(element) {
- var up = element.offsetTop;
- var left = element.offsetLeft;
- var width = element.offsetWidth;
- var height = element.offsetHeight;
-
- while (element.offsetParent) {
- element = element.offsetParent;
- up += element.offsetTop;
- left += element.offsetLeft;
- }
-
- return {up: up, left: left, width: width, height: height};
- }
-
- // Pretty much copy-pasted from every other link following script.
- function isInViewport(element) {
- offset = computeElementPosition(element);
-
- var up = offset.up;
- var left = offset.left;
- var width = offset.width;
- var height = offset.height;
-
- return up < window.pageYOffset + window.innerHeight &&
- left < window.pageXOffset + window.innerWidth &&
- (up + height) > window.pageYOffset &&
- (left + width) > window.pageXOffset;
- }
-
- function isVisible(element) {
- if (element == document) {
- return true;
- }
-
- if (!element){
- return false;
- }
-
- if (element.style) {
- if (element.style.display == 'none' || element.style.visibiilty == 'hidden') {
- return false;
- }
- }
-
- return isVisible(element.parentNode);
- }
-
- function generateHintContainer() {
- var container = document.getElementById(uzblContainerID);
- if (container) {
- container.parentNode.removeChild(container);
- }
-
- container = document.createElement('div');
- container.id = uzblContainerID;
-
- if (document.body) {
- document.body.appendChild(container);
- }
- return container;
- }
-
- // Generate everything that is to be hinted, as per the given matchSpec.
- // hintables[0] refers to the items, hintables[1] to their labels.
- function generateHintables(matchSpec) {
- var hintables = [[], []];
-
- var itemsFromXPath = document.evaluate(matchSpec, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
-
- for (var i = 0; i < itemsFromXPath.snapshotLength; ++i) {
- var element = itemsFromXPath.snapshotItem(i);
- if (element && isVisible(element) && isInViewport(element)) {
- hintables[0].push(element);
- }
- }
-
- // Assign labels to each hintable. Can't be combined with the previous
- // step, because we didn't know how many there were at that time.
- var hintLength = hintables.length;
- for (var i = 0; i < hintables[0].length; ++i) {
- var code = translate(i.toString(), translation);
- hintables[1].push(code.lpad(translation[0], hintLength));
- }
-
- return hintables;
- }
-
- // Filter the hintables based on input from the user. Makes the screen less
- // cluttered after the user has typed some prefix of hint labels.
- function filterHintables(hintables, target) {
- var filtered = [[], []];
-
- var targetPattern = new RegExp("^" + target);
-
- for (var i = 0; i < hintables[0].length; i++) {
- if (hintables[1][i].match(targetPattern)) {
- filtered[0].push(hintables[0][i]);
- filtered[1].push(hintables[1][i].substring(target.length));
- }
- }
-
- return filtered;
- }
-
- // TODO make this use the container variable from main, instead of searching
- // for it?
- function clearHints() {
- var container = document.getElementById(uzblContainerID);
- if (container) {
- container.parentNode.removeChild(container);
- }
- }
-
- // So that we can offer this as a separate function.
- this.clearHints = clearHints;
-
- function makeHint(node, code, styler) {
- var position = computeElementPosition(node);
- var hint = document.createElement('div');
-
- hint.name = uzblID;
- hint.innerText = code;
- hint.style.display = 'inline';
-
- hint.style.margin = '0px';
- hint.style.padding = '1px';
- hint.style.position = 'absolute';
- hint.style.zIndex = '10000';
-
- hint.style.left = position.left + 'px';
- hint.style.top = position.up + 'px';
-
- var img = node.getElementsByTagName('img');
- if (img.length > 0) {
- hint.style.left = position.left + img[0].width / 2 + 'px';
- }
-
- hint.style.textDecoration = 'none';
- hint.style.webkitBorderRadius = '6px';
- hint.style.webkitTransform = 'scale(1) rotate(0deg) translate(-6px, -5px)';
-
- hint = styler(hint); // So that custom hint stylers can override the above.
- return hint;
- }
-
-
- function drawHints(container, hintables, styler) {
- for (var i = 0; i < hintables[0].length; i++) {
- hint = makeHint(hintables[0][i], hintables[1][i], styler);
- container.appendChild(hint);
- }
-
- if (document.body) {
- document.body.appendChild(container);
- }
- }
-
- // The main hinting function. I don't know how to do default values to
- // functions, so all arguments must be specified. Use generics if you must.
- this.follow = function(target, matchSpec, handler, hintStyler) {
- var container = generateHintContainer(); // Get a container to hold all hints.
- var allHintables = generateHintables(matchSpec); // Get all items that can be hinted.
- hintables = filterHintables(allHintables, target); // Filter them based on current input.
-
- clearHints(); // Clear existing hints, if any.
-
- if (hintables[0].length == 0) {
- // Nothing was hinted, user pressed an unknown key, maybe?
- // Do nothing.
- } else if (hintables[0].length == 1) {
- handler(hintables[0][0]); // Only one hint remains, handle it.
- } else {
- drawHints(container, hintables, hintStyler); // Draw whatever hints remain.
- }
-
- return;
- };
-}
-
-// Make on-click links clickable.
-try {
- HTMLElement.prototype.click = function() {
- if (typeof this.onclick == 'function') {
- this.onclick({
- type: 'click'
- });
- }
- };
-} catch(e) {}
-
-follower = new Follower();