1
0

Refactor code structure and enhance comments: improve readability and maintainability across content.js, popup.js, and associated styles

This commit is contained in:
Chipperfluff 2026-01-19 23:16:43 +01:00
parent db63c489b8
commit 705e7cad7e
4 changed files with 43 additions and 3 deletions

View File

@ -1,11 +1,15 @@
// Current settings snapshot.
let whitelist = [];
let whitelistHandles = [];
let debugEnabled = false;
const memberOnlyIndex = new Map();
const DATA_PREFIX = "chipperfluff-nobs";
// In-memory index of known member-only videos.
const memberOnlyIndex = new Map(); // id -> { channelKey, hidden }
// Data-* attribute prefix for DOM tags.
const DATA_PREFIX = "chipperfluff-nobs"; // nobs = "no-bs" (member-only videos)
/* ---------------- CSS injection ---------------- */
// Inject a single style tag for hiding matched videos.
function injectStyleOnce() {
if (document.getElementById("member-filter-style")) return;
@ -23,6 +27,7 @@ injectStyleOnce();
/* ---------------- load whitelist ---------------- */
// Debug helpers (no output unless debug is enabled).
function debugLog(...args) {
if (!debugEnabled) return;
console.log("[MemberFilter]", ...args);
@ -38,6 +43,7 @@ function debugGroupEnd() {
console.groupEnd();
}
// Normalize strings so matching is consistent.
function normalizeKey(value) {
return value
.toLowerCase()
@ -47,6 +53,7 @@ function normalizeKey(value) {
.trim();
}
// Support legacy strings and new { name, handle } entries.
function normalizeWhitelist(list) {
const names = [];
const handles = [];
@ -63,6 +70,7 @@ function normalizeWhitelist(list) {
return { names, handles };
}
// Read settings and re-apply visibility immediately.
function loadSettings() {
chrome.storage.local.get({ whitelist: [], debug: false }, data => {
const { names, handles } = normalizeWhitelist(data.whitelist);
@ -81,11 +89,12 @@ function loadSettings() {
loadSettings();
// Reload settings if popup changes them
// Reload settings if popup changes them.
chrome.storage.onChanged.addListener(loadSettings);
/* ---------------- detection logic ---------------- */
// Detect member-only videos and tag them for future updates.
function process(root = document) {
if (isWhitelistedChannelPage()) return;
const badges = root.querySelectorAll("badge-shape");
@ -142,6 +151,7 @@ function process(root = document) {
});
}
// Apply whitelist to already-tagged member-only videos.
function updateKnownVisibility() {
if (isWhitelistedChannelPage()) {
revealAll();
@ -164,17 +174,20 @@ function updateKnownVisibility() {
}
}
// Allow full visibility on whitelisted channel pages.
function isWhitelistedChannelPage() {
const handle = getChannelHandleFromUrl(location.href);
if (!handle) return false;
return whitelistHandles.includes(normalizeKey(handle));
}
// Extract @handle from a channel URL.
function getChannelHandleFromUrl(url) {
const match = url.match(/youtube\.com\/@([^/]+)/i);
return match ? match[1] : "";
}
// Remove hidden flags from any tagged videos.
function revealAll() {
const videos = document.querySelectorAll(
`[data-${DATA_PREFIX}-hidden="true"]`
@ -182,6 +195,7 @@ function revealAll() {
videos.forEach(video => video.removeAttribute(`data-${DATA_PREFIX}-hidden`));
}
// Choose a stable identifier for indexing.
function getVideoId(video, url) {
const dataId = video.getAttribute("data-video-id");
if (dataId) return dataId;
@ -190,6 +204,7 @@ function getVideoId(video, url) {
return fallbackId || "";
}
// Read the channel key from DOM or fallback to text.
function getChannelKey(video) {
const stored = video.getAttribute(`data-${DATA_PREFIX}-channel`);
if (stored) return stored;
@ -201,11 +216,13 @@ function getChannelKey(video) {
return channel ? normalizeKey(channel) : "";
}
// Find a video element by stored id.
function findVideoById(id) {
const escaped = cssEscape(id);
return document.querySelector(`[data-${DATA_PREFIX}-id="${escaped}"]`);
}
// Safe attribute selector escaping.
function cssEscape(value) {
if (window.CSS && CSS.escape) return CSS.escape(value);
return String(value).replace(/["\\]/g, "\\$&");
@ -215,6 +232,7 @@ function cssEscape(value) {
process();
// Watch for new items so member-only tags are applied as they load.
const observer = new MutationObserver(mutations => {
for (const m of mutations) {
for (const node of m.addedNodes) {

View File

@ -1,3 +1,4 @@
/* Base layout */
body {
font-family: "Trebuchet MS", "Verdana", sans-serif;
min-width: 260px;
@ -7,6 +8,7 @@ body {
color: #2b2014;
}
/* Title */
h3 {
margin: 0 0 10px;
letter-spacing: 0.4px;
@ -25,6 +27,7 @@ h3 {
margin: 6px 0 8px;
}
/* Form rows */
.row {
display: flex;
gap: 6px;
@ -36,6 +39,7 @@ h3 {
justify-content: space-between;
}
/* Inputs */
input {
flex: 1;
padding: 6px 8px;
@ -45,6 +49,7 @@ input {
font-size: 12px;
}
/* Buttons */
button {
padding: 6px 10px;
border: 1px solid #b2985b;
@ -64,12 +69,14 @@ button:hover {
filter: brightness(0.95);
}
/* List */
ul {
padding-left: 0;
margin: 0;
list-style: none;
}
/* List rows */
li {
display: flex;
justify-content: space-between;
@ -88,6 +95,7 @@ li button {
background: #f2a285;
}
/* Debug toggle row */
.toggle {
display: flex;
gap: 6px;

View File

@ -6,12 +6,14 @@
<link rel="stylesheet" href="popup.css" />
</head>
<body>
<!-- Title + short description -->
<h3>Channel Whitelist</h3>
<p class="subtext">
Hide member-only videos by default. Whitelisted creators stay visible.
Add the channel name and its @handle or link.
</p>
<!-- Inputs -->
<div class="row">
<input id="channelInput" placeholder="Channel name" />
</div>
@ -20,6 +22,7 @@
<button id="addBtn">Add</button>
</div>
<!-- Settings -->
<div class="row settings">
<label class="toggle">
<input id="debugToggle" type="checkbox" />
@ -31,6 +34,7 @@
Debug logs will appear in the console only when enabled.
</p>
<!-- Whitelist list -->
<ul id="channelList"></ul>
<script src="popup.js"></script>

View File

@ -1,3 +1,4 @@
// UI elements.
const input = document.getElementById("channelInput");
const linkInput = document.getElementById("channelLinkInput");
const addBtn = document.getElementById("addBtn");
@ -5,11 +6,13 @@ const list = document.getElementById("channelList");
const debugToggle = document.getElementById("debugToggle");
const resetBtn = document.getElementById("resetBtn");
// Default settings payload.
const DEFAULTS = {
whitelist: [],
debug: false
};
// Render whitelist and debug state.
function loadChannels() {
chrome.storage.local.get(DEFAULTS, ({ whitelist, debug }) => {
list.innerHTML = "";
@ -32,6 +35,7 @@ function loadChannels() {
});
}
// Add a new whitelist entry with name + handle.
function addChannel() {
const name = input.value.trim();
const link = linkInput.value.trim();
@ -49,6 +53,7 @@ function addChannel() {
linkInput.value = "";
}
// Normalize storage entries to { name, handle } objects.
function normalizeWhitelist(whitelist) {
return (whitelist || []).map(item => {
if (typeof item === "string") {
@ -58,6 +63,7 @@ function normalizeWhitelist(whitelist) {
});
}
// Remove a whitelist entry by index.
function removeChannel(index) {
chrome.storage.local.get(DEFAULTS, ({ whitelist }) => {
const entries = normalizeWhitelist(whitelist);
@ -66,14 +72,17 @@ function removeChannel(index) {
});
}
// Persist debug toggle.
function setDebug(enabled) {
chrome.storage.local.set({ debug: Boolean(enabled) });
}
// Reset all settings to defaults.
function resetDefaults() {
chrome.storage.local.set(DEFAULTS, loadChannels);
}
// Accept @handle or full channel URL.
function normalizeHandle(value) {
const atMatch = value.match(/@([^/?#]+)/);
if (atMatch) return atMatch[1].trim();
@ -83,6 +92,7 @@ function normalizeHandle(value) {
return "";
}
// Wire UI events.
addBtn.onclick = addChannel;
debugToggle.onchange = e => setDebug(e.target.checked);
resetBtn.onclick = resetDefaults;