// ==UserScript== // @name TopicLive+ Reworked // @namespace TopicLive+JVC // @description Affiche les nouveaux messages d'un topic en direct. // @author StrangerFruit, sur une base de : moyaona, lantea/atlantis, kiwec // @match https://www.jeuxvideo.com/forums/* // @updateURL https://raw.githubusercontent.com/DreamboxMinerva/TopicLive-Reworked/main/TopicLive-Reworked.user.js // @downloadURL https://raw.githubusercontent.com/DreamboxMinerva/TopicLive-Reworked/main/TopicLive-Reworked.user.js // @run-at document-end // @require https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js // @icon https://image.noelshack.com/fichiers/2026/25/5/1781893261-logo.png // @version 0.6 // @grant GM_xmlhttpRequest // @connect raw.githubusercontent.com // @connect tiktok.com // @connect vxinstagram.com // @connect publish.twitter.com // @connect platform.twitter.com // @connect api.streamable.com // @connect webmshare.com // @connect api.fxtwitter.com // @noframes // ==/UserScript== async function extractPayloadGzip() { const scripts = document.getElementsByTagName('script'); for (let s of scripts) { const content = s.textContent || ''; if (content.includes('forumsAppPayload')) { const match = content.match(/forumsAppPayload\s*=\s*["']?([^"']+)["']?/); if (match && match[1]) { try { const binaryString = atob(match[1]); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) bytes[i] = binaryString.charCodeAt(i); const stream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream('gzip')); const decompressed = await new Response(stream).text(); return JSON.parse(decompressed); } catch(e) { return null; } } } } return null; } class Page { constructor($page) { this.$page = $page; } obtenirMessages() { const msgs = []; this.trouver(`${TL.class_msg}:not(.msg-pseudo-blacklist)`).each(function() { msgs.push(new Message($(this))); }); return msgs; } maj() { if (TL.options.sound) { try { TL.son.play(); } catch (err) { console.error(`[TopicLive+] Erreur son : ${err}`); } } try { if (!TL.ongletActif) { TL.updateCounters(); } } catch (err) { console.error(`[TopicLive+] Erreur favicon (maj) : ${err}`); } try { this.Transformation(); } catch (err) { console.error(`[TopicLive+] Erreur jsli.Transformation() : ${err}`); } const nb_messages = $(`${TL.class_msg}:not(.msg-pseudo-blacklist)`).size(); if (nb_messages > 100) { let messagesASupprimer = nb_messages - 100; if (messagesASupprimer % 2 !== 0) messagesASupprimer++; $(`${TL.class_msg}:not(.msg-pseudo-blacklist)`).slice(0, messagesASupprimer).remove(); } dispatchEvent(new CustomEvent('topiclive:doneprocessing', { 'detail': { jvcake: TL.jvCake } })); } performScroll() { const $firstUnreadMessage = TL.unreadMessageAnchors[0]; if (!$firstUnreadMessage || $firstUnreadMessage.length === 0) return; const targetScrollTop = $firstUnreadMessage.offset().top - 100; $('html, body').animate({ scrollTop: targetScrollTop }, 800); } scan() { TL.ajaxTs = this.trouver('#ajax_timestamp_liste_messages').val(); TL.ajaxHash = this.trouver('#ajax_hash_liste_messages').val(); const connectesText = this.trouver('.nb-connect-fofo').text().trim(); if (connectesText && TL.$tl_connected_counter) { TL.$tl_connected_counter.text(connectesText.split(' ')[0]); } $('.nb-connect-fofo').text(this.trouver('.nb-connect-fofo').text()); const isTextareaFocused = $(TL.formu.obtenirMessage()).is(':focus'); let distanceFromBottom; if (isTextareaFocused) { distanceFromBottom = document.documentElement.scrollHeight - $(window).scrollTop(); } if ($(TL.class_msg).length === 0) { TL.majUrl(this); TL.loop(); return; } let messages_a_afficher = []; const nvMsgs = this.obtenirMessages(); // ── NOUVEAU : détection dernière page avec nouvelle pagination React ── const isOnLastPage = $('.pagination__item--next.pagination__item--disabled').length > 0; if (isOnLastPage) { for (const ancienMsg of TL.messages) { const stillExists = nvMsgs.some(nvMsg => nvMsg.id_message === ancienMsg.id_message); if (!stillExists && !ancienMsg.supprime) { ancienMsg.supprime = true; ancienMsg.$message.addClass('topiclive-deleted'); const $originalButton = ancienMsg.trouver('.messageUser__action[title="Citer le message"]'); if ($originalButton.length > 0) { const $cleanButton = $originalButton.clone(false); $originalButton.replaceWith($cleanButton); $cleanButton.on('click', (e) => { e.preventDefault(); const $msgTextarea = TL.formu.obtenirMessage(); const datePropre = ancienMsg.date.trim().replace(/\s+/g, ' '); const pseudoPropre = ancienMsg.pseudo.trim().replace(/\s+/g, ' '); const messageContent = ancienMsg.trouver(TL.class_contenu)[0].innerText.trim(); let nvmsg = `> Le ${datePropre} ${pseudoPropre} a écrit :\n>`; nvmsg += `${messageContent.split('\n').join('\n> ')}\n\n`; if ($msgTextarea[0].value === '') { Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value").set.call($msgTextarea[0], `${nvmsg}\n`); } else { Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value").set.call($msgTextarea[0], `${$msgTextarea[0].value}\n\n${nvmsg}`); } $msgTextarea[0].dispatchEvent(new Event("input", { bubbles: true })); location.hash = '#forums-post-message-editor'; setTimeout(() => { $msgTextarea[0].focus(); }, 50); }); } } } } try { for (let nvMsg of nvMsgs) { let nv = true; for (let ancienMsg of TL.messages) { if (ancienMsg.id_message == nvMsg.id_message) { nv = false; ancienMsg.update(nvMsg); break; } } if (nv && isOnLastPage) { TL.messages.push(nvMsg); TL.nvxMessages++; nvMsg.$message.hide(); nvMsg.fixAvatar(); nvMsg.fixBlacklist(); nvMsg.fixCitation(TL.ajaxTs, TL.ajaxHash); nvMsg.initPartialQuote(); nvMsg.fixDeroulerCitation(); nvMsg.fixImages(); $(`${TL.class_pagination}:last`).before(nvMsg.$message); TL.mediaEmbed.processNode(nvMsg.$message); messages_a_afficher.push({ message: nvMsg, cancelled: false }); dispatchEvent(new CustomEvent('topiclive:newmessage', { 'detail': { id: nvMsg.id_message, jvcake: TL.jvCake, cancel: () => { evt.cancelled = true; } } })); } } } catch (err) { console.error(`[TopicLive+] Erreur nouveaux messages : ${err}`); } TL.majUrl(this); if (messages_a_afficher.length > 0) { setTimeout(() => { let maj = false; let $firstNewMessageToShow = null; for (let msg of messages_a_afficher) { if (msg.cancelled) { TL.nvxMessages--; } else { if (!$firstNewMessageToShow) $firstNewMessageToShow = msg.message.$message; msg.message.$message.fadeIn('slow', function() { TL.mediaEmbed.processNode(this); }); TL.addUnreadAnchor(msg.message.$message); maj = true; } } if (isTextareaFocused) { const newScrollTop = document.documentElement.scrollHeight - distanceFromBottom; $(window).scrollTop(newScrollTop); } if (maj) { this.maj(); if (TL.justPostedMessageId) { const messageSelector = `${TL.class_msg}[id="message-${TL.justPostedMessageId}"]`; const $myNewMessage = $(messageSelector); if ($myNewMessage.length > 0) { TL.isChatModeActive = true; const targetScrollTop = $myNewMessage.offset().top - 100; $('html, body').stop().animate({ scrollTop: targetScrollTop }, 800); TL.updateCounters(); } TL.justPostedMessageId = null; } else if (TL.isChatModeActive && !isTextareaFocused) { if ($firstNewMessageToShow) { const targetScrollTop = $firstNewMessageToShow.offset().top - 100; $('html, body').animate({ scrollTop: targetScrollTop }, 800); } } else { TL.updateCounters(); } } }, 1000); } TL.loop(); } Transformation() { $('.JvCare').each(function() { const $span = $(this); let classes = $span.attr('class'); const href = TL.jvCake(classes); classes = classes.split(' '); const index = classes.indexOf('JvCare'); classes.splice(index, index + 2); classes.unshift('xXx'); classes = classes.join(' '); $span.replaceWith(`${$span.html()}`); }); $('.user-avatar-msg').each(function() { const $elem = $(this); const newsrc = $elem.attr('data-srcset'); if (newsrc != 'undefined') { $elem.attr('src', newsrc); $elem.removeAttr('data-srcset'); } }); } trouver(chose) { return this.$page.find(chose); } } /** * Représente un message unique du forum. */ class Message { constructor($message) { if (TL.estMP) { this.id_message = 'MP'; } else { // ── NOUVEAU : l'ID est dans l'attribut id="message-XXXX" ── const rawId = $message.attr('id') || ''; this.id_message = parseInt(rawId.replace('message-', ''), 10); } // ── NOUVEAU : nouveaux sélecteurs React ── this.date = $('.messageUser__date', $message).text().replace(/[\r\n]|#[0-9]+$/g, ''); this.edition = $message.find('.info-edition-msg').text(); this.$message = $message; this.pseudo = $('.messageUser__label', $message).text().replace(/[\r\n]/g, ''); this.supprime = false; } fixAvatar() { let avatar = this.trouver('.user-avatar-msg, .avatar__image'); avatar.attr('src', avatar.data('src') || avatar.attr('src')); } fixBlacklist() { this.trouver('.bloc-options-msg > .picto-msg-tronche, .msg-pseudo-blacklist .btn-blacklist-cancel').on('click', () => { $.ajax({ url: '/forums/ajax_forum_blacklist.php', data: { id_alias_msg: this.$message.attr('data-id-alias'), action: this.$message.attr('data-action'), ajax_hash: $('#ajax_hash_preference_user').val() }, dataType: 'json', success: ({ erreur }) => { if (erreur && erreur.length) { TL.alert(erreur); } else { document.location.reload(); } } }); }); } fixCitation(timestamp, hash) { if (this.$message.find('.messageUser__action[title="Citer le message"]').length === 0) { this.buildActionButtons(); } this.$message.find('.messageUser__action[title="Citer le message"]').off('click').on('click', () => { const $msg = TL.formu.obtenirMessage(); const datePropre = this.date.trim().replace(/\s+/g, ' '); const pseudoPropre = this.pseudo.trim().replace(/\s+/g, ' '); const contentNode = this.trouver(TL.class_contenu)[0]; const txt = contentNode ? contentNode.innerText.trim() : ''; let nvmsg = `> Le ${datePropre} ${pseudoPropre} a écrit :\n>`; nvmsg += `${txt.split('\n').join('\n> ')}\n\n`; if ($msg[0].value === '') { Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value").set.call($msg[0], `${nvmsg}\n`); } else { Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value").set.call($msg[0], `${$msg[0].value}\n\n${nvmsg}`); } $msg[0].dispatchEvent(new Event("input", { bubbles: true })); location.hash = '#forums-post-message-editor'; setTimeout(() => { $msg[0].focus(); }, 50); }); } buildActionButtons() { const actions = TL.messagesActionsMap ? TL.messagesActionsMap[this.id_message] : null; const pseudoPropre = this.pseudo.trim().replace(/\s+/g, ' '); const pmUrl = actions?.privateMessage?.url || `https://www.jeuxvideo.com/messages-prives/nouveau.php?all_dest=${encodeURIComponent(pseudoPropre)}`; const blacklistUrl = actions?.blacklist?.url || null; const reportUrl = actions?.report?.url || null; const innerHtml = `
${reportUrl ? `` : ''} ${blacklistUrl ? `` : ''} Envoyer un message privé
`; let $headerActions = this.$message.find('.messageUser__headerActions'); if ($headerActions.length === 0) { const $header = this.$message.find('.messageUser__header'); if ($header.length > 0) { $header.append('
'); $headerActions = this.$message.find('.messageUser__headerActions'); } } if ($headerActions.length > 0 && $headerActions.find('.tl-quote-btn').length === 0) { $headerActions.html(innerHtml); const $wrap = $headerActions.find('.tl-more-wrap'); const $moreButton = $wrap.find('.tl-more-btn'); const $menu = $wrap.find('.tl-more-menu'); $headerActions.find('.tl-menu-item').hover( function() { $(this).css('background', 'rgba(255,255,255,0.08)'); }, function() { $(this).css('background', 'transparent'); } ); const openModal = () => $menu.css('display', 'flex'); const closeModal = () => $menu.css('display', 'none'); $moreButton.off('click').on('click', (e) => { e.preventDefault(); e.stopPropagation(); if ($menu.css('display') === 'flex') { closeModal(); } else { openModal(); } }); $(document).off(`click.tlmenu-${this.id_message}`).on(`click.tlmenu-${this.id_message}`, (e) => { if (!$(e.target).closest($wrap).length) closeModal(); }); if (reportUrl) { $wrap.find('.tl-report-btn').off('click').on('click', () => { closeModal(); window.open(reportUrl, 'signalement', 'width=600,height=500'); }); } if (blacklistUrl) { $wrap.find('.tl-blacklist-btn').off('click').on('click', () => { closeModal(); fetch(blacklistUrl, { credentials: 'include' }) .then(() => { this.$message.css('opacity', '0.4'); }) .catch(err => console.error('[TopicLive+] Erreur blacklist:', err)); }); } } } initPartialQuote() { const partialQuoteEvent = async (pointerEvent) => { await new Promise(resolve => setTimeout(resolve, 50)); const selection = window.getSelection(); const selectedText = selection.toString().trim(); if (!selectedText.length) return; const messageContentNode = this.trouver(TL.class_contenu)[0]; const selectionContainer = selection.getRangeAt(0).commonAncestorContainer; if (!messageContentNode.contains(selectionContainer)) return; TL.$partialQuoteButton[0].onclick = () => this.buildPartialQuote(selectedText); const rect = selection.getRangeAt(0).getBoundingClientRect(); const top = rect.bottom + window.scrollY + 10; const left = rect.left + (rect.width / 2) + window.scrollX; TL.$partialQuoteButton.css({ top: `${top}px`, left: `${left}px` }).addClass('active'); }; this.$message[0].onpointerup = (pe) => partialQuoteEvent(pe); this.$message[0].oncontextmenu = (pe) => partialQuoteEvent(pe); } buildPartialQuote(selection) { const textarea = TL.formu.obtenirMessage()[0]; if (!textarea) return; const datePropre = this.date.trim().replace(/\s+/g, ' '); const pseudoPropre = this.pseudo.trim().replace(/\s+/g, ' '); const newQuoteHeader = `> Le ${datePropre} ${pseudoPropre} a écrit :`; const quotedText = selection.replace(/\n/g, '\n> '); const fullQuote = `${newQuoteHeader}\n> ${quotedText}\n\n`; const currentContent = textarea.value.length === 0 ? '' : `${textarea.value.trim()}\n\n`; Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set.call(textarea, `${currentContent}${fullQuote}`); textarea.dispatchEvent(new Event('input', { bubbles: true })); textarea.focus(); textarea.setSelectionRange(textarea.value.length, textarea.value.length); TL.$partialQuoteButton.removeClass('active'); } fixDeroulerCitation() { this.trouver('.text-enrichi-forum > blockquote.blockquote-jv > blockquote').each(function() { const $quote = $(this); const $buttonOpenQuote = $('
'); $quote.prepend($buttonOpenQuote); $buttonOpenQuote.on('click', function() { const $blockquote = $buttonOpenQuote.closest('.blockquote-jv'); const visible = $blockquote.attr('data-visible'); $blockquote.attr('data-visible', visible === '1' ? '' : '1'); }); }); } fixImages() { this.trouver(TL.class_contenu).find('img').each(function() { const $img = $(this); const src = $img.attr('src'); const extension = $img.attr('alt').split('.').pop(); if (src && src.includes('/minis/')) { const direct = src.replace(/\/minis\/(.*)\.\w+$/, `/fichiers/$1.${extension}`); $img.attr('src', direct); $img.css('object-fit', 'contain'); } }); } trouver(chose) { return this.$message.find(chose); } update(nvMessage) { if (this.edition == nvMessage.edition) return; this.edition = nvMessage.edition; this.trouver(TL.class_contenu).html(nvMessage.trouver(TL.class_contenu).html()); TL.page.Transformation(); TL.mediaEmbed.processNode(this.$message); this.fixImages(); this.fixDeroulerCitation(); dispatchEvent(new CustomEvent('topiclive:edition', { 'detail': { id: this.id_message, jvcake: TL.jvCake } })); } } /** * Gère le formulaire de réponse. */ class Formulaire { constructor() { this.formSessionData = null; this.selectedGroup = "1"; this.observerLeBouton('.postMessage'); this.observerLeMenuModeration(); } observerLeBouton(selecteurBouton) { const observer = new MutationObserver((mutations, obs) => { if (document.querySelector(selecteurBouton)) { this.hook(); obs.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true }); } observerLeMenuModeration() { const SELECTEUR_MENU_MODO = '#form_alias_rang'; const setupListener = (selectElement) => { selectElement.addEventListener('change', () => { this.selectedGroup = selectElement.value; }); this.selectedGroup = selectElement.value; }; const observer = new MutationObserver((mutations, obs) => { const menuModo = document.querySelector(SELECTEUR_MENU_MODO); if (menuModo) { setupListener(menuModo); obs.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true }); } observerLeBouton(selecteurBouton) { // Avec la délégation, pas besoin d'attendre que le bouton existe // On attache directement sur document $(document).off('click.topiclive', selecteurBouton) .on('click.topiclive', selecteurBouton, (e) => this.envoyer(e)); } _getForumPayload() { try { return JSON.parse(atob(unsafeWindow.jvc.forumsAppPayload)); } catch (e) { return null; } } _getTopicId() { return $('#bloc-formulaire-forum').attr('data-topic-id'); } _getForumId() { const match = window.location.pathname.match(/forums\/(?:1|42)-(?[0-9]+)-/); return match ? match.groups.forumid : null; } _setTextAreaValue(textarea, value) { const prototype = Object.getPrototypeOf(textarea); const nativeSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set; nativeSetter.call(textarea, value); textarea.dispatchEvent(new Event('input', { bubbles: true })); } envoyer(e) { e.preventDefault(); e.stopImmediatePropagation(); const $boutonEnvoi = $('.postMessage'); const $labelBouton = $boutonEnvoi.find('.postMessage__label'); const $msgTextarea = $('#message_topic'); const message = $msgTextarea.val(); if (message.trim() === '') { TL.alert('Le message est vide.'); return; } let dataObject = {}; if (this.formSessionData) { dataObject = { ...this.formSessionData }; } else { const forumPayload = this._getForumPayload(); if (forumPayload && forumPayload.formSession) { dataObject = { ...forumPayload.formSession }; } else { this.afficherErreurs("Impossible de récupérer les informations de session initiales."); return; } } dataObject.text = message; dataObject.topicId = this._getTopicId(); dataObject.forumId = this._getForumId(); dataObject.group = this.selectedGroup; dataObject.messageId = "undefined"; dataObject.ajax_hash = $('#ajax_hash_liste_messages').val(); $boutonEnvoi.prop('disabled', true); $labelBouton.text('Envoi...'); const self = this; const handleSuccess = (response) => { // ── NOUVEAU : détection dernière page avec nouvelle pagination ── const isOnLastPage = $('.pagination__item--next.pagination__item--disabled').length > 0; self._setTextAreaValue($msgTextarea[0], ''); if (isOnLastPage) { if (response.redirectUrl) { try { const url = new URL(response.redirectUrl, window.location.origin); const hash = url.hash; if (hash && hash.startsWith('#post_')) { TL.justPostedMessageId = hash.replace('#post_', ''); } } catch (err) { console.error("[TopicLive+] Erreur d'analyse de l'URL de redirection.", err); } } setTimeout(() => TL.charger(), 500); } else { if (response.redirectUrl) { localStorage.setItem('tl_force_live_mode', 'true'); window.location.href = response.redirectUrl; } else { // ── NOUVEAU : sélecteur pagination dernière page ── const $bouton = $('.pagination__item--last:not(.pagination__item--disabled)'); let lastPageUrl = ''; if ($bouton.length > 0) { lastPageUrl = $bouton.attr('href') || TL.jvCake($bouton.attr('class')); } if (lastPageUrl) { localStorage.setItem('tl_force_live_mode', 'true'); window.location.href = lastPageUrl; } else { location.reload(); } } } }; $.ajax({ type: 'POST', url: 'https://www.jeuxvideo.com/forums/message/add', data: dataObject, dataType: 'json', success: (response) => { if (response.formSession) self.formSessionData = response.formSession; const hasSessionError = response.errors && response.errors.session; if (hasSessionError) { let retryDataObject = { ...self.formSessionData, text: message, topicId: self._getTopicId(), forumId: self._getForumId(), group: this.selectedGroup, messageId: "undefined", ajax_hash: $('#ajax_hash_liste_messages').val() }; $.ajax({ type: 'POST', url: 'https://www.jeuxvideo.com/forums/message/add', data: retryDataObject, dataType: 'json', success: (finalResponse) => { if (finalResponse.errors && Object.keys(finalResponse.errors).length > 0) { self.afficherErreurs(Object.values(finalResponse.errors).join('\n')); } else { handleSuccess(finalResponse); } }, error: () => self.afficherErreurs('La relance automatique a échoué (erreur réseau).'), complete: () => { $boutonEnvoi.prop('disabled', false); $labelBouton.text('Poster'); } }); } else if (response.errors && Object.keys(response.errors).length > 0) { self.afficherErreurs(Object.values(response.errors).join('\n')); $boutonEnvoi.prop('disabled', false); $labelBouton.text('Poster'); } else { handleSuccess(response); $boutonEnvoi.prop('disabled', false); $labelBouton.text('Poster'); } }, error: () => { self.afficherErreurs('Une erreur réseau est survenue lors de l\'envoi du message.'); $boutonEnvoi.prop('disabled', false); $labelBouton.text('Poster'); } }); } afficherErreurs(msg) { TL.alert(msg); } obtenirMessage($form) { if (typeof $form == 'undefined') $form = this.obtenirFormulaire(); return $form.find('#message_topic, #message_reponse'); } obtenirFormulaire($page) { if (typeof $page === 'undefined') $page = $(document); return $page.find('#forums-post-message-editor'); } } /** * Gère la favicon. */ class Favicon { constructor() { try { this.imageLoaded = false; this.pendingText = ''; this.canv = $('').get(0); this.canv.width = 192; this.canv.height = 192; this.context = this.canv.getContext('2d'); this.image = new Image(); this.image.onload = () => { this.imageLoaded = true; if (this.pendingText) this.maj(this.pendingText); }; this.image.src = 'https://www.jeuxvideo.com/favicon.png'; this.maj(''); } catch (err) { console.error(`[TopicLive+] Erreur init favicon : ${err}`); } } clear() { this.context.clearRect(0, 0, this.canv.width, this.canv.height); if (this.imageLoaded) this.context.drawImage(this.image, 0, 0); } maj(txt) { this.pendingText = txt; if (!this.imageLoaded) return; this.clear(); if (txt && txt !== '') { const radius = 70, borderWidth = 8, centerX = radius + borderWidth, centerY = radius + borderWidth; const font = 'bold 120px Arial Black', verticalTextOffset = 8, shadowOffset = 6; this.context.beginPath(); this.context.arc(centerX, centerY, radius + borderWidth, 0, 2 * Math.PI); this.context.fillStyle = 'white'; this.context.fill(); this.context.beginPath(); this.context.arc(centerX, centerY, radius, 0, 2 * Math.PI); this.context.fillStyle = '#0074ff'; this.context.fill(); this.context.font = font; this.context.textAlign = 'center'; this.context.textBaseline = 'middle'; this.context.fillStyle = 'black'; this.context.fillText(txt, centerX + shadowOffset, centerY + verticalTextOffset + shadowOffset); this.context.fillStyle = 'white'; this.context.fillText(txt, centerX, centerY + verticalTextOffset); } this.replace(); } replace() { $('link[rel*="icon"]').remove(); this.lien = $('', { href: this.canv.toDataURL('image/png'), rel: 'shortcut icon', type: 'image/png' }); $('head').append(this.lien); } setCloudflareIcon() { const cloudflareLogo = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTkuMzUgMTAuMDRDMTguNjcgNi41OSAxNS42NCA0IDEyIDRDOS4xMSA0IDYuNiA1LjY0IDUuMzUgOC4wNEMyLjM0IDguMzYgMCAxMC45MSAwIDE0QzAgMTcuNzEgMi42OSAyMCA2IDIwSDE5QzIxLjc2IDIwIDI0IDE3Ljc2IDI0IDE1QzI0IDEyLjM2IDIxLjk1IDEwLjIyIDE5LjM1IDEwLjA0WiIgZmlsbD0iI0Y0ODAyMiIvPjwvc3ZnPg=='; $('link[rel*="icon"]').remove(); this.lien = $('', { href: cloudflareLogo, rel: 'shortcut icon', type: 'image/svg+xml' }); $('head').append(this.lien); } set410Icon() { const errorIcon16 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAALEUExURUxpcf7ihv/0oeuVNfK1Xv7/wf//6uqPMPS3WvzRff/Vc/nOfv/sh//Pbf3WdEI9Rv7iiP7ce//2rv/Nb/zknv/bevutMv/gkv/wrPzgmfbFcvrXjPzkmv7xq//hf//skP3cgv/mhfGvVP/shF1KOXZNMP/Xff3poP/FYf/AXPmaDf/rhO2ePa+Viv/legAPPf62SvvQd/6NFS0rN/SkPfvZiNp0FoZFD7VUFf7ecf6UGP+fIur//xwYIvOIFuiKH+yIGY6Ki59rPi8rM/+dG1JPXXpwY56hqtyxZ+eQKfvTdURDUEZEUF5cZ/n39pZaHU1JUvnWgv7caIqHjXh5hTk4Q9t7F3dzd0FEVTw6RZViOpaUlpqYm/2QE+aGGEkyJI1pTE9MUtTT02daUyorOX99gPSMGBYVIv+REfCLGP+mDPmLFLFuJWRfYuqHFko3LuKEGEZCSf3ajf71s//4uP/riP/qhv/Zdf/lg//efP/1t//qn/3Sb//ll926b/vGbf/NavfWdP/0o/3iesiaXP/pgv/ddP/XcNSfV/q3VvK4VuvDav7uov/BUv/jeP/fdMSUV//aaLNpGv/rkP3GS/+ySN6QH+OvR//faP/ecP/HNP+yJ+aqMf2jIf66KfS3IvmZJKlnG7FiDfzigP/GKNqIGv6UFumNK2tna/+wJvC9Qv/AJuq0OCMiMP+nIvnOX/6kIpSGaC8wR//mdOyQGZ+WkkpLW4d/eO6dGy8sNsybN7qNPfilII2QnHhoXe3s7NbX24JqULO5x519UKCenyktQ66HSjk1Pv6TFG1rcLSwrj9EXS8qL83MzSEeJ8vKzmpmayMfKGRpfjo/TpWYpyomLY6OltDPzzQxOUFAU3x6fmxwfI6KjRscKZORlWpoctTT1Lu9wpqYm42Lj/+YHnJucp6eojc0PS8rMjU0PtiFJPONGDdGz4cAAABxdFJOUwCN+hdmAgIBAwL97P38aAIDY/392/4V/v7bdLu77P78cf2R/QIDAuX9+hj8agf9HP1ybK/nthuaGv7w/AHz4Ha9/uHY/aL9/LX+2kmW/vz9ytr+/fxveP7+bPz8+P7A/v3++/6I/r79AdwWdeb93X3+CABJWgAAAAlwSFlzAAALEwAACxMBAJqcGAAAARtJREFUGNMBEAHv/gAAABAAFiAcFBkbGgMACAcAAAAFABEdehh7fRdyCwQACQAABgABdBIfIR4VCg0TfyIAJgAADnMCDHV4eXd8gCgpiSwAACongiN2foGGh4iKjTCVNDYAMYwrhYOEi5GUkpabnTugPwA1ky6Oj5CXmp6ipamwRKZAAFGjOZmYnJ+kq7i8rrRJpz4ASrNSr6qsurvGw8G+tU22QgBIsUa30NnU0svCxb3AU61QAEWyVM/W3+Da2M3Hyb9XcUMATLlb197n4+LV3MzEyk6oPQBV0VxiZejm4d3b01hHQXEzAEvOYeXpZ21jX2BaT6E3Ly0APFlkb2zqcOvkXcg6MgAAJAAAAGgAamtmaW5eVjgAJQ8Ah7RsxA/wK1MAAABXelRYdFJhdyBwcm9maWxlIHR5cGUgaXB0YwAAeJzj8gwIcVYoKMpPy8xJ5VIAAyMLLmMLEyMTS5MUAxMgRIA0w2QDI7NUIMvY1MjEzMQcxAfLgEigSi4A6hcRdPJCNZUAAAAASUVORK5CYII='; $('link[rel*="icon"]').remove(); this.lien = $('', { href: errorIcon16, rel: 'shortcut icon', type: 'image/png' }); $('head').append(this.lien); } } class MediaEmbed { constructor() { this.twitterWidgetScriptLoaded = false; } processNode(node) { if (!TL.options) return; const links = $(node).find('.txt-msg a:not([data-processed])'); links.each((index, link) => { const $link = $(link); const href = $link.attr('href'); if (!href || !$link.text().trim().startsWith('http')) return; $link.attr('data-processed', 'true'); if (TL.options.embedTiktok && href.includes('tiktok.com/')) { this.handleTikTok($link, href); } else if (TL.options.embedInstagram && href.includes('instagram.com/')) { this.handleInstagram($link, href); } else if (TL.options.embedYoutube && (href.includes('youtube.com/') || href.includes('youtu.be/'))) { this.handleYouTube($link, href); } else if (TL.options.embedTwitter && (href.includes('twitter.com/') || href.includes('x.com/'))) { this.handleTwitter($link, href); } else if (TL.options.embedWebmshare && href.includes('webmshare.com/')) { this.handleWebmshare($link, href); } else if (TL.options.embedStreamable && href.includes('streamable.com/')) { this.handleStreamable($link, href); } if (TL.options.embedVocaroo) { if (href.includes('vocaroo.com/') || href.includes('voca.ro/')) return this.handleVocaroo($link, href); } }); } handleWebmshare($linkElement, url) { const match = url.match(/https:\/\/webmshare\.com\/(?:play\/)?(?[\w]+)/i); if (!match) return; const videoUrl = `https://s1.webmshare.com/${match.groups.id}.webm`; const $video = $('