- // ==UserScript==
- // @name Pitchfork Reviews with r/indieheads Comments from Reddit
- // @namespace http://tampermonkey.net/
- // @version 1.0
- // @description Load and display Reddit comments from r/indieheads on Pitchfork album review pages.
- // @author TA
- // @license MIT
- // @match https://pitchfork.com/reviews/albums/*
- // @grant GM_xmlhttpRequest
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- // --- Utility Functions (developed in previous steps) ---
-
- /**
- * Extracts the Album Name from the Pitchfork page.
- * @returns {string|null} The album name or null if not found.
- */
- function extractAlbumName() {
- const albumElement = document.querySelector('h1[data-testid="ContentHeaderHed"]');
- return albumElement ? albumElement.textContent.trim() : null;
- }
-
- /**
- * Extracts the Artist Name(s) from the Pitchfork page.
- * @returns {string|string[]|null} The artist name(s) or null if not found.
- */
- function extractArtistName() {
- const artistElements = document.querySelectorAll('ul[class*="SplitScreenContentHeaderArtistWrapper"] div[class*="SplitScreenContentHeaderArtist"]');
- if (!artistElements.length) {
- return null;
- }
- const artists = Array.from(artistElements).map(el => el.textContent.trim());
- // Return a single string if only one artist, array if multiple
- return artists.length === 1 ? artists[0] : artists;
- }
-
- /**
- * Formats artist and album names into Reddit search query strings.
- * Returns separate queries for FRESH ALBUM and ALBUM DISCUSSION threads.
- *
- * @param {string|string[]} artistName The name of the artist(s).
- * @param {string} albumName The name of the album.
- * @returns {Object} Object with freshAlbumQuery and albumDiscussionQuery properties.
- */
- function formatAlbumSearchQueries(artistName, albumName) {
- // If artistName is an array, join with ' & ' for the query
- const formattedArtist = Array.isArray(artistName) ? artistName.join(' & ') : artistName;
-
- // Create simpler queries that are more likely to match
- // Remove quotes and brackets which can cause search issues
- const freshAlbumQuery = `FRESH ALBUM ${formattedArtist} ${albumName}`;
- const albumDiscussionQuery = `ALBUM DISCUSSION ${formattedArtist} ${albumName}`;
-
- // Return both queries separately
- return {
- freshAlbumQuery,
- albumDiscussionQuery
- };
- }
-
- /**
- * Constructs a Reddit search URL for the r/indieheads subreddit's JSON API endpoint.
- * Cleans the query by removing problematic characters like slashes and ampersands.
- *
- * @param {string} query The search query string.
- * @returns {string} The constructed Reddit search JSON API URL.
- */
- function buildIndieHeadsSearchJsonUrl(query) {
- // Clean the query by removing slashes, ampersands, percent signs, and plus signs with spaces
- // that might interfere with the search functionality
- const cleanedQuery = query
- .replace(/[\/&%+]/g, ' ') // Replace slashes, ampersands, percent signs, and plus signs with spaces
- .replace(/\s+/g, ' ') // Replace multiple spaces with a single space
- .trim(); // Remove leading/trailing spaces
-
- const encodedQuery = encodeURIComponent(cleanedQuery);
- const searchUrl = `https://www.reddit.com/r/indieheads/search.json?q=${encodedQuery}&restrict_sr=on&sort=relevance&t=all`;
- return searchUrl;
- }
-
- /**
- * Identifies relevant Reddit thread URLs from search results based on title patterns.
- * Processes FRESH ALBUM and ALBUM DISCUSSION results separately.
- * Ensures no duplicate threads are added.
- *
- * @param {Array<Object>} freshAlbumResults The results from the FRESH ALBUM search.
- * @param {Array<Object>} albumDiscussionResults The results from the ALBUM DISCUSSION search.
- * @param {string} artistName The name of the artist(s).
- * @param {string} albumName The name of the album.
- * @returns {Array<Object>} An array of objects {title: string, url: string} for all matching threads.
- */
- function identifyRelevantThreads(freshAlbumResults, albumDiscussionResults, artist, albumName) {
- const relevantThreads = [];
- // Track URLs to avoid duplicates
- const addedUrls = new Set();
-
- // Helper function to find the best thread from search results
- const findBestThread = (results, threadType) => {
- if (!results || !Array.isArray(results) || results.length === 0) {
- console.log(`No ${threadType} search results found.`);
- return null;
- }
-
- console.log(`Processing ${results.length} ${threadType} search results.`);
-
- // Look for an exact match first
- for (const item of results) {
- if (item.kind === "t3" && item.data && item.data.title && item.data.permalink) {
- const title = item.data.title;
- const url = "https://www.reddit.com" + item.data.permalink;
-
- // Skip if we've already added this URL
- if (addedUrls.has(url)) {
- console.log(`Skipping duplicate thread: "${title}"`);
- continue;
- }
-
- // Check if this is the right type of thread
- if (title.toLowerCase().includes(threadType.toLowerCase()) &&
- title.toLowerCase().includes(albumName.toLowerCase())) {
- console.log(`Found ${threadType} thread: "${title}"`);
- return { title, url };
- }
- }
- }
-
- // If no exact match, take the first result that contains the album name
- for (const item of results) {
- if (item.kind === "t3" && item.data && item.data.title && item.data.permalink) {
- const title = item.data.title;
- const url = "https://www.reddit.com" + item.data.permalink;
-
- // Skip if we've already added this URL
- if (addedUrls.has(url)) {
- console.log(`Skipping duplicate thread: "${title}"`);
- continue;
- }
-
- if (title.toLowerCase().includes(albumName.toLowerCase())) {
- console.log(`Found ${threadType} thread (partial match): "${title}"`);
- return { title, url };
- }
- }
- }
-
- console.log(`No matching ${threadType} thread found.`);
- return null;
- };
-
- // Find the best thread for each type
- const freshAlbumThread = findBestThread(freshAlbumResults, "FRESH ALBUM");
-
- // Add FRESH ALBUM thread if found
- if (freshAlbumThread) {
- relevantThreads.push(freshAlbumThread);
- addedUrls.add(freshAlbumThread.url); // Track the URL to avoid duplicates
- console.log(`Added FRESH ALBUM thread: "${freshAlbumThread.title}"`);
- }
-
- // Find ALBUM DISCUSSION thread
- const albumDiscussionThread = findBestThread(albumDiscussionResults, "ALBUM DISCUSSION");
-
- // Add ALBUM DISCUSSION thread if found and not a duplicate
- if (albumDiscussionThread && !addedUrls.has(albumDiscussionThread.url)) {
- relevantThreads.push(albumDiscussionThread);
- addedUrls.add(albumDiscussionThread.url);
- console.log(`Added ALBUM DISCUSSION thread: "${albumDiscussionThread.title}"`);
- } else if (albumDiscussionThread) {
- console.log(`Skipping duplicate ALBUM DISCUSSION thread: "${albumDiscussionThread.title}"`);
- }
-
- console.log(`Found ${relevantThreads.length} unique relevant threads`);
- return relevantThreads;
- }
-
- /**
- * Fetches comments from a given Reddit thread URL using the .json endpoint.
- * Note: This uses GM_xmlhttpRequest for cross-origin requests in Userscripts.
- *
- * @param {string} threadUrl The URL of the Reddit thread.
- * @returns {Promise<Array<Object>|null>} A promise that resolves with an array of comment data or null on error.
- */
- function fetchRedditComments(threadUrl) {
- console.log(`[fetchRedditComments] Attempting to fetch comments for: ${threadUrl}`);
- return new Promise((resolve, reject) => {
- // Append .json to the thread URL to get the JSON data
- const jsonUrl = threadUrl.endsWith('.json') ? threadUrl : threadUrl + '.json';
-
- console.log(`[fetchRedditComments] Requesting URL: ${jsonUrl}`);
-
- // Use GM_xmlhttpRequest for cross-origin requests
- GM_xmlhttpRequest({
- method: "GET",
- url: jsonUrl,
- onload: function(response) {
- console.log(`[fetchRedditComments] Received response for ${jsonUrl}. Status: ${response.status}`);
- try {
- if (response.status === 200) {
- console.log(`[fetchRedditComments] Response Text for ${jsonUrl}: ${response.responseText.substring(0, 500)}...`); // Log beginning of response
- const data = JSON.parse(response.responseText);
- console.log("[fetchRedditComments] Successfully parsed JSON response.");
- // The JSON response for a thread includes two arrays: [submission, comments]
- // We need the comments array (index 1)
- if (data && data.length === 2 && data[1] && data[1].data && data[1].data.children) {
- console.log(`[fetchRedditComments] Found comment data. Number of top-level items: ${data[1].data.children.length}`);
- // Process the raw comment data to extract relevant info and handle replies
- const comments = processComments(data[1].data.children);
- console.log(`[fetchRedditComments] Processed comments. Total processed: ${comments.length}`);
- resolve(comments);
- } else {
- console.error("[fetchRedditComments] Unexpected Reddit JSON structure:", data);
- resolve(null); // Resolve with null for unexpected structure
- }
- } else {
- console.error("[fetchRedditComments] Error fetching Reddit comments:", response.status, response.statusText);
- resolve(null); // Resolve with null on HTTP error
- }
- } catch (e) {
- console.error("[fetchRedditComments] Error parsing Reddit comments JSON:", e);
- resolve(null); // Resolve with null on parsing error
- }
- },
- onerror: function(error) {
- console.error("[fetchRedditComments] GM_xmlhttpRequest error fetching Reddit comments:", error);
- resolve(null); // Resolve with null on request error
- }
- });
- });
- }
-
- /**
- * Recursively processes raw Reddit comment data to extract relevant info and handle replies.
- * Filters out 'more' comments placeholders.
- *
- * @param {Array<Object>} rawComments The raw comment children array from Reddit API.
- * @returns {Array<Object>} An array of processed comment objects.
- */
- function processComments(rawComments) {
- const processed = [];
- if (!rawComments || !Array.isArray(rawComments)) {
- return processed;
- }
-
- for (const item of rawComments) {
- // Skip 'more' comments placeholders
- if (item.kind === 'more') {
- continue;
- }
-
- // Ensure it's a comment and has the necessary data
- if (item.kind === 't1' && item.data) {
- const commentData = item.data;
- const processedComment = {
- author: commentData.author,
- text: commentData.body,
- score: commentData.score,
- created_utc: commentData.created_utc,
- replies: [] // Initialize replies array
- };
-
- // Recursively process replies if they exist
- if (commentData.replies && commentData.replies.data && commentData.replies.data.children) {
- processedComment.replies = processComments(commentData.replies.data.children);
- }
-
- processed.push(processedComment);
- }
- }
- return processed;
- }
-
- // --- HTML Structures and Injection ---
-
- const REDDIT_COMMENTS_SECTION_HTML = `
- <div class="reddit-comments-section">
- <h3>Reddit Comments from r/indieheads</h3>
- <div class="reddit-comments-tabs">
- <!-- Tab buttons will be added here -->
- </div>
- <div class="reddit-comments-content">
- <!-- Comment content areas will be added here -->
- <!-- Each area will have a data-thread-id or similar to link to the tab -->
- </div>
- </div>
- `;
-
- /**
- * Injects HTML content after the last paragraph in the article.
- * @param {string|HTMLElement} content The HTML string or HTMLElement to inject.
- */
- function injectAfterLastParagraph(content) {
- // Find the article element
- const article = document.querySelector('article');
- if (!article) {
- console.error('Article element not found for injection');
- return;
- }
-
- // Find all paragraphs within the article, excluding those with class "disclaimer"
- const paragraphs = Array.from(article.querySelectorAll('p')).filter(p =>
- !p.classList.contains('disclaimer') &&
- !p.closest('.disclaimer') // Also exclude paragraphs inside elements with class "disclaimer"
- );
-
- if (paragraphs.length === 0) {
- console.error('No valid paragraphs found in article for injection');
- return;
- }
-
- // Get the last paragraph
- const lastParagraph = paragraphs[paragraphs.length - 1];
-
- // Insert content after the last paragraph
- if (typeof content === 'string') {
- lastParagraph.insertAdjacentHTML('afterend', content);
- } else {
- lastParagraph.insertAdjacentElement('afterend', content);
- }
- }
-
- // Function to render comments to HTML (basic structure)
- function renderCommentsHtml(comments, level = 0) {
- let html = `<ul class="reddit-comment-list level-${level}">`;
- if (!comments || comments.length === 0) {
- html += '<li>No comments found for this thread.</li>';
- } else {
- // Filter out deleted comments
- const validComments = comments.filter(comment =>
- comment.author !== "[deleted]" && comment.text !== "[deleted]"
- );
-
- if (validComments.length === 0) {
- html += '<li>No valid comments found for this thread.</li>';
- } else {
- validComments.forEach(comment => {
- html += `<li class="reddit-comment">`;
-
- // Add collapse button for top-level comments
- if (level === 0) {
- html += `<div class="comment-meta">
- <b>${comment.author}</b> (${comment.score} points)
- <button class="comment-collapse-button">[−]</button>
- </div>`;
- } else {
- html += `<div class="comment-meta"><b>${comment.author}</b> (${comment.score} points)</div>`;
- }
-
- // Process comment text for special content
- let processedText = comment.text;
-
- // Process Giphy embeds first
- processedText = processedText.replace(/!\[gif\]\(giphy\|([a-zA-Z0-9]+)(?:\|downsized)?\)/g, (match, giphyId) => {
- return `
- <div class="giphy-embed-container">
- <iframe src="https://giphy.com/embed/${giphyId}"
- width="480" height="270" frameBorder="0"
- class="giphy-embed" allowFullScreen></iframe>
- </div>
- `;
- });
-
- // Process Reddit image links
- processedText = processedText.replace(/(https:\/\/preview\.redd\.it\/[a-zA-Z0-9]+\.(jpeg|jpg|png|gif)\?[^\s)]+)/g, (match, imageUrl) => {
- return `
- <div class="reddit-image-container">
- <img src="${imageUrl}" alt="Reddit Image" class="reddit-inline-image" />
- </div>
- `;
- });
-
- // Process Markdown image syntax for Reddit images
- processedText = processedText.replace(/!\[.*?\]\((https:\/\/preview\.redd\.it\/[a-zA-Z0-9]+\.(jpeg|jpg|png|gif)\?[^\s)]+)\)/g, (match, imageUrl) => {
- return `
- <div class="reddit-image-container">
- <img src="${imageUrl}" alt="Reddit Image" class="reddit-inline-image" />
- </div>
- `;
- });
-
- // Process basic Markdown formatting
-
- // Bold text
- processedText = processedText.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
-
- // Italic text
- processedText = processedText.replace(/\*([^*]+)\*/g, '<em>$1</em>');
-
- // Block quotes - simple implementation
- processedText = processedText.replace(/^(>|>)\s*(.*?)$/gm, '<blockquote>$2</blockquote>');
-
- // Parse Markdown links
- processedText = processedText.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, linkText, url) => {
- return `<a href="${url}" target="_blank" rel="noopener noreferrer">${linkText}</a>`;
- });
-
- // Parse plain URLs
- processedText = processedText.replace(/(?<!["\'])(https?:\/\/[^\s<>[\]()'"]+)(?![^<]*>)/g, (match, url) => {
- return `<a href="${url}" target="_blank" rel="noopener noreferrer">${url}</a>`;
- });
-
- // Handle line breaks - simple approach
- processedText = processedText.replace(/\n\n+/g, '</p><p>');
- processedText = processedText.replace(/\n/g, '<br>');
-
- // Wrap in paragraph tags if not already
- if (!processedText.startsWith('<p>') &&
- !processedText.startsWith('<div') &&
- !processedText.startsWith('<blockquote') &&
- !processedText.includes('<div class="giphy-embed-container">') &&
- !processedText.includes('<div class="reddit-image-container">')) {
- processedText = `<p>${processedText}</p>`;
- }
-
- html += `<div class="comment-body">${processedText}</div>`;
- if (comment.replies && comment.replies.length > 0) {
- html += renderCommentsHtml(comment.replies, level + 1);
- }
- html += `</li>`;
- });
- }
- }
- html += `</ul>`;
- return html;
- }
-
- function setupCommentCollapse() {
- document.querySelectorAll('.comment-collapse-button').forEach(button => {
- button.addEventListener('click', function() {
- const commentLi = this.closest('.reddit-comment');
- commentLi.classList.toggle('collapsed');
-
- // Update button text
- if (commentLi.classList.contains('collapsed')) {
- this.textContent = '[+]';
- } else {
- this.textContent = '[−]';
- }
- });
- });
- }
-
- // --- CSS Styles ---
- function injectStyles() {
- const styles = `
- @media (min-width: 2400px) {
- #main-content div[class^="GridWrapper"] {
- max-width: 2000px;
- }
- }
- .reddit-comments-section {
- margin-top: 30px;
- padding: 20px;
- border-top: 1px solid #ddd;
- font-family: inherit;
- }
- .reddit-comments-tabs {
- display: flex;
- flex-wrap: wrap;
- margin-bottom: 15px;
- }
- .reddit-tab-button {
- padding: 8px 12px;
- margin-right: 5px;
- margin-bottom: 5px;
- background: #f0f0f0;
- border: 1px solid #ccc;
- border-radius: 4px;
- cursor: pointer;
- }
- .reddit-tab-button:not(.active):hover {
- background: #f8f8f8;
- }
- .reddit-tab-button:hover, .reddit-tab-button:active, .reddit-tab-button:focus {
- text-decoration: none;
- }
- .reddit-tab-button.active {
- background: #e0e0e0;
- border-color: #aaa;
- font-weight: bold;
- }
- .reddit-comment-list {
- list-style-type: none;
- padding-left: 0;
- }
- .reddit-comment-list.level-0 {
- padding-left: 0;
- margin-left: 0;
- }
- .reddit-comment-list.level-1 {
- padding-left: 20px;
- border-left: 2px solid #eee;
- }
- .reddit-comment-list.level-2,
- .reddit-comment-list.level-3,
- .reddit-comment-list.level-4,
- .reddit-comment-list.level-5 {
- padding-left: 20px;
- border-left: 2px solid #f5f5f5;
- }
- .reddit-comment {
- margin-bottom: 15px;
- }
- .reddit-image-container {
- margin-top: 10px;
- }
- .comment-meta {
- font-size: .9em;
- margin-bottom: 5px;
- color: #666;
- }
- .comment-body {
- line-height: 1.5;
- }
- /* Markdown formatting styles */
- .comment-body strong {
- font-weight: 700;
- }
- .comment-body em {
- font-style: italic;
- }
- .comment-body blockquote {
- border-left: 3px solid #c5c1ad;
- margin: 8px 0;
- padding: 0 8px 0 12px;
- color: #646464;
- background-color: #f8f9fa;
- }
- /* Paragraph styling */
- .comment-body p {
- margin: .8em 0;
- }
-
- .comment-body p:first-child {
- margin-top: 0;
- }
- .comment-body p:last-child {
- margin-bottom: 0;
- }
- .comment-body blockquote p {
- margin: .4em 0;
- }
- .reddit-comment.collapsed .comment-body,
- .reddit-comment.collapsed .reddit-comment-list {
- display: none;
- }
- .reddit-comment.collapsed {
- opacity: 0.7;
- }
- .comment-collapse-button {
- background: none;
- border: none;
- color: #0079d3;
- cursor: pointer;
- font-size: 12px;
- margin-left: 5px;
- padding: 0;
- }
- .comment-collapse-button:hover {
- text-decoration: underline;
- }
- `;
-
- const styleElement = document.createElement('style');
- styleElement.textContent = styles;
- document.head.appendChild(styleElement);
- }
-
-
- // --- Main Execution Logic ---
-
- async function init() {
- console.log("Pitchfork Reddit Comments Userscript started.");
-
- // Inject CSS styles
- injectStyles();
-
- const artist = extractArtistName();
- const album = extractAlbumName();
-
- if (!artist || !album) {
- console.log("Could not extract artist or album name. Exiting.");
- return;
- }
-
- console.log(`Found Artist: ${artist}, Album: ${album}`);
-
- const queries = formatAlbumSearchQueries(artist, album);
- console.log(`Search queries:`, queries);
-
- // Make separate search requests for each query type
- const freshAlbumUrl = buildIndieHeadsSearchJsonUrl(queries.freshAlbumQuery);
- const albumDiscussionUrl = buildIndieHeadsSearchJsonUrl(queries.albumDiscussionQuery);
-
- console.log(`Fresh Album Search URL: ${freshAlbumUrl}`);
- console.log(`Album Discussion Search URL: ${albumDiscussionUrl}`);
-
- // Function to perform a search request
- const performSearch = (url) => {
- return new Promise((resolve, reject) => {
- GM_xmlhttpRequest({
- method: "GET",
- url: url,
- onload: function(response) {
- try {
- console.log(`[Search Request] Received response. Status: ${response.status}`);
- if (response.status === 200) {
- const searchData = JSON.parse(response.responseText);
- console.log("[Search Request] Successfully parsed JSON response.");
- if (searchData && searchData.data && searchData.data.children) {
- resolve(searchData.data.children);
- } else {
- console.error("[Search Request] Unexpected Reddit search JSON structure:", searchData);
- resolve([]);
- }
- } else {
- console.error("[Search Request] Error fetching Reddit search results:", response.status, response.statusText);
- resolve([]);
- }
- } catch (e) {
- console.error("[Search Request] Error parsing Reddit search JSON:", e);
- resolve([]);
- }
- },
- onerror: function(error) {
- console.error("[Search Request] GM_xmlhttpRequest error fetching Reddit search results:", error);
- resolve([]);
- }
- });
- });
- };
-
- try {
- // Perform both searches in parallel
- const [freshAlbumResults, albumDiscussionResults] = await Promise.all([
- performSearch(freshAlbumUrl),
- performSearch(albumDiscussionUrl)
- ]);
-
- // Identify relevant threads from both result sets
- const relevantThreads = identifyRelevantThreads(
- freshAlbumResults,
- albumDiscussionResults,
- typeof artist === 'string' ? artist : artist.join(' & '),
- album
- );
-
- if (relevantThreads.length === 0) {
- console.log("No relevant Reddit threads found.");
- const noThreadsMessage = document.createElement('p');
- noThreadsMessage.textContent = 'No relevant Reddit threads found for this review.';
- noThreadsMessage.style.fontStyle = 'italic';
- noThreadsMessage.style.marginTop = '20px'; // Add some spacing
- injectAfterLastParagraph(noThreadsMessage);
- return;
- }
-
- console.log(`Found ${relevantThreads.length} relevant thread(s):`, relevantThreads);
-
- // Inject the main comments section container
- injectAfterLastParagraph(REDDIT_COMMENTS_SECTION_HTML);
-
- const commentsSection = document.querySelector('.reddit-comments-section');
- const tabsArea = commentsSection.querySelector('.reddit-comments-tabs');
- const contentArea = commentsSection.querySelector('.reddit-comments-content');
-
- // Fetch comments and build tabs/content
- for (let i = 0; i < relevantThreads.length; i++) {
- const thread = relevantThreads[i];
- console.log(`Fetching comments for thread: ${thread.title} (${thread.url})`);
- const comments = await fetchRedditComments(thread.url);
-
- // Generate tab button
- const tabButton = document.createElement('button');
- tabButton.classList.add('reddit-tab-button');
- tabButton.textContent = thread.title + ' ';
- tabButton.setAttribute('data-thread-index', i);
-
- // Add a direct link icon that opens the Reddit thread in a new tab
- const linkIcon = document.createElement('a');
- linkIcon.href = thread.url;
- linkIcon.target = '_blank';
- linkIcon.rel = 'noopener noreferrer'; // Security best practice for target="_blank"
- linkIcon.innerHTML = '🔗';
- linkIcon.title = 'Open Reddit thread in new tab';
- linkIcon.style.fontSize = '0.8em';
- linkIcon.style.opacity = '0.7';
- linkIcon.style.textDecoration = 'none'; // Remove underline
- linkIcon.style.marginLeft = '5px';
-
- tabButton.appendChild(linkIcon);
- tabsArea.appendChild(tabButton);
-
- // Generate comment content area
- const threadContent = document.createElement('div');
- threadContent.classList.add('reddit-tab-content');
- threadContent.setAttribute('data-thread-index', i);
- threadContent.style.display = 'none'; // Hide by default
-
- if (comments) {
- threadContent.innerHTML = renderCommentsHtml(comments);
- // Set up collapse functionality for this tab's comments
- setupCommentCollapse();
- } else {
- threadContent.innerHTML = '<p>Could not load comments for this thread.</p>';
- }
- contentArea.appendChild(threadContent);
-
- // Activate the first tab and content by default
- if (i === 0) {
- tabButton.classList.add('active');
- threadContent.style.display = 'block';
- }
- }
-
- // Add event listeners for tab switching
- const tabButtons = tabsArea.querySelectorAll('.reddit-tab-button');
- const tabContents = contentArea.querySelectorAll('.reddit-tab-content');
-
- tabButtons.forEach(button => {
- button.addEventListener('click', () => {
- const threadIndex = button.getAttribute('data-thread-index');
-
- // Deactivate all tabs and hide all content
- tabButtons.forEach(btn => btn.classList.remove('active'));
- tabContents.forEach(content => content.style.display = 'none');
-
- // Activate the clicked tab and show corresponding content
- button.classList.add('active');
- const activeContent = document.querySelector(`.reddit-tab-content[data-thread-index="${threadIndex}"]`);
- activeContent.style.display = 'block';
-
- // Re-initialize collapse functionality for the newly displayed tab
- setupCommentCollapse();
- });
- });
-
- } catch (error) {
- console.error("Error during search process:", error);
- }
- }
-
- // Run the initialization function
- init();
-
- })();