JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
blank tabs black
[userscripts.git] / numbered_links.user.js
index e442ca8..09684c0 100644 (file)
@@ -76,16 +76,16 @@ var forms = document.forms;
 //Calculate element position to draw the hint
 //Pretty accurate but on fails in some very fancy cases
 function element_position(el) {
-    var x = el.offsetLeft;
-    var y = el.offsetTop;
-    var width = el.offsetWidth;
-    var height = el.offsetHeight;
-    while (el.offsetParent) {
-        el = el.offsetParent;
-        y += el.offsetTop;
-        x += el.offsetLeft;
-    }
-    return { x: x, y: y, width: width, height: height };
+       var x = el.offsetLeft;
+       var y = el.offsetTop;
+       var width = el.offsetWidth;
+       var height = el.offsetHeight;
+       while (el.offsetParent) {
+               el = el.offsetParent;
+               y += el.offsetTop;
+               x += el.offsetLeft;
+       }
+       return { x: x, y: y, width: width, height: height };
 }
 
 // Simulate a click on the element
@@ -118,138 +118,160 @@ function click_element(target, options) {
 
 //Calculate if an element is visible
 function isVisible(el) {
-    if (el == doc) {
-        return true;
-    }
-    if (!el) {
-        return false;
-    }
-    if (!el.parentNode) {
-        return false;
-    }
-    if (el.style) {
-        if (el.style.display == 'none') {
-            return false;
-        }
-        if (el.style.visibility == 'hidden') {
-            return false;
-        }
-    }
-    return isVisible(el.parentNode);
+       if (el == doc) {
+               return true;
+       }
+       if (!el) {
+               return false;
+       }
+       if (!el.parentNode) {
+               return false;
+       }
+       if (el.style) {
+               if (el.style.display == 'none') {
+                       return false;
+               }
+               if (el.style.visibility == 'hidden') {
+                       return false;
+               }
+       }
+       return isVisible(el.parentNode);
 }
 //Calculate if an element is on the viewport.
 function elementInViewport(el) {
-    var pos = element_position(el);
-    return pos.y < window.pageYOffset + window.innerHeight && pos.x < window.pageXOffset + window.innerWidth && (pos.y + pos.height) > window.pageYOffset && (pos.x + pos.width) > window.pageXOffset;
+       var pos = element_position(el);
+       return pos.y < window.pageYOffset + window.innerHeight && pos.x < window.pageXOffset + window.innerWidth && (pos.y + pos.height) > window.pageYOffset && (pos.x + pos.width) > window.pageXOffset;
 }
 //Removes all hints/leftovers that might be generated
 //by this script.
 function removeAllHints() {
-    var elements = doc.getElementById(uzbldivid);
-    if (elements) {
-        elements.parentNode.removeChild(elements);
-    }
+       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!
 function generateHint(el, label) {
-    var pos = element_position(el);
-    var hint = doc.createElement('div');
-    hint.setAttribute('name', uzblid);
-    hint.innerText = label;
-    hint.style.display = 'inline';
-    hint.style.backgroundColor = '#B9FF00';
-    hint.style.border = '2px solid #4A6600';
-    hint.style.color = 'black';
-    hint.style.fontSize = '9px';
-    hint.style.fontWeight = 'bold';
-    hint.style.lineHeight = '9px';
-    hint.style.margin = '0px';
-    hint.style.width = 'auto'; // fix broken rendering on w3schools.com
-    hint.style.padding = '1px';
-    hint.style.position = 'absolute';
-    hint.style.zIndex = '1000';
-    hint.style.textTransform = 'uppercase';
-    hint.style.left = Math.max(-1, (pos.x - (7 + label.length * 9))) + 'px';
-    hint.style.top = (pos.y + 1) + 'px';
-    var img = el.getElementsByTagName('img');
-    //if (img.length > 0) {
-        //hint.style.top = pos.x + img[0].height / 2 - 6 + 'px';
-    //}
-    hint.style.textDecoration = 'none';
-    // hint.style.webkitBorderRadius = '6px'; // slow
-    return hint;
+       var pos = element_position(el);
+       var hint = doc.createElement('div');
+       hint.setAttribute('name', uzblid);
+       hint.innerText = label;
+       hint.style.display = 'inline';
+       hint.style.backgroundColor = '#B9FF00';
+       hint.style.border = '2px solid #4A6600';
+       hint.style.color = 'black';
+       hint.style.fontSize = '9px';
+       hint.style.fontWeight = 'bold';
+       hint.style.lineHeight = '9px';
+       hint.style.margin = '0px';
+       hint.style.width = 'auto'; // fix broken rendering on w3schools.com
+       hint.style.padding = '1px';
+       hint.style.position = 'absolute';
+       hint.style.zIndex = '1000';
+       hint.style.textTransform = 'uppercase';
+       hint.style.left = Math.max(-1, (pos.x - (7 + label.length * 9))) + 'px';
+       hint.style.top = (pos.y + 1) + 'px';
+       var img = el.getElementsByTagName('img');
+       //if (img.length > 0) {
+               //hint.style.top = pos.x + img[0].height / 2 - 6 + 'px';
+       //}
+       hint.style.textDecoration = 'none';
+       // hint.style.webkitBorderRadius = '6px'; // slow
+       return hint;
 }
-//Here we choose what to do with an element if we
-//want to "follow" it. On form elements we "select"
-//or pass the focus, on links we try to perform a click,
-//but at least set the href of the link. (needs some improvements)
-function clickElem(item) {
-    removeAllHints();
-    if (item) {
-        var name = item.tagName;
-        if (name == 'A') {
-            click_element(item);
-            window.location = item.href;
-        } else if (name == 'INPUT') {
-            var type = item.getAttribute('type').toUpperCase();
-            if (type == 'TEXT' || type == 'FILE' || type == 'PASSWORD') {
-                item.focus();
-                item.select();
-            } else {
-                item.click();
-            }
-        } else if (name == 'TEXTAREA' || name == 'SELECT') {
-            item.focus();
-            item.select();
-        } else {
-            click_element(item);
-            window.location = item.href;
-        }
-    }
+
+// This function takes action on the element chosen by entering the "numbered
+// link" shortcut key(s)
+function do_element(item) {
+       removeAllHints();
+       if (item) {
+               var name = item.tagName;
+               if (name == 'A') {
+                       click_element(item);
+                       window.location = item.href;
+               } else if (name == 'INPUT') {
+                       var type = (item.getAttribute('type') || 'text').toLowerCase();
+                       switch (type) {
+                               case 'checkbox':
+                                       item.checked = !item.checked;
+                               break;
+                               case 'radio':
+                                       item.checked = true;
+                               break;
+                               case 'submit':
+                               case 'reset':
+                               case 'image':
+                               case 'button':
+                                       item.click(); // only tested on submit buttons
+                               break;
+                               case 'file': // don't think js can activate this, so focus
+                               case 'text':
+                               case 'password':
+                               case 'search':
+                               case 'number':
+                               case 'email':
+                               case 'url':
+                               case 'range':
+                               default:
+                                       // give it keyboard focus
+                                       item.focus();
+                                       item.select();
+                               break;
+                       }
+               } else if (name == 'TEXTAREA' || name == 'SELECT') {
+                       item.focus();
+                       item.select();
+               } else {
+                       // as a catch-all, try simulating a mouse click on that element
+                       click_element(item);
+                       window.location = item.href;
+               }
+       }
 }
+
 //Returns a list of all links (in this version
 //just the elements itself, but in other versions, we
 //add the label here.
 function addLinks() {
-    var res = [[], []];
-    for (var l = 0; l < links.length; l++) {
-        var li = links[l];
-        if (isVisible(li) && elementInViewport(li)) {
-            res[0].push(li);
-        }
-    }
-    return res;
+       var res = [[], []];
+       for (var l = 0; l < links.length; l++) {
+               var li = links[l];
+               if (isVisible(li) && elementInViewport(li)) {
+                       res[0].push(li);
+               }
+       }
+       return res;
 }
 //Same as above, just for the form elements
 function addFormElems() {
-    var 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 && isVisible(el) && elementInViewport(el)) {
-                res[0].push(el);
-            }
-        }
-    }
-    return res;
+       var 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 && isVisible(el) && elementInViewport(el)) {
+                               res[0].push(el);
+                       }
+               }
+       }
+       return res;
 }
 //Draw all hints for all elements passed. "len" is for
 //the number of chars we should use to avoid collisions
 function reDrawHints(elems, chars) {
-    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 = generateHint(elems[0][i], label);
-            hintdiv.appendChild(h);
-        }
-    }
-    if (document.body) {
-        document.body.appendChild(hintdiv);
-    }
+       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 = generateHint(elems[0][i], label);
+                       hintdiv.appendChild(h);
+               }
+       }
+       if (document.body) {
+               document.body.appendChild(hintdiv);
+       }
 }
 // pass: number of keys
 // returns: key length
