Skip to content

Instantly share code, notes, and snippets.

@azu
Created February 26, 2009 02:36
Show Gist options
  • Save azu/70604 to your computer and use it in GitHub Desktop.
Save azu/70604 to your computer and use it in GitHub Desktop.
javascript:
if (window.hah){
hah.removeHints();
}
window.hah = {
hintKeys : new String('asdfghjkl'),
selector: 'a[href]:not([href^="mailto:"]), input:not([type="hidden"]), textarea, select, img[onclick], button',
xpath: '//a[@href]|//input[not(@type=\x22hidden\x22)]|//textarea|//select|//img[@onclick]|//button',
xpathMode : true,
hintColorLink : '#FFFF00',
hintColorForm : '#00FFFF',
hintColorFocused : '#FF00FF',
keyMap : {'8': 'Bkspc', '46': 'Delete', '32': 'Space', '13':'Enter', '16': 'Shift', '17': 'Ctrl', '18': 'Alt'},
hintKeysLength : null,
hintContainerId : 'hintContainer',
hintElements : [],
inputKey : '',
lastMatchHint : null,
k : 0,
rectFixForOpera : function (e, s) {
if (!window.opera || e.tagName.toLowerCase() != 'a' || s.cssFloat != 'none') return null;
var c = e.firstChild;
var cIsAfterWhiteSpace = true;
while (cIsAfterWhiteSpace) {
if (!c) return {
left: 0,
right: 0
};
cIsAfterWhiteSpace = (c.nodeName.toLowerCase() == 'br' || c.nodeType == 8 || c.nodeType == 3 && !/\S/.test(c.nodeValue));
if (c instanceof HTMLImageElement || c.tagName && c.tagName.toLowerCase() != 'br' && getComputedStyle(c, null).display == 'block') return c.getBoundingClientRect();
c = c.nextSibling;
}
return null;
},
getAbsolutePosition : function (elem, html, body, inWidth, inHeight) {
var style = getComputedStyle(elem, null);
if (style.visibility === 'hidden' || style.opacity === '0') return false;
var rect = hah.rectFixForOpera(elem, style) || elem.getClientRects()[0];
if (rect && rect.right - rect.left && rect.left >= 0 && rect.top >= -5 && rect.bottom <= inHeight + 5 && rect.right <= inWidth) {
return {
top: (body.scrollTop || html.scrollTop) - html.clientTop + rect.top,
left: (body.scrollLeft || html.scrollLeft) - html.clientLeft + rect.left
}
}
return false;
},
createText : function (num) {
var text = '';
var l = hah.hintKeysLength;
var iter = 0;
while (num >= 0) {
var n = num;
num -= Math.pow(l, 1 + iter++);
}
for (var i = 0; i < iter; i++) {
var r = n % l;
n = Math.floor(n / l);
text = hah.hintKeys.charAt(r) + text;
}
return text;
},
getXPathElements : function(win){
function resolv(p) { if (p == 'xhtml') return 'http://www.w3.org/1999/xhtml'; }
var result = win.document.evaluate(hah.xpath, win.document, resolv, 7, null);
for (var i = 0, arr = [], len = result.snapshotLength; i < len; i++){
arr[i] = result.snapshotItem(i);
}
return arr;
},
start : function(win){
if (!hah) var hah = top.hah;
var html = win.document.documentElement;
var body = win.document.body;
var inWidth = win.innerWidth;
var inHeight = win.innerHeight;
var df = document.createDocumentFragment();
var div = df.appendChild(document.createElement('div'));
div.id = hah.hintContainerId;
var elems = hah.xpathMode===true?
hah.getXPathElements(win) :
Array.prototype.slice.call(win.document.querySelectorAll(hah.selector));
elems.forEach(function(elem){
var pos = hah.getAbsolutePosition(elem, html, body, inWidth, inHeight);
if (pos === false) return;
var hint = hah.createText(hah.k);
var span = win.document.createElement('span');
span.appendChild(document.createTextNode(hint));
var st = span.style;
st.position = 'absolute';
st.zIndex = '2147483647';
st.color = '#000';
st.backgroundColor = elem.hasAttribute('href') === true? hah.hintColorLink: hah.hintColorForm;
st.fontSize = '10pt';
st.fontFamily = 'monospace';
st.lineHeight = '10pt';
st.padding = '0px';
st.margin = '0px';
st.textTransform = 'uppercase';
st.opacity = '0.7';
st.left = Math.max(0, pos.left - 8) + 'px';
st.top = Math.max(0, pos.top - 8) + 'px';
hah.hintElements[hint] = span;
span.element = elem;
div.appendChild(span);
hah.k++;
});
win.document.addEventListener('keypress', this, true);
win.document.body.appendChild(df);
},
end : function(win){
if (!hah) var hah = top.hah;
var div = win.document.getElementById(hah.hintContainerId);
win.document.removeEventListener('keypress', this, true);
if (div){
win.document.body.removeChild(div);
}
},
drawHints : function(){
var frame = window.frames;
if (!document.getElementsByTagName('frameset')[0])
hah.start(window);
Array.prototype.forEach.call(frame, function(elem){
try{
hah.start(elem);
}catch(e){
alert(e);
}
})
},
removeHints : function(){
var frame = top.frames;
if (!document.getElementsByTagName('frameset')[0])
hah.end(top);
Array.prototype.forEach.call(frame, function(elem){
try{
hah.end(elem);
}catch(e){
}
})
},
blurHint : function(){
if (hah.lastMatchHint){
hah.lastMatchHint.style.backgroundColor = hah.lastMatchHint.element.hasAttribute('href')===true?
hah.hintColorLink: hah.hintColorForm;
}
},
resetInput : function(){
hah.inputKey ='';
hah.blurHint();
hah.lastMatchHint = null;
},
handleEvent : function(event){
var key = event.keyCode || event.charCode;
if (key in hah.keyMap === false){
hah.removeHints();
return;
}
var onkey = hah.keyMap[key];
switch(onkey){
case 'Enter':
hah.lastMatchHint.element.hasAttribute('href')===true?
hah.resetInput():
hah.removeHints();
case 'Shift':
case 'Ctrl':
case 'Alt': return;
}
event.preventDefault();
event.stopPropagation();
switch(onkey){
case 'Bkspc' :
case 'Delete' :
if (!hah.inputKey){
hah.removeHints();
return;
}
hah.resetInput();
return;
case 'Space':
hah.removeHints();
return;
default :
hah.inputKey += onkey;
};
hah.blurHint();
if (hah.inputKey in hah.hintElements === false){
hah.resetInput();
hah.inputKey += onkey;
}
hah.lastMatchHint = hah.hintElements[hah.inputKey];
hah.lastMatchHint.style.backgroundColor = hah.hintColorFocused;
hah.lastMatchHint.element.focus();
},
init : function(){
hah.hintKeysLength = hah.hintKeys.length;
hah.hintKeys.split('').forEach(function(l) { hah.keyMap[l.charCodeAt(0)] = l; });
if (hah.xpathMode === true){
hah.xpath += '|' + hah.xpath.replace(/\/\//g, '//xhtml:');
}
},
};
hah.init();
hah.drawHints();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment