您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
3/29/2025, 9:06:28 AM
// ==UserScript== // @name Footnotes on the side at paulgraham.com // @namespace Violentmonkey Scripts // @match https://www.paulgraham.com/* // @grant none // @version 1.0 // @author p-himik // @description 3/29/2025, 9:06:28 AM // @license MIT // ==/UserScript== window.addEventListener('load', function () { const layout_table = document.querySelector('body > table'); const text_table = layout_table.querySelector('table'); const text_cell = text_table.querySelector('td'); const default_width = '435px' const footnotes_margin = '-500px'; const wrapper_div = document.createElement('div'); const text_div = document.createElement('div'); text_div.style.maxWidth = default_width; text_div.replaceChildren(...text_cell.childNodes); wrapper_div.append(text_div); text_table.replaceWith(wrapper_div); const convert_font_to_span = (font) => { const span = document.createElement('span'); span.style.fontFamily = font.getAttribute('face'); span.style.color = font.getAttribute('color'); const size = font.getAttribute('size'); if (size) { let size_num = parseInt(size); if (!(size.startsWith('+') || size.startsWith('-'))) { const default_size = 3; size_num = size_num - default_size; } const em_change = 0.2; const font_size = 1 + size_num * em_change; if (!isNaN(font_size)) { span.style.fontSize = font_size + 'rem'; } } span.replaceChildren(...font.childNodes); font.replaceWith(span); } // noinspection JSDeprecatedSymbols text_div.querySelectorAll('font').forEach(font => { convert_font_to_span(font); }); const footnotes_start = [...document.querySelectorAll('b')].filter(b => b.innerText === 'Notes')?.at(0); if (footnotes_start) { const get_new_footnote_span = (start_anchor, end_marker) => { const footnote_span = document.createElement('span'); const prev = start_anchor.previousSibling; if (prev.nodeName === '#text' && prev.nodeValue.trim().endsWith('[')) { const text = prev.nodeValue; prev.nodeValue = text.substring(0, text.lastIndexOf('[')); } footnote_span.append('[') footnote_span.append(start_anchor.cloneNode(true)); let follower = start_anchor.nextSibling; while (follower && follower !== end_marker) { if (follower.nodeName === '#text' && follower.nodeValue.trim().endsWith('[')) { const text = follower.nodeValue; follower.nodeValue = text.substring(0, text.lastIndexOf('[')); } const next_follower = follower.nextSibling; footnote_span.append(follower); follower = next_follower; } start_anchor.parentNode.replaceChild(footnote_span, start_anchor); return footnote_span; } const footnotes_anchors = document.querySelectorAll('a[name^="f"][name$="n"]'); const last_anchor = footnotes_anchors[footnotes_anchors.length - 1]; const last_footnote_end_marker = document.querySelector(`a[name="${last_anchor.name}"] ~ br + br + br`); const footnotes_div = document.createElement('div'); footnotes_anchors.forEach((anchor, i) => { const font_size = getComputedStyle(anchor).fontSize; const next_marker = footnotes_anchors[i + 1] || last_footnote_end_marker; const footnote_span = get_new_footnote_span(anchor, next_marker); footnote_span.style.fontSize = font_size; footnote_span.style.maxWidth = default_width; const in_text_anchor = text_div.querySelector(`a[href="#${anchor.name}"]`); if (in_text_anchor) { const wrapper = document.createElement('span'); in_text_anchor.after(wrapper); wrapper.append(in_text_anchor); wrapper.append(footnote_span); footnote_span.style.float = 'right'; footnote_span.style.marginRight = footnotes_margin; footnote_span.style.position = 'relative'; footnote_span.style.color = 'initial'; } else { footnotes_div.append(footnote_span); } }); const remove_children = (start_node, end_node) => { if (start_node.parentNode !== end_node.parentNode) { throw new Error('start_node and end_node must have the same parent'); } let node = start_node; while (node && node !== end_node) { const next_node = node.nextSibling; node.remove(); node = next_node; } end_node.remove(); } remove_children(footnotes_start, last_footnote_end_marker); text_div.after(footnotes_div); } } );