Created
December 17, 2008 00:00
-
-
Save edvakf/36875 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
javascript: (function(_HaH) { | |
var originalTitle = document.title; | |
var hintKeys = new String('asdfghjkl'); | |
var choices = []; | |
var choice = ''; | |
var xpath = '//a[@href]|//input[not(@type=\x22hidden\x22)]|//textarea|//select|//img[@onclick]|//button'; | |
xpath += '|' + xpath.replace(/\/\//g, '//xhtml:'); | |
var defaultType = 'HaHxlink'; | |
function defaultAction(e) { | |
e.focus(); | |
var ev = document.createEvent('MouseEvents'); | |
ev.initEvent('click', null, null); | |
e.dispatchEvent(ev) | |
} | |
var HaHElements = [{ | |
accessor: function(e) { | |
var tag = e.tagName.toLowerCase(); | |
return ((tag == 'a' && /^(#|javascript:void\(0\);?)$/.test(e.href)) || /^(textarea|button|select|input|img)$/.test(tag)); | |
}, | |
type:defaultType, | |
action:defaultAction | |
}, | |
{ | |
accessor: function(e) { | |
return e.tagName.toLowerCase() == 'a'; | |
}, | |
type: 'HaHlink', | |
action: (window.opera)?defaultAction:function(e){if(e.href)location.href = e.href;} | |
}]; | |
if (_HaH && _HaH.types) { | |
_HaH.types.forEach(function(item) { | |
if (item.xpath) xpath += '|' + item.xpath; | |
HaHElements.unshift({ | |
accessor: item.accessor, | |
type: item.type || defaultType, | |
action: item.action || defaultAction, | |
color: item.color, | |
}) | |
}) | |
} | |
function addCSS() { | |
var style = document.getElementById('HaH-style-element'); | |
var d = '#HaH-div-element'; | |
var cssText = [d, 'span{font-size:10pt; padding:0pt \x20 1pt; margin:0; line-height:10pt; position:absolute; z-index:2147483647; opacity:.7; color:#000;}', d, '.HaHlink{background-color:#FF0;}', d, '.HaHxlink{background-color:#0FF;}', d, '.HaHselected{background-color:#F0F;}'].join('\x20'); | |
HaHElements.forEach(function(item){if(item.color)cssText+=d+'\x20.'+item.type+'{background-color:'+item.color+';}'}); | |
if (style) { | |
if(style.innerHTML!=cssText)style.innerHTML=cssText; | |
}else{ | |
style = document.createElement('style'); | |
style.type = 'text/css'; | |
style.id = 'HaH-style-element'; | |
style.innerHTML = cssText; | |
document.getElementsByTagName('head')[0].appendChild(style); | |
} | |
} | |
hints = []; | |
var df = document.createDocumentFragment(); | |
function castHint(_ele, _x, _y) { | |
for (var i = 0; i < HaHElements.length; i++) { | |
if (HaHElements[i].accessor(_ele)) { | |
var _type = HaHElements[i].type; | |
var _action = HaHElements[i].action; | |
break; | |
} | |
} | |
if (!_type) return; | |
if (!hints.length) addCSS(); | |
var _text = createText(hints.length); | |
var hint = { | |
element: _ele, | |
type: _type, | |
action: _action, | |
text: _text, | |
x: _x, | |
y: _y | |
}; | |
hint.hintNode = addHintNode(hint); | |
hints[hints.length] = hint; | |
} | |
function addHintNode(hint) { | |
var span = document.createElement('span'); | |
span.className = hint.type; | |
span.style.left = Math.max(0,hint.x-8) + 'px'; | |
span.style.top = Math.max(0,hint.y-8) + 'px'; | |
span.innerHTML = hint.text; | |
df.appendChild(span); | |
return span; | |
} | |
function getHintByText(txt) { | |
return hints[retrieveNumber(txt)]; | |
} | |
function retrieveNumber(text) { | |
text += ''; | |
var num = 0; | |
for (var i = 0, | |
l = text.length; i < l; i++) { | |
var fix = (i == 0) ? 0 : 1; | |
var t = text.charAt(l - i - 1); | |
num += (hintKeys.indexOf(t) + fix) * Math.pow(hintKeys.length, i); | |
} | |
return num; | |
} | |
function createText(num) { | |
var text = ''; | |
var l = hintKeys.length; | |
var 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 rectFixForOpera(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; | |
} | |
/* function rectFixForOpera(e, s) { | |
if(!window.opera||s.display!='inline'||s.cssFloat!='none')return null; | |
var c = e.firstChild,ec = e.firstElementChild; | |
if(!ec){ | |
if(!/img/i.test(e.tagName)) | |
while(c){ | |
if(c.nodeType==3&&/\S/.test(c.nodeValue))return null; | |
c=c.nextSibling; | |
} | |
return {left:0,right:0}; | |
}else{ | |
if(!/(br|img)/i.test(ec.tagName)&&getComputedStyle(ec,null).display=='inline')return null; | |
while(c){ | |
if(c.nodeType==3&&/\S/.test(c.nodeValue))return null; | |
c=c.nextSibling; | |
} | |
return ec.getBoundingClientRect(); | |
} | |
}*/ | |
function getAbsolutePosition(elem) { | |
var style = getComputedStyle(elem, null); | |
var rect = rectFixForOpera(elem, style) || elem.getClientRects()[0]; | |
if (rect && rect.right - rect.left > 0 && style.visibility != 'hidden' && style.opacity != 0 && rect.left >= 0 && rect.top >= -5 && rect.bottom <= window.innerHeight + 5 && rect.right <= window.innerWidth) { | |
var html = document.documentElement; | |
var body = document.body; | |
return { | |
top: (body.scrollTop || html.scrollTop) - html.clientTop + rect.top, | |
left: (body.scrollLeft || html.scrollLeft) - html.clientLeft + rect.left | |
} | |
} | |
return false; | |
} | |
function drawHints() { | |
var div = document.getElementById('HaH-div-element'); | |
if (div) div.innerHTML = ''; | |
function resolv(p){ | |
if(p=='xhtml')return 'http://www.w3.org/1999/xhtml'; | |
} | |
var result = document.evaluate(xpath,document,resolv,7,null); | |
for(var i=0,l=result.snapshotLength;i<l;i++ ) { | |
var ele=result.snapshotItem(i); | |
var pos = getAbsolutePosition(ele); | |
if (!pos) continue; | |
castHint(ele, pos.left, pos.top); | |
} | |
if (!hints.length) return; | |
if (!div) { | |
div = document.createElement('div'); | |
div.id = 'HaH-div-element'; | |
} | |
div.appendChild(df); | |
document.body.appendChild(div); | |
document.addEventListener('keypress', interpretHaHKeyStroke, true); | |
document.title += '\x20-\x20'; | |
} | |
function removeHints() { | |
document.title = originalTitle; | |
if (interpretHaHKeyStroke) { | |
document.removeEventListener('keypress', interpretHaHKeyStroke, true); | |
} | |
document.body.removeChild(document.getElementById('HaH-div-element')); | |
delete hints,df,choices; | |
} | |
var baseFuncs = { | |
doNothing: function() { | |
}, | |
pushChoice: function(mod) { | |
if (mod) return removeHints; | |
if (choice != '') { | |
choices.push(choice); | |
getHintByText(choice).hintNode.className = 'HaHselected'; | |
choice = ''; | |
document.title += '\x20'; | |
} | |
}, | |
popChoice: function(mod) { | |
if (mod) return removeHints; | |
if (choice != '') { | |
choice = ''; | |
} else { | |
if (choices.length) { | |
var lastchoice = getHintByText(choices.pop()); | |
lastchoice.hintNode.className = lastchoice.type; | |
} | |
} | |
document.title = originalTitle + '\x20-\x20' + choices.join('\x20'); | |
}, | |
pushLetter: function(mod, key) { | |
if (mod) return removeHints(); | |
choice += ''; | |
if (getHintByText(choice + key)) { | |
choice += key; | |
var lastHint = '' + hints[hints.length - 1].text; | |
if (choice.length == lastHint.length || choice.length == lastHint.length - 1 && retrieveNumber(choice) > retrieveNumber(lastHint.substr(0, choice.length))) { | |
getHintByText(choice).hintNode.className = 'HaHselected'; | |
choices.push(choice); | |
choice = ''; | |
document.title += '\x20'; | |
} | |
} | |
var choicestring = (choices.length) ? choices.join('\x20') + '\x20' + choice: choice; | |
document.title = originalTitle + '\x20-\x20' + choicestring; | |
} | |
}; | |
var keyMap = { | |
'8' : 'Bkspc', | |
'32': 'Space', | |
'16': 'Shift', | |
'17': 'Ctrl', | |
'18': 'Alt' | |
}; | |
var keyFuncMap = { | |
'Bkspc': baseFuncs.popChoice, | |
'Space': baseFuncs.pushChoice, | |
}; | |
hintKeys.split('').forEach(function(l){keyMap[l.charCodeAt(0)] = l}); | |
hintKeys.split('').forEach(function(k){keyFuncMap[k] = baseFuncs.pushLetter;}); | |
['Shift', 'Ctrl', 'Alt'].forEach(function(k){keyFuncMap[k] = baseFuncs.doNothing;}); | |
function defaultFuncTemplate(func){ | |
return function(mod){ | |
if(choice)choices.push(choice); | |
var ary=[]; | |
choices.forEach(function(c,i){ | |
var hint = getHintByText(c); | |
if(hint && choices.indexOf(c)==i) ary.push(hint); | |
}); | |
func(mod,ary); | |
removeHints(); | |
} | |
} | |
function addExtraFunc(func,keyName,keyCode){ | |
keyMap[(keyCode||keyName.charCodeAt(0))] = keyName; | |
keyFuncMap[keyName] = defaultFuncTemplate(func); | |
} | |
addExtraFunc(function(mod,ary){ | |
var windowname = (ary.length==1&&!mod) ?'_top':'_blank'; | |
ary.forEach(function(e){if(e.type=='HaHlink')window.open(e.element,windowname)}); | |
},'Enter',13); | |
addExtraFunc(function(mod,ary){ | |
if(mod)return; | |
var firstHint=ary[0]; | |
if(firstHint)firstHint.action(firstHint.element); | |
},';'); | |
if (_HaH && _HaH.keys) { | |
_HaH.keys.forEach(function(e){addExtraFunc(e.func,e.keyName,e.keyCode)}); | |
} | |
function interpretHaHKeyStroke(e) { | |
var key = keyMap[(e.keyCode || e.which)]; | |
e.preventDefault(); | |
e.stopPropagation(); | |
if (!key) return removeHints(); | |
var modified = (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey); | |
keyFuncMap[key](modified, key) && removeHints(); | |
} | |
drawHints(); | |
})(window.HitaHint); | |
This works on Chrome, too. However, on Google search results pages, not all result links receive a hint. Could you look at this, please?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is just awesome. Looks working fine on Safari too. Many thanks!