Skip to content

Instantly share code, notes, and snippets.

@Griever
Created February 25, 2009 16:48
Show Gist options
  • Save Griever/70266 to your computer and use it in GitHub Desktop.
Save Griever/70266 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 : '\x23FFFF00',
hintColorForm : '\x2300FFFF',
hintColorFocused : '\x23FF00FF',
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 = this.rectFixForOpera(elem, getComputedStyle(elem, null)) || elem.getClientRects()[0];
if (rect && rect.right - rect.left >= 0 && 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 = this.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 = this.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(this.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){
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 = this.hintContainerId;
var elems = this.xpathMode===true?
this.getXPathElements(win) :
Array.prototype.slice.call(win.document.querySelectorAll(this.selector));
elems.forEach(function(elem){
var pos = this.getAbsolutePosition(elem, html, body, inWidth, inHeight);
if (pos === false) return;
var hint = this.createText(this.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? this.hintColorLink: this.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';
this.hintElements[hint] = span;
span.element = elem;
div.appendChild(span);
this.k++;
}, this);
win.document.addEventListener('keypress', this, true);
win.document.body.appendChild(df);
},
end : function(win){
var div = win.document.getElementById(this.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])
this.start(window);
Array.prototype.forEach.call(frame, function(elem){
try{
this.start(elem);
}catch(e){
}
}, this);
},
removeHints : function(){
var frame = top.frames;
if (!document.getElementsByTagName('frameset')[0])
this.end(top);
Array.prototype.forEach.call(frame, function(elem){
try{
this.end(elem);
}catch(e){
}
}, this);
},
blurHint : function(){
if (this.lastMatchHint){
this.lastMatchHint.style.backgroundColor = this.lastMatchHint.element.hasAttribute('href')===true?
this.hintColorLink: this.hintColorForm;
}
},
resetInput : function(){
this.inputKey ='';
this.blurHint();
this.lastMatchHint = null;
},
handleEvent : function(event){
var key = event.keyCode || event.charCode;
if (key in this.keyMap === false){
this.removeHints();
return;
}
var onkey = this.keyMap[key];
switch(onkey){
case 'Enter':
this.resetInput();
this.removeHints();
case 'Shift':
case 'Ctrl':
case 'Alt': return;
}
event.preventDefault();
event.stopPropagation();
switch(onkey){
case 'Bkspc' :
case 'Delete' :
if (!this.inputKey){
this.removeHints();
return;
}
this.resetInput();
return;
case 'Space':
this.removeHints();
return;
default :
this.inputKey += onkey;
};
this.blurHint();
if (this.inputKey in this.hintElements === false){
this.resetInput();
this.inputKey += onkey;
}
this.lastMatchHint = this.hintElements[this.inputKey];
this.lastMatchHint.style.backgroundColor = this.hintColorFocused;
this.lastMatchHint.element.focus();
},
init : function(){
this.hintKeysLength = this.hintKeys.length;
this.hintKeys.split('').forEach(function(l) { this.keyMap[l.charCodeAt(0)] = l; }, this);
if (this.xpathMode == true){
this.xpath += '|' + this.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