// ==UserScript== // @name JVChat Premium FORK by Rand0max // @description Outil de discussion (debug les crashs et citations par Shiho Lantea / en attendant le debug de Rando) // @author Blaff & Rand0max // @namespace JVChatPremium // @license MIT // @version 0.2.3.11 // @match http://*.jeuxvideo.com/forums/42-* // @match https://*.jeuxvideo.com/forums/42-* // @match http://*.jeuxvideo.com/forums/1-* // @match https://*.jeuxvideo.com/forums/1-* // @grant GM.getResourceText // @grant GM_getResourceText // @grant GM_addStyle // @resource JVCHAT_CSS https://raw.githubusercontent.com/Lantea-Git/jvchat-forked/refs/heads/debug/jvchat-premium.css // @downloadURL https://github.com/Lantea-Git/jvchat-forked/raw/refs/heads/debug/JVChat_Premium.user.js // @updateURL https://github.com/Lantea-Git/jvchat-forked/raw/refs/heads/debug/JVChat_Premium.user.js // @run-at document-end // ==/UserScript== /* API : les développeurs peuvent créer des "plugins" pour JVChat à l'aide d'un système d'évènements. Cela permet par exemple à un deuxième userscript de modifier les messages au moment où ils sont ajoutés par JVChat, afin que chacun puisse personnaliser son affichage et ajouter de nouvelles fonctionnalités. Pour cela, il suffit d'écouter l'évènement "jvchat:newmessage". Celui-ci est émis chaque fois qu'un novueau message est ajouté, il contient l'identifiant dudit message à récupérer dans le DOM via le data-attribut "jvchat-id". Il existe aussi l'évènement "jvchat:activation" qui est émis une seule fois : à l'initialisation lorsque le topic pass en mode "JVChat" après appui sur le bouton. L'évènement "jvchat:activated" qui est émis une seule fois : après l'initialisation lorsque les messages sont chargés. Il existe enfin l'évènement "jvchat:postmessage". Celui-ci est émis chaque fois qu'un message est posté par l'utilisateur actuel. Il contient l'identifiant du message, le contenu du message, et l'auteur du message. Exemple pour cacher les messages qui contiennent certains mot-clefs: // my_plugin.user.js let keywords = ["foobar", "barbaz"] addEventListener("jvchat:newmessage", function(event) { // L'id du message est stocké dans event.detail.id // L'attribut event.detail.isEdit est mis à "true" s'il s'agit d'un message édité let message = document.querySelector(`.jvchat-message[jvchat-id="${event.detail.id}"]`); let text = message.querySelector(".txt-msg").textContent; for (let keyword of keywords) { if (text.includes(keyword)) { message.style.display = "none"; return; } } }); */ let freshHash = undefined; let freshDeletionHash = undefined; let freshForm = undefined; let freshPayload = undefined; let firstMessageId = undefined; let firstMessageDate = undefined; let lastEditionTime = {}; // id => [timestamp, edition, deleted] let messagesByPage = {}; let userConnected = undefined; let updateIntervals = [2, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 10, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 30, 30, 30, 30, 30, 30, 30, 30, 60]; let transisitions = [0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 19, 19, 19, 19, 19, 19, 19, 19, 31]; let updateIntervalIdx = 2; let updateIntervalMax = updateIntervals.length - 1; let isLocked = false; let isError = false; let isAlert = false; let isReduced = true; let nbNewMessage = 0; let topicNbMessages = 0; let favicon = undefined; let faviconLoaded = false; let faviconTextWhenLoaded = ""; let currentUser = { notif: undefined, mp: undefined, author: undefined, avatar: undefined }; let currentTopicTitle = undefined; let turboActivated = false; let turboDateActivated = undefined; let turboWarnTimeoutId = -1; let turboDateSessions = []; let refreshDegraded = false; let refreshDegradedTimeoutId = -1; let timeoutedDates = []; let sondageChoices = undefined; let urlToFetch = undefined; let urlToRefreshInfos = undefined; let urlToCheckEdited = undefined; let currentFetchedPage = 1; let currentTimeoutId = -1; let shouldCheckEdited = false; let checkEditedInterval = 30000; let postingMessage = false; let fetchingMessages = false; let leavingTopic = false; let storageKey = "jvchat-premium-configuration"; let ringBell = undefined; let configuration = undefined; function defaultConfig() { return { default_reduced: false, turbo_delay: 500, max_width: 100, hide_alerts: false, play_sound: false, night_mode: false, load_images: false, hide_mosaic: false, turbo_alerted: false, sound: "data:audio/mp3;base64, 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KZ+4/J4hhPMbxk9uZlkvh6/jLtS6T9noZlleNzM1apGt+7CwkdfONPpLpNT0rivtF6d5JRejlihnmuTMIf3OWsl7KKKapZHQ3IBmnFlUchyHaOIzN+Syuo2SUVYPlNqCn+n4xG78SrR1lD/QTSyWlcKHJychjdSVSWMwDAM3EY7Rf/////4vzXlEEQJGHKsO7LsZ6U2J2PwPQf/////wNDUARKBH5g6tXeyKSKCcnRpY3DcYAAEAAAMEoLwyQhqjAOAaMLkNqt00SR7TGnEwMCcLkwGAN/0YLQF4kEeYBQBxgMgDf5zDxADC4wy5TXmbBm6JJimNECMEY0V/+ZUKbI8RFS75hwiwRgQhhQ3//mSGBgiSmIDAYQ8RaowYaYQy///4FAgIFCFcICDBAACE//uSxO0AK+Iu+xnsAAT7PuBXPaAAsrSBICGWxf///q8USLIIaMqLIIUF1n1hhHpwYQrd////69i6hehbyJiOCcatiVjSXVaU3Fpsebiy2x/////+jW3RSxAe2JliKcPPIim67osRh10V2u7ALDWcx1hv///////DiG7T48oOzeXsjTHhtmap3fdFMdYWZaysLddlhtaIrlpqFlMSp//////////3Hh1Qdf8HMPUvasytPttG5svXpBLO1LJlr//////7WYlRuzEotEYCfqZiTvX4k5UPRJynekxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqkxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqv/7ksQ5A8AAAaQcAAAgAAA0gAAABKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=" }; } function saveConfig() { let config = JSON.stringify(configuration); localStorage.setItem(storageKey, config); } function loadConfig() { let config = JSON.parse(localStorage.getItem(storageKey) || "{}"); for (let key in config) { if (Object.prototype.hasOwnProperty.call(config, key) && Object.prototype.hasOwnProperty.call(configuration, key)) { configuration[key] = config[key]; } } } function getTimestamp() { return new Date().getTime(); } function escapeHtml(str, isAttribute) { str = str.replace(/&/g, "&").replace(//g, ">"); if (isAttribute) { str = str.replace(/"/g, """).replace(/'/g, "'"); } return str; } function getForm(doc) { return doc.querySelector('#forums-post-message-editor > form'); } function getHash(doc) { let hash = doc.querySelector("#ajax_hash_liste_messages") if (hash) { return hash.getAttribute("value"); } // Fallback: extract from payload let payload = getPayload(doc); if (payload && payload.ajaxToken) { return payload.ajaxToken; } return undefined; } function getDeletionHash(doc) { let hash = doc.querySelector("#ajax_hash_moderation_forum") if (hash) { return hash.getAttribute("value"); } // Fallback: extract from payload let payload = getPayload(doc); if (payload && payload.topicActions && payload.topicActions.deleteMessageUrl) { let match = payload.topicActions.deleteMessageUrl.match(/ajax_hash=([a-f0-9]+)/i); if (match) return match[1]; } return undefined; } function getJvcHash(type = "liste_messages") { if (type === "liste_messages" && typeof freshHash !== 'undefined') { return freshHash; } if (type === "moderation_forum" && typeof freshDeletionHash !== 'undefined') { return freshDeletionHash; } const hashElement = document.querySelector(`#ajax_hash_${type}`); if (hashElement) return hashElement.value; // Fallback: extract from payload if (type === "liste_messages") { return getHash(document); } else if (type === "moderation_forum") { return getDeletionHash(document); } return undefined; } function manageTextareaSimpleHeight() { const textarea = getTextArea(); const postButton = document.querySelector('.postMessage'); if (!textarea || !postButton) { return; } textarea.addEventListener('focus', function () { textarea.classList.toggle("jvchat-textarea-focus", true); }); postButton.addEventListener('click', function () { textarea.classList.toggle("jvchat-textarea-focus", false); }); } function getTopicLocked(elem) { // Old structure let lock = elem.getElementsByClassName("message-lock-topic")[0]; if (lock !== undefined) { let reason = lock.getElementsByTagName("span")[0].textContent.trim(); return `Le topic a été verrouillé pour la raison suivante : "${reason}"`; } // New structure: check payload try { let payload = freshPayload || getForumPayload(); //FreshPayload for actualize on polling if (payload && payload.forum && payload.forum.isForumReadOnly) { let reason = payload.forum.lockReason?.post?.message || "raison inconnue"; return `Le topic a été verrouillé pour la raison suivante : "${reason}"`; } } catch { /* ignore */ } return undefined; } function getTopicError(elem) { let error = elem.getElementsByClassName("img-erreur")[0]; if (error === undefined) { return error; } return `Le topic présente une erreur: ${error.getAttribute("alt")}`; } function autoHideTurnstileErrorMessages() { const captchaContainers = document.querySelectorAll('.js-captcha'); captchaContainers.forEach(container => { const observer = new MutationObserver((mutationsList) => { for (const mutation of mutationsList) { if (mutation.type === 'childList' || mutation.type === 'characterData' || mutation.type === 'subtree') { const childDivs = Array.from(container.children).filter(el => el.tagName === 'DIV'); if (childDivs.length >= 2) { const turnstileWidgetDiv = childDivs[childDivs.length - 1]; if (turnstileWidgetDiv && turnstileWidgetDiv.textContent.includes("[Cloudflare Turnstile]")) { turnstileWidgetDiv.style.display = 'none'; } } } } }); observer.observe(container, { childList: true, subtree: true, characterData: true }); }); } function parseSondage(elem) { // Old structure let blocSondage = elem.getElementsByClassName("bloc-sondage")[0]; if (blocSondage) { let intitule = blocSondage.getElementsByClassName("intitule-sondage")[0].textContent; let answered = !!(blocSondage.getElementsByClassName("result-pourcent")[0]); let choix = blocSondage.getElementsByClassName("tab-choix")[0].getElementsByTagName("tr"); let results = []; if (answered) { for (let ch of choix) { let pourcent = parseInt(ch.getElementsByClassName("pourcent")[0].innerHTML.trim().split(" ")[0]); let response = ch.getElementsByClassName("reponse")[0].textContent.trim(); results.push({ response: response, pourcent: pourcent }); } } else { for (let ch of choix) { let btnResponse = ch.getElementsByClassName("btn-sondage-reponse")[0]; let response = btnResponse.textContent.trim(); let sondageId = btnResponse.getAttribute("data-id-sondage"); let responseId = btnResponse.getAttribute("data-id-reponse"); results.push({ response: response, sondageId: sondageId, responseId: responseId }); } } let votes = parseInt(blocSondage.getElementsByClassName("pied-result")[0].innerHTML.trim().split(" ")[0]); return { answered: answered, intitule: intitule, results: results, votes: votes }; } // New structure: extract from payload try { let payload = freshPayload || getForumPayload(); //FreshPayload for actualize on polling if (payload && payload.survey && payload.survey.hasSurvey && payload.survey.data) { let surveyData = payload.survey.data; let intitule = surveyData.title || ""; let answered = surveyData.hasVoted || surveyData.isClosed || false; let results = []; if (surveyData.responses) { for (let answer of surveyData.responses) { results.push({ response: answer.text || "", pourcent: answer.percentage || 0, sondageId: surveyData.id, responseId: answer.id }); } } let votes = surveyData.totalResponses || 0; return { answered: answered, intitule: intitule, results: results, votes: votes }; } } catch { /* ignore */ } return null; } function tryCatch(func) { function wrapped(optArg) { try { func(optArg); } catch (err) { let message = `Une erreur est survenue dans JVChat Premium: '${err.message}' (function '${func.name}', line ${err.lineNumber})`; console.error("===== JVCHAT ERROR ====="); console.error(message) console.error(err); console.error("========================"); try { addAlertbox("danger", message); } catch { alert(message); } } } return wrapped; } function toggleTextarea() { isReduced = !isReduced; configuration["default_reduced"] = isReduced; saveConfig(); let isDown = isScrollDown(); //document.getElementById("bloc-formulaire-forum").getElementsByClassName("messageEditor__buttonEdit")[0]?.classList.toggle("jvchat-hide"); document.getElementById("jvchat-enlarge")?.classList.toggle("jvchat-hide"); document.getElementById("jvchat-reduce")?.classList.toggle("jvchat-hide"); document.getElementById("jvchat-post")?.classList.toggle("jvchat-hide"); document.getElementById("bloc-formulaire-forum").classList.toggle("jvchat-reduced"); setTextareaHeight(); if (isDown) { setScrollDown(); } } function parseURL(url) { let regex = /^(.*?)(\/\d+-\d+-\d+-)(\d+)(-\d+-\d+-\d+-)(.*?)(\.htm)(.*)$/i; let [_, domain, ids, page, nums, title, htm, anchor] = url.match(regex); return { domain: domain, ids: ids, page: page, nums: nums, title: title, htm: htm, anchor: anchor }; } function buildURL(dict) { return `${dict.domain}${dict.ids}${dict.page}${dict.nums}${dict.title}${dict.htm}${dict.anchor}`; } function getForum(document) { let links = document.querySelectorAll(".spreadContainer nav.breadcrumb > a"); let title = ""; let forumLink = ""; for (let i = links.length - 1; i >= 0; i--) { forumLink = links[i]; title = forumLink.textContent.trim(); if (title.startsWith("Forum ")) { break; } } return { href: forumLink.getAttribute("href"), title: title.replace("Forum ", "") }; } function getLastPage(document) { // New structure: pagination with .pagination__button--last let lastPageLink = document.querySelector(".pagination__button--last"); if (lastPageLink) { let href = lastPageLink.getAttribute("href"); if (href) { let match = href.match(/\/forums\/\d+-\d+-\d+-(\d+)-/); if (match) return parseInt(match[1]); } } // Fallback: old structure let blocPages = document.getElementsByClassName("bloc-liste-num-page")[0]; if (blocPages) { let spans = blocPages.getElementsByTagName("span"); let lastPage = 1; for (let span of spans) { let page = parseInt(span.textContent.trim()); if (!isNaN(page) && page > lastPage) { lastPage = page; } } return lastPage; } // New structure fallback: scan every pagination link/item for the highest page number. // This handles cases where .pagination__button--last is absent (e.g. on the last page) // or when JVC omits it on intermediate pages. let maxPage = 1; let paginationEls = document.querySelectorAll( ".pagination__button, .pagination__item, .pagination__dropdownList a, .pagination__dropdownList span" ); for (let el of paginationEls) { let href = el.getAttribute && el.getAttribute("href"); if (href) { let m = href.match(/\/forums\/\d+-\d+-\d+-(\d+)-/); if (m) { let n = parseInt(m[1]); if (!isNaN(n) && n > maxPage) maxPage = n; continue; } } let txt = (el.textContent || "").trim(); if (/^\d+$/.test(txt)) { let n = parseInt(txt); if (!isNaN(n) && n > maxPage) maxPage = n; } } if (maxPage > 1) { return maxPage; } // Last resort: current page is the last page (topic has only one page) let current = document.querySelector(".pagination__item--current"); if (current) return parseInt(current.textContent.trim()) || 1; return 1; } function parseMessage(elem) { // New JVC structure: div.messageUser#message-XXXXXXX let isNewStructure = elem.classList.contains("messageUser"); if (isNewStructure) { let authorElem = elem.querySelector(".messageUser__label"); let author = authorElem ? authorElem.textContent.trim() : ""; let blacklisted = false; let avatarElem = elem.querySelector(".avatar__image"); let avatar = avatarElem ? avatarElem.getAttribute("src") : undefined; let dateElem = elem.querySelector(".messageUser__date"); let date = dateElem ? dateElem.textContent.trim() : ""; let content = elem.querySelector(".messageUser__msg"); if (content) { content.classList.add("txt-msg"); } let id = parseInt(elem.id.replace("message-", "")); let editedElem = elem.querySelector(".messageUser__dateEdit"); let edited = undefined; if (editedElem) { let msgEdited = editedElem.textContent.trim(); let match = msgEdited.match(/Message édité le .*? à (.*?) par/i); if (match) edited = match[1]; } let signalerHTML = ""; let signalElem = elem.querySelector('.messageUser__action[title="Faire un signalement"]'); if (signalElem) { let jvChatSignalElem = signalElem.cloneNode(true); jvChatSignalElem.classList.toggle("jvchat-picto", true); jvChatSignalElem.classList.add("jvchat-signal"); signalerHTML = jvChatSignalElem.outerHTML; } return { author: author, dateString: date, date: parseDate(date), avatar: avatar, edited: edited, id: id, content: content, blacklisted: blacklisted, signaler: signalerHTML }; } // Legacy JVC structure fallback let conteneurs = elem.getElementsByClassName("conteneur-message"); let conteneur = conteneurs[conteneurs.length - 1]; let author = conteneur.getElementsByClassName("bloc-pseudo-msg")[0].textContent.trim(); let blacklisted = conteneurs[0].classList.contains("conteneur-message-blacklist"); let avatar = conteneur.getElementsByClassName("user-avatar-msg")[0]; if (avatar !== undefined) { avatar = avatar.getAttribute("data-src"); } let date = conteneur.getElementsByClassName("bloc-date-msg")[0].textContent.trim(); let content = conteneur.getElementsByClassName("txt-msg")[0]; let id = parseInt(elem.getAttribute("data-id")); let edited = elem.getElementsByClassName("info-edition-msg")[0]; if (edited !== undefined) { let msgEdited = edited.textContent.trim(); edited = msgEdited.match(/Message édité le .*? à (.*?) par/i)[1]; } let signalerHTML = ""; const options = elem.getElementsByClassName("bloc-options-msg")[0]; if (options) { let signalElem = options.querySelector(".picto-msg-exclam"); if (signalElem) { let jvChatSignalElem = signalElem.cloneNode(true); jvChatSignalElem.classList.toggle("jvchat-picto", true); jvChatSignalElem.classList.add("jvchat-signal"); signalerHTML = jvChatSignalElem.outerHTML; } } return { author: author, dateString: date, date: parseDate(date), avatar: avatar, edited: edited, id: id, content: content, blacklisted: blacklisted, signaler: signalerHTML }; } function parseUserInfo(elem) { let accountMp = elem.getElementsByClassName("headerAccount__pm")[0]; if (accountMp === undefined) { return { author: undefined, avatar: undefined, mp: undefined, notif: undefined }; } let accountNotif = elem.getElementsByClassName("headerAccount__notif")[0]; let avatarBox = elem.getElementsByClassName("headerAccount__avatar")[0]; let authorBox = elem.getElementsByClassName("headerAccount__pseudo")[0]; let mp = parseInt(accountMp.getAttribute("data-val")); let notif = parseInt(accountNotif.getAttribute("data-val")); let avatar = avatarBox.style["background-image"].slice(5, -2).replace("/avatar-md/", "/avatar/"); let author = authorBox.textContent.trim(); return { author: author, avatar: avatar, mp: mp, notif: notif }; } function getPage(elem) { // New structure let pageActive = elem.querySelector(".pagination__item--current"); if (pageActive) { return parseInt(pageActive.textContent.trim()) || 1; } // Old structure fallback let pageActiveOld = elem.getElementsByClassName("page-active")[0]; let page = 1; if (pageActiveOld !== undefined) { page = parseInt(pageActiveOld.textContent.trim()); } return page; } function parseTopicInfo(elem) { // New structure let titleElem = elem.querySelector(".titleMessagesUsers__title"); if (!titleElem) { titleElem = elem.querySelector("#bloc-title-forum"); } let title = titleElem ? titleElem.textContent.trim() : ""; let connectedElem = elem.querySelector(".userCount__number"); if (!connectedElem) { connectedElem = elem.getElementsByClassName("nb-connect-fofo")[0]; } let connected = connectedElem ? parseInt(connectedElem.textContent.trim()) : 0; // New structure fallback: read from payload (forumInfo.header.btnVal) if (!connected) { try { let payload = getPayload(elem); let panels = payload && payload.sidebar && payload.sidebar.panels; if (Array.isArray(panels)) { for (let panel of panels) { if (panel && panel.header && typeof panel.header.btnVal === "number") { connected = panel.header.btnVal; break; } } } if (!connected && payload && payload.forumInfo && payload.forumInfo.header) { connected = parseInt(payload.forumInfo.header.btnVal) || connected; } } catch (e) { /* ignore */ } } let lastPage = getLastPage(elem); let page = getPage(elem); return { title: title, connected: connected, lastPage: lastPage, page: page }; } function fixMessage(elem) { let jvcares = Array.from(elem.getElementsByClassName("JvCare")); for (let jvcare of jvcares) { let a = document.createElement("a"); a.setAttribute("target", "_blank"); a.setAttribute("href", jvCake(jvcare.getAttribute("class"))); a.innerHTML = jvcare.innerHTML; jvcare.outerHTML = a.outerHTML; } let togglableQuotes = Array.from(elem.querySelectorAll(".messageUser__msg.txt-msg > blockquote > blockquote")); for (let togglableQuote of togglableQuotes) { let toggleButton = document.createElement("button"); toggleButton.classList.add("message__collapsedQuote"); togglableQuote.insertBefore(toggleButton, togglableQuote.firstChild); // The click event is bound in the "dontScrollOnExpand()" function } } function jvCake(cls) { let base16 = '0A12B34C56D78E9F', lien = '', s = cls.split(' ')[1]; for (let i = 0; i < s.length; i += 2) { lien += String.fromCharCode(base16.indexOf(s.charAt(i)) * 16 + base16.indexOf(s.charAt(i + 1))); } return lien; } function detectMosaic(elem) { let imagesShack = elem.querySelectorAll(".img-shack, .message__urlImg"); if (imagesShack.length < 4) { return; } let mosaics = {}; let regex1 = /^.+\/(?:[0-9]+-)+[0-9]{1,2}-([a-z0-9]+)\.\w+$/i; let regex2 = /^.+\/(?:[0-9]+-)+row-[0-9]+-col-[0-9](?:-[0-9]+)?\.\w+$/i; for (let image of imagesShack) { if (!image.src) continue; //Fell Back Span let match1 = image.src.match(regex1); if (match1) { let [_, identifier] = match1; if (Object.prototype.hasOwnProperty.call(mosaics, identifier)) { mosaics[identifier].push(image); } else { mosaics[identifier] = [image]; } continue; } let match2 = image.src.match(regex2); if (match2) { if (Object.prototype.hasOwnProperty.call(mosaics, "@rowcol")) { mosaics["@rowcol"].push(image); } else { mosaics["@rowcol"] = [image]; } continue; } } for (let identifier in mosaics) { let images = mosaics[identifier]; if (images.length < 4) { continue; } images[0].parentNode.classList.add("jvchat-mosaic-root"); images[0].classList.add("jvchat-mosaic"); for (let image of images.slice(1)) { image.parentNode.classList.add("jvchat-mosaic"); } } } function improveImages(elem) { let imagesShack = elem.querySelectorAll(".img-shack, .message__urlImg"); for (let image of imagesShack) { if (!image.src) { //CSS Image Span => transform to image or cancel const largeImg = image.dataset.srcBackground; if (!largeImg) continue; const tagImg = document.createElement('img'); tagImg.src = largeImg; tagImg.className = image.className; tagImg.style.paddingBottom = '0'; image.replaceWith(tagImg); image = tagImg; } let src = image.src; let parent = image.parentNode; let extension = parent.href.split(".").pop(); let direct = src.replace(/(.*?)\/minis\/(.*)\.\w+/i, "$1/fichiers/$2." + extension); image.setAttribute("data-src-mini", src); image.setAttribute("data-src-direct", direct); image.classList.add("jvchat-loadable-image"); parent.href = direct; if (extension.toUpperCase() === "GIF") { image.src = direct; image.classList.remove("jvchat-loadable-image"); } else if (configuration["load_images"]) { image.src = direct; } src = image.src; image.setAttribute("onerror", `this.onerror=null;this.src=this.getAttribute("data-src-direct");this.classList.remove("jvchat-loadable-image");`); } } function replacePostButton(clickEvent) { const oldElement = document.querySelector('.postMessage'); const newElement = oldElement.cloneNode(true); oldElement.parentNode.replaceChild(newElement, oldElement); newElement.onclick = clickEvent; newElement.type = "button"; } function getPanelHtml() { const panelHtml = `
Les paramètres sont automatiquement sauvegardés et mis à jour lorsque vous les modifiez.
Joue un son de notification lorsqu'un nouveau message est posté et que vous êtes sur un onglet différent.
Active un mode nuit pour protéger vos petits yeux fatigués le soir.
Remplace les miniatures NoelShack avec l'image source complète afin de laisser apparaître la transparence (cela sollicite davantage votre connexion Internet).
Cache automatiquement les mosaïques d'images NoelShack pour réduire le flooding.
Ajuste le délai d'actualisation des messages en mode turbo (celui-ci permet une meilleure réactivité lors d'un quiz mais ne doit pas être activé continuellement).
Configure l'espace utilisé horizontalement par les messages (une valeur réduite facilite la lecture sur les écrans larges).
" + child.textContent + "";
break;
}
case "BLOCKQUOTE": {
if (prevIsP) {
quote = quote.trimEnd() + "\n" + reverseMessage(child).replace(/^/gm, '> ') + "\n\n";
} else {
quote += reverseMessage(child).replace(/^/gm, '> ') + "\n\n";
}
break;
}
case "#text": {
// The "isInit" check is to prevent the empty text surroudning message
// However, it may happen that the root node contains valid text child, so it need to be added somehow
// For some reason, an "new line" may be missing in this case, so just add it
if (!isInit || child.textContent.trim() !== "") {
quote += child.textContent;
if (isInit && !quote.endsWith("\n")) {
quote += "\n";
}
}
break;
}
default: {
break;
}
}
if (name == "P") {
prevIsP = true;
} else {
prevIsP = false;
}
}
quote = quote.replace(/(\n){3,}/g, '\n\n');
if (startsWithSpoil && isInit) {
quote = "\n" + quote.trimEnd();
} else {
quote = quote.trim();
}
if (isInit) {
quote = quote.replace(/^/gm, '> ');
}
return quote;
}
function buildQuoteEvent(messageId) {
const message = document.querySelector(`.jvchat-message[jvchat-id='${messageId}']`);
const quoteButton = message.querySelector('.jvchat-quote');
const author = message.querySelector('.jvchat-author').textContent.trim();
const date = message.querySelector('.jvchat-date').getAttribute('to-quote');
quoteButton.addEventListener('click', () => {
let header = `> Le ${date} ${author} a écrit :\n`;
let quoted = reverseMessage(message.querySelector(".txt-msg"), true);
let content = header + quoted + '\n\n';
let textarea = getTextArea();
if (isReduced) {
toggleTextarea();
}
insertAtCursor(textarea, content);
textarea.focus();
});
}
function addMessages(messages, editing, requestTimestamp) {
let main = document.getElementById("jvchat-main");
let hasNewMessages = false;
let init = true;
let toInsert = "";
let newMessagesIds = [];
for (let message of messages) {
let date = message.date;
let id = message.id;
if (init === true && !editing) {
init = false;
let now = new Date();
let delta = now - date;
if (delta > 5 * 60 * 1000 + checkEditedInterval) {
shouldCheckEdited = false;
} else {
shouldCheckEdited = true;
}
}
if (message.blacklisted) {
continue;
}
if (firstMessageId === undefined) {
firstMessageId = id;
firstMessageDate = date;
}
// Attention à 2 choses: le changement d'heure et le fait qu'un message suivant un autre peut avoir un id inférieur au précédent
if (id < firstMessageId && date < firstMessageDate) {
continue;
}
let referenced = Object.prototype.hasOwnProperty.call(lastEditionTime, id);
let edited = message.edited;
if (referenced) {
let [timestamp, edition, deleted] = lastEditionTime[id];
if (deleted) {
continue;
}
if (timestamp >= requestTimestamp || edition === edited) {
continue;
}
}
let newBloc = makeMessage(message);
lastEditionTime[id] = [requestTimestamp, edited, false];
if (referenced) {
let selector = `.jvchat-message[jvchat-id="${id}"]`;
let oldBloc = main.querySelector(selector).closest(".jvchat-bloc-message");
let isDown = isScrollDown();
oldBloc.outerHTML = newBloc;
if (isDown) {
setScrollDown();
}
buildQuoteEvent(id);
let event = new CustomEvent('jvchat:newmessage', { 'detail': { id: id, isEdit: true } });
dispatchEvent(event);
continue;
}
hasNewMessages = true;
if (nbNewMessage === 0 && document.hidden) {
let hrs = document.getElementsByClassName("jvchat-ruler");
let lastHr = hrs[hrs.length - 1];
lastHr.setAttribute("id", "jvchat-ruler-new");
}
toInsert += newBloc;
newMessagesIds.push(id);
nbNewMessage++;
}
if (toInsert !== "") {
let isDown = isScrollDown();
main.insertAdjacentHTML("beforeend", toInsert);
if (isDown) {
setScrollDown();
}
}
if (editing) {
return;
}
if (isScrollDown()) {
let blocMessages = main.getElementsByClassName("jvchat-bloc-message");
let nb = blocMessages.length;
if (nb > 100) {
for (let i = 0; i < nb - 100; i++) {
main.removeChild(blocMessages[0]);
}
setScrollDown();
}
}
if (hasNewMessages) {
if (!turboActivated && !refreshDegraded) {
decreaseUpdateInterval();
}
if (document.hidden) {
setFavicon(nbNewMessage > 99 ? 99 : nbNewMessage);
if (configuration["play_sound"]) {
ringBell.pause();
ringBell.currentTime = 0;
ringBell.play();
}
}
for (let newMessageId of newMessagesIds) {
buildQuoteEvent(newMessageId);
let event = new CustomEvent('jvchat:newmessage', { 'detail': { id: newMessageId, isEdit: false } });
dispatchEvent(event);
}
} else {
if (!turboActivated && !refreshDegraded) {
increaseUpdateInterval();
}
}
}
function submitSondageAnswer(event) {
let target = event.target;
if (!target) {
return;
}
if (target.classList.contains("click-sondage")) {
let reponseNum = parseInt(target.getAttribute("sondage-reponse-num"));
let sondageId = sondageChoices[reponseNum]["sondageId"];
let reponseId = sondageChoices[reponseNum]["responseId"];
/* Legacy JVC
let topicId = urlToFetch["ids"].split("-")[2];
let url = `https://www.jeuxvideo.com/forums/ajax_topic_sondage_vote.php?id_topic=${topicId}&id_sondage_reponse=${reponseId}&id_sondage=${sondageId}&ajax_hash=${freshHash}`;
function onSuccess(res) {
if (res.erreur.length > 0) {
for (let err of res.erreur) {
addAlertbox("danger", err);
}
return;
}
let dom = document.createElement("html");
dom.innerHTML = res["html"];
let sondage = parseSondage(dom);
if (!sondage) {
addAlertbox("warning", "Erreur lors de la récupération du sondage");
return;
}
setSondage(sondage);
}
function onError(err, _) {
addAlertbox("danger", err);
}
function onTimeout(err) {
addAlertbox("warning", err);
}
request("POST", url, onSuccess, onError, onTimeout, undefined, true, 5000, false);
END LEGACY */
// New structure
let topicId = getTopicId();
let payload = freshPayload || getForumPayload();
let surveyAjaxHash = payload?.survey?.ajaxToken;
let url = `https://www.jeuxvideo.com/forums/survey/vote`;
let formData = {};
formData.ajax_hash = surveyAjaxHash;
formData.id_topic = topicId;
formData.id_sondage = sondageId;
formData.id_sondage_reponse = reponseId;
function onSuccess(res) {
if (handleApiResponseError(res, "vote sondage")) return;
let surveyData = res.survey?.data;
if (!surveyData) {
addAlertbox("warning", "Erreur lors de la récupération du sondage");
return;
}
setSondage({
answered: surveyData.hasVoted,
intitule: surveyData.title,
votes: surveyData.totalResponses,
results: surveyData.responses.map(r => ({
response: r.text,
pourcent: r.percentage,
sondageId: surveyData.id,
responseId: r.id
}))
});
}
function onError(err, _) {
addAlertbox("danger", err);
}
function onTimeout(err) {
addAlertbox("warning", err);
}
//NEW END POINT IN FORM DATA
request("POST", url, onSuccess, onError, onTimeout, makeFormData(formData), true, 5000, false);
}
}
function setSondage(sondage) {
let choix = document.getElementById("jvchat-sondage-choix");
if (sondage["answered"]) {
choix.removeEventListener("click", submitSondageAnswer);
choix.classList.remove("notanswered");
} else {
if (!sondageChoices) {
sondageChoices = sondage["results"];
}
choix.addEventListener("click", submitSondageAnswer);
choix.classList.add("notanswered");
}
if (!choix.firstChild) {
document.getElementById("jvchat-sondage-intitule").innerHTML = escapeHtml(sondage["intitule"]);
let results = sondage["results"];
for (let i = 0; i < results.length; i++) {
let res = results[i];
let tr = `