There are 1 previous version of this script.
// ==UserScript==
// @name Paul Graham click-to-inline footnotes
// @version 1.4: Also show HTML comments.
// @version 1.3: Bugfix: never inject the same footnote twice.
// @version 1.2: More resilient; registers only one listener, to body.
// @version 1.1: Added linkage both ways, and paulgraham.com inclusion.
// @namespace http://www.lysator.liu.se/~jhs/userscript
// @description Allows you to click footnote references at paulgraham.com to bring them to your eyes, rather than bring your eyes to the footnotes. (And afterwards, finding your way back.) Now with corresponding feature to unfold HTML comments.
// @include http://paulgraham.com/*
// @include http://www.paulgraham.com/*
// ==/UserScript==
var footnote_links = '//a[starts-with(@href, "#f")][font]';
var r1 = 'preceding::text()[1]';
var r2 = '(following-sibling::a[@name][1] | following-sibling::b[1])/' +
'preceding-sibling::br[2]';
document.addEventListener('click', inline_linked_footnote, true);
$x(footnote_links).forEach(arm_footnote_injector);
$x('//comment()').filter(hasText).forEach(showComment);
function hasText(x) {
return x.nodeValue.replace(/<[^>]*>|^\s+|\s+$/g, '').length;
}
function showComment(c, n) {
n = String.fromCharCode(n + 'a'.charCodeAt());
var s = document.createElement('span');
var a = document.createElement('a');
s.style.color = '#777';
a.style.color = '#444';
a.name = 'comm-'+ n;
a.href = '#'+ a.name;
a.textContent = n;
a.addEventListener('click', expandComment, true);
s.appendChild(document.createTextNode(' <'));
s.appendChild(a);
s.appendChild(document.createTextNode('> '));
c.parentNode.insertBefore(s, c);
}
function expandComment(e) {
e.stopPropagation();
e.preventDefault();
var me = e.target;
var lt = me.previousSibling; lt.nodeValue = ' <!-- ';
var gt = me.nextSibling; gt.nodeValue = ' --> ';
var rc = me.parentNode.nextSibling;
var rp = me.parentNode;
var span = document.createElement('span');
//span.innerHTML = ': '+ rc.nodeValue;
//rp.insertBefore(span, gt);
//me.removeEventListener('click', expandComment, true);
span.innerHTML = rc.nodeValue;
rp.replaceChild(span, me);
}
function arm_footnote_injector( a ) {
var note = get_footnote( a );
a.name = note.back;
note.target.href = '#' + note.back;
}
function get_footnote( a ) {
var id = a.hash.substr( 1 );
return { id:id, back:id+'-back', target:document.anchors.namedItem( id ) };
}
function inline_linked_footnote( e ) {
var node = e.target;
var a = node.parentNode; // as we got the font[parent::a] tag
if( !node.nodeName.match( /font/i ) || !a.pathname == location.pathname )
return; // not a footnote reference
var note = get_footnote( a );
if( a.nextSibling.data != ']' )
return; // already lifted in the footnote
e.preventDefault();
e.stopPropagation();
var footnote = copy_between( r1, r2, note.target );
var down = footnote.firstChild; // former target anchor
down.href = '#'+ note.id;
down.name = note.back;
a.parentNode.replaceChild( footnote, a );
}
function trace() {
unsafeWindow.console && unsafeWindow.console.trace();
}
function log() {
unsafeWindow.console && unsafeWindow.console.log.apply( this, arguments );
}
function copy_between( start, end, node ) {
var range = document.createRange();
range.setStartAfter( typeof start == 'string' ? $X( start, node ) : start );
range.setEndBefore( typeof end == 'string' ? $X( end, node ) : end );
return range.cloneContents();
}
function foreach( xpath, cb, root ) {
var results = $x( xpath, root ), node, i;
if( results )
for( i = 0; node = results[i]; i++ )
cb( node, i );
}
function $X( xpath, root ) {
var got = $x( xpath, root );
return got instanceof Array ? got[0] : got;
}
function $x( xpath, root ) {
try {
var doc = root ? root.evaluate ? root : root.ownerDocument : document;
var got = doc.evaluate( xpath, root||doc, null, 0, null ), next, result = [];
switch( got.resultType )
{
case got.STRING_TYPE:
return got.stringValue;
case got.NUMBER_TYPE:
return got.numberValue;
case got.BOOLEAN_TYPE:
return got.booleanValue;
default:
while( next = got.iterateNext() )
result.push( next );
return result;
}
} catch( e ) {
trace();
log( e );
}
}