@@ -289,40 +311,40 @@ function labelToInt(label) {
 }
 //Put it all together
 function followLinks(follow) {
-    if(follow.charAt(0) == 'l') {
-        follow = follow.substr(1);
-        charset = 'thsnlrcgfdbmwvz-/';
-    }
-    var s = follow.split('');
-    var linknr = labelToInt(follow);
-    var linkelems = addLinks();
-    var formelems = addFormElems();
-    var elems = [linkelems[0].concat(formelems[0]), linkelems[1].concat(formelems[1])];
-    var len = labelLength(elems[0].length);
-    var oldDiv = doc.getElementById(uzbldivid);
-    var leftover = [[], []];
-    if (s.length == len && linknr < elems[0].length && linknr >= 0) {
-        clickElem(elems[0][linknr]);
-        got = '';
-        active = 0;
-    } else {
-        for (var j = 0; j < elems[0].length; j++) {
-            var b = true;
-            var label = 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);
-            }
-        }
-        reDrawHints(leftover, s.length);
-    }
+       if(follow.charAt(0) == 'l') {
+               follow = follow.substr(1);
+               charset = 'thsnlrcgfdbmwvz-/';
+       }
+       var s = follow.split('');
+       var linknr = labelToInt(follow);
+       var linkelems = addLinks();
+       var formelems = addFormElems();
+       var elems = [linkelems[0].concat(formelems[0]), linkelems[1].concat(formelems[1])];
+       var len = labelLength(elems[0].length);
+       var oldDiv = doc.getElementById(uzbldivid);
+       var leftover = [[], []];
+       if (s.length == len && linknr < elems[0].length && linknr >= 0) {
+               do_element(elems[0][linknr]);
+               got = '';
+               active = 0;
+       } else {
+               for (var j = 0; j < elems[0].length; j++) {
+                       var b = true;
+                       var label = 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);
+                       }
+               }
+               reDrawHints(leftover, s.length);
+       }
 }
 
 // from your event handler you can: return stop_event(e)
