X-Git-Url: https://jasonwoof.com/gitweb/?p=userscripts.git;a=blobdiff_plain;f=numbered_links.user.js;h=ea6eff51f794528f9cff4f441769bbcf4fcc494a;hp=3598b6898ad2147c0695e55f40bf9af77ed167d6;hb=3d4609e17cb03df8922895b519ae4b2bdabee520;hpb=bf5038a97153dc63d2c1be4c06e89343fb730f67 diff --git a/numbered_links.user.js b/numbered_links.user.js index 3598b68..ea6eff5 100644 --- a/numbered_links.user.js +++ b/numbered_links.user.js @@ -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) @@ -362,16 +384,56 @@ function init() { } } else { // general keybinding, unrelated to numbered links - if (document.activeElement == document.body) { // FIXME get more specific + 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': window.scrollBy(0, -200); + stop_event(e); break; case 't': 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 + document.activeElement.blur(); + stop_event(e); } } }