Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- (function () {
- 'use strict';
- /* ==========================================================
- HACKER NEWS COMMENT ENHANCER
- ----------------------------------------------------------
- This script improves Hacker News comment readability and
- structure by performing several transformations:
- Processing pipeline:
- 1. Identify the OP in comment threads
- 2. Convert "> quote" text into styled blockquotes
- 3. Convert "*" bullet lines into diamond bullets
- 4. Normalize header navigation markup
- 5. Apply syntax highlighting to code blocks
- 6. Enhance code blocks with line numbers
- 7. Apply typographic improvements (Typeset.js)
- Each step is isolated so features can be added or removed
- without affecting the rest of the pipeline.
- ========================================================== */
- /* ==========================================================
- 1. OP COMMENT INDICATOR
- ----------------------------------------------------------
- Hacker News does not visually distinguish comments made
- by the original poster (OP).
- This function finds the OP username from the story header
- and adds a small "OP" badge next to any comment written
- by that same user.
- ========================================================== */
- function addOPIndicators() {
- const opUser = document.querySelector('.fatitem .hnuser');
- if (!opUser) return;
- const opName = opUser.textContent.trim();
- document.querySelectorAll('tr.athing.comtr').forEach((row) => {
- const user = row.querySelector('.hnuser');
- if (!user) return;
- if (user.textContent.trim() === opName) {
- // Prevent duplicate badges if script runs twice
- if (user.nextElementSibling?.classList?.contains('hn-op-badge')) return;
- const badge = document.createElement('span');
- badge.className = 'hn-op-badge';
- badge.textContent = ' OP';
- badge.style.fontSize = '11px';
- badge.style.marginLeft = '6px';
- badge.style.padding = '2px 5px';
- badge.style.borderRadius = '4px';
- badge.style.background = 'rgba(90,169,255,0.15)';
- badge.style.color = '#5aa9ff';
- user.after(badge);
- }
- });
- }
- /* ==========================================================
- 2. CONVERT TEXT QUOTES → BLOCKQUOTES
- ----------------------------------------------------------
- Hacker News users often write quotes using:
- > quoted text
- This function detects those patterns and converts them
- into actual <blockquote> elements so they can be styled
- properly with CSS.
- ========================================================== */
- function convertQuotes() {
- document.querySelectorAll('.commtext').forEach((comm) => {
- if (comm.dataset.hnQuotesProcessed) return;
- comm.dataset.hnQuotesProcessed = 'true';
- // TreeWalker allows scanning only text nodes
- const walker = document.createTreeWalker(comm, NodeFilter.SHOW_TEXT);
- const toReplace = [];
- while (walker.nextNode()) {
- const node = walker.currentNode;
- const trimmed = node.nodeValue.trim();
- if (trimmed.startsWith('> ')) {
- toReplace.push(node);
- }
- }
- toReplace.forEach((node) => {
- const text = node.nodeValue.trim().replace(/^>\s?/, '');
- const blockquote = document.createElement('blockquote');
- blockquote.textContent = text;
- node.parentNode.replaceChild(blockquote, node);
- });
- });
- }
- /* ==========================================================
- 3. NORMALIZE HEADER NAV
- ----------------------------------------------------------
- Hacker News wraps the active navigation link inside <b>.
- That breaks modern flex layouts.
- This function unwraps the <b> tag so navigation becomes
- structurally consistent and easier to style.
- ========================================================== */
- function normalizeHeaderNav() {
- const nav = document.querySelector('.pagetop');
- if (!nav) return;
- nav.querySelectorAll('b:not(.hnname)').forEach((b) => {
- const link = b.querySelector('a');
- if (!link) return;
- b.replaceWith(link);
- });
- }
- /* ==========================================================
- 4. CODE BLOCK ENHANCEMENTS
- ----------------------------------------------------------
- Hacker News renders code inside:
- <pre><code>
- This function:
- • Normalizes indentation
- • Adds line numbers
- • Wraps each line in a structured element
- This enables nicer styling and readable line numbers.
- ========================================================== */
- function enhanceCodeBlocks() {
- document.querySelectorAll('pre > code').forEach((code) => {
- if (code.dataset.hnCodeEnhanced) return;
- code.dataset.hnCodeEnhanced = true;
- const rawLines = code.textContent.replace(/\n$/, '').split('\n');
- // Detect common indentation and remove it
- const baseIndent = rawLines[0].match(/^\s*/)[0].length;
- const lines = rawLines.map((l) => l.slice(baseIndent));
- code.innerHTML = '';
- lines.forEach((line, i) => {
- const row = document.createElement('div');
- row.className = 'hn-code-line';
- const num = document.createElement('span');
- num.className = 'hn-code-number';
- num.textContent = i + 1;
- const text = document.createElement('span');
- text.className = 'hn-code-text';
- text.textContent = line || ' ';
- row.appendChild(num);
- row.appendChild(text);
- code.appendChild(row);
- });
- });
- }
- /* ==========================================================
- 5. CONVERT * BULLETS → DIAMOND BULLETS
- ----------------------------------------------------------
- Hacker News does not support lists in comments.
- Users often simulate lists using:
- * item
- This function converts those lines into structured
- elements that can be styled with CSS.
- ========================================================== */
- function convertStarBullets() {
- document.querySelectorAll('.commtext').forEach((comm) => {
- if (comm.dataset.hnBulletsProcessed) return;
- comm.dataset.hnBulletsProcessed = 'true';
- const walker = document.createTreeWalker(comm, NodeFilter.SHOW_TEXT);
- const targets = [];
- while (walker.nextNode()) {
- const node = walker.currentNode;
- const text = node.nodeValue.trim();
- if (/^[-*]\s+/.test(text)) {
- targets.push(node);
- }
- }
- targets.forEach((node) => {
- const text = node.nodeValue.trim().replace(/^[-*]\s+/, '');
- const bullet = document.createElement('div');
- bullet.className = 'hn-bullet';
- bullet.textContent = text;
- node.parentNode.replaceChild(bullet, node);
- });
- });
- }
- /* ==========================================================
- 6. CONVERT NUMBERED LISTS → <ol>
- ----------------------------------------------------------
- Hacker News users often simulate ordered lists using:
- 1. item
- 2. item
- 3. item
- This function detects those paragraphs and converts them
- into a proper <ol> list.
- ========================================================== */
- function convertNumberedLists() {
- document.querySelectorAll('.commtext').forEach((comm) => {
- if (comm.dataset.hnNumbersProcessed) return;
- comm.dataset.hnNumbersProcessed = 'true';
- const nodes = Array.from(comm.querySelectorAll('p'));
- let list = null;
- nodes.forEach((p) => {
- const text = p.textContent.trim();
- const match = text.match(/^(\d+)\.\s+(.*)/);
- if (match) {
- if (!list) {
- list = document.createElement('ol');
- list.className = 'hn-auto-list';
- p.before(list);
- }
- const li = document.createElement('li');
- li.textContent = match[2];
- list.appendChild(li);
- p.remove();
- } else {
- list = null;
- }
- });
- });
- }
- /* ==========================================================
- 6. SYNTAX HIGHLIGHTING
- ----------------------------------------------------------
- Uses Highlight.js if it is available.
- This step runs before code enhancement so that syntax
- highlighting is applied to the raw code content.
- ========================================================== */
- function highlightCodeBlocks() {
- if (typeof hljs === 'undefined') return;
- document.querySelectorAll('pre code').forEach((block) => {
- const text = block.textContent;
- const looksLikeCode =
- /[{}();=<>]/.test(text) ||
- /\b(function|const|let|var|class|return|if|for|while)\b/.test(text);
- if (!looksLikeCode) return;
- block.dataset.hnRealCode = 'true';
- hljs.highlightElement(block);
- });
- }
- /* ==========================================================
- 7. TYPOGRAPHY (TYPESET.JS)
- ----------------------------------------------------------
- Applies typographic improvements such as:
- • smart quotes
- • proper apostrophes
- • typographic ellipses
- Only runs if Typeset.js is available.
- ========================================================== */
- function typesetComments() {
- if (!window.Typeset) return;
- document.querySelectorAll('.commtext').forEach((comment) => {
- if (comment.dataset.typesetDone) return;
- comment.dataset.typesetDone = 'true';
- Typeset.typesetElement(comment);
- });
- }
- /* ==========================================================
- CONVERT HN HTML QUOTE PATTERN
- ----------------------------------------------------------
- Hacker News sometimes renders quotes like:
- ><i>quoted text</i>
- instead of plain "> quoted text".
- This converts that structure into a proper blockquote.
- ========================================================== */
- function convertInlineQuotes() {
- document.querySelectorAll('.commtext').forEach((comm) => {
- const walker = document.createTreeWalker(comm, NodeFilter.SHOW_TEXT);
- const targets = [];
- while (walker.nextNode()) {
- const node = walker.currentNode;
- if (node.nodeValue.trim() === '>') {
- const next = node.nextSibling;
- if (next && next.nodeType === 1) {
- targets.push({ marker: node, content: next });
- }
- }
- }
- targets.forEach(({ marker, content }) => {
- const blockquote = document.createElement('blockquote');
- blockquote.appendChild(content.cloneNode(true));
- marker.parentNode.replaceChild(blockquote, marker);
- content.remove();
- });
- });
- }
- /* ==========================================================
- INIT PIPELINE
- ----------------------------------------------------------
- Executes all transformations in the correct order.
- Order matters:
- - text transforms
- - code transforms
- - typography last
- ========================================================== */
- function init() {
- addOPIndicators();
- convertQuotes();
- convertInlineQuotes();
- convertStarBullets();
- convertNumberedLists();
- normalizeHeaderNav();
- highlightCodeBlocks();
- enhanceCodeBlocks();
- typesetComments();
- }
- /* ==========================================================
- START SCRIPT
- ----------------------------------------------------------
- Ensures the DOM is ready before modifying the page.
- ========================================================== */
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', init);
- } else {
- init();
- }
- })();
Advertisement
Add Comment
Please, Sign In to add comment