Last active
March 21, 2024 13:05
-
-
Save MohamedElashri/80d7871b229e987a471cbccca6d6761a to your computer and use it in GitHub Desktop.
My HN dracula custom userscript (and other things)
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
// ==UserScript== | |
// @name Hacker News Comprehensive Enhancements (Dracula) | |
// @namespace http://melashri.net/hn | |
// @version 1.3 | |
// @description A comprehensive enhancement script for Hacker News | |
// @author melashri | |
// @match https://news.ycombinator.com/* | |
// @grant GM_addStyle | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
// --------------------------- | |
// Configuration Settings | |
// --------------------------- | |
const autoCollapse = false; // Enable/disable auto-collapse feature | |
const numberOfRoots = 5; // Number of root comments to display | |
const numberOfReplies = 3; // Number of replies to display | |
const numberOfRepliesOfReplies = 1; // Number of replies' replies to display | |
const FIX_NUMBERING = false; // Enable/disable re-numbering of posts after ad removal | |
// CSS Injection | |
// --------------------------- | |
// Inject custom CSS for various elements on the page | |
/// Global, Container, Text, Link, Header, News Listing Styles | |
const userStyleCSS = ` | |
/* Global Styles */ | |
body { | |
margin: 0 !important; | |
padding: 0 !important; | |
background: #282a36 !important; /* Light blue background */ | |
} | |
/* Main Container Styles */ | |
#hnmain { | |
background: #44475a !important; /* White background */ | |
max-width: 95% !important; /* Use max-width for scalability */ | |
box-shadow: 0px 0px 30px 3px rgba(7, 43, 132, .2) !important; /* Box shadow for depth */ | |
border-left: 1px solid #fff !important; | |
border-right: 1px solid #fff !important; | |
margin: 0 auto; /* Center the container */ | |
} | |
/* Responsive design for smaller screens */ | |
@media screen and (max-width: 600px) { | |
#hnmain { | |
width: 100% !important; /* Full width on small screens */ | |
box-shadow: none !important; /* Optional: remove shadow on small screens */ | |
} | |
#hnmain > tbody > tr:nth-child(4) > td > table > tbody > tr > td { | |
background-color: #6272a4; | |
} | |
} | |
/* Text and Link Styles */ | |
#hnmain td[bgcolor="#ff6600"] a { | |
color: #000000 !important; /* Black color for header links */ | |
} | |
td { | |
color: #bd93f9 !important; /* Red text color for table data */ | |
} | |
a:link{ | |
color: #bd93f9; | |
} | |
/* Dead Links Styling */ | |
.dead a:link:hover, .dead a:visited { | |
background: #0d0d0d !important; /* Dark background for dead links */ | |
color: #fff !important; /* White text color for contrast */ | |
} | |
.dead a:link:hover::before { | |
content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAMAAAAR8Wy4AAAATlBMVEX///9NTU38/PxjY2OVlZX5+fny8vLDw8Nzc3P29vbh4eHLy8uysrKmpqaKioqDg4Pm5uZVVVXV1dW8vLyPj495eXlra2ve3t7b29taWlqwMuwSAAAAk0lEQVQI1z2OVw7EMAhEsXHBvcROuf9FlzjZzAcSTwN6cEenXYg9Sngjp1jJfxDEmxNgtSI+O24g3Q0sPYCsNAxUrRkzUcbh+tDQGx3KyKKgJr+3E7oqNEtBHDNjUR3AGbM1EaO4tlMuK57cbSOYz8zbampIx6MdbEFrvVcBsw0ajktEqRNNtuI/nj0S+xm37pPSP4ZHBjEu/RG/AAAAAElFTkSuQmCC) !important; | |
background: #fff !important; | |
color: #fff !important; | |
padding: 3px 3px 2px 3px !important; | |
margin-right: 3px !important; | |
border: 1px solid #000 !important; | |
} | |
.dead a:link:hover::after { | |
content: "[DEAD LINK]"; | |
background: #939090 !important; | |
color: #fff !important; | |
padding: 0 4px !important; | |
} | |
/* Header Styling */ | |
#hnmain td[bgcolor="#ff6600"], .pagetop { | |
background: #44475a !important; /* Dracula's "current line" color for the header background */ | |
color: #f8f8f2 !important; /* Dracula's foreground color for text */ | |
} | |
.pagetop a[href="news"], .pagetop a:link, .pagetop a:visited { | |
color: #f8f8f2 !important; /* Ensuring header links are also in the Dracula foreground color */ | |
text-shadow: 0 0 5px rgba(255, 255, 255, 0.2); /* Optional: Adding a subtle glow to make it stand out */ | |
} | |
/* Adjusting the header link colors */ | |
#hnmain td[bgcolor="#ff6600"] a { | |
color: #50fa7b !important; /* Dracula green for a vibrant look */ | |
font-size: 14px !important | |
} | |
/* Correcting the linear gradient in the header which does not fit the Dracula theme */ | |
#hnmain td[bgcolor="#ff6600"] { | |
} | |
/* Enhancing text contrast and visibility */ | |
.pagetop a:not(:first-child):hover { | |
text-decoration: underline !important; | |
color: #ff79c6 !important; /* Dracula pink for hover states to add visual interest */ | |
} | |
/* Front Page News Listing Styles */ | |
.title:first-child { | |
padding-right: 8px !important; | |
padding-left: 8px !important; | |
color: #6272a4 !important; | |
font-size: 11px !important; | |
} | |
.title:not(:first-child) { | |
font-size: 16px !important; | |
font-family: "Verdana", "Lucida Grande", "Open Sans", "Bitstream Vera Sans", sans-serif !important; | |
padding-left: 8px !important; | |
} | |
.title:not(:first-child) > a { | |
border-bottom: 1px solid #620303 !important; | |
} | |
.title:not(:first-child) > a:hover { | |
text-decoration: underline !important; | |
border: none !important; | |
} | |
.title > a:visited { | |
color: #6272a4 !important; | |
} | |
.comhead a:link, .subtext a:visited { | |
color: #8be9fd; | |
} | |
.title .sitebit { | |
color: #f8f8f2 !important; | |
} | |
.subtext { | |
font-size: 12px !important; | |
color: #ff5555 !important; | |
padding-left: 8px !important; | |
} | |
tr[style="height:5px"] { | |
height: 16px !important; | |
} | |
.title > a[href^="news?p="] { | |
color: #ff79c6 !important; | |
} | |
/* Comment Section Styles */ | |
#hnmain > tbody > tr:first-child + tr + tr > td > table + br + br + table { | |
padding-right: 10px !important; | |
} | |
#hnmain > tbody > tr:first-child + tr + tr > td > table > tbody > tr:first-child > .title > a { | |
color: #ffb86c !important; | |
} | |
#hnmain > tbody > tr:nth-child(1) > td > table > tbody > tr > td:nth-child(3) > span { | |
color: #ffb86c; | |
} | |
.commtext { | |
color: #f8f8f2 | |
} | |
textarea, input { | |
background-color: #282a36; | |
color: #f8f8f2; | |
border-color: #8be9fd | |
} | |
.score { | |
color: #ffb86c; | |
} | |
#karma { | |
color: #ff5555; | |
} | |
.comment { | |
font-family: "Lucida Grande", "Open Sans", "Bitstream Vera Sans", "Verdana", sans-serif !important; | |
font-size: 110% !important; | |
line-height: 1.5 !important; | |
} | |
.default { | |
padding: 10px 0 !important; | |
} | |
.default p { | |
margin-bottom: 6px !important; | |
} | |
.comment a { | |
text-decoration: none !important; | |
border-bottom: 1px solid #6272a4 !important; | |
} | |
/* Comment Link Styles */ | |
.commtext a:link, .commtext a:visited { | |
color: #ff79c6 !important; /* Dracula pink color for comment links */ | |
} | |
a[href^="reply"] { | |
background: linear-gradient(to top, rgba(232,228,221,1) 0%, rgba(243,239,237,1) 100%) !important; | |
padding: 2px 6px 3px 6px !important; | |
border-radius: 5px !important; | |
border: 2px solid #a07a42 !important; | |
text-shadow: 0px 1px 0px #ffffff; | |
} | |
a[href^="reply"]:hover { | |
background: linear-gradient(to bottom, rgba(232,228,221,1) 0%, rgba(243,239,237,1) 100%) !important; | |
padding: 2px 6px 3px 6px !important; | |
border-radius: 5px !important; | |
border: 2px solid #c67d0f !important; | |
text-shadow: 0px -1px 0px #ffffff; | |
} | |
.default .comhead { | |
opacity: 1 !important; | |
color: #f60 !important; | |
} | |
.default .comhead:hover { | |
opacity: 1 !important; | |
} | |
.comhead a[href^="user?"] { | |
font-weight: bold !important; | |
color: #f90 !important; | |
} | |
.comment pre { | |
max-width: 750px !important; | |
font-size: 12px !important; | |
} | |
#hnmain > tbody > tr:first-child + tr + tr > td > table + br + br + table .votearrow { | |
margin-top: 11px !important; | |
} | |
/* Dimmed Comment Styling */ | |
.c5a, .c73, .c82, .c88, .c9c, | |
.cae, .cbe, .cce, .cdd { | |
transition: color 0.5s !important; | |
} | |
.c5a:hover, .c73:hover, .c82:hover, .c88:hover, .c9c:hover, | |
.cae:hover, .cbe:hover, .cce:hover, .cdd:hover { | |
color: #6272a4 !important; | |
} | |
/* Footer Styles */ | |
#hnmain > tbody > tr:last-child { | |
background: #44475a !important; /* Dracula secondary background */ | |
} | |
`; | |
// Custom CSS for quotes | |
const quoteCSS = ` | |
.quote { | |
border-left: 3px solid #ff79c6; /* Vibrant pink for contrast */ | |
padding: 6px 6px 6px 9px; | |
font-style: italic; | |
background-color: #282a36; /* Dark gray background typical of Dracula */ | |
color: #f8f8f2; /* Light gray text for readability */ | |
border-radius: 5px; /* Retaining the original border radius */ | |
} | |
`; | |
// Custom CSS for vote arrows | |
const voteArrowCSS = ` | |
:root { | |
--colour-hn-orange: #ffb86c; | |
--colour-hn-orange-pale: rgba(255, 102, 0, 0.05); | |
--colour-voted: #cccccc; /* Colour for voted arrows */ | |
--border-radius: 8px; | |
} | |
.votelinks { | |
min-width: 32px; | |
} | |
.votearrow { | |
background: var(--colour-hn-orange-pale); | |
border-radius: var(--border-radius); | |
color: var(--colour-hn-orange); | |
display: block; | |
width: 24px; | |
height: 24px; | |
font-size: 16px; | |
position: relative; | |
top: 2px; | |
cursor: pointer; /* Makes it clear that it's clickable */ | |
} | |
.votearrow:hover, .votearrow.voted { | |
background: var(--colour-hn-orange); | |
color: white; | |
} | |
.votearrow.voted { | |
background: var(--colour-voted); | |
color: var(--colour-hn-orange); | |
} | |
.votearrow:after { | |
content: "⇧"; | |
} | |
/* Change submission text color to Dracula pink */ | |
.title > a { | |
color: #ff79c6 !important; /* Dracula pink color */ | |
} | |
/* Optional: Make the text darker for better readability */ | |
/* If the current text color is not black, you can set it to pure black */ | |
.title > a { | |
color: #000000 !important; /* Black color for better readability */ | |
} | |
`; | |
// Additional CSS for hover effects | |
const fadeOnHoverCSS = ` | |
.verticalBar:hover { | |
background-color: #282a36 !important; | |
} | |
`; | |
// --------------------------- | |
// Lists | |
// --------------------------- | |
const topics = [ | |
"Trump", "Governments", "Israel", "Biden", "Congress", "Sex", "Housing" , | |
"Court", "Israeli","Elite", "Musk", "Military", "Iran", "Iranian", "Boeing", | |
"artist", "artists", "emacs", "twitter", "antisemitism", "islam", "ukraine", | |
"north korea", "Tesla", "SpaceX", "DoD", "pantagon", "Matlab", "union", "unions", | |
"cats", "cat", "facebook", "tiktok", "snapchat", | |
]; | |
const sites = [ | |
"foxnews.com", "bbc.co.uk", "bbc.com", "cnn.com", "economist.com" | |
]; | |
const users = [ | |
// **sanity** | |
"rendall", "usehackernews", "rewmie", "kaba0", "Natsu", | |
"dijit", "Aloisius", "josephcsible", "iddan", "deadbabe", | |
"Ferret7446", "johnwheeler", | |
// **Islamphopia** | |
"YZF", "throwaway5959", "rottencupcakes", "riku_iki", | |
"llimos", "physicles", "coryrc", "motoxpro", "hirako2000", | |
"richardfeynman", "vasilipupkin", "tptacek", "wonderwonder", "ericfrazier", | |
"weatherlite", "rrook", "wslh", "rythmshifter", "GuB-42", "elromulous", "AnimalMuppet", | |
"prmph", "readthenotes1","gruez", "me_me_me", "prmph", "slibhb", "xpl", "ekianjo", | |
"tekknik", "dijit", "sheff_ne", "Always_Anon", "llm_trw", "samsin", "numpad0", "screye", | |
"brink", "mschuster91", "GardenLetter27","xdennis","JumpCrisscross","tguvot","rickydroll", | |
"megaman821", "RoyTyrell", "bryanlarsen", "myth_drannon", "ftyhbhyjnjk", "nsguy", | |
"pvg","candiodari", | |
]; | |
const projects = [ | |
{ | |
name: "Archive.is", | |
url: "https://archive.is/", | |
}, | |
{ | |
name: "12ft.io", | |
url: "https://12ft.io/", | |
}, | |
{ | |
name: "Archive.org", | |
url: "https://web.archive.org/web/", | |
}, | |
]; | |
// source https://github.com/MostlyEmre/hn-anti-paywall | |
const paywalls = [ | |
"adweek.com", | |
"ad.nl", | |
"ambito.com", | |
"americanbanker.com", | |
"baltimoresun.com", | |
"barrons.com", | |
"bloomberg.com", | |
"bloombergquint.com", | |
"bndestem.nl", | |
"bostonglobe.com", | |
"bd.nl", | |
"brisbanetimes.com.au", | |
"businessinsider.com", | |
"caixinglobal.com", | |
"centralwesterndaily.com.au", | |
"cen.acs.org", | |
"chicagotribune.com", | |
"corriere.it", | |
"chicagobusiness.com", | |
"dailypress.com", | |
"gelderlander.nl", | |
"groene.nl", | |
"demorgen.be", | |
"denverpost.com", | |
"speld.nl", | |
"destentor.nl", | |
"tijd.be", | |
"volkskrant.nl", | |
"df.cl", | |
"editorialedomani.it", | |
"dynamed.com", | |
"ed.nl", | |
"elmercurio.com", | |
"elmundo.es", | |
"elpais.com", | |
"elperiodico.com", | |
"elu24.ee", | |
"britannica.com", | |
"estadao.com.br", | |
"examiner.com.au", | |
"expansion.com", | |
"fnlondon.com", | |
"financialpost.com", | |
"ft.com", | |
"firstthings.com", | |
"foreignpolicy.com", | |
"fortune.com", | |
"genomeweb.com", | |
"glassdoor.com", | |
"globes.co.il", | |
"grubstreet.com", | |
"haaretz.com", | |
"haaretz.co.il", | |
"harpers.org", | |
"courant.com", | |
"hbr.org", | |
"hbrchina.org", | |
"heraldsun.com.au", | |
"fd.nl", | |
"historyextra.com", | |
"humo.be", | |
"ilmanifesto.it", | |
"inc.com", | |
"interest.co.nz", | |
"investorschronicle.co.uk", | |
"lanacion.com.ar", | |
"repubblica.it", | |
"lastampa.it", | |
"latercera.com", | |
"lavoixdunord.fr", | |
"lecho.be", | |
"ledevoir.com", | |
"leparisien.fr", | |
"lesechos.fr", | |
"loebclassics.com", | |
"lrb.co.uk", | |
"labusinessjournal.com", | |
"latimes.com", | |
"medium.com", | |
"medscape.com", | |
"mexiconewsdaily.com", | |
"sloanreview.mit.edu", | |
"technologyreview.com", | |
"mv-voice.com", | |
"nationalgeographic.com", | |
"nationalpost.com", | |
"nzz.ch", | |
"newstatesman.com", | |
"nydailynews.com", | |
"nymag.com", | |
"nzherald.co.nz", | |
"nrc.nl", | |
"ntnews.com.au", | |
"ocregister.com", | |
"orlandosentinel.com", | |
"paloaltoonline.com", | |
"parool.nl", | |
"postimees.ee", | |
"pzc.nl", | |
"qz.com", | |
"quora.com", | |
"gelocal.it", | |
"republic.ru", | |
"reuters.com", | |
"sandiegouniontribune.com", | |
"sfchronicle.com", | |
"scientificamerican.com", | |
"seekingalpha.com", | |
"slate.com", | |
"sofrep.com", | |
"startribune.com", | |
"statista.com", | |
"stuff.co.nz", | |
"\"sueddeutsche.de\"", | |
"sun-sentinel.com", | |
"techinasia.com", | |
"telegraaf.nl", | |
"time.com", | |
"adelaidenow.com.au", | |
"theadvocate.com.au", | |
"theage.com.au", | |
"the-american-interest.com", | |
"theathletic.com", | |
"theathletic.co.uk", | |
"theatlantic.com", | |
"afr.com", | |
"theaustralian.com.au", | |
"bizjournals.com", | |
"canberratimes.com.au", | |
"thecourier.com.au", | |
"couriermail.com.au", | |
"thecut.com", | |
"dailytelegraph.com.au", | |
"thediplomat.com", | |
"economist.com", | |
"theglobeandmail.com", | |
"theherald.com.au", | |
"thehindu.com", | |
"irishtimes.com", | |
"japantimes.co.jp", | |
"kansascity.com", | |
"themarker.com", | |
"mercurynews.com", | |
"themercury.com.au", | |
"mcall.com", | |
"thenation.com", | |
"thenational.scot", | |
"news-gazette.com", | |
"newyorker.com", | |
"nytimes.com", | |
"theolivepress.es", | |
"inquirer.com", | |
"thesaturdaypaper.com.au", | |
"seattletimes.com", | |
"spectator.com.au", | |
"spectator.co.uk", | |
"spectator.us", | |
"smh.com.au", | |
"telegraph.co.uk", | |
"thestar.com", | |
"wsj.com", | |
"washingtonpost.com", | |
"thewrap.com", | |
"the-tls.co.uk", | |
"towardsdatascience.com", | |
"trouw.nl", | |
"tubantia.nl", | |
"vanityfair.com", | |
"vn.nl", | |
"vulture.com", | |
"journalnow.com", | |
"wired.com", | |
"zeit.de" | |
]; | |
// --------------------------- | |
// Functions | |
// --------------------------- | |
// | |
// Function to find elements containing text starting with '>' | |
function findElementContentsStartingWithQuoteChar(elementNames) { | |
let nodes = []; | |
elementNames.forEach(elementName => { | |
const es = document.evaluate(`//${elementName}[contains(text(), '>')]`, document.body, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); | |
for (let i = 0; i < es.snapshotLength; i++) { | |
nodes.push(es.snapshotItem(i)); | |
} | |
}); | |
return nodes; | |
} | |
// Function to apply custom styling to quotes | |
function applyCustomStylingToQuotes(nodes) { | |
nodes.forEach((n, index) => { | |
const textNode = Array.from(n.childNodes).find((n) => n.nodeType === Node.TEXT_NODE && n.textContent.trim().startsWith('>')); | |
if (textNode) { | |
const p = document.createElement('p'); | |
p.classList.add('quote'); | |
if (textNode.textContent.trim() === ">") { | |
const quotedContent = textNode.nextSibling; | |
p.innerHTML = quotedContent.innerHTML.trim(); | |
quotedContent.remove(); | |
} else { | |
p.innerHTML = textNode.textContent.replace(">", "").trim(); | |
} | |
n.replaceChild(p, textNode); | |
} | |
}); | |
} | |
// function to remove inline ads | |
function hideInlineAdsOnHN() { | |
// Select all posts (rows with class "athing") | |
let things = document.querySelectorAll("tr.athing"); | |
// Iterate over each post | |
for(let i = 0; i < things.length; i++) { | |
// Ensure the nextSibling exists and is the expected subtext row | |
if(things[i].nextSibling && things[i].nextSibling.querySelector("td.subtext")) { | |
// Check if the subtext row has exactly two child elements | |
if(things[i].nextSibling.querySelector("td.subtext").childElementCount == 2) { | |
// Hide the post row | |
things[i].style.display = "none"; | |
// Hide the subtext row | |
things[i].nextSibling.style.display = "none"; | |
// Check and hide the additional related element, which could be spacing or further details | |
// Make sure to safely check for the existence of next siblings to avoid errors | |
let possibleAdDetailsRow = things[i].nextSibling.nextSibling; | |
if(possibleAdDetailsRow && possibleAdDetailsRow.nextSibling) { | |
possibleAdDetailsRow.nextSibling.style.display = "none"; | |
} | |
} | |
} | |
} | |
} | |
// Helper function to determine if a row is a spacer | |
function isSpacerRow(row) { | |
// Implement logic to determine if a row is used as a spacer | |
// This can be based on class, style, or lack of certain elements | |
return row.style.height === "5px"; // To be fine tuned | |
} | |
// Helper function to determine if a row is a metadata row | |
function isMetadataRow(row) { | |
// Implement logic to determine if a row contains metadata | |
// This can be based on the presence of certain classes or specific text | |
// Adjust the condition based on the actual structure of the page | |
return row.querySelector('.subtext') !== null; | |
} | |
// Function to hide elements containing specific text | |
function hideElementsByText(selector, text, includeParent = false) { | |
const elements = document.querySelectorAll(selector); | |
const lowerCaseText = text.toLowerCase(); // Convert the text to lowercase | |
elements.forEach(el => { | |
if (el.textContent.toLowerCase().includes(lowerCaseText)) { | |
let elementToHide = includeParent ? el.closest('tr') : el; | |
if (elementToHide) { | |
// Hide the parent <tr> | |
let parentRow = elementToHide.closest('tr'); | |
if (parentRow) { | |
parentRow.style.display = 'none'; | |
// Hide the next sibling if it's a spacer or metadata row | |
let nextSiblingRow = parentRow.nextElementSibling; | |
if (nextSiblingRow && nextSiblingRow.tagName === "TR") { | |
if (isSpacerRow(nextSiblingRow) || isMetadataRow(nextSiblingRow)) { | |
nextSiblingRow.style.display = 'none'; | |
} | |
} | |
// Additionally, check for and hide a previous spacer if present | |
let previousSiblingRow = parentRow.previousElementSibling; | |
if (previousSiblingRow && previousSiblingRow.tagName === "TR") { | |
if (isSpacerRow(previousSiblingRow)) { | |
previousSiblingRow.style.display = 'none'; | |
} | |
} | |
} | |
} | |
} | |
}); | |
} | |
// Function to apply filters based on topics, sites, and users | |
function applyFilters() { | |
topics.forEach(topic => { | |
hideElementsByText('.title a', topic, true); | |
}); | |
sites.forEach(site => { | |
hideElementsByText('.title .sitebit', site, true); | |
}); | |
users.forEach(user => { | |
hideElementsByText('.comhead > a[href^="user?"]', user, true); | |
}); | |
} | |
// Function to bypass paywalls and add redirection | |
function passTheButter(node, paywalls, projects) { | |
// Check if node.nextSibling is an element and has querySelector | |
if (node.nextSibling && node.nextSibling.nodeType === Node.ELEMENT_NODE) { | |
let meta = node.nextSibling.querySelector(".subtext"); | |
// Additional check for meta existence | |
if (meta) { | |
let link = node.querySelector(".titleline a").href; | |
let domain = node.querySelector("span.sitestr") ? node.querySelector("span.sitestr").innerText : ""; | |
let paywall = paywalls.find((paywall) => domain.includes(paywall)); | |
if (paywall) { | |
let paywallSpan = document.createElement("span"); | |
paywallSpan.appendChild(document.createTextNode(" | 💰")); | |
projects.forEach((project) => { | |
const anchor = document.createElement("a"); | |
const line = document.createElement("span"); | |
line.textContent = " | "; | |
anchor.setAttribute("href", `${project.url}${link}`); | |
anchor.setAttribute("target", "_blank"); | |
anchor.setAttribute("rel", "noopener noreferrer"); | |
anchor.textContent = project.name; | |
paywallSpan.appendChild(line); | |
paywallSpan.appendChild(anchor); | |
}); | |
meta.appendChild(paywallSpan); | |
} | |
} else { | |
console.log("Meta element not found for node: ", node); | |
} | |
} else { | |
console.log("Unexpected node type or null nextSibling for node: ", node); | |
} | |
} | |
// Function to inject CSS into the document | |
function injectCSS(css) { | |
const style = document.createElement('style'); | |
style.type = 'text/css'; | |
style.appendChild(document.createTextNode(css)); | |
document.head.appendChild(style); | |
} | |
// --------------------------- | |
// Feature Implementations | |
// --------------------------- | |
// Highlight number of comments in bold | |
function highlightComments() { | |
let commentLinks = document.querySelectorAll("td.subtext > span.subline > a[href^='item?id']"); | |
for (let i = 0; i < commentLinks.length; i++) { | |
let commentMatch = commentLinks[i].innerHTML.match(/^([0-9]+)/); | |
if (commentMatch) { | |
let numComments = commentMatch[0]; | |
let plural = numComments > 1 ? "s" : ""; | |
commentLinks[i].innerHTML = `<span style="font-weight: bold; color: #f1fa8c;">${numComments}</span> comment${plural}`; | |
} | |
} | |
} | |
// Highlight the submission author | |
function highlightSubmissionAuthor() { | |
// Check if the current page is a submission page and not running on iOS | |
if (window.location.href.includes('item?id=') && !/iPad|iPhone|iPod/.test(navigator.userAgent)) { | |
let originalPoster = document.querySelector('.hnuser'); | |
if (originalPoster) { | |
originalPoster = originalPoster.innerText; | |
GM_addStyle(` | |
a[href='user?id=${originalPoster}'] { | |
background: #e0f7fa !important; | |
color: #c40707 !important; | |
padding: 5px 10px !important; | |
border-radius: 10px !important; | |
font-weight: bold !important; | |
font-size: 12px !important; | |
} | |
`); | |
} | |
} | |
} | |
// Add click event to toggle the voted class on vote arrows | |
function addVoteArrowClickEvent() { | |
document.addEventListener('click', function(event) { | |
if (event.target.classList.contains('votearrow')) { | |
event.target.classList.toggle('voted'); | |
} | |
}, false); | |
} | |
// Function to toggle hiding a comment and all its sub-comments. | |
(function() { | |
// Pre-calculate and cache the comments length to avoid recalculating | |
const comments = document.querySelectorAll('.athing'); | |
const commentsLength = comments.length; | |
// Function to toggle comment visibility | |
const toggleComment = (comment, collapse) => { | |
const text = comment.querySelector('.comment'); | |
const displayStyle = text.style.display === '' ? 'none' : ''; | |
text.style.display = displayStyle; | |
// Update collapse button text | |
collapse.textContent = displayStyle === '' ? '[-]' : '[+]'; | |
}; | |
// Optimize by avoiding repeated DOM queries for the same elements | |
for (let i = 0; i < commentsLength; i++) { | |
const comment = comments[i]; | |
const indentImg = comment.querySelector('td.ind img'); | |
// Skip OP and comments without indentation | |
if (indentImg === null) continue; | |
const collapse = document.createElement('a'); | |
Object.assign(collapse.style, { cursor: 'pointer', marginRight: '5px', fontFamily: 'monospace' }); | |
collapse.className = 'addon-comment-collapse'; | |
collapse.textContent = '[-]'; | |
collapse.onclick = () => { | |
const originIndent = indentImg.width; | |
let minIgnoreIndent = Number.MAX_SAFE_INTEGER; | |
toggleComment(comment, collapse); // Toggle the clicked comment | |
for (let j = i + 1; j < commentsLength; j++) { | |
const nextComment = comments[j]; | |
const nextIndentImg = nextComment.querySelector('td.ind img'); | |
const indent = nextIndentImg ? nextIndentImg.width : 0; | |
if (indent <= originIndent) break; | |
if (indent >= minIgnoreIndent) continue; | |
nextComment.style.display = comment.querySelector('.comment').style.display; | |
const subCollapse = nextComment.querySelector('.addon-comment-collapse'); | |
if (subCollapse && subCollapse.textContent == '[+]') { | |
minIgnoreIndent = indent + 1; | |
} else { | |
minIgnoreIndent = Number.MAX_SAFE_INTEGER; | |
} | |
} | |
}; | |
const commentHead = comment.querySelector('.comhead'); | |
commentHead.insertBefore(collapse, commentHead.firstChild); | |
} | |
})(); | |
// --------------------------- | |
// Initialization | |
// --------------------------- | |
// Inject all custom CSS | |
injectCSS(userStyleCSS + quoteCSS + voteArrowCSS + fadeOnHoverCSS); | |
// Add bypass to submissions and links | |
let titles = document.querySelectorAll("table tr.athing"); | |
let postTitle = document.querySelector("tbody table.fatitem tr.athing"); | |
postTitle ? passTheButter(postTitle, paywalls, projects) : titles.forEach((title) => { | |
passTheButter(title, paywalls, projects); | |
}); | |
// Apply custom styling to quotes | |
const commentNodes = findElementContentsStartingWithQuoteChar(['span', 'p']); | |
applyCustomStylingToQuotes(commentNodes); | |
// Run the filter function on page load | |
applyFilters(); | |
// Remove inline ads | |
hideInlineAdsOnHN(); | |
// Add event listeners and other initialization code | |
highlightComments(); | |
highlightSubmissionAuthor(); | |
addVoteArrowClickEvent(); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment