Skip to content

Instantly share code, notes, and snippets.

@edvakf
Created August 27, 2010 05:14
Show Gist options
  • Save edvakf/552831 to your computer and use it in GitHub Desktop.
Save edvakf/552831 to your computer and use it in GitHub Desktop.
javascript:(function(){
var hintkeys = 'asdfghjkl';
function createText(num) {
var text = '', l = hintkeys.length, iter = 0;
while (num >= 0) {
var n = num;
num -= Math.pow(l, 1 + iter++);
}
for (var i = 0; i < iter; i++) {
r = n % l;
n = Math.floor(n / l);
text = hintkeys.charAt(r) + text;
}
return text;
}
function retrieveNumber(text) {
text += '';
for (var i=0,n=0,l=text.length; i<l; i++) {
var fix = (i == 0) ? 0 : 1;
var t = text.charAt(l - i - 1);
n += (hintkeys.indexOf(t) + fix) * Math.pow(hintkeys.length, i);
}
return n;
}
var Root = document.compatMode === 'BackCompat' ? document.body : document.documentElement;
var rootHeight = Root.clientHeight;
var rootWidth = Root.clientWidth;
var yScroll = window.pageYOffset;
var xScroll = window.pageXOffset;
function getAbsolutePosition(elem) {
var rect;
if (rect=getInViewRect(elem)){
return {
y: yScroll + rect.top,
x: xScroll + rect.left
}
}
return null;
}
function hasTextBeforeImage(elem, img) {
var r = document.createRange();
r.setStart(elem,0);
r.setEndBefore(elem.firstElementChild);
return /\S/.test(r.cloneContents().textContent);
}
function getInViewRect(elem) {
if (elem.childElementCount === 1 && elem.firstElementChild instanceof HTMLImageElement && !hasTextBeforeImage(elem, elem.firstElementChild)){
elem = elem.firstElementChild;
}
var rect = elem.getClientRects()[0];
if (!rect) return null;
/*can't use window.innerWidth/Height since it breaks when zoomed*/
if (rect.left < 0 || rect.left > rootWidth || rect.top < 0 || rect.top > rootHeight) return null;
var e = document.elementFromPoint(rect.left, rect.top);
if (e && (e === elem || elem.contains(e))) return rect;
return null;
}
var hints = [];
function drawHints() {
var elems = document.querySelectorAll('a[href],*[onclick],input:not([type="hidden"]),textarea,button,select');
var df = document.createDocumentFragment();
var count = 0;
Array.prototype.forEach.call(elems,function(elem){
var pos = getAbsolutePosition(elem);
if (pos) {
var span = document.createElement('span');
span.setAttribute('style',[
'font-size:10pt;',
'padding:0pt 1pt;',
'margin:0;',
'line-height:10pt;',
'position:absolute;',
'z-index:2147483647;',
'opacity:.7;',
'color:#000;',
'background-color:#FF0;',
'left:', Math.max(0,pos.x-8), 'px;',
'top:', Math.max(0,pos.y-8), 'px;',
].join(''));
span.textContent = createText(count++);
df.appendChild(span);
hints.push({
elem : elem,
label : span,
text : span.textContent,
});
}
});
if (!hints.length) return;
var div = document.createElement('div');
div.setAttribute('id', 'HaH-div-element');
div.appendChild(df);
document.body.appendChild(div);
}
var choice = '', choiceHint;
function pushLetter(key, e){
var hint = hints[retrieveNumber(choice+key)];
if (hint){
choice += key;
var lastHint = hints[hints.length - 1].text;
hint.label.style.backgroundColor = '#ff00ff';
focus(hint.elem);
e.preventDefault();
if (choiceHint) {
choiceHint.label.style.backgroundColor = '#ffff00';
}
choiceHint = hint;
} else {
unloadHaH();
}
}
function focus(elem){
if (!elem.id) elem.id = 'HaH-temp';
var a = document.createElement('a');
a.href = '#';
a.style = 'position:fixed;width:1px;height:1px;top:0;left:0;nav-right:#' + elem.id;
document.body.appendChild(a);
a.focus();
document.moveFocusRight();
document.body.removeChild(a);
if (elem.id === 'HaH-temp') elem.id = '';
}
function unloadHaH(){
if (choiceHint) choiceHint.label.style.backgroundColor = '#FF0';
choice = '';
var div = document.getElementById('HaH-div-element');
if (div) div.parentNode.removeChild(div);
document.removeEventListener('keypress',handler,true);
document.removeEventListener('focus',focusFix,true);
}
function handler(e){
var key = String.fromCharCode(e.keyCode);
if (hintkeys.indexOf(key) >= 0) return pushLetter(key, e);
unloadHaH();
}
function initHaH(){
drawHints();
document.addEventListener('keypress',handler,true);
document.addEventListener('focus',focusFix,true);
}
function focusFix(e){
/*Opera doesn't fire keydown, keypress nor keyup when focusing on an input(able) element*/
var t = e.target;
if (t instanceof HTMLTextAreaElement || (t instanceof HTMLInputElement && (!t.type || t.type === 'text' || t.type === 'password'))) unloadHaH();
}
initHaH();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment