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(/&/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(/&/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 | + |