Skip to content

Instantly share code, notes, and snippets.

@satyr
Created March 16, 2009 12:11
Show Gist options
  • Select an option

  • Save satyr/79857 to your computer and use it in GitHub Desktop.

Select an option

Save satyr/79857 to your computer and use it in GitHub Desktop.
yet another Hit-a-Hint implementation
// ==UserScript== function cmd_
// @name hit@hint
// @description pseudo Hit-a-Hint for Ubiquity/Greasemonkey
// @namespace http://d.hatena.ne.jp/murky-satyr
// @include *
// ==/UserScript==
function pageLoad_HatH(doc, win){ if((win = doc.defaultView) === win.top){{}
HatH(win, doc, {
key: 'asdfghjkl',
bind: {
go: ['C_\\', 29 /* unconvert */],
ok: 'S_return C_return', //does default action, then continues hint-mode
bs: ['back_space', 29], //undo
click: ';', dblclick: ':',
mouseup: '[', mousedown: ']',
mouseover: ',', mouseout: '.',
},
css: ''+<><![CDATA[
$pan {
position:absolute; z-index:2147483647;
color:#000; background-color:#ef7; opacity:0.7;
font:bold 9pt/1 "Consolas",monospace; text-transform:uppercase;
margin:0; padding:0 1px; border:1px solid #aaa}
$pan.hit {background-color:#fcf}
]]></>,
/// each hint's offset from its associated element
offset: {x: -3, y: -2},
/// base point of screen (set negative values to allow overflowed elements)
origin: {x: -6, y: -4},
/// xpath fragments for specific URIs
xpath: {
'^':
'a[@href] img[@onclick] input[not(@type="hidden")] textarea select button',
'^http://\\w\\.hatena\\.ne\\.jp/':
['img[@class="hatena-star-comment-button"]', 'img[@title="Add Star"]'],
'^http://www\\.google\\.com/reader/':
['div[contains(concat(" ",@class," ")," lhn-button ")'+
' or contains(concat(" ",@class," ")," goog-button ")]'],
},
/// guide
//sign: {__noSuchMethod__: isNaN},
})}}
this.CmdUtils || pageLoad_HatH(document);
function HatH(window, document,
{id, key, css, bind, sign, xpath, offset, origin}){
const KeyEvent = document.createEvent('KeyEvents'),
K = (key || 'asdfghjkl').split(''), L = K.length;
Bind = bind || {go: 'C_z', ok: 'RETURN'},
ID = id || 'HatH', IDD = ID +'Div', IDS = ID +'Style',
{x: OffsetX, y: OffsetY} = offset || {x: -8, y: -8},
{x: OriginX, y: OriginY} = origin || {x: -4, y: -4},
XPath = mixpath(xpath).map(function(s) this + s +'|'+ this +'xhtml:' + s,
'/html/body/descendant::').join('|'),
CSS = (css.replace(/\$/g, '#'+ IDD +'>s')
.replace(/([^;\s])\s*\}/g, '$1;}').replace(/;/g, ' !important;')),
Sign = sign || {
new: function SNew(){ this._ = document.title; this.put('') },
put: function SPut(txt, lmn){
var msg = '@'+ txt;
if(lmn){
var name = lmn.nodeName.toUpperCase();
msg += (' '+ (name === 'INPUT' ? lmn.type.toUpperCase() : name) +
' '+ (lmn.href || lmn.src || lmn.value || '') +
' '+ (lmn.title || lmn.alt || lmn.textContent || ''));
}
document.title = msg;
},
end: function SEnd(){ document.title = this._ },
},
Hints = {
Hint: {
set on(b)(this.span.className = b ? (this.item.focus(), 'hit') : '', b),
},
txt: '',
new: function HNew(){ this.dic = {}, this.len = 0 },
end: function HEnd(){ this.dic = this.len = null },
add: function HAdd(span, item){
var n = this.len++;
this.dic[span.textContent = K[n] || (K[n] = K[~-(n / L)] + K[n % L])]
= {__proto__: this.Hint, span: span, item: item};
},
get now() this.dic[this.txt],
set: function HSet(t){
var h = this.now;
if(h) h.on = false;
return this.txt = (h = this.dic[t]) ? h.on = t : '';
},
spray: function HSpray(win, doc){
this.css(doc);
var box = doc.createElement('div'), {max} = Math, i = -1,
xpr = doc.evaluate(XPath, doc, nsr, 7, null), l = xpr.snapshotLength;
while(++i < l){
var lmn = xpr.snapshotItem(i), stl = win.getComputedStyle(lmn, null);
if(stl.visibility === 'hidden' || stl.opacity === '0') continue;
var {top, right, bottom, left} = lmn.getBoundingClientRect();
if(left >= right || top >= bottom ||
OriginX > left || OriginY > top ||
right > win.innerWidth - OriginX ||
bottom > win.innerHeight - OriginY) continue;
var spn = doc.createElement('span');
spn.setAttribute(
'style', ('left:'+ max(0, left + win.scrollX + OffsetX) +'px'+
';top:'+ max(0, top + win.scrollY + OffsetY) +'px'));
this.add(box.appendChild(spn), lmn);
}
box.id = IDD;
doc.body.appendChild(box);
},
sweep: function HSweep(doc){
var box = doc.getElementById(IDD);
box && doc.body.removeChild(box);
},
css: function HCss(doc){
if(doc.getElementById(IDS)) return;
var style = doc.createElement('style');
style.innerHTML = CSS,
doc.body.appendChild(style).id = IDS;
},
},
Do = (['click', 'dblclick', 'mouseup', 'mousedown', 'mouseover', 'mouseout']
.reduce(function(d, h)(d[h] = mouser(h), d),
{$: input, go: begin, ok: unhit, bs: back})),
Runner = handler({go: Bind.go});
Hitter = (function()(delete Bind.go,
Bind.$ = [k.charCodeAt() for each(k in K)],
handler(Bind, end)))();
cast(function(win){ win.addEventListener('keypress', Runner, true) });
function start(win, doc){
win.removeEventListener('keypress', Runner, true);
doc.getElementsByTagName('frameset').length || Hints.spray(win, doc);
win.addEventListener('keypress', Hitter, true);
}
function reset(win, doc){
win.removeEventListener('keypress', Hitter, true);
Hints.sweep(doc);
win.addEventListener('keypress', Runner, true);
}
function begin(){
Sign.new();
Hints.new();
cast(start);
}
function end(){
cast(reset);
Hints.end();
Sign.end();
return true;
}
function hit(txt)(Hints.set(txt)
? (Sign.put(Hints.txt, Hints.now.item), true)
: (Sign.put(''), false));
function input(e){
var key = String.fromCharCode(e.keyCode || e.which);
hit(Hints.txt + key) || hit(key);
}
function unhit() !hit();
function back(){ Hints.txt ? hit(Hints.txt.slice(0, -1)) : end() }
function mouser(type){
var detail = type === 'dblclick' ? 2 : 1;
return function mouse(e){
var lm = Hints.now.item;
var me = document.createEvent('MouseEvents');
me.initMouseEvent(type, 1, 1, e.view, detail,
e.screenX, e.screenY, e.clientX, e.clientY,
e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
1, lm);
lm.dispatchEvent(me);
}
}
function cast(fn){
fn(window, document);
Array.forEach(window.frames, function(window){
try { var {document} = window } catch(_){ return }
fn(window, document);
});
}
function handler(dic, els){
const F = {__noSuchMethod__: els || function() true},
C = 9, A = 10, S = 11, M = 12, Modic = {C_:1<<C,A_:1<<A,S_:1<<S,M_:1<<M};
for(var d in dic) for each(var k in arr(dic[d]))
F[typeof k === 'number' ? k : translate(k)] = Do[d];
return function(e){
with(e) F[ctrlKey << C | altKey << A | shiftKey << S | metaKey << M |
(keyCode || which)](e) || preventDefault(stopPropagation());
}
function translate(key){
if(/^(?:[CAM]_)*[A-Z!\"#$%&\'()=~|`+*{}<>?]$/.test(key)) key = 'S_'+ key;
return key.match(/[CASM]_|.+/giy).reduce(acc, 0);
}
function acc(n, k) n | (k.length === 1 && k.charCodeAt() ||
Modic[k = k.toUpperCase()] ||
KeyEvent['DOM_VK_'+ k]);
}
function arr(x) x == null ? [] : x.map ? x : x.match ? x.match(/\S+/g) : [x];
function nsr(p) p === 'xhtml' && 'http://www.w3.org/1999/xhtml';
function mixpath(xps){
var {URL} = document, a = [], {push} = a;
for(var k in xps) ~URL.search(k) && push.apply(a, arr(xps[k]));
return a;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment