PageSourceSearch

https://seletronic.com.br/wp-content/themes/selepower/assets/js/selepower-comments.min.js?ver=1789357826

js seletronic.com.br collected 2026-09-24 08:51:16 UTC 57,922 bytes, 1,073 lines download raw bytes

1// --- ENGINE MÚLTIPLA DE COMENTÁRIOS INSTANCIADOS (STYLE FACEBOOK) ---
2document.addEventListener('DOMContentLoaded', () => {
3
4    // Função Criadora de Escopo Fechado (Closure Constructor)
5    // Tudo que rola aqui dentro afeta APENAS O FORMULÁRIO que foi passado (container)
6    window.initCommentForm = function(container) {
7        if(!container) return;
8
9        // Core Elements Isolados
10        const textarea = container.querySelector('.sp-comment-textarea');
11        const submitBtn = container.querySelector('.sp-comment-submit-btn');
12        const formPanel = container.querySelector('.sp-advanced-comment-form');
13        const postIdInput = container.querySelector('.sp_post_id');
14        const postId = postIdInput ? postIdInput.value : '';
15        const parentIdInput = container.querySelector('.sp_comment_parent');
16        const editIdInput = container.querySelector('.sp_edit_comment_id');
17        const spinner = container.querySelector('.sp-comment-spinner');
18        
19        // Emojis (Native OS Trigger approach)
20        const emojiBtn = container.querySelector('.sp-emoji-trigger');
21
22        // Mentions Elements
23        const mentionsDropdown = container.querySelector('.sp-mentions-dropdown');
24        const mentionsList = container.querySelector('.sp-mentions-list');
25
26        // Estado da Menção Local
27        let isMentioning = false;
28        let mentionQuery = '';
29        let currentFocus = 0;
30        
31        if(!textarea) return;
32
33        // 1. Controle UI do ContentEditable
34        textarea.addEventListener('input', function() {
35            const hasMedia = this.querySelector('img, iframe, .sp-yt-embed-preview');
36            if(this.textContent.trim().length > 0 || hasMedia) {
37                submitBtn.classList.remove('opacity-50', 'cursor-not-allowed', 'bg-gray-100', 'dark:bg-darksurface', 'text-gray-500');
38                submitBtn.classList.add('bg-seleblue', 'text-white', 'shadow-md', 'pointer-events-auto');
39            } else {
40                submitBtn.classList.add('opacity-50', 'cursor-not-allowed', 'bg-gray-100', 'dark:bg-darksurface', 'text-gray-500');
41                submitBtn.classList.remove('bg-seleblue', 'text-white', 'shadow-md', 'pointer-events-auto');
42            }
43        });
44
45        const mentionBtn = container.querySelector('.sp-mention-trigger');
46        if(mentionBtn) {
47            mentionBtn.addEventListener('click', () => {
48                textarea.focus();
49                if(typeof closeEmojiDropdown === 'function') closeEmojiDropdown();
50                if(typeof closeVideoDropdown === 'function') closeVideoDropdown();
51                document.execCommand('insertText', false, '@');
52                textarea.dispatchEvent(new KeyboardEvent('keyup', { key: '@', bubbles: true }));
53            });
54        }
55        
56        const imageUploadInput = container.querySelector('.sp-comment-image-upload');
57        const imageTrigger = container.querySelector('.sp-image-trigger');
58        if (imageTrigger && imageUploadInput) {
59            imageTrigger.addEventListener('click', () => {
60                // Aqui podemos injetar var do php no header. 
61                if (typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in !== '1') {
62                    const loginBtn = document.querySelector('.trigger-login-modal');
63                    if(loginBtn) loginBtn.click();
64                    return;
65                }
66                if (textarea.querySelectorAll('.sp-comment-gif, .sp-comment-upload, .sp-yt-embed-preview').length >= 1) {
67                    alert('Você já anexou uma mídia neste comentário! Remova a atual para inserir outra.');
68                    return;
69                }
70                imageUploadInput.click();
71            });
72        }
73
74        if (imageUploadInput) {
75            imageUploadInput.addEventListener('change', function(e) {
76                const file = e.target.files[0];
77                if (!file) return;
78                
79                if (file.size > 2 * 1024 * 1024) {
80                    alert('A imagem é muito grande. O limite máximo é 2MB.');
81                    this.value = '';
82                    return;
83                }
84                if (textarea.querySelectorAll('.sp-comment-gif, .sp-comment-upload, .sp-yt-embed-
84preview').length >= 1) {
85                    alert('Você já anexou uma mídia neste comentário! Remova a atual para inserir outra.');
86                    this.value = '';
87                    return;
88                }
89                if (!file.type.match(/^image\/(jpeg|png|webp|gif)$/)) {
90                    alert('Formato não suportado. Use JPEG, PNG, WEBP ou GIF.');
91                    this.value = '';
92                    return;
93                }
94                
95                textarea.setAttribute('contenteditable', 'false');
96                textarea.style.opacity = '0.5';
97                submitBtn.disabled = true;
98                
99                const reader = new FileReader();
100                reader.onload = function(event) {
101                    const img = new Image();
102                    img.onload = function() {
103                        const canvas = document.createElement('canvas');
104                        const MAX_DIM = 1080;
105                        let width = img.width;
106                        let height = img.height;
107                        
108                        if (width > MAX_DIM || height > MAX_DIM) {
109                            if (width > height) {
110                                height = Math.round(height * (MAX_DIM / width));
111                                width = MAX_DIM;
112                            } else {
113                                width = Math.round(width * (MAX_DIM / height));
114                                height = MAX_DIM;
115                            }
116                        }
117                        
118                        canvas.width = width;
119                        canvas.height = height;
120                        const ctx = canvas.getContext('2d');
121                        ctx.drawImage(img, 0, 0, width, height);
122                        
123                        const base64Image = canvas.toDataURL('image/jpeg', 0.70);  
124                        
125                        const formData = new FormData();
126                        formData.append('action', 'sp_upload_comment_media');
127                        formData.append('image_data', base64Image);
128                        formData.append('nonce', selepowerAjax.media_nonce); 
129                        
130                        fetch(selepowerAjax.ajaxurl, { method: 'POST', body: formData })
131                            .then(res => res.json())
132                            .then(res => {
133                                textarea.setAttribute('contenteditable', 'true');
134                                textarea.style.opacity = '1';
135                                submitBtn.disabled = false;
136                                
137                                if (res.success) {
138                                    textarea.focus();
139                                    const htmlBlob = `<br><img src="${res.data.url}" class="sp-comment-upload max-w-full rounded-lg shadow-sm border border-gray-100 dark:border-gray-800 my-2" contenteditable="false"><br>`;
140                                    document.execCommand('insertHTML', false, htmlBlob);
141                                    textarea.dispatchEvent(new Event('input'));
142                                } else {
143                                    alert(res.data.message || 'Falha ao processar arquivo no servidor.');
144                                }
145                            })
146                            .catch(e => {
147                                textarea.setAttribute('contenteditable', 'true');
148                                textarea.style.opacity = '1';
149                                submitBtn.disabled = false;
150                                alert('Erro de conexão ao enviar imagem.');
151                            });
152                    };
153                    img.src = event.target.result;
154                };
155                reader.readAsDataURL(file);
156                this.value = ''; 
157            });
158        }
159
160        textarea.addEventListener('keyup', (e) => {
161            if (isMentioning && (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter')) return;
162            const sel = window.getSelection();
163            if(!sel.rangeCount) return;
164            const range = sel.getRangeAt(0);
165            
166            if (range.startContainer.parentNode && range.startContainer.parentNode.tagName === 'SPAN') {
167                closeMentionsDropdown(); return;
168            }
169            if (range.startContainer.nodeType !== Node.TEXT_NODE) return;
170
171            const cursorOffset = range.startOffset;
172            const textToCursor = range.startContainer.textContent.substring(0, cursorOffset);
173            const match = textToCursor.match(/(?:^|\s)@([\w.-]*)$/);
174
175            if (match !== null) {
176                isMentioning = true;
177                mentionQuery = match[1];
178                container.spCurrentRange = range.cloneRange();
179                container.spMentionQueryLength = match[1].length + 1; 
180
181                if (mentionQuery.length >= 2) {
182                    fetch(selepowerAjax.ajaxurl + '?action=sp_search_mention_users&q=' + encodeURIComponent(mentionQuery))
183                        .then(res => res.json())
184                        .then(data => {
185                            if(data.success && data.data.length > 0) renderMentionsDropdown(data.data);
186                            else closeMentionsDropdown();
187                        });
188                } else {
189                    mentionsDropdown.classList.remove('scale-95', 'opacity-0', 'hidden');
190                    mentionsList.innerHTML = '<li class="p-3 text-xs text-gray-500 text-center">Continue digitando...</li>';
191                }
192            } else {
193                closeMentionsDropdown();
194            }
195        });
196
197        textarea.addEventListener('keydown', (e) => {
198            if (!isMentioning) return;
199            let items = mentionsList.querySelectorAll('li.mention-item');
200            if (items.length === 0) return;
201            if (e.key === 'ArrowDown') { currentFocus++; addActive(items); e.preventDefault(); } 
202            else if (e.key === 'ArrowUp') { currentFocus--; addActive(items); e.preventDefault(); } 
203            else if (e.key === 'Enter') {
204                e.preventDefault();
205                if (currentFocus > -1 && items[currentFocus]) items[currentFocus].click();
206            }
207        });
208
209        function addActive(items) {
210            for (let i = 0; i < items.length; i++) {
211                items[i].classList.remove('bg-blue-50', 'dark:bg-blue-900/30', 'border-seleblue');
212                items[i].classList.add('border-transparent');
213            }
214            if (currentFocus >= items.length) currentFocus = 0;
215            if (currentFocus < 0) currentFocus = (items.length - 1);
216            items[currentFocus].classList.add('bg-blue-50', 'dark:bg-blue-900/30', 'border-seleblue');
217            items[currentFocus].classList.remove('border-transparent');
218        }
219
220        function renderMentionsDropdown(users) {
221            mentionsList.innerHTML = '';
222            currentFocus = 0; 
223            users.forEach((user, index) => {
224                const li = document.createElement('li');
225                li.className = `mention-item cursor-pointer p-2 flex items-center gap-2 border-l-2 transition-colors ${index === 0 ? 'bg-blue-50 dark:bg-blue-900/30 border-seleblue' : 'border-transparent hover:bg-gray-50 dark:hover:bg-[#111]'}`;
226                li.innerHTML = `<img src="${user.avatar}" class="w-6 h-6 rounded-full border border-gray-200 dark:border-gray-700"><div class="flex flex-col leading-tight"><span class="text-sm font-semibold text-gray-800 dark:text-gray-200">${user.name}</span><span class="text-[10px] text-gray-500">@${user.slug}</span></div>`;
227                
228                li.addEventListener('click', function(e) {
229                    e.preventDefault();
230                    const sel = window.getSelection();
231                    if (container.spCurrentRange) {
232                        sel.removeAllRanges();
233                        sel.addRange(container.spCurrentRange);
234                        for(let i=0; i < container.spMentionQueryLength; i++){
235                            document.execCommand('delete', false, null);
236                        }
237                        const htmlBlob = `&nbsp;<span class="text-seleblue dark:text-blue-400 font-bold bg-blue-50 dark:bg-blue-900/30 px-1 rounded" contenteditable="false" data-userid="${user.id}">${user.name}</span>&nbsp;`;
238                        document.execCommand('insertHTML', false, htmlBlob);
239                    }
240                    textarea.dispatchEvent(new Event('input'));
241                    closeMentionsDropdown();
242                });
243                mentionsList.appendChild(li);
244            });
245            mentionsDropdown.classList.remove('hidden');
246            setTimeout(() => mentionsDropdown.classList.remove('scale-95', 'opacity-0'), 10);
247        }
248
249        const closeMentionsDropdown = () => {
250            isMentioning = false;
251            mentionsDropdown.classList.add('scale-95', 'opacity-0');
252            setTimeout(() => mentionsDropdown.classList.add('hidden'), 150);
253            currentFocus = -1;
254        };
255
256        // Emojis Local
257        const emojiDropdown = container.querySelector('.sp-emoji-dropdown');
258        const emojiGrid = container.querySelector('.sp-emoji-grid');
259        const spEmojis = ["😂","😍","😭","🔥","❤️","🙏","👍","😁","😃","😅","😊","💯","✨","🎉","🙌","😘","🥰","😆","😔","😏","💪","👏","🤦","🤷","😬","😱","🤯","🤢","🤮","😡","🤬","👀","🧠","🤔","🤝","👌","✌️","🤞","👎","🍻","🚀","💸","🤑","🛒","📱","💻","🎮","🎧"];
260
261        if (emojiGrid && emojiGrid.innerHTML.trim() === '') {
262            spEmojis.forEach(emoji => {
263                const btn = document.createElement('button');
264                btn.type = 'button';
265                btn.className = 'w-8 h-8 flex items-center justify-center text-xl hover:bg-gray-100 dark:hover:bg-gray-800 rounded cursor-pointer transition-colors';
266                btn.innerText = emoji;
267                btn.addEventListener('click', (e) => {
268                    e.preventDefault();
269                    textarea.focus();
270                    if (container.spLastCaretRange) {
271                        const sel = window.getSelection();
272                        sel.removeAllRanges();
273                        sel.addRange(container.spLastCaretRange);
274                    }
275                    document.execCommand('insertText', false, emoji);
276                    textarea.dispatchEvent(new Event('input')); 
277                    const newSel = window.getSelection();
278                    if(newSel.rangeCount) container.spLastCaretRange = newSel.getRangeAt(0).cloneRange();
279                });
280                emojiGrid.appendChild(btn);
281            });
282        }
283
284        if(emojiBtn) {
285            emojiBtn.addEventListener('click', (e) => {
286                e.preventDefault();
287                const sel = window.getSelection();
288                if (sel.rangeCount > 0 && textarea.contains(sel.focusNode)) {
289                    container.spLastCaretRange = sel.getRangeAt(0).cloneRange();
290                }
291                
292                if (emojiDropdown.classList.contains('hidden')) {
293                    closeMentionsDropdown();
294                    closeVideoDropdown();
295                    closeGiphyDropdown();
296                    emojiDropdown.classList.remove('hidden');
297                    setTimeout(() => emojiDropdown.classList.remove('scale-95', 'opacity-0'), 10);
298                } else {
299                    closeEmojiDropdown();
300                }
301            });
302        }
303
304        const closeEmojiDropdown = () => {
305            if(emojiDropdown) {
306                emojiDropdown.classList.add('scale-95', 'opacity-0');
307                setTimeout(() => emojiDropdown.classList.add('hidden'), 150);
308            }
309        };
310        const cxEmoji = container.querySelector('.sp-cx-emoji');
311        if(cxEmoji) cxEmoji.addEventListener('click', closeEmojiDropdown);
312        
313        // Videos Local
314        const videoDropdown = container.querySelector('.sp-video-dropdown');
315        const cxVideo = container.querySelector('.sp-cx-video');
316        const ytAddBtn = container.querySelector('.sp-yt-add-btn');
317        const ytInput = container.querySelector('.sp-yt-input');
318        
319        container.querySelector('.sp-video-trigger')?.addEventListener('click', (e) => {
320            e.preventDefault();
321            if (typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in !== '1') {
322                const loginBtn = document.querySelector('.trigger-login-modal');
323                if(loginBtn) loginBtn.click();
324                return;
325            }
326            if (textarea.querySelectorAll('.sp-comment-gif, .sp-comment-upload, .sp-yt-embed-
326preview').length >= 1) {
327                alert('Você já anexou uma mídia neste comentário! Remova a atual para inserir outra.');
328                return;
329            }
330
331            const sel = window.getSelection();
332            if (sel.rangeCount > 0 && textarea.contains(sel.focusNode)) {
333                container.spLastCaretRange = sel.getRangeAt(0).cloneRange();
334            }
335
336            if (videoDropdown.classList.contains('hidden')) {
337                closeMentionsDropdown();
338                closeEmojiDropdown();
339                closeGiphyDropdown();
340                videoDropdown.classList.remove('hidden');
341                setTimeout(() => videoDropdown.classList.remove('scale-95', 'opacity-0'), 10);
342                setTimeout(() => ytInput.focus(), 150);
343            } else {
344                closeVideoDropdown();
345            }
346        });
347
348        const closeVideoDropdown = () => {
349            if(videoDropdown) {
350                videoDropdown.classList.add('scale-95', 'opacity-0');
351                setTimeout(() => videoDropdown.classList.add('hidden'), 150);
352            }
353        };
354        if(cxVideo) cxVideo.addEventListener('click', closeVideoDropdown);
355
356        if (ytAddBtn) {
357            ytAddBtn.addEventListener('click', () => {
358                const url = ytInput.value.trim();
359                if (!url) return;
360                const match = url.match(/(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([\w-]{11})/);
361                if (!match || !match[1]) {
362                    alert("Link inválido. Copie uma URL oficial do YouTube!");
363                    return;
364                }
365                if (textarea.querySelectorAll('.sp-comment-gif, .sp-comment-upload, .sp-yt-embed-preview').length >= 1) {
366                    alert('Você já anexou uma mídia neste comentário! Remova a atual para inserir outra.');
367                    return;
368                }
369                const videoId = match[1];
370                textarea.focus();
371                if (container.spLastCaretRange) {
372                    const sel = window.getSelection();
373                    sel.removeAllRanges();
374                    sel.addRange(container.spLastCaretRange);
375                }
376                const embedHtml = `<br><div class="sp-yt-embed-preview relative w-full max-w-[450px] aspect-video rounded-xl overflow-hidden my-2 border border-gray-200 dark:border-gray-800 pointer-events-none" contenteditable="false" data-ytid="${videoId}">
377                    <iframe width="100%" height="100%" src="https://www.youtube.com/embed/${videoId}" frameborder="0" allowfullscreen class="pointer-events-none"></iframe>
378                    <div class="absolute inset-0 bg-black/10 z-10 flex items-center justify-center backdrop-blur-sm">YT</div>
379                </div><br>`;
380                document.execCommand('insertHTML', false, embedHtml);
381                ytInput.value = '';
382                closeVideoDropdown();
383                textarea.dispatchEvent(new Event('input'));
384            });
385        }
386
387        // Giphy Local
388        const giphyAPIKey = 'cIMBNhFtlA70SoPkx2QfkyAdR5UbaNC3';
389        const giphyDropdown = container.querySelector('.sp-giphy-dropdown');
390        const giphySearch = container.querySelector('.sp-giphy-search');
391        const giphyGrid = container.querySelector('.sp-giphy-grid');
392        const cxGiphy = container.querySelector('.sp-cx-giphy');
393        
394        let giphyLoaded = false; let giphyDebounceTimer; let giphyOffset = 0;
395        let isFetchingGiphy = false; let currentGiphyQuery = ''; let currentGiphyType = 'trending'; let giphyHasMore = true;
396
397        container.querySelector('.sp-giphy-trigger')?.addEventListener('click', (e) => {
398            e.preventDefault();
399            if (typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in !== '1') {
400                const loginBtn = document.querySelector('.trigger-login-modal');
401                if(loginBtn) loginBtn.click();
402                return;
403            }
404            if (textarea.querySelectorAll('.sp-comment-gif, .sp-comment-upload, .sp-yt-embed-
404preview').length >= 1) {
405                alert('Você já anexou uma mídia neste comentário! Remova a atual para inserir outra.');
406                return;
407            }
408
409            const sel = window.getSelection();
410            if (sel.rangeCount > 0 && textarea.contains(sel.focusNode)) {
411                container.spLastCaretRange = sel.getRangeAt(0).cloneRange();
412            }
413
414            if (giphyDropdown.classList.contains('hidden')) {
415                closeMentionsDropdown();
416                closeEmojiDropdown();
417                closeVideoDropdown();
418                giphyDropdown.classList.remove('hidden');
419                setTimeout(() => giphyDropdown.classList.remove('scale-95', 'opacity-0'), 10);
420                setTimeout(() => giphySearch.focus(), 150);
421                if(!giphyLoaded) { fetchGiphy('trending'); giphyLoaded = true; }
422            } else {
423                closeGiphyDropdown();
424            }
425        });
426
427        const closeGiphyDropdown = () => {
428            if(giphyDropdown) {
429                giphyDropdown.classList.add('scale-95', 'opacity-0');
430                setTimeout(() => giphyDropdown.classList.add('hidden'), 150);
431            }
432        };
433        if(cxGiphy) cxGiphy.addEventListener('click', closeGiphyDropdown);
434
435        if (giphySearch) {
436            giphySearch.addEventListener('input', (e) => {
437                clearTimeout(giphyDebounceTimer);
438                const query = e.target.value.trim();
439                giphyDebounceTimer = setTimeout(() => {
440                    if (query.length > 0) fetchGiphy('search', query);
441                    else fetchGiphy('trending');
442                }, 500);
443            });
444        }
445
446        if (giphyGrid) {
447            giphyGrid.addEventListener('scroll', () => {
448                if (giphyGrid.scrollTop + giphyGrid.clientHeight >= giphyGrid.scrollHeight - 50) {
449                    if(!isFetchingGiphy && giphyHasMore) fetchGiphy(currentGiphyType, currentGiphyQuery, true); 
450                }
451            });
452        }
453
454        function fetchGiphy(type, query = '', isAppend = false) {
455            if (isFetchingGiphy || (!giphyHasMore && isAppend)) return;
456            isFetchingGiphy = true;
457            currentGiphyType = type; currentGiphyQuery = query;
458
459            const loadingHtml = `<div class="col-span-2 text-center text-xs font-bold text-gray-400 py-8" id="sp-giphy-loader"><div class="inline-block w-5 h-5 border-2 border-seleblue border-t-transparent rounded-full animate-spin mb-2"></div><br>Buscando...</div>`;
460            if (!isAppend) {
461                giphyOffset = 0; giphyHasMore = true; giphyGrid.innerHTML = loadingHtml;
462            } else {
463                giphyGrid.insertAdjacentHTML('beforeend', loadingHtml);
464            }
465
466            let endpoint = `https://api.giphy.com/v1/gifs/trending?api_key=${giphyAPIKey}&limit=12&rating=g&offset=${giphyOffset}`;
467            if (type === 'search') {
468                const qEncoded = encodeURIComponent(query);
469                endpoint = `https://api.giphy.com/v1/gifs/search?api_key=${giphyAPIKey}&q=${qEncoded}&limit=12&rating=g&lang=pt&offset=${giphyOffset}`;
470            }
471
472            fetch(endpoint).then(res => res.json()).then(data => {
473                isFetchingGiphy = false;
474                const activeLoader = container.querySelector('#sp-giphy-loader');
475                if (activeLoader) activeLoader.remove();
476                if (!isAppend) giphyGrid.innerHTML = '';
477                
478                if(data.data && data.data.length > 0) {
479                    giphyOffset += data.data.length;
480                    if(data.pagination && data.pagination.total_count <= giphyOffset) giphyHasMore = false;
481                    data.data.forEach(gif => {
482                        const imgUrl = gif.images.fixed_width_downsampled.url; 
483                        const finalImgUrl = gif.images.fixed_height.url; 
484                        
485                        const btn = document.createElement('button');
486                        btn.type = 'button';
487                        btn.className = 'w-full h-[100px] overflow-hidden rounded border border-gray-200 dark:border-gray-800 focus:border-seleblue focus:outline-none transition-all hover:opacity-80 active:scale-95';
488                        btn.innerHTML = `<img src="${imgUrl}" class="w-full h-full object-cover" loading="lazy">`;
489                        
490                        btn.addEventListener('click', (e) => {
491                            e.preventDefault();
492                            if (textarea.querySelectorAll('.sp-comment-gif, .sp-comment-upload, .sp-yt-embed-
492preview').length >= 1) {
493                                alert('Você só pode anexar 1 mídia (Imagem, GIF ou YouTube) por comentário!');
494                                closeGiphyDropdown(); return;
495                            }
496                            textarea.focus();
497                            if (container.spLastCaretRange) {
498                                const sel = window.getSelection();
499                                sel.removeAllRanges(); sel.addRange(container.spLastCaretRange);
500                            }
501                            const embedHtml = `&nbsp;<span class="sp-gif-wrapper relative inline-block max-w-[280px] sm:max-w-[320px] my-2 select-none" contenteditable="false">
502                                <img src="${finalImgUrl}" class="sp-comment-gif w-full rounded-xl shadow-md border border-gray-200 dark:border-gray-700 pointer-events-none" data-gurl="${finalImgUrl}" data-gid="${gif.id}">
503                                <button type="button" class="sp-remove-gif absolute -top-2 -right-2 w-6 h-6 bg-red-500 text-white rounded-full shadow-lg border-2 border-white dark:border-[#222] flex items-center justify-center font-bold text-sm leading-none hover:bg-red-600 transition-colors pointer-events-auto" title="Remover GIF">&times;</button>
504                            </span>&nbsp;`;
505                            document.execCommand('insertHTML', false, embedHtml);
506                            closeGiphyDropdown();
507                            textarea.dispatchEvent(new Event('input')); 
508                        });
509                        giphyGrid.appendChild(btn);
510                    });
511                } else if (!isAppend) {
512                    giphyGrid.innerHTML = `<div class="col-span-2 text-center text-xs font-bold text-gray-400 py-8">Nenhum GIF encontrado.</div>`;
513                }
514            }).catch(err => {
515                isFetchingGiphy = false;
516                if (!isAppend) giphyGrid.innerHTML = `<div class="col-span-2 text-center text-xs font-bold text-red-500 py-8">Erro do Giphy</div>`;
517            });
518        }
519
520        // Caret Watchers
521        textarea.addEventListener('blur', () => {
522            const sel = window.getSelection();
523            if (sel.rangeCount > 0 && textarea.contains(sel.focusNode)) {
524                container.spLastCaretRange = sel.getRangeAt(0).cloneRange();
525            }
526        });
527
528        textarea.addEventListener('keyup', () => {
529            const sel = window.getSelection();
530            if (sel.rangeCount > 0 && textarea.contains(sel.focusNode)) {
531                container.spLastCaretRange = sel.getRangeAt(0).cloneRange();
532            }
533        });
534
535        const savedCommentKey = 'sp_forum_draft_post_' + postId;
536        const pendingDraft = localStorage.getItem(savedCommentKey);
537        // Master Form Auto Recovery (only for master form!)
538        if(container.id === 'respond' && pendingDraft && typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in === '1') {
539            textarea.innerHTML = pendingDraft;
540            localStorage.removeItem(savedCommentKey);
541            textarea.dispatchEvent(new Event('input'));
542        }
543
544        // Submit API
545        submitBtn.addEventListener('click', () => {
546            if (typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in !== '1') {
547                if(container.id === 'respond') localStorage.setItem(savedCommentKey, textarea.innerHTML);
548                const loginBtn = document.querySelector('.trigger-login-modal');
549                if(loginBtn) loginBtn.click();
550                return;
551            }
552
553            const clone = textarea.cloneNode(true);
554            clone.querySelectorAll('.sp-remove-gif, .sp-remove-yt').forEach(btn => btn.remove());
555            clone.querySelectorAll('img.emoji, img.wp-smiley').forEach(img => {
556                const char = img.getAttribute('alt'); if(char) img.replaceWith(char);
557            });
558            clone.querySelectorAll('span[data-userid]').forEach(span => {
559                span.replaceWith(`@[user:${span.getAttribute('data-userid')}] `);
560            });
561            clone.querySelectorAll('img.sp-comment-upload').forEach(img => {
562                img.replaceWith(`\n\n[img]${img.getAttribute('src')}[/img]\n\n`);
563            });
564            clone.querySelectorAll('img.sp-comment-gif').forEach(img => {
565                img.replaceWith(`\n\n[giphy]${img.getAttribute('data-gid')}[/giphy]\n\n`); 
566            });
567            clone.querySelectorAll('.sp-yt-embed-preview').forEach(div => {
568                div.replaceWith(`\n\n[youtube]${div.getAttribute('data-ytid')}[/youtube]\n\n`);
569            });
570            clone.querySelectorAll('div, p').forEach(el => {
571                el.insertAdjacentText('beforebegin', '\n');
572                el.replaceWith(...el.childNodes);
573            });
574            clone.querySelectorAll('br').forEach(br => br.replaceWith('\n'));
575
576            let commentTxt = clone.innerHTML.replace(/&nbsp;/g, ' ').trim();
577            if(commentTxt.replace(/<[^>]*>?/gm, '').trim().length === 0) return;
578
579            submitBtn.classList.add('hidden');
580            spinner.classList.remove('hidden');
581            textarea.setAttribute('contenteditable', 'false');
582
583            const isEditingMode = editIdInput && editIdInput.value !== '0';
584            const formData = new URLSearchParams();
585            
586            if (isEditingMode) {
587                formData.append('action', 'sp_user_edit_comment');
588                formData.append('nonce', selepowerAjax.comment_nonce);
589                formData.append('comment_id', editIdInput.value);
590                formData.append('content', commentTxt);
591            } else {
592                formData.append('action', 'sp_submit_comment');
593                formData.append('comment', commentTxt);
594                formData.append('comment_post_ID', postId);
595                formData.append('comment_parent', parentIdInput.value || '0');
596            }
597            
598            fetch(selepowerAjax.ajaxurl, { method: 'POST', body: formData, headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'} })
599            .then(res => { if(!res.ok) throw new Error('Erro na rede'); return res.json(); })
600            .then(data => {
601                if(!data.success) throw new Error(data.data?.message || 'Erro');
602
603                if (isEditingMode) {
604                    const editingNode = document.querySelector(`.sp-comment-meta-holder[data-cid="${editIdInput.value}"] .sp-raw-content`);
605                    if(editingNode) {
606                        editingNode.innerHTML = data.data.html || commentTxt.replace(/\n\n/g, '<p></p>').replace(/\n/g, '<br/>');
607                        editingNode.classList.remove('hidden'); 
608                        const footerMeta = editingNode.closest('.sp-comment-meta-holder').querySelector('footer .opacity-80');
609                        if(footerMeta && !footerMeta.innerHTML.includes('(Editado)')) {
610                            const edNodeSpan = document.createElement('span');
611                            edNodeSpan.className = 'text-[10px] bg-gray-200/50 dark:bg-gray-800/40 px-1.5 py-0.5 rounded shadow-sm text-gray-500 font-bold tracking-wide';
612                            edNodeSpan.innerText = '(Editado)';
613                            footerMeta.appendChild(edNodeSpan);
614                        }
615                    }
616                    if(container.id !== 'respond') container.remove(); 
617                } else {
618                    const html = data.data.html;
619                    const parser = new DOMParser();
620                    const vDom = parser.parseFromString(html, 'text/html');
621                    const newCommentNode = vDom.body.firstElementChild;
622                    if (newCommentNode) {
623                        const commentList = document.querySelector('.comment-list');
624                        if (commentList) {
625                            const pid = parentIdInput.value || '0';
626                            if (pid !== "0") {
627                                const parentLi = document.getElementById('comment-' + pid);
628                                if (parentLi) {
629                                    let childrenContainer = parentLi.querySelector('.children');
630                                    if (!childrenContainer) {
631                                        childrenContainer = document.createElement('ol');
632                                        childrenContainer.className = 'children';
633                                        parentLi.appendChild(childrenContainer);
634                                    }
635                                    childrenContainer.insertAdjacentElement('beforeend', newCommentNode);
636                                }
637                                if(container.id !== 'respond') container.remove(); // Destrói o form clonado respondendo
638                            } else {
639                                const emptyAviso = document.querySelector('.comment-list').nextElementSibling;
640                                if(emptyAviso && emptyAviso.innerHTML.includes('Nenhum')) emptyAviso.remove();
641                                commentList.insertAdjacentElement('afterbegin', newCommentNode);
642                            }
643                        }
644                    }
645                }
646
647                if (container.id === 'respond') {
648                    // Reset master form
649                    textarea.innerHTML = ''; 
650                    textarea.setAttribute('contenteditable', 'true');
651                    spinner.classList.add('hidden');
652                    submitBtn.classList.remove('hidden', 'bg-seleblue', 'text-white', 'pointer-events-auto');
653                    submitBtn.classList.add('bg-gray-100', 'dark:bg-darksurface', 'text-gray-500', 'opacity-50', 'curs
653or-not-allowed');
654                }
655            })
656            .catch(err => {
657                alert("Erro: " + err.message);
658                textarea.setAttribute('contenteditable', 'true');
659                spinner.classList.add('hidden');
660                submitBtn.classList.remove('hidden');
661            });
662        });
663
664        // Rich text Toolbar
665        container.querySelectorAll('.sp-format-btn').forEach(btn => {
666            btn.addEventListener('click', (e) => {
667                e.preventDefault();
668                document.execCommand(btn.getAttribute('data-cmd'), false, null);
669                textarea.focus();
670                textarea.dispatchEvent(new Event('input'));
671            });
672        });
673        
674        // Cancel Action (Destrói clone ou Reseta master)
675        const cancelBtn = container.querySelector('.sp-cancel-reply');
676        if (cancelBtn) {
677            cancelBtn.addEventListener('click', () => {
678                if (editIdInput && editIdInput.value !== '0') {
679                    const hk = document.querySelector(`.sp-comment-meta-holder[data-cid="${editIdInput.value}"] .sp-raw-content.hidden`);
680                    if(hk) hk.classList.remove('hidden');
681                }
682                if (container.id === 'respond') {
683                    textarea.innerHTML = '';
684                    parentIdInput.value = '0';
685                    if (editIdInput) editIdInput.value = '0';
686                    const rb = container.querySelector('.sp-reply-banner');
687                    if(rb) rb.classList.add('hidden');
688                } else {
689                    container.remove(); // Fim do clone!
690                }
691            });
692        }
693    };
694
695    // Inicializa a Caixa Mestre do Topo da Página
696    const masterForm = document.getElementById('respond');
697    if (masterForm) initCommentForm(masterForm);
698
699
700    // ==========================================
701    // DELEGATION MESTRE PARA EVENTOS GLOBAIS
702    // ==========================================
703    
704    // Controlador Global de Cliques Fora da Área Isolada do Fórum + Delete Listeners Internos
705    document.addEventListener('click', (e) => {
706        // Botão Fechar GIF ou Imagem ou Youtube (Delegation)
707        if (e.target && (e.target.classList.contains('sp-remove-gif') || e.target.classList.contains('sp-remove-yt'))) {
708            e.preventDefault();
709            const wrapper = e.target.closest('.sp-gif-wrapper, .sp-img-wrapper, .sp-yt-embed-preview');
710            if (wrapper) {
711                const ta = wrapper.closest('.sp-comment-textarea');
712                wrapper.remove();
713                if(ta) ta.dispatchEvent(new Event('input')); 
714            }
715            return;
716        }
717
718        // Close Dropdowns if clicked outside their form instance
719        document.querySelectorAll('.sp-form-instance').forEach(form => {
720            if(!form.contains(e.target)) {
721                // hide all dropdowns inside this form
722                form.querySelectorAll('.sp-mentions-dropdown, .sp-emoji-dropdown, .sp-video-dropdown, .sp-giphy-dropdown').forEach(drp => {
723                    drp.classList.add('hidden');
724                });
725            }
726        });
727    });
728
729    // Voto Comentário AJAX
730    document.addEventListener('click', function(e) {
731        const btn = e.target.closest('.sp-vote-btn');
732        if (!btn) return;
733        
734        if (typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in !== '1') {
735            alert("Faça login para votar!"); return;
736        }
737        
738        const commentId = btn.getAttribute('data-id');
739        const type = btn.getAttribute('data-type');
740        const sc = btn.parentElement.querySelector('.sp-vote-score');
741        const orig = sc.textContent; sc.textContent = '...';
742
743        fetch(selepowerAjax.ajaxurl, {
744            method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
745            body: new URLSearchParams({ action: 'sp_vote_comment', comment_id: commentId, vote_type: type })
746        }).then(r => r.json()).then(data => {
747            if(data.success) {
748                sc.textContent = data.data.score;
749                sc.className = `sp-vote-score text-[11px] min-w-[16px] text-center px-1 font-black ${data.data.score > 0 ? 'text-green-500' : (data.data.score < 0 ? 'text-red-500' : 'text-gray-500 dark:text-gray-400')}`;
750            } else sc.textContent = orig;
751        }).catch(() => { sc.textContent = orig; });
752    });
753
754    // Paginador Root (Ver mais comentários)
755    document.addEventListener('click', function(e) {
756        const loadBtn = e.target.closest('#sp-load-more-comments');
757        if (!loadBtn) return;
758        e.preventDefault();
759        
760        const postid = loadBtn.getAttribute('data-postid');
761        const page = parseInt(loadBtn.getAttribute('data-page')) || 2;
762        const sortSelect = document.getElementById('sp-comment-sort');
763        const sortBy = sortSelect ? sortSelect.value : 'vtop';
764        const ldr = loadBtn.querySelector('.sp-ldr');
765        
766        if (ldr) ldr.classList.remove('hidden'); 
767        loadBtn.classList.add('opacity-50', 'pointer-events-none');
768        
769        fetch(selepowerAjax.ajaxurl, {
770            method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
771            body: new URLSearchParams({ action: 'sp_load_more_comments', post_id: postid, page: page, sort_by: sortBy })
772        }).then(r => r.json()).then(res => {
773            if (res.success && res.data.html) {
774                const list = document.querySelector('.comment-list');
775                if (list) list.insertAdjacentHTML('beforeend', res.data.html);
776                
777                if (res.data.has_more) {
778                    loadBtn.setAttribute('data-page', page + 1);
779                    if (ldr) ldr.classList.add('hidden'); 
780                    loadBtn.classList.remove('opacity-50', 'pointer-events-none');
781                } else {
782                    const wrapper = document.getElementById('sp-comments-pagination-wrapper');
783                    if (wrapper) wrapper.remove();
784                }
785            } else {
786                if (ldr) ldr.classList.add('hidden'); 
787                loadBtn.classList.remove('opacity-50', 'pointer-events-none');
788            }
789        });
790    });
791
792    // Evento de Troca de Ordenador de Comentários (Select Box)
793    const sortSelect = document.getElementById('sp-comment-sort');
794    if (sortSelect) {
795        sortSelect.addEventListener('change', function(e) {
796            const list = document.querySelector('.comment-list');
797            const wrapper = document.getElementById('sp-comments-pagination-wrapper');
798            const loadBtn = document.getElementById('sp-load-more-comments');
799            
800            if(list) list.style.opacity = '0.5';
801            
802            const postid = document.querySelector('.sp_post_id').value;
803            const sortBy = this.value;
804            const page = 1;
805            
806            fetch(selepowerAjax.ajaxurl, {
807                method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
808                body: new URLSearchParams({ action: 'sp_load_more_comments', post_id: postid, page: page, sort_by: sortBy })
809            }).then(r => r.json()).then(res => {
810                if (res.success) {
811                    if (list) {
812                        list.innerHTML = res.data.html;
813                        list.style.opacity = '1';
814                    }
815                    if (res.data.has_more) {
816                        if (wrapper && loadBtn) {
817                            wrapper.classList.remove('hidden');
818                            loadBtn.setAttribute('data-page', '2');
819                        }
820                    } else {
821                        if (wrapper) wrapper.classList.add('hidden');
822                    }
823                }
824            });
825        });
826    }
827
828    // Paginador de Respostas (Filhos)
829    document.addEventListener('click', function(e) {
830        const btn = e.target.closest('.sp-load-more-replies');
831        if (!btn) return; e.preventDefault();
832        
833        const pid = btn.getAttribute('data-parent'); const postid = btn.getAttribute('data-postid');
834        const citem = btn.closest('.load-more-replies-container');
835        const ldr = btn.querySelector('.sp-ldr');
836        if (ldr) ldr.classList.remove('hidden'); btn.classList.add('opacity-50', 'pointer-events-none');
837        
838        fetch(selepowerAjax.ajaxurl, {
839            method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
840            body: new URLSearchParams({ action: 'sp_load_more_replies', parent_id: pid, post_id: postid })
841        }).then(r => r.json()).then(res => {
842            if (res.success && res.data.html) { citem.insertAdjacentHTML('beforebegin', res.data.html); citem.remove(); } 
843            else { if (ldr) ldr.classList.add('hidden'); btn.classList.remove('opacity-50', 'pointer-events-none'); }
844        });
845    });
846
847    // Youtube FakeBox
848    const ytModal = document.getElementById('sp-forum-video-modal');
849    const ytCloseBtn = document.getElementById('sp-forum-close-btn');
850    const ytWrapper = document.getElementById('sp-forum-iframe-wrapper');
851    if (ytModal && ytWrapper) {
852        document.addEventListener('click', function(e) {
853            const fb = e.target.closest('.sp-yt-fakebox');
854            if (fb) {
855                e.preventDefault(); const vId = fb.getAttribute('data-ytid');
856                if(!vId) return;
857                document.body.style.overflow = 'hidden';
858                ytWrapper.innerHTML = `<iframe width="100%" height="100%" src="https://www.youtube.com/embed/${vId}?autoplay=1&rel=0&modestbranding=1" frameborder="0" allowfullscreen class="absolute inset-0 border-none"></iframe>`;
859                ytModal.classList.remove('hidden');
859 ytModal.classList.add('flex');
860                setTimeout(() => { ytModal.classList.remove('opacity-0'); ytWrapper.classList.remove('scale-95'); ytWrapper.classList.add('scale-100'); }, 10);
861            }
862        });
863        const closeYM = () => {
864            ytModal.classList.add('opacity-0'); ytWrapper.classList.remove('scale-100'); ytWrapper.classList.add('scale-95');
865            setTimeout(() => { ytModal.classList.remove('flex'); ytModal.classList.add('hidden'); ytWrapper.innerHTML = ''; document.body.style.overflow = ''; }, 300);
866        };
867        if(ytCloseBtn) ytCloseBtn.addEventListener('click', closeYM);
868        ytModal.addEventListener('click', (e) => { if(e.target === ytModal) closeYM(); });
869        document.addEventListener('keydown', (e) => { if(e.key === 'Escape' && !ytModal.classList.contains('hidden')) closeYM(); });
870    }
871
872    // CLONADOR RESPONDER E EDITAR (A MAGIA!) /////////////////
873    document.addEventListener('click', function(e) {
874        
875        // --- RESPONDER ---
876        const rBtn = e.target.closest('.sp-reply-trigger');
877        if (rBtn) {
878            e.preventDefault();
879            if (typeof selepowerAjax !== 'undefined' && selepowerAjax.is_logged_in !== '1') {
880                const loginBtn = document.querySelector('.trigger-login-modal');
881                if(loginBtn) loginBtn.click();
882                return;
883            }
884            
885            const cid = rBtn.getAttribute('data-commentid');
886            const targetEl = document.getElementById('comment-' + cid);
887            if(!targetEl || !masterForm) return;
888
889            // Remove box anterior se houver
890            let prev = targetEl.querySelector(':scope > .sp-form-instance');
891            if(prev) prev.remove();
892
893            const clone = masterForm.cloneNode(true);
894            clone.id = ''; // não é master
895            
896            // Layout de card
897            clone.classList.remove('pb-8', 'mb-8', 'border-b', 'border-gray-100', 'dark:border-gray-800');
898            clone.classList.add('mt-2', 'p-3', 'md:p-4', 'bg-gray-50/50', 'dark:bg-[#1f1f1f]', 'rounded-2xl', 'shadow-sm', 'border', 'border-gray-200/60', 'dark:border-gray-800/80');
899
900            targetEl.appendChild(clone);
901            
902            clone.querySelector('.sp_comment_parent').value = cid;
903            clone.querySelector('.sp_edit_comment_id').value = '0';
904            
905            const rb = clone.querySelector('.sp-reply-banner');
906            rb.classList.remove('hidden'); rb.classList.add('flex');
907            clone.querySelector('.sp-reply-text').innerHTML = `<span>Respondendo a <strong>@${rBtn.getAttribute('data-author')}</strong></span>`;
908            
909            const authId = rBtn.getAttribute('data-authorid') || '0';
910            const htmlBlob = `<span class="text-seleblue dark:text-blue-400 font-bold bg-blue-50 dark:bg-blue-900/30 px-1 rounded" contenteditable="false" data-userid="${authId}">${rBtn.getAttribute('data-author')}</span>&nbsp;`;
911            
912            initCommentForm(clone);
913            
914            const ta = clone.querySelector('.sp-comment-textarea');
915            ta.innerHTML = htmlBlob;
916            ta.focus();
917            const sel = window.getSelection(); sel.selectAllChildren(ta); sel.collapseToEnd();
918            ta.dispatchEvent(new Event('input'));
919            setTimeout(() => { clone.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 50);
920            return;
921        }
922
923        // --- EDITAR ---
924        const eBtn = e.target.closest('.sp-opt-edit');
925        if (eBtn) {
926            e.preventDefault();
927            const md = eBtn.closest('.sp-comment-meta-holder');
928            const cid = md.getAttribute('data-cid');
929            const raw = md.querySelector('.sp-raw-content');
930            const dp = eBtn.closest('.sp-comment-dropdown');
931            if(dp) dp.remove();
932
933            const targetEl = document.getElementById('comment-' + cid);
934            if(!targetEl || !masterForm) return;
935
936            let prev = targetEl.querySelector(':scope > .sp-form-instance');
937            if(prev) prev.remove();
938
939            const clone = masterForm.cloneNode(true);
940            clone.id = '';
941            clone.classList.remove('pb-8', 'mb-8', 'border-b', 'border-gray-100', 'dark:border-gray-800');
942            clone.classList.add('mt-2', 'p-3', 'md:p-4', 'bg-gray-50/50', 'dark:bg-[#1f1f1f]', 'rounded-2xl', 'shadow-sm', 'border', 'border-gray-200/60', 'dark:border-gray-800/80');
943
944            targetEl.appendChild(clone);
945            if(raw) raw.classList.add('hidden');
946
947            clone.querySelector('.sp_comment_parent').value = '0';
948            clone.querySelector('.sp_edit_comment_id').value = cid;
949            
950            const rb = clone.querySelector('.sp-reply-banner');
951            rb.classList.remove('hidden'); rb.classList.add('flex');
952            clone.querySelector('.sp-reply-text').innerHTML = `<span>✏️ Editando...</span>`;
953
954            initCommentForm(clone);
955
956            const ta = clone.querySelector('.sp-comment-textarea');
957            ta.innerHTML = raw.innerHTML.trim();
958
959            // Rehidratar Wrappers...
960            ta.querySelectorAll('img.sp-comment-gif').forEach(img => {
961                if(!img.closest('.sp-gif-wrapper')) {
962                    const finalImgUrl = img.getAttribute('src');
963                    let gid = img.getAttribute('data-gid') || (finalImgUrl.match(/media\/([\w-]+)\//) || [])[1];
964                    if(gid) {
965                        const wrp = document.createElement('span'); wrp.className = 'sp-gif-wrapper relative inline-block max-w-[280px] sm:max-w-[320px] my-2 select-none';
966                        wrp.setAttribute('contenteditable','false'); img.setAttribute('data-gid', gid);
967                        img.classList.add('sp-comment-gif','w-full','rounded-xl','pointer-events-none');
968                        img.parentNode.insertBefore(wrp, img); wrp.appendChild(img);
969                        wrp.insertAdjacentHTML('beforeend', '<button type="button" class="sp-remove-gif absolute -top-2 -right-2 w-6 h-6 bg-red-500 text-white rounded-full flex items-center justify-center pointer-events-auto">&times;</button>');
970                    }
971                }
972            });
973            ta.querySelectorAll('img.sp-comment-upload').forEach(img => {
974                if(!img.closest('.sp-img-wrapper')) {
975                    const wrp = document.createElement('span'); wrp.className = 'sp-img-wrapper relative inline-block max-w-[300px] sm:max-w-[400px] my-2 select-none';
976                    wrp.setAttribute('contenteditable','false');
977                    img.parentNode.insertBefore(wrp, img); wrp.appendChild(img);
978                    wrp.insertAdjacentHTML('beforeend', '<button type="button" class="sp-remove-gif absolute -top-2 -right-2 w-6 h-6 bg-red-500 text-white rounded-full flex items-center justify-center pointer-events-auto">&times;</button>');
979                }
980            });
981            ta.querySelectorAll('a.mention-link').forEach(link => {
982                const uid = link.getAttribute('href').split('/').filter(Boolean).pop();
983                const bSpan = document.createElement('span');
984                bSpan.className = 'text-seleblue dark:text-blue-400 font-bold bg-blue-50 dark:bg-blue-900/30 px-1 rounded';
985                bSpan.setAttribute('contenteditable', 'false'); bSpan.setAttribute('data-userid', uid||'0');
986                bSpan.innerText = link.innerText; link.replaceWith(bSpan);
987            });
988            ta.querySelectorAll('.sp-yt-fakebox').forEach(div => {
989                const ytid = div.getAttribute('data-ytid');
990                if(ytid) {
991                    const hr = document.createElement('div');
992                    hr.innerHTML = `<div class="sp-yt-embed-preview relative max-w-[400px] aspect-video my-2 select-none bg-gray-100 dark:bg-gray-800 rounded-xl" contenteditable="false" data-ytid="${ytid}"><img src="https://i.ytimg.com/vi/${ytid}/hqdefault.jpg" class="opacity-80 pointer-events-none w-full h-full object-cover">
993                    <button class="sp-remove-yt absolute -top-2 -right-2 w-6 h-6 bg-red-500 text-white rounded-full flex items-center justify-center pointer-events-auto">&times;</button></div>`;
994                    div.replaceWith(hr.firstElementChild);
995                }
996            });
997
998            ta.focus();
999            const sel = window.getSelection(); sel.selectAllChildren(ta); sel.collapseToEnd();
1000            ta.dispatchEvent(new Event('input'));
1001            setTimeout(() => { clone.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 50);
1002            return;
1003        }
1004
1005        // DELETAR (Root Node ejetor)
1006        const dBtn = e.target.closest('.sp-opt-delete');
1007        if (dBtn) {
1008            e.preventDefault();
1009            if (!confirm('Tem certeza?')) return;
1010            const md = dBtn.closest('.sp-comment-meta-holder');
1011            const cid = md.getAttribute('data-cid');
1012            
1013            // Destrói forms ativos atrelados pra trás do target root!
1014            const rootNode = document.getElementById('comment-' + cid);
1015            if(rootNode) rootNode.querySelectorAll('.sp-form-instance').forEach(f => {
1016                if(f.id !== 'respond') f.remove();
1017            });
1018
1019            const fs = new URLSearchParams({action: 'sp_user_delete_comment', nonce: selepowerAjax.comment_nonce, comment_id: cid });
1020            md.style.opacity = '0.5';
1021            fetch(selepowerAjax.ajaxurl, { method: 'POST', body: fs }).then(r=>r.json()).then(r=>{
1022                if(r.success) {
1023                    rootNode.style.transition = 'all 0.5s'; rootNode.style.opacity = '0'; rootNode.style.height = '0px'; rootNode.style.overflow = 'hidden';
1024                    setTimeout(() => rootNode.remove(), 500);
1025                } else alert(r.data.message);
1026            });
1027            const nd = dBtn.closest('.sp-comment-dropdown'); if(nd) nd.remove();
1028            return;
1029        }
1030
1031        // DROPDOWN (PONTINHOS) - Gerado Dinâmico (Lembrar o que o usuário me pediu na task anterior)
1032        const trigger = e.target.closest('.sp-comment-options-trigger');
1033        if (trigger) {
1034            e.preventDefault();
1035            const bubble = trigger.closest('.group\\/bubble') || trigger.closest('.group\/bubble'); 
1036            const metaHolder = trigger.closest('.sp-comment-meta-holder');
1037            
1038            const existingDrop = bubble.querySelector('.sp-comment-dropdown');
1039            if(existingDrop && !existingDrop.classList.contains('hidden')) {
1040                existingDrop.remove(); return;
1041            }
1042            document.querySelectorAll('.sp-comment-dropdown').forEach(d => d.remove());
1043            const isOwn = trigger.getAttribute('data-auth') === 'own';
1044            const dropdownHTML = `
1045                <div class="sp-comment-dropdown absolute top-10 right-0 w-36 bg-white dark:bg-[#1a1a1a] rounded-xl shadow-xl border border-gray-100 dark:border-gray-800 py-1.5 z-[99] text-[13px] font-bold text-gray-700 transition-all duration-150 transform">
1046                    ${isOwn ? `
1047                        <button type="button" class="sp-opt-edit w-full text-left px-4 py-2 hover:bg-gray-50 flex items-center gap-2">✏️ Editar</button>
1048                        <button type="button" class="sp-opt-delete w-full text-left px-4 py-2 hover:bg-red-50 text-red-500 flex items-center gap-2">🗑️ Excluir</button>
1049                    ` : `
1050                        <button type="button" class="sp-opt-report w-full text-left px-4 py-2 hover:bg-gray-50 flex items-center gap-2">🚩 Denunciar</button>
1051                    `}
1052                </div>
1053            `;
1054            bubble.insertAdjacentHTML('beforeend', dropdownHTML);
1055            return;
1056        }
1057
1058        // DENUNCIAR
1059        const repBtn = e.target.closest('.sp-opt-report');
1060        if (repBtn) {
1061            e.preventDefault();
1062            const md = repBtn.closest('.sp-comment-meta-holder');
1063            const rModal = document.getElementById('sp-report-modal');
1064            if(rModal) {
1065                document.getElementById('sp-report-target-cid').value = md.getAttribute('data-cid');
1066                rModal.classList.remove('hidden'); rModal.classList.add('flex');
1067            }
1068            repBtn.closest('.sp-comment-dropdown').remove();
1069            return;
1070        }
1071    }); // Fim Delegation
1072
1073}); // Fim DOMContentLoaded

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.