@@ -339,19 +361,11 @@ function init() {
        document.addEventListener(
                'keydown',
                function(e) {
-                       // [de]activate on ^C
                        // deactivate on ESC
-                       if(
-                               (e.ctrlKey && e.keyCode == 67)
-                               || (e.keyCode == 27 && active == 1)
-                       ) {
-                               if(active) {
-                                       got = '';
-                                       removeAllHints();
-                               } else {
-                                       followLinks(got);
-                               }
-                               active = 1 - active;
+                       if(e.keyCode == 27 && active == 1) {
+                               got = '';
+                               removeAllHints();
+                               active = 0;
                                return stop_event(e);
                        } else {
                                if(active == 1 && !e.ctrlKey && !e.shiftKey && !e.altKey) {
@@ -361,8 +375,23 @@ function init() {
                                                return stop_event(e);
                                        }
                                } else {
-                                       // general keybinding, unrelated to numbered links
-                                       if (document.activeElement == document.body) { // FIXME get more specific
+                                       // general keybinding, (mostly unrelated to numbered links)
+                                       var active_type = (document.activeElement.type || 'a').toLowerCase();
+                                       var typing = true;
+                                       if (document.activeElement == document.body) {
+                                               typing = false;
+                                       } else {
+                                               switch ((document.activeElement.type || 'a').toLowerCase()) {
+                                                       case 'checkbox':
+                                                       case 'radio':
+                                                       case 'submit':
+                                                       case 'reset':
+                                                       case 'image':
+                                                       case 'button':
+                                                               typing = false;
+                                               }
+                                       }
+                                       if (!typing) {
                                                var c = key_to_char[e.keyCode];
                                                switch (c) {
                                                        case 'c':
@@ -373,6 +402,25 @@ function init() {
                                                                window.scrollBy(0, 200);
                                                                stop_event(e);
                                                        break;
+                                                       case 'h':
+                                                               if (e.shiftKey) {
+                                                                       history.back()
+                                                                       stop_event(e);
+                                                               }
+                                                       break;
+                                                       case 'n':
+                                                               if (e.shiftKey) {
+                                                                       history.forward()
+                                                                       stop_event(e);
+                                                               }
+                                                       break;
+                                                       case 'f':
+                                                               if (!active && !e.ctrlKey) {
+                                                                       followLinks(got);
+                                                                       active = 1;
+                                                                       stop_event(e);
+                                                               }
+                                                       break;
                                                }
                                        } else if (e.keyCode == 27) {
                                                // unfocus on ESC