View difference between Paste ID: Snr9up4v and ypST7SVx
SHOW: | | - or go back to the newest paste.
1
javascript:(function() {
2
  console.log('[Reddit BBCode Tool] Activated - Click copy buttons on posts/comments');
3
  const USE_DATA_URL_FOR_IMAGES = false;
4
5
  function htmlToBBCode(html) {
6
    const textarea = document.createElement('textarea');
7
    textarea.innerHTML = html;
8
    const decoded = textarea.value;
9
    return decoded
10
      .replace(/<h1>([\s\S]+?)<\/h1>/g, '[HEADING=1][b]$1[/b][/HEADING]\n\n')
11
      .replace(/<h2>([\s\S]+?)<\/h2>/g, '[HEADING=2][b]$1[/b][/HEADING]\n\n')
12
      .replace(/<h3>([\s\S]+?)<\/h3>/g, '[HEADING=3][b]$1[/b][/HEADING]\n\n')
13
      .replace(/<a href="([^"]+)"[^>]*>([\s\S]+?)<\/a>/g, (m, url, text) => {
14
        const cleanUrl = url.replace(/&/g, '&amp;');
15
        const cleanText = text.replace(/<(?!\/?(?:b|i|s|u)\b)[^>]*>/gi, '');
16
        return `[u][url='${cleanUrl}']${cleanText}[/url][/u]`;
17
      })
18
      .replace(/<img src="([^"]+)"[^>]*>/g, '[img]$1[/img]')
19
      .replace(/<(b|strong)>([^<]+)<\/(b|strong)>/g, '[b]$2[/b]')
20
      .replace(/<(i|em)>([^<]+)<\/(i|em)>/g, '[i]$2[/i]')
21
      .replace(/<s>([^<]+)<\/s>/g, '[s]$1[/s]')
22
      .replace(/<blockquote>([\s\S]+?)<\/blockquote>/g, '[quote]$1[/quote]')
23
      .replace(/<pre>([\s\S]+?)<\/pre>/g, '[code]$1[/code]')
24
      .replace(/<code>([^<]+)<\/code>/g, '[icode]$1[/icode]')
25
      .replace(/<ol>([\s\S]+?)<\/ol>/g, (match, listContent) => '[LIST=1]\n' + listContent.replace(/<li>([\s\S]+?)<\/li>/g, '[*] $1\n') + '[/LIST]')
26
      .replace(/<ul>([\s\S]+?)<\/ul>/g, (match, listContent) => '[LIST]\n' + listContent.replace(/<li>([\s\S]+?)<\/li>/g, '[*] $1\n') + '[/LIST]')
27
      .replace(/<\/p>|<\s*br\s*\/?>/gi, '\n')
28
      .replace(/<\/?[^>]+>/g, '')
29
      .replace(/&amp;/g, '&')
30
      .replace(/\n{3,}/g, '\n\n')
31
      .replace(/^\s+|\s+$/g, '');
32
  }
33
34
  function extractTextWithLinks(html) {
35
    const parser = new DOMParser();
36
    const doc = parser.parseFromString(html, 'text/html');
37
    let text = doc.body.innerText.trim();
38
    const links = Array.from(doc.body.querySelectorAll('a'));
39
    const images = Array.from(doc.body.querySelectorAll('img'));
40
    let reconstructedText = text;
41
    links.forEach(link => {
42
      const linkText = link.textContent.trim();
43
      const linkUrl = link.href;
44
      reconstructedText = reconstructedText.replace(linkText, `[u][url]${linkUrl}[/url][/u]`);
45
    });
46
    images.forEach(image => {
47
      let src = image.src;
48
      if (USE_DATA_URL_FOR_IMAGES) {
49
        src = image.getAttribute('data-url') || src.replace(/preview\\.redd\\.it/, 'i.redd.it');
50
      }
51
      reconstructedText += `\n[img]${src}[/img]`;
52
    });
53
    return reconstructedText.trim();
54
  }
55
56
  function extractPostUrl(element) {
57
    return element.querySelector('a.bylink[href*="/comments/"]')?.href || element.querySelector('a.title[href*="/comments/"]')?.href || (element.hasAttribute('data-permalink') ? `https://www.reddit.com${element.getAttribute('data-permalink')}` : window.location.href);
58
  }
59
60
  function getParentComments(commentElement, maxDepth = 10) {
61-
      const parentContainer = currentComment.parentElement.closest('.sitetable.listing, .child') || currentComment.parentElement.closest('shreddit-comment');
61+
62-
      if (!parentContainer) break;
62+
63-
      const parentComment = parentContainer.parentElement.closest('.thing.comment, shreddit-comment');
63+
64-
      if (!parentComment) break;
64+
65-
      const commentId = parentComment.id || parentComment.getAttribute('data-fullname');
65+
66-
      if (seenIds.has(commentId)) break;
66+
      let parentId = null;
67-
      seenIds.add(commentId);
67+
      let parentCommentElement = null;
68-
      const datetime = parentComment.querySelector('time')?.getAttribute('title') || parentComment.querySelector('time')?.getAttribute('datetime') || 'Unknown';
68+
69-
      const contentHtml = parentComment.querySelector('.md, div[slot="comment"]');
69+
      
70-
      const permalink = parentComment.querySelector('a.bylink')?.href || (parentComment.getAttribute('permalink') ? `https://www.reddit.com${parentComment.getAttribute('permalink')}` : window.location.href);
70+
      parentId = currentComment.getAttribute('parentid');
71
      if (parentId && parentId.startsWith('t1_')) {
72
        parentCommentElement = document.querySelector(`shreddit-comment[thingid="${parentId}"]`);
73
      }
74-
      currentComment = parentComment;
74+
75
      
76
      if (!parentCommentElement) {
77
        const parentLink = currentComment.querySelector('a[data-parent-id]');
78
        if (parentLink) {
79
          parentId = parentLink.getAttribute('data-parent-id');
80
          if (parentId.startsWith('t1_')) {
81
            parentCommentElement = document.querySelector(`.thing.comment[data-fullname="${parentId}"]`);
82
          }
83
        } else {
84
          
85
          const parentThing = currentComment.parentElement.closest('.thing.comment');
86
          if (parentThing) {
87
            parentId = parentThing.getAttribute('data-fullname') || parentThing.id;
88
            parentCommentElement = parentThing;
89
          }
90
        }
91
      }
92
93
      if (!parentCommentElement || !parentId || seenIds.has(parentId)) {
94
        break;
95
      }
96
      seenIds.add(parentId);
97
98
      const datetime = parentCommentElement.querySelector('time')?.getAttribute('title') || parentCommentElement.querySelector('time')?.getAttribute('datetime') || 'Unknown';
99
      const contentHtml = parentCommentElement.querySelector('.md, div[slot="comment"]');
100
      const permalink = parentCommentElement.querySelector('a.bylink')?.href || (parentCommentElement.getAttribute('permalink') ? `https://www.reddit.com${parentCommentElement.getAttribute('permalink')}` : window.location.href);
101
      const contentText = contentHtml ? extractTextWithLinks(contentHtml.innerHTML) : 'No content';
102
      const contentBBCode = htmlToBBCode(contentText);
103
104
      parents.unshift({ permalink, spoilerContent: `[spoiler="text"]\nCommented on ${datetime}\n\n${contentBBCode}\n[/spoiler]` });
105
      currentComment = parentCommentElement;
106
    }
107
    return parents;
108
  }
109
110
  function createDepthDropdown(maxDepth, callback, button) {
111
    const existingDropdown = document.querySelector('.depth-dropdown');
112
    if (existingDropdown) existingDropdown.remove();
113
    const dropdown = document.createElement('select');
114
    dropdown.className = 'depth-dropdown';
115
    dropdown.style.position = 'absolute';
116
    dropdown.style.marginLeft = '5px';
117
    dropdown.style.padding = '2px';
118
    dropdown.style.fontSize = '12px';
119
    for (let i = 0; i <= maxDepth; i++) {
120
      const option = document.createElement('option');
121
      option.value = i;
122
      option.textContent = i === 0 ? 'Only this' : `${i} parent${i > 1 ? 's' : ''}`;
123
      dropdown.appendChild(option);
124
    }
125
    dropdown.addEventListener('change', () => {
126
      callback(parseInt(dropdown.value));
127
      dropdown.remove();
128
    });
129
    dropdown.addEventListener('blur', () => dropdown.remove());
130
    button.insertAdjacentElement('afterend', dropdown);
131
    dropdown.focus();
132
  }
133
134
  function createCopyButtons(element) {
135-
      const mediaContent = [ `Posted on ${datetime}`, ...(imgSection ? [imgSection] : []), ...(videoUrl ? [videoUrl] : []) ];
135+
136
    existingButtons.forEach(btn => btn.remove());
137
    const copyButton = document.createElement('button');
138
    copyButton.textContent = '📋';
139
    copyButton.title = 'Copy content to BBCode';
140
    copyButton.className = 'copy-btn';
141
    copyButton.style.cssText = 'cursor:pointer;background:green;color:white;margin-left:5px;padding:2px 6px;border:none;border-radius:4px;';
142
    const nestedButton = document.createElement('button');
143
    nestedButton.textContent = '📋';
144
    nestedButton.title = 'Copy content with nested parents';
145
    nestedButton.className = 'nested-btn';
146
    nestedButton.style.cssText = 'cursor:pointer;background:blue;color:white;margin-left:5px;padding:2px 6px;border:none;border-radius:4px;';
147
    if (element.tagName.toLowerCase() === 'shreddit-post' || element.classList.contains('thing') && element.classList.contains('link')) {
148
      const url = extractPostUrl(element);
149
      let header = '', body = '', images = new Set();
150
      const title = element.querySelector('h1[slot="title"], .title > a')?.textContent.trim() || '';
151
      const flair = element.querySelector('.linkflairlabel span')?.textContent.trim() || '';
152
      const datetime = element.querySelector('time')?.getAttribute('title') || element.querySelector('time')?.getAttribute('datetime') || 'Unknown';
153
      const videoUrl = element.dataset.url || element.getAttribute('data-url');
154
      header = `${flair ? `[i][${flair}][/i] ` : ''}[b]${title}[/b]\n\n${url}\n\n`;
155
      Array.from(element.querySelectorAll('gallery-carousel li img, .media-preview img'))
156
        .forEach(img => {
157
          let src = img.src;
158
          if (USE_DATA_URL_FOR_IMAGES) {
159
            src = img.getAttribute('data-url') || src.replace(/preview\\.redd\\.it/, 'i.redd.it');
160
          }
161
          if (!src.includes('redditstatic.com/video-') && !src.includes('old.reddit.com/static/checkmark.svg')) {
162
            images.add(src);
163
          }
164
        });
165
      const textBody = element.querySelector('div[slot="text-body"], .md');
166
      if (textBody) body += htmlToBBCode(extractTextWithLinks(textBody.innerHTML));
167
      const imgSection = Array.from(images).map(u => `[img]${u}[/img]`).join('\n');
168
      const mediaContent = [`Posted on ${datetime}`, ...(imgSection ? [imgSection] : []), ...(videoUrl ? [videoUrl] : [])];
169
      const fullPayload = `${header}[spoiler="text"]\n${mediaContent.join('\n\n')}\n\n${body.trim()}\n[/spoiler]`;
170
      copyButton.addEventListener('click', (e) => {
171
        e.preventDefault();
172-
          const items = [ ...parents.slice(0, depth), { permalink, spoilerContent } ];
172+
173
        navigator.clipboard.writeText(fullPayload.replace(/\n{3,}/g, '\n\n'))
174
          .then(() => showToast('Post copied!'))
175
          .catch(err => console.error('[Reddit BBCode Tool] Copy failed:', err));
176
      });
177
      nestedButton.style.display = 'none';
178
      const target = element.querySelector('div[slot="credit-bar"], .tagline') || element;
179
      target.appendChild(copyButton);
180
      target.appendChild(nestedButton);
181
    } else if (element.tagName.toLowerCase() === 'shreddit-comment' || element.classList.contains('comment')) {
182
      const permalink = element.getAttribute('permalink') ? `https://www.reddit.com${element.getAttribute('permalink')}` : element.querySelector('a.bylink')?.href || window.location.href;
183
      const contentHtml = element.querySelector('div[slot="comment"], .md');
184
      if (!contentHtml) {
185
        console.log('[Reddit BBCode Tool] No content found for comment');
186
        return;
187
      }
188
      const datetime = element.querySelector('time')?.getAttribute('title') || element.querySelector('time')?.getAttribute('datetime') || 'Unknown';
189
      const contentText = extractTextWithLinks(contentHtml.innerHTML);
190
      const contentBBCode = htmlToBBCode(contentText);
191
      const spoilerContent = `[spoiler="text"]\nCommented on ${datetime}\n\n${contentBBCode}\n[/spoiler]`;
192
      copyButton.addEventListener('click', (e) => {
193
        e.preventDefault();
194
        e.stopPropagation();
195
        navigator.clipboard.writeText(`${permalink}\n${spoilerContent}`.replace(/\n{3,}/g, '\n\n'))
196
          .then(() => showToast('Comment copied!'))
197
          .catch(err => console.error('[Reddit BBCode Tool] Copy failed:', err));
198
      });
199
      nestedButton.addEventListener('click', (e) => {
200
        e.preventDefault();
201
        e.stopPropagation();
202
        const parents = getParentComments(element);
203
        const maxDepth = parents.length;
204
        createDepthDropdown(maxDepth, (depth) => {
205
          const items = [...parents.slice(0, depth), { permalink, spoilerContent }];
206
          const payload = items
207
            .map((item, index) => {
208
              const indent = '│ '.repeat(index);
209
              const commentLines = `${item.permalink}\n${item.spoilerContent}`.split('\n');
210
              return commentLines.map(line => `${indent}${line}`).join('\n');
211
            })
212
            .join('\n\n').replace(/\n{3,}/g, '\n\n');
213
          navigator.clipboard.writeText(payload)
214
            .then(() => showToast(`Comment with ${depth} parent${depth === 1 ? '' : 's'} copied!`))
215
            .catch(err => console.error('[Reddit BBCode Tool] Nested copy failed:', err));
216
        }, nestedButton);
217
      });
218
      const target = element.querySelector('div[slot="actionRow"], .tagline') || element;
219
      target.appendChild(copyButton);
220
      target.appendChild(nestedButton);
221
    }
222
  }
223
224
  function showToast(message) {
225
    const toast = document.createElement('div');
226
    toast.textContent = message;
227
    toast.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);padding:10px;border-radius:5px;background:#fff;color:#000;z-index:1000;box-shadow:0 0 10px rgba(0,0,0,0.2);';
228
    document.body.appendChild(toast);
229
    setTimeout(() => toast.remove(), 2000);
230
  }
231
232
  document.querySelectorAll('shreddit-post, shreddit-comment, .thing.link, .thing.comment').forEach(createCopyButtons);
233
})();