View difference between Paste ID: Gi3XQxSk and v0EKePW6
SHOW: | | - or go back to the newest paste.
1-
// ==UserScript==
1+
javascript:(function() {
2-
// @name         Reddit BBCode Converter
2+
    console.log("Nc5 based on Nc6v3.4-2 code"); 
3-
// @namespace    http://tampermonkey.net/
3+
4-
// @version      2.7
4+
    var tweets = Array.from(document.querySelectorAll('.timeline-item'));
5-
// @description  Adds a floating "RC" button to convert Reddit content to BBCode, with options for nesting and image handling. Deactivating removes buttons.
5+
    var tweetTexts = [];
6-
// @author       You
6+
    var userTweets = []; 
7-
// @match        *://old.reddit.com/*
7+
    var imgCounter = 0;
8-
// @match        *://www.reddit.com/*
8+
    var globalTweetCount = 0;
9-
// @match        *://reddit.com/*
9+
    var totalTweetCount = 0; 
10-
// @grant        GM_setValue
10+
    var outputChains = [];
11-
// @grant        GM_getValue
11+
12-
// @grant        GM_addStyle
12+
    
13-
// @run-at       document-idle
13+
    
14-
// ==/UserScript==
14+
    
15
    
16-
(function() {
16+
    tweets.forEach(function(tweet) {
17-
    'use strict';
17+
        
18
        
19-
    let USE_DATA_URL_FOR_IMAGES = GM_getValue('useDataUrlForImages', false);
19+
        var tweetLinkElement = tweet.querySelector('a.tweet-link[href*="/status/"], a[href*="/status/"][title]');
20-
    let IS_ACTIVE = GM_getValue('isActive', true);
20+
21-
    let observer;
21+
        if (tweetLinkElement && tweetLinkElement.href) {
22
            var fullTweetUrl = tweetLinkElement.href;
23-
    // Add styles for the button and toast notification
23+
            
24-
    GM_addStyle(`
24+
            fullTweetUrl = fullTweetUrl.replace('nitter.poast.org', 'twitter.com').replace('xcancel.com', 'twitter.com');
25-
        #bbcode-toggle-button {
25+
            if (fullTweetUrl.includes('/status/')) {
26-
            position: fixed;
26+
                fullTweetUrl = fullTweetUrl.split('?')[0]; 
27-
            bottom: 20px;
27+
28-
            left: 20px;
28+
            userTweets.push(fullTweetUrl);
29-
            width: 40px;
29+
30-
            height: 40px;
30+
31-
            background-color: #007bff;
31+
32-
            color: white;
32+
33-
            border: none;
33+
    tweets.forEach(function(tweet) {
34-
            border-radius: 50%;
34+
        var tweetAuthor = tweet.querySelector('.username');
35-
            font-size: 16px;
35+
        if (tweetAuthor) {
36-
            cursor: pointer;
36+
            var tweetText = tweet.querySelector('.tweet-content').innerHTML;
37-
            z-index: 9999;
37+
38-
            display: flex;
38+
            
39-
            justify-content: center;
39+
            tweetText = tweetText.replace(/<a[^>]+href="([^"]+)"[^>]*>@<[^<]+<\/a>/g, (match, p1) => `[U][URL]${p1}[/URL][/U]`);
40-
            align-items: center;
40+
            tweetText = tweetText.replace(/<a[^>]+href="([^"]+)"[^>]*>[^<]+<\/a>/g, (match, p1) => `[U][URL]${p1}[/URL][/U]`);
41-
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
41+
            tweetText = tweetText.replace(/<br>/g, '\n');
42-
            font-family: sans-serif; /* Consistent font */
42+
            tweetText = tweetText.replace(/<span[^>]+data-sanitized-url="([^"]+)"[^>]*>[^<]+<\/span>/g, (match, p1) => `[U][URL]${p1}[/URL][/U]`);
43
            tweetText = tweetText.replace(/<span[^>]+data-url="([^"]+)"[^>]*>[^<]+<\/span>/g, (match, p1) => `[U][URL]${p1}[/URL][/U]`);
44-
        #bbcode-toggle-button.inactive {
44+
            tweetText = tweetText.replace(/<span[^>]+data-expanded-url="([^"]+)"[^>]*>[^<]+<\/span>/g, (match, p1) => `[U][URL]${p1}[/URL][/U]`);
45-
            background-color: #dc3545; /* Red when inactive */
45+
            tweetText = tweetText.replace(/<span[^>]+title="([^"]+)"[^>]*>[^<]+<\/span>/g, (match, p1) => `[U][URL]${p1}[/URL][/U]`);
46
            tweetText = tweetText.replace(/<a[^>]+href="([^"]+)"[^>]*>[^<]+<\/a>/g, (match, p1) => `[U][URL]${p1}[/URL][/U]`);
47-
        .bbcode-toast {
47+
            tweetText = tweetText.replace(/]*>/g, '\n');
48-
            position: fixed;
48+
            tweetText = tweetText.replace(/<[^>]+>/g, '');
49-
            top: 50%;
49+
            tweetText = tweetText.replace(/ /g, ' ');
50-
            left: 50%;
50+
51-
            transform: translate(-50%,-50%);
51+
            var images = Array.from(tweet.querySelectorAll('.attachment.image img'));
52-
            padding: 10px;
52+
            var imageUrlsForThisTweet = images.map(img => {
53-
            border-radius: 5px;
53+
                var src = decodeURIComponent(img.src);
54-
            background: rgba(0,0,0,0.7);
54+
                var url = new URL(src, window.location.href);
55-
            color: white;
55+
                url.hostname = 'pbs.twimg.com';
56-
            z-index: 10000;
56+
                
57-
            box-shadow: 0 2px 10px rgba(0,0,0,0.3);
57+
                
58-
            font-family: sans-serif;
58+
                url.pathname = url.pathname
59-
            font-size: 14px;
59+
                    .replace('/pic/', '/media/')
60-
            text-align: center; /* Center toast text */
60+
                    .replace('/media/media/', '/media/') 
61
                    .replace(/\/media\/.*\/media\//, '/media/'); 
62-
        .depth-dropdown {
62+
                url.search = '';
63-
            position: absolute;
63+
                return `[img]${url.href}[/img]`;
64-
            margin-left: 5px;
64+
            }).join('\n');
65-
            padding: 2px;
65+
            imgCounter += images.length;
66-
            font-size: 12px;
66+
            if (imageUrlsForThisTweet) {
67-
            z-index: 10001;
67+
                tweetText += `\n\n${imageUrlsForThisTweet}`;
68-
            border: 1px solid #ccc;
68+
69-
            border-radius: 3px;
69+
70-
            background-color: white;
70+
            var videos = Array.from(tweet.querySelectorAll('video'));
71-
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
71+
            var videoUrlsForThisTweet = videos.map(video => {
72-
            font-family: sans-serif;
72+
                var source = video.querySelector('source');
73
                if (source && source.src) {
74-
        .copy-btn, .nested-btn {
74+
                    var src = source.src;
75-
            cursor: pointer;
75+
                    if (src && (src.startsWith('https://video') && (src.includes('twimg.com/ext_tw_video/') || src.includes('twimg.com/amplify_video/')))) {
76-
            background: green;
76+
                        src = src.split('?')[0];
77-
            color: white;
77+
                        return `[U][URL]${src}[/URL][/U]`;
78-
            margin-left: 5px;
78+
79-
            padding: 2px 6px;
79+
80-
            border: none;
80+
                return '';
81-
            border-radius: 4px;
81+
            }).filter(url => url !== '').join('\n');
82-
            font-size: 12px;
82+
            if (videoUrlsForThisTweet) {
83-
            transition: background-color 0.2s ease;
83+
                tweetText += `\n\n${videoUrlsForThisTweet}`;
84-
            font-family: sans-serif;
84+
85-
            display: inline-block; /* Ensure they behave well with text */
85+
86
            globalTweetCount++;
87-
        .nested-btn {
87+
            totalTweetCount++;
88-
            background: blue;
88+
            tweetTexts.push({ text: tweetText, author: tweetAuthor.textContent.slice(1), index: globalTweetCount });
89
90-
        .copy-btn:hover {
90+
            
91-
            background-color: #0056b3;
91+
            if (imgCounter >= 20 || tweet === tweets[tweets.length - 1]) {
92
                if (userTweets.length > 0) {
93-
        .nested-btn:hover {
93+
                    var firstNewChainUrl = userTweets.shift(); 
94-
            background-color: #0033a0;
94+
                    var formattedText = `\n` + [
95
                        firstNewChainUrl, 
96-
    `);
96+
                        `[SPOILER="thread continued"]\n${userTweets.map(url => {
97
                            
98-
    // Create the floating button
98+
                            
99-
    const toggleButton = document.createElement('button');
99+
                            if (url && url.includes('/status/')) {
100-
    toggleButton.id = 'bbcode-toggle-button';
100+
                                return url.split('?')[0];
101-
    toggleButton.textContent = 'RC';
101+
102-
    if (!IS_ACTIVE) {
102+
                            return url;
103-
        toggleButton.classList.add('inactive');
103+
                        }).join('\n')}\n[/SPOILER]`,
104
                        `[SPOILER="full text & large images"]\n\n${tweetTexts.map((tweet, index) => `${globalTweetCount - tweetTexts.length + index + 1}/${globalTweetCount}\n@${tweet.author}\n${tweet.text}\n`).join('\n')}\n\n[COLOR=rgb(184, 49, 47)][B][SIZE=5]To post tweets in this format, more info here: [URL]https://www.thecoli.com/threads/tips-and-tricks-for-posting-the-coli-megathread.984734/post-52211196[/URL][/SIZE][/B][/COLOR]\n[/SPOILER]`
105-
    document.body.appendChild(toggleButton);
105+
                    ].join('\n');
106
107-
    toggleButton.addEventListener('click', () => {
107+
                    formattedText = formattedText.replace(/(\d+\/\d+)\s@/g, '$1\n@');
108-
        IS_ACTIVE = !IS_ACTIVE;
108+
                    outputChains.push(formattedText);
109-
        GM_setValue('isActive', IS_ACTIVE);
109+
                    userTweets = []; 
110-
        if (IS_ACTIVE) {
110+
                    imgCounter = 0;
111-
            toggleButton.classList.remove('inactive');
111+
                    tweetTexts = [];
112-
            initializeCopyButtons();
112+
                    totalTweetCount = 0;
113
                }
114-
            toggleButton.classList.add('inactive');
114+
115-
            removeAllCopyButtons();
115+
116-
            if (observer) {
116+
117-
                observer.disconnect();
117+
118
    var finalFormattedText = '';
119
    if (outputChains.length > 0) {
120
        finalFormattedText = outputChains.join('\n\n');
121
    }
122-
    function showToast(message) {
122+
123-
        const toast = document.createElement('div');
123+
    
124-
        toast.textContent = message;
124+
    
125-
        toast.className = 'bbcode-toast';
125+
    if (userTweets.length > 0) {
126-
        document.body.appendChild(toast);
126+
        var remainingFormattedText = `\n` + [
127-
        setTimeout(() => toast.remove(), 2000);
127+
            userTweets[0], 
128
            `[SPOILER="thread continued"]\n${userTweets.slice(1).map(url => { 
129
                if (url && url.includes('/status/')) {
130-
    function htmlToBBCode(html) {
130+
                    return url.split('?')[0];
131-
        return html
131+
132-
            .replace(/<a[^>]*href=["']([^"']*)["'][^>]*>([\s\S]+?)<\/a>/g, (m, url, text) => {
132+
                return url;
133-
                const cleanUrl = url.replace(/&amp;/g, '&');
133+
            }).join('\n')}\n[/SPOILER]`,
134-
                const cleanText = text.replace(/<(?!\/?(?:b|i|s|u)\b)[^>]*>/gi, '');
134+
            `[SPOILER="full text & large images"]\n\n${tweetTexts.map((tweet, index) => `${globalTweetCount - tweetTexts.length + index + 1}/${globalTweetCount}\n@${tweet.author}\n${tweet.text}\n`).join('\n')}\n\n[COLOR=rgb(184, 49, 47)][B][SIZE=5]To post tweets in this format, more info here: [URL]https://www.thecoli.com/threads/tips-and-tricks-for-posting-the-coli-megathread.984734/post-52211196[/URL][/SIZE][/B][/COLOR]\n[/SPOILER]`
135-
                return `[u][url='${cleanUrl}']${cleanText}[/url][/u]`;
135+
        ].join('\n');
136-
            })
136+
137-
            .replace(/<img[^>]*src=["']([^"']*)["'][^>]*>/g, '[img]$1[/img]')
137+
        remainingFormattedText = remainingFormattedText.replace(/(\d+\/\d+)\s@/g, '$1\n@');
138-
            .replace(/<(b|strong)>([^<]+)<\/(b|strong)>/g, '[b]$2[/b]')
138+
        if (finalFormattedText) {
139-
            .replace(/<(i|em)>([^<]+)<\/(i|em)>/g, '[i]$2[/i]')
139+
            finalFormattedText += '\n\n[threads continued]\n\n' + remainingFormattedText;
140-
            .replace(/<s>([^<]+)<\/s>/g, '[s]$1[/s]')
140+
141-
            .replace(/<blockquote>([\s\S]+?)<\/blockquote>/g, '[quote]$1[/quote]')
141+
            finalFormattedText = remainingFormattedText;
142-
            .replace(/<pre>([\s\S]+?)<\/pre>/g, '[code]$1[/code]')
142+
143-
            .replace(/<code>([^<]+)<\/code>/g, '[icode]$1[/icode]')
143+
    } else if (tweetTexts.length > 0) {
144-
            .replace(/<ol>([\s\S]+?)<\/ol>/g, (match, listContent) => '[LIST=1]\n' + listContent.replace(/<li>([\s\S]+?)<\/li>/g, '[*] $1\n') + '[/LIST]')
144+
        
145-
            .replace(/<ul>([\s\S]+?)<\/ul>/g, (match, listContent) => '[LIST]\n' + listContent.replace(/<li>([\s\S]+?)<\/li>/g, '[*] $1\n') + '[/LIST]')
145+
        
146-
            .replace(/<\/p>|<\s*br\s*\/?>/gi, '\n')
146+
        var remainingFormattedText = `\n` + [
147-
            .replace(/<\/?[^>]+>/g, '')
147+
            window.location.href.replace('nitter.poast.org', 'twitter.com').replace('xcancel.com', 'twitter.com').split('?')[0],
148-
            .replace(/&amp;/g, '&')
148+
            `[SPOILER="full text & large images"]\n\n${tweetTexts.map((tweet, index) => `${globalTweetCount - tweetTexts.length + index + 1}/${globalTweetCount}\n@${tweet.author}\n${tweet.text}\n`).join('\n')}\n\n[COLOR=rgb(184, 49, 47)][B][SIZE=5]To post tweets in this format, more info here: [URL]https://www.thecoli.com/threads/tips-and-tricks-for-posting-the-coli-megathread.984734/post-52211196[/URL][/SIZE][/B][/COLOR]\n[/SPOILER]`
149-
            .replace(/\n{3,}/g, '\n\n')
149+
        ].join('\n');
150-
            .replace(/^\s+|\s+$/g, '');
150+
151
        remainingFormattedText = remainingFormattedText.replace(/(\d+\/\d+)\s@/g, '$1\n@');
152
        finalFormattedText = remainingFormattedText;
153-
    function extractTextWithLinks(html) {
153+
154-
        const parser = new DOMParser();
154+
155-
        const doc = parser.parseFromString(html, 'text/html');
155+
    
156-
        let text = doc.body.innerText.trim();
156+
    var textArea = document.createElement('textarea');
157-
        const links = Array.from(doc.body.querySelectorAll('a'));
157+
    textArea.value = finalFormattedText;
158-
        const images = Array.from(doc.body.querySelectorAll('img'));
158+
    document.body.appendChild(textArea);
159
    textArea.select();
160-
        let reconstructedText = text;
160+
    document.execCommand('copy');
161
    document.body.removeChild(textArea);
162-
        links.forEach(link => {
162+
163-
            const linkText = link.textContent.trim();
163+
    var notificationBox = document.createElement('div');
164-
            const linkUrl = link.href;
164+
    notificationBox.style.position = 'fixed';
165-
            if (linkText && reconstructedText.includes(linkText)) {
165+
    notificationBox.style.bottom = '20px';
166-
                reconstructedText = reconstructedText.replace(linkText, `[u][url]${linkUrl}[/url][/u]`);
166+
    notificationBox.style.left = '20px';
167
    notificationBox.style.padding = '10px';
168-
        });
168+
    notificationBox.style.backgroundColor = 'white';
169
    notificationBox.style.border = '1px solid black';
170-
        images.forEach(image => {
170+
    notificationBox.innerText = `Copied: ${globalTweetCount} tweets`;
171-
            let src = image.src;
171+
    document.body.appendChild(notificationBox);
172-
            if (USE_DATA_URL_FOR_IMAGES) {
172+
173-
                src = image.getAttribute('data-url') || src.replace(/preview\.redd\.it/, 'i.redd.it');
173+
    setTimeout(function() {
174
        notificationBox.style.opacity = '0';
175-
            reconstructedText += `\n[img]${src}[/img]`;
175+
        setTimeout(function() {
176-
        });
176+
            document.body.removeChild(notificationBox);
177
        }, 1000);
178-
        return reconstructedText.trim();
178+
    }, 2000);
179
180
    console.log('Tweet collection process completed and copied to clipboard.');
181-
    function extractPostUrl(element) {
181+