// ==UserScript==
// @name JVChat Premium
// @description Outil de discussion instantanée pour les forums de Jeuxvideo.com
// @author Blaff
// @namespace JVChatPremium
// @license MIT
// @version 0.1.105
// @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 none
// @downloadURL https://update.greasyfork.org/scripts/369381/JVChat%20Premium.user.js
// @updateURL https://update.greasyfork.org/scripts/369381/JVChat%20Premium.meta.js
// ==/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.
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;
}
}
});
*/
/*
TODO:
- Smooth transition on append messages (slide-in plutôt que jump)
- Détection captcha
- Bouton actualiser les messages (+ afficher le delai courrant d'actualisation)
- Notification avec @pseudo
- Blacklist
- Pouvoir voir les anciens messages
- La leftbar ne se rétrécie pas si le titre du topic n'a pas d'espaces
*/
let CSS = ``;
let PANEL = `
Profil
Topic
Forum
Sondage
Configuration
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.
Active l'affichage des niveaux des utilisateurs.
? ms
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).
`;
let freshHash = undefined;
let freshDeletionHash = undefined;
let freshForm = 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 refreshInfosAcceptable = [];
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 refreshInfosTimeoutId = -1;
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,
show_user_levels: 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 (config.hasOwnProperty(key) && configuration.hasOwnProperty(key)) {
configuration[key] = config[key];
}
}
}
function getTimestamp() {
return new Date().getTime();
}
function escape(str, isAttribute) {
str = str.replace(/&/g, "&").replace(//g, ">");
if (isAttribute) {
str = str.replace(/"/g, """).replace(/'/g, "'");
}
return str;
}
function getForm(doc) {
return doc.getElementsByClassName('form-post-message')[0];
}
function getHash(doc) {
let hash = doc.querySelector("#ajax_hash_liste_messages")
if (!hash) {
return undefined;
}
return hash.getAttribute("value");
}
function getDeletionHash(doc) {
let hash = doc.querySelector("#ajax_hash_moderation_forum")
if (!hash) {
return undefined;
}
return hash.getAttribute("value");
}
function getTopicLocked(elem) {
let lock = elem.getElementsByClassName("message-lock-topic")[0];
if (lock === undefined) {
return lock;
}
let reason = lock.getElementsByTagName("span")[0].textContent.trim();
return `Le topic a été verrouillé pour la raison suivante : "${reason}"`;
}
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 parseSondage(elem) {
let blocSondage = elem.getElementsByClassName("bloc-sondage")[0];
if (!blocSondage) {
return null;
}
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 };
}
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 (e) {
alert(message);
}
}
}
return wrapped;
}
function toggleTextarea() {
isReduced = !isReduced;
configuration["default_reduced"] = isReduced;
saveConfig();
let isDown = isScrollDown();
document.getElementById("bloc-formulaire-forum").getElementsByClassName("jv-editor-toolbar")[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) {
let blocPages = document.getElementsByClassName("bloc-liste-num-page")[0];
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;
}
function parseMessage(elem) {
let conteneurs = elem.getElementsByClassName("conteneur-message");
let conteneur = conteneurs[conteneurs.length - 1];
let hasBadge = conteneur.getElementsByClassName("bloc-genesis-pass").length > 0;
let authorElem = conteneur.getElementsByClassName("bloc-pseudo-msg")[0];
let isModerator = authorElem.classList.contains("text-modo");
let isAdmin = authorElem.classList.contains("text-admin");
let author = authorElem.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];
let userLevelElem = conteneur.getElementsByClassName("bloc-user-level text-muted")[0];
let userLevel = userLevelElem ? userLevelElem.textContent.trim() : '';
if (edited !== undefined) {
let msgEdited = edited.textContent.trim();
edited = msgEdited.match(/Message édité le .*? à (.*?) par/i)[1];
}
return {
author: author,
dateString: date,
date: parseDate(date),
avatar: avatar,
edited: edited,
id: id,
content: content,
blacklisted: blacklisted,
hasBadge: hasBadge,
isMod: isModerator,
isAdmin: isAdmin,
level: userLevel
};
}
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) {
let pageActive = elem.getElementsByClassName("page-active")[0];
let page = 1;
if (pageActive !== undefined) {
page = parseInt(pageActive.textContent.trim());
}
return page;
}
function parseTopicInfo(elem) {
let title = elem.querySelector("#bloc-title-forum").textContent.trim();
let connected = parseInt(elem.getElementsByClassName("nb-connect-fofo")[0].textContent.trim());
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(".text-enrichi-forum > blockquote > blockquote"));
for (let togglableQuote of togglableQuotes) {
let toggleButton = document.createElement("div");
toggleButton.classList.add("nested-quote-toggle-box");
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.getElementsByClassName("img-shack");
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) {
let match1 = image.src.match(regex1);
if (match1) {
let [_, identifier] = match1;
if (mosaics.hasOwnProperty(identifier)) {
mosaics[identifier].push(image);
} else {
mosaics[identifier] = [image];
}
continue;
}
let match2 = image.src.match(regex2);
if (match2) {
if (mosaics.hasOwnProperty("@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.getElementsByClassName("img-shack");
for (let image of imagesShack) {
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 clearPage(document) {
let buttons = `
`;
document.head.insertAdjacentHTML("beforeend", CSS);
let previsu = document.getElementById("bloc-formulaire-forum").getElementsByClassName("previsu-editor")[0];
if (previsu) {
previsu.parentElement.removeChild(previsu);
}
let messageTopic = document.getElementById("message_topic");
if (messageTopic) {
messageTopic.classList.add("jvchat-textarea");
messageTopic.setAttribute("placeholder", "Hop hop hop, le message ne va pas s'écrire tout seul !");
messageTopic.insertAdjacentHTML("afterend", buttons);
messageTopic.addEventListener("keydown", tryCatch(postMessageIfEnter));
document.getElementById("jvchat-post").addEventListener("click", tryCatch(postMessage));
document.getElementById("jvchat-enlarge").addEventListener("click", tryCatch(toggleTextarea));
document.getElementById("jvchat-reduce").addEventListener("click", tryCatch(toggleTextarea));
}
document.getElementsByClassName("conteneur-messages-pagi")[0].insertAdjacentHTML("afterbegin", "
`;
return html;
}
function parseDate(string) {
let [date, time] = string.toLowerCase().split("à");
let [day, month, year] = date.trim().split(" ");
let [hour, minute, second] = time.trim().split(":");
let monthIndex = ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"].indexOf(month.trim().toLowerCase());
return new Date(parseInt(year), monthIndex, parseInt(day), parseInt(hour), parseInt(minute), parseInt(second));
}
function addMessages(messages, editing, requestTimestamp) {
let main = document.getElementById("jvchat-main");
let hasNewMessages = false;
let init = true;
let toInsert = "";
let newMessagesIds = [];
let fiveMinutesInMillis = 300000; // 5 minutes
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 > fiveMinutesInMillis + checkEditedInterval) {
shouldCheckEdited = false;
} else {
shouldCheckEdited = true;
}
}
if (message.blacklisted) {
continue;
}
// 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 = lastEditionTime.hasOwnProperty(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);
let isEditable = (new Date() - date <= fiveMinutesInMillis);
if (isEditable) {
newBloc = newBloc.replace(/<\/span>/g, '');
setTimeout(() => {
let msgElements = main.querySelectorAll(`.jvchat-message[jvchat-id="${id}"]`);
msgElements.forEach(msgElement => {
let editButton = msgElement.querySelector('.jvchat-edit');
if (editButton) {
editButton.style.display = 'none';
}
});
}, fiveMinutesInMillis);
} else {
newBloc = newBloc.replace(/<\/span>/g, '');
}
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();
}
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) {
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"];
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);
}
}
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 = escape(sondage["intitule"]);
let results = sondage["results"];
for (let i = 0; i < results.length; i++) {
let res = results[i];
let tr = `
${res["pourcent"]} %
${escape(res["response"])}
`;
choix.insertAdjacentHTML("beforeend", tr);
}
} else {
let trs = choix.getElementsByClassName("result-pourcent");
for (let i = 0; i < trs.length; i++) {
let res = sondage["results"][i];
let tr = trs[i];
tr.getElementsByClassName("pourcent")[0].innerHTML = `${res["pourcent"]} %`;
tr.getElementsByTagName("span")[0].style["width"] = `${res["pourcent"]}%`;
}
}
document.getElementById("jvchat-sondage-votes").innerHTML = `(${sondage["votes"]} votes)`;
}
function setUser(document, user) {
let isConnected = (user.author !== undefined);
if (isConnected) {
if (user.author !== currentUser.author) {
let pseudo = document.getElementById("jvchat-user-pseudo");
pseudo.innerHTML = user.author;
let avatarLink = document.getElementById("jvchat-user-avatar-link");
let notifLink = document.getElementById("jvchat-user-notif-link");
avatarLink.setAttribute("href", `https://www.jeuxvideo.com/profil/${user.author.toLowerCase()}?mode=infos`);
notifLink.setAttribute("href", `https://www.jeuxvideo.com/profil/${user.author.toLowerCase()}?mode=abonnements`);
}
if (user.avatar !== currentUser.avatar) {
let avatar = document.getElementById("jvchat-user-avatar");
avatar.style["background-image"] = `url("${user.avatar}")`;
}
if (user.mp !== currentUser.mp) {
let mp = document.getElementById("jvchat-user-mp");
mp.setAttribute("data-val", user.mp);
if (user.mp > 0) {
mp.classList.add("has-notif");
} else {
mp.classList.remove("has-notif");
}
}
if (user.notif !== currentUser.notif) {
let notif = document.getElementById("jvchat-user-notif");
notif.setAttribute("data-val", user.notif);
if (user.notif > 0) {
notif.classList.add("has-notif");
} else {
notif.classList.remove("has-notif");
}
}
}
if ((userConnected === undefined && isConnected) || (userConnected !== undefined && isConnected !== userConnected)) {
document.getElementById("jvchat-profil").classList.toggle("jvchat-hide");
let isDown = isScrollDown();
document.getElementById("bloc-formulaire-forum").classList.toggle("jvchat-hide");
if (isDown) {
setScrollDown();
}
}
if (userConnected !== undefined) {
if (isConnected && !userConnected) {
addAlertbox("success", "Vous êtes désormais connecté");
} else if (!isConnected && userConnected) {
addAlertbox("warning", "Vous avez été déconnecté");
}
}
userConnected = isConnected;
currentUser = user;
if (configuration["show_user_levels"]) {
toggleShowUserLevels();
}
}
function setTopicTitle(document, topicTitle) {
if (topicTitle !== currentTopicTitle) {
currentTopicTitle = topicTitle;
document.getElementById("jvchat-topic-title").innerHTML = escape(topicTitle);
}
}
function setTopicNbConnected(document, nbConnected) {
let txt = `${nbConnected} connectés`;
if (!(nbConnected > 1)) {
if (nbConnected === undefined) {
txt = "? connectés";
} else {
txt = txt.slice(0, -1);
}
}
document.getElementById("jvchat-topic-nb-connected").innerHTML = txt;
}
function setTopicNbMessages(document, nbMessages) {
let txt = `${nbMessages} messages`;
if (!(nbMessages > 1)) {
if (nbMessages === undefined) {
txt = "? messages";
} else {
txt = txt.slice(0, -1);
}
}
document.getElementById("jvchat-topic-nb-messages").innerHTML = txt;
if (nbMessages !== undefined) {
topicNbMessages = nbMessages;
}
}
function triggerJVChat() {
// TamperMonkey / Chrome bug: https://github.com/Tampermonkey/tampermonkey/issues/705#issuecomment-493895776
if (window) {
if (window.clearTimeout) {
window.clearTimeout = window.clearTimeout.bind(window);
}
if (window.clearInterval) {
window.clearInterval = window.clearInterval.bind(window);
}
if (window.setTimeout) {
window.setTimeout = window.setTimeout.bind(window);
}
if (window.setInterval) {
window.setInterval = window.setInterval.bind(window);
}
window.onbeforeunload = function (event) {
leavingTopic = true;
}
}
freshHash = getHash(document);
freshDeletionHash = getDeletionHash(document);
freshForm = getForm(document);
favicon = makeFavicon();
let topicUrl = document.URL;
let topic = parseTopicInfo(document);
let user = parseUserInfo(document);
let sondage = parseSondage(document);
urlToFetch = parseURL(topicUrl);
urlToFetch.page = 1;
urlToFetch.anchor = "";
urlToRefreshInfos = parseURL(topicUrl);
urlToRefreshInfos.page = 1;
urlToRefreshInfos.anchor = "";
urlToCheckEdited = parseURL(topicUrl);
urlToCheckEdited.page = 1;
urlToCheckEdited.anchor = "";
configuration = defaultConfig();
loadConfig();
ringBell = new Audio(configuration["sound"]);
clearPage(document);
setUser(document, user);
setTopicTitle(document, topic.title);
setTopicNbMessages(document, undefined);
setTopicNbConnected(document, topic.connected);
if (sondage) {
document.getElementById("jvchat-sondage").classList.remove("jvchat-hide")
setSondage(sondage);
}
document.getElementById("jvchat-topic-title").setAttribute("href", buildURL(urlToFetch));
let forum = getForum(document);
let forumSide = document.getElementById("jvchat-forum-title");
forumSide.setAttribute("href", forum.href);
forumSide.innerHTML = escape(forum.title);
let defaultReduced = configuration["default_reduced"];
let messageTopic = document.getElementById("message_topic");
if (messageTopic && (defaultReduced === false || (messageTopic.value !== ""))) {
toggleTextarea();
}
let event = new CustomEvent('jvchat:activation');
dispatchEvent(event);
let page = topic.lastPage > 1 ? topic.lastPage - 1 : topic.lastPage;
updateMessages(page, true);
setInterval(tryCatch(checkEdited), checkEditedInterval);
}
function refreshNoLongerDegraded() {
removeDegradedRefreshWarning();
timeoutedDates = [];
refreshDegraded = false;
refreshDegradedTimeoutId = -1;
}
function scheduleDegradedRefreshWarning() {
if (!refreshDegraded) {
let now = getTimestamp();
let lookupDate = now - 60 * 1000;
let nbClean = 0;
for (let date of timeoutedDates) {
if (date < lookupDate) {
nbClean++;
} else {
break;
}
}
for (i = 0; i < nbClean; i++) {
timeoutedDates.shift();
}
timeoutedDates.push(now);
if (timeoutedDates.length >= 3) {
refreshDegraded = true;
updateIntervalIdx = 2;
setDegradedRefreshWarning();
} else {
return;
}
}
if (refreshDegradedTimeoutId !== -1) {
clearTimeout(refreshDegradedTimeoutId);
}
refreshDegradedTimeoutId = setTimeout(tryCatch(refreshNoLongerDegraded), 30000);
}
function updateMessages(page, goToLast) {
if (postingMessage && turboActivated) {
// Postpone message fetching, posting the message is priorized
fetchingMessages = false;
currentTimeoutId = setTimeout(tryCatch(function postponedUpdate() {
updateMessages(page, goToLast);
}), 100);
return;
}
let timestamp = getTimestamp();
function scheduleNextUpdate(interval, p, goLast) {
fetchingMessages = false;
currentTimeoutId = setTimeout(tryCatch(function scheduledUpdate() {
updateMessages(p, goLast);
}), interval);
};
function onSuccess(res) {
let parsed = parsePage(res, timestamp);
let lastPage = parsed.lastPage;
let currPage = parsed.page;
let interval = turboActivated ? configuration["turbo_delay"] : updateIntervals[updateIntervalIdx] * 1000;
if (page < lastPage && goToLast) {
updateMessages(page + 1, true);
} else if (currPage < page || parsed.nbMessagesPage === 0) { // Bug des messages supprimés
scheduleNextUpdate(interval, page - 1, false);
} else if (page > lastPage) {
updateMessages(lastPage, true);
} else {
scheduleNextUpdate(interval, page, true);
}
}
function onError(err, code) {
if (code === 0) {
scheduleDegradedRefreshWarning();
scheduleNextUpdate(turboActivated ? configuration["turbo_delay"] : 10000, page, true);
return
}
if (!isError) {
isError = true;
isLocked = false;
isAlert = false;
setFixedAlert("danger", err, true);
}
scheduleNextUpdate(turboActivated ? configuration["turbo_delay"] : 60000, page, true);
}
function onTimeout(_) {
scheduleDegradedRefreshWarning();
scheduleNextUpdate(turboActivated ? configuration["turbo_delay"] : 5000, page, true);
}
let timeout = 10000;;
if (turboActivated) {
timeout = 5000;
}
fetchingMessages = true;
currentFetchedPage = page;
urlToFetch.page = page;
urlToFetch.domain = "https://www.jeuxvideo.com/forums";
let urlLastPage = buildURL(urlToFetch);
request("GET", urlLastPage, onSuccess, onError, onTimeout, undefined, false, timeout, turboActivated);
}
function checkEdited() {
if (!shouldCheckEdited || currentFetchedPage === 1 || isError) {
return;
}
urlToCheckEdited.page = currentFetchedPage - 1;
let urlPrevLastPage = buildURL(urlToCheckEdited);
let timestamp = getTimestamp();
function onSuccess(res) {
let newMessages = [];
let edited = res.getElementsByClassName("info-edition-msg");
for (let msg of edited) {
let bloc = msg.closest(".bloc-message-forum");
newMessages.push(parseMessage(bloc));
}
addMessages(newMessages, true, timestamp, false);
findDeletedMessages(res, timestamp);
}
function onError(_, _) { }
function onTimeout(_) { }
request("GET", urlPrevLastPage, onSuccess, onError, onTimeout, undefined, false, 20000);
}
function checkDeleted(id) {
let url = `https://www.jeuxvideo.com/jvchat/forums/message/${id}`;
let requestTimestamp = getTimestamp();
function onSuccess(_) {
// Le message existe, il a disparu de la page car un message plus ancien a été supprimé
}
function onError(_, status) {
let [timestamp, edition, deleted] = lastEditionTime[id];
if (status === 410 && !deleted) {
if (timestamp >= requestTimestamp) {
return;
}
let msg = document.querySelector(`.jvchat-message[jvchat-id="${id}"]`);
if (msg) {
msg.classList.add("jvchat-message-deleted");
lastEditionTime[id] = [timestamp, edition, true];
}
}
}
function onTimeout(_) { }
request("GET", url, onSuccess, onError, onTimeout, undefined, false, 20000, false);
}
function parseAlerts(res) {
let alerts = [];
let alertsDiv = res.getElementsByClassName("alert");
for (let a of alertsDiv) {
let closable = a.getElementsByClassName("btn-close")[0] !== undefined;
let type = "danger";
if (a.classList.contains("alert-warning")) {
type = "warning";
} else if (a.classList.contains("alert-success")) {
type = "success";
}
for (let p of a.getElementsByTagName("p")) {
let message = p.textContent.trim();
alerts.push({ type: type, message: message, closable: closable });
}
}
return alerts;
}
function increaseUpdateInterval() {
if (updateIntervalIdx < updateIntervalMax) {
updateIntervalIdx++;
}
}
function decreaseUpdateInterval() {
updateIntervalIdx = transisitions[updateIntervalIdx];
}
function parsePage(res, requestTimestamp) {
let error = getTopicError(res);
if (error !== undefined) {
if (!isError) {
updateIntervalIdx = updateIntervalMax;
isError = true;
isLocked = false;
isAlert = false;
setFixedAlert("danger", error, true);
}
return { lastPage: undefined, page: undefined, alert: true, nbMessagesPage: 0 }
}
if (isError) {
isError = false;
updateIntervalIdx = 2;
removeFixedAlert("Le topic ne retourne plus d'erreur", true);
}
let form = getForm(res);
if (form !== undefined) {
freshForm = form;
}
let hash = getHash(res);
if (hash !== undefined) {
freshHash = hash;
}
let deletionHash = getDeletionHash(res);
if (deletionHash !== undefined) {
freshDeletionHash = deletionHash;
}
let messages = getMessages(res);
addMessages(messages, false, requestTimestamp, false);
let user = parseUserInfo(res);
setUser(document, user);
let topic = parseTopicInfo(res);
findDeletedMessages(res, requestTimestamp);
let nbMessages = (topic.lastPage - 1) * 20;
if (topic.page === topic.lastPage) {
nbMessages += messages.length;
}
if (topic.page === topic.lastPage || nbMessages > topicNbMessages) {
setTopicNbMessages(document, nbMessages);
}
setTopicNbConnected(document, topic.connected);
let locked = getTopicLocked(res);
let isLocked_ = (locked !== undefined);
if (isLocked_ && !isLocked) {
updateIntervalIdx = updateIntervalMax;
setFixedAlert("warning", locked, true);
isAlert = false;
} else if (!isLocked_ && isLocked) {
updateIntervalIdx = 0;
removeFixedAlert("Le topic a été dévérouillé", true);
}
isLocked = isLocked_;
let alerts = parseAlerts(res);
let unclosableAlert = undefined;
for (let alert of alerts) {
if (alert.closable) {
addAlertbox(alert.type, alert.message);
} else {
unclosableAlert = alert;
}
}
let isAlert_ = (unclosableAlert !== undefined);
if (!isLocked) {
if (isAlert_ && !isAlert) {
setFixedAlert("warning", unclosableAlert.message, false);
} else if (!isAlert_ && isAlert) {
removeFixedAlert(undefined, false);
}
isAlert = isAlert_;
}
let sondage = parseSondage(res);
if (sondage) {
setSondage(sondage);
}
return {
page: topic.page, lastPage: topic.lastPage,
nbMessagesPage: messages.length, alert: isLocked_ || (alerts.length > 0)
};
}
function addAlertbox(type, message) {
// type: success / warning / danger
let alert = `
${escape(message)}
`;
document.getElementById("jvchat-fixed-alert").insertAdjacentHTML("afterend", alert);
}
function setFixedAlert(type, message, changeFavicon) {
if (changeFavicon) {
setFavicon("⨯");
}
if (configuration["hide_alerts"]) {
addAlertbox(type, message);
return
}
document.getElementById("jvchat-fixed-alert").getElementsByClassName("alert-row")[0].innerHTML = escape(message);
document.getElementById("jvchat-fixed-alert").setAttribute("class", `alert alert-${type}`);
}
function removeFixedAlert(message, changeFavicon) {
document.getElementById("jvchat-fixed-alert").classList.add("jvchat-hide");
if (message !== undefined) {
addAlertbox("success", message);
}
if (changeFavicon) {
if (document.hidden && nbNewMessage > 0) {
setFavicon(nbNewMessage > 99 ? 99 : nbNewMessage);
} else {
setFavicon("");
}
}
}
function setTurboWarning() {
let message = "Le mode turbo est resté activé pendant plus d'une minute, les requêtes pour récupérer les nouveaux messages risquent d'être ralenties";
document.getElementById("jvchat-turbo-warning").getElementsByClassName("alert-row")[0].innerHTML = message;
document.getElementById("jvchat-turbo-warning").setAttribute("class", "alert alert-warning");
}
function removeTurboWarning() {
document.getElementById("jvchat-turbo-warning").classList.add("jvchat-hide");
}
function setDegradedRefreshWarning() {
let message = "Les serveurs de JVC semblent surchargés, l'actualisation des nouveaux messages peut s'en voir dégradée";
document.getElementById("jvchat-degraded-refresh-warning").getElementsByClassName("alert-row")[0].innerHTML = message;
document.getElementById("jvchat-degraded-refresh-warning").setAttribute("class", "alert alert-warning");
}
function removeDegradedRefreshWarning() {
document.getElementById("jvchat-degraded-refresh-warning").classList.add("jvchat-hide");
}
function makeJVChatButton() {
let cls = 'btn-jvchat';
let text = 'JVChat';
let btn = ``;
return btn;
}
function addJVChatButton(document) {
let css = ``
document.head.insertAdjacentHTML("beforeend", css);
let blocPreRight = document.getElementsByClassName("bloc-pre-right");
let jvchatButton = makeJVChatButton();
for (let bloc of blocPreRight) {
bloc.insertAdjacentHTML('afterbegin', jvchatButton);
}
}
function bindJVChatButton(document) {
let buttons = document.getElementsByClassName('btn-jvchat');
for (let btn of buttons) {
btn.addEventListener('click', tryCatch(triggerJVChat));
}
}
function request(mode, url, callbackSuccess, callbackError, callbackTimeout, data, json, timeout, nocache) {
json = !!json;
let xhr = new XMLHttpRequest();
xhr.timeout = timeout;
xhr.ontimeout = tryCatch(function xhrOnTimeout() {
callbackTimeout(`La délai d'attente de la requête a expiré`);
});
xhr.onerror = tryCatch(function xhrOnError() {
callbackError(`La requête a échoué (${xhr.status}): ${xhr.statusText}`, xhr.status);
});
xhr.onabort = tryCatch(function xhrOnAbort() {
if (!leavingTopic) {
callbackTimeout(`La requête a été interrompue pour une raison inconnue`);
}
});
xhr.onload = tryCatch(function xhrOnLoad() {
if (xhr.status !== 200) {
callbackError(`La requête a retourné une erreur (${xhr.status}): ${xhr.statusText}`, xhr.status);
return;
}
callbackSuccess(xhr.response, xhr.response.URL);
});
if (data === undefined) {
data = null;
}
if (json) {
xhr.responseType = "json";
} else {
xhr.responseType = "document";
}
xhr.open(mode, url, true);
if (nocache) {
xhr.setRequestHeader("Cache-Control", "no-cache, no-store, must-revalidate");
}
xhr.send(data);
};
// On copie/colle le code de TopicLive et on se sent développeur :)
function makeFavicon() {
let canvas = document.createElement("canvas");
canvas.width = 16;
canvas.height = 16;
let context = canvas.getContext('2d');
let image = new Image();
image.onload = function () {
faviconLoaded = true;
setFavicon(faviconTextWhenLoaded);
}
image.src = 'data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAADyZgAA72IAAO9iAADvYQAA7mEAAO9iAADvYgAAAkD9AABA/wAAPv8AAD7/AAA+/wAAPv8AAD7/AAAAAAAAAAAA72IAAO5hAADtYAAA72IABO9iAATtZAYA62kRAP+KAAAAO/8AADv/AAA7/wAAO/8AADz/AAA8/wAAAAAAAAAAAO9iAAHuYQA57mEAie5hAK/uYQCv7mIChexpDzBQP4gAADv/DAA8/3sAPP+XADz/hAA8/xUAPP8AAD7/AAAAAADuYQB77mEA6e5hAP/uYQD/7mEA/+5kBf/laxjh32wVRwA7/zwAPP/1ADz//wA8//0APP9UADz/AAA8/wAAAAAA7mEAoO5hAP3uYQD/7mEA6u5hAPTsZwv/3msd/8RhHMgVP+GdADz//wA8//8APP//ADz/oQA9/wIAPf8AAAAAAO5hAA7uYQCI7mEAke5hACvuYwNb6WoT8NZpHv+hVC37Fz7a9QA8//8APP//ADz//wA8/+AAPP8kADz/AAA+/wDuYQAA7mIABe5iAAXtZAYA7W0WEeNsG9LLZBv/c0pd/wQ8+P8APP//ADz/8AA8//4APP/+ADz/ZQA8/wAAO/8A7mEAAO5hAADuYQAA5G0cAOluGxHaax/Pulwd/z5Cov8APP//ADz/9gA8/4oAPP/tADz//wA8/7IAPf8HADz/AAAAAAAAAAAAAAAAAN1sHwDibh8R0Wcdz5pSNf8UPd7/ADz//wA8/9cAPP8pADz/wwA8//8APP/qADz/MAA8/wAAAAAAAAAAAAAAAADTaCAA2WwhEsVhGc9pSWv/ATz9/wA8//8APP+cADr/AAA8/4MAPP//ADz//wA8/3YAPP8AAAAAAAAAAAAAAAAAx2MeANFnGxKvVyDPNUGw/wA8//8APP/7ADz/VgA8/wAAPP9AADz/8wA8//8APP/BADz/DwAAAAAAAAAAAAAAALhbGwDUXwAShU1H0BA95v8APP//ADz/3QA8/x8APP8AADz/EQA8/8sAPP//ADz/8AA8/0cAAAAAAAAAAAAAAACkUBoAwVIAEkdDlNMBPP3/ADz//wA8/6QAPf8CAD3/AAA7/wAAPP+LADz//wA8//8APP+hAAAAAAAAAAAAAAAAbEVcAG9FVQ0YPtqUADz/uAA8/7YBPP5MADz/AAA8/wAAPP8AADz/OgA8/7MAPP+3ADz/oQAAAAAAAAAAAAAAAAA5/wAAOP8AADv/BQA8/wYAPP4FBzzzAQE8/QAAPf8AADz/AAA9/wEAPP8FADz/BgA8/wYAAAAAAAAAAAAAAAAnP8QAJT/HAAY89gAAPP8AADz/AAM8+QABPP0AAAAAAAA8/wAAPP8AADz/AAA8/wAAPP8AAAMAAAADAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAADgAAAA4AAAAOAAAADgAAAA4AAAAOAAAADgAAAA4CAAAA==';
return { canvas: canvas, context: context, image: image };
};
function setFavicon(txt) {
if (!faviconLoaded) {
faviconTextWhenLoaded = txt;
return;
}
let fav = document.getElementById("jvchat-favicon");
if (fav) {
fav.parentElement.removeChild(fav);
}
favicon.context.clearRect(0, 0, favicon.canvas.width, favicon.canvas.height);
favicon.context.drawImage(favicon.image, 0, 0);
if (txt !== '') {
let context = favicon.context;
context.fillStyle = 'DodgerBlue';
context.fillRect(0, 0, context.measureText(txt).width + 3, 11);
context.fillStyle = 'white';
context.font = 'bold 10px Verdana';
context.textBaseline = 'bottom';
context.fillText(txt, 1, 11);
}
let url = favicon.canvas.toDataURL('image/png');
let icon = ``;
document.head.insertAdjacentHTML("beforeend", icon);
}
function reverseMessage(node, isInit, isUl) {
let quote = "";
let prevIsP = false;
let startsWithSpoil = false;
for (let child of node.childNodes) {
let name = child.nodeName;
switch (name) {
case "P": {
quote += reverseMessage(child) + "\n\n";
break;
}
case "STRONG": {
quote += "'''" + reverseMessage(child) + "'''";
break;
}
case "U": {
quote += "" + reverseMessage(child) + "";
break;
}
case "S": {
quote += "" + reverseMessage(child) + "";
break;
}
case "EM": {
quote += "''" + reverseMessage(child) + "''";
break;
}
case "BR": {
quote += "\n";
break;
}
case "UL": {
quote += reverseMessage(child, false, true) + "\n\n";
break;
}
case "OL": {
quote += reverseMessage(child, false, false) + "\n\n";
break;
}
case "LI": {
if (isUl === true) {
quote += "* " + reverseMessage(child) + "\n";
} else {
quote += "# " + reverseMessage(child) + "\n";
}
break;
}
case "DIV": {
let classList = child.classList;
if (classList.contains("bloc-spoil-jv")) {
if (quote === "") {
startsWithSpoil = true;
}
quote += "" + reverseMessage(child) + "\n\n"
} else if (classList.contains("contenu-spoil")) {
quote += reverseMessage(child);
}
break;
}
case "SPAN": {
let classList = child.classList;
if (classList.contains("bloc-spoil-jv")) {
quote += "" + reverseMessage(child) + "";
} else if (classList.contains("contenu-spoil")) {
quote += reverseMessage(child);
}
break;
}
case "LABEL": {
break;
}
case "INPUT": {
break;
}
case "IMG": {
quote += child.alt;
break;
}
case "A": {
if (child.href) {
quote += child.href;
} else {
quote += reverseMessage(child);
}
break;
}
case "PRE": {
quote += reverseMessage(child) + "\n\n";
break;
}
case "CODE": {
quote += "" + 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 reverseQuote(blocMessage) {
let author = blocMessage.getElementsByClassName("jvchat-author")[0].textContent.trim();
let date = blocMessage.getElementsByClassName("jvchat-date")[0].getAttribute("to-quote");
let header = `> Le ${date} ${author} a écrit :\n`;
let quoted = reverseMessage(blocMessage.getElementsByClassName("txt-msg")[0], true);
return header + quoted + '\n\n';
}
function insertAtCursor(input, textToInsert) {
const value = input.value;
const start = input.selectionStart;
const end = input.selectionEnd;
input.value = value.slice(0, start) + textToInsert + value.slice(end);
input.selectionStart = input.selectionEnd = start + textToInsert.length;
}
function dontScrollOnExpand(event) {
let target = event.target;
if (!target) {
return;
}
let classes = target.classList;
if (classes.contains("nested-quote-toggle-box")) {
let isDown = isScrollDown();
let blockQuote = target.closest(".blockquote-jv");
let visible = blockQuote.getAttribute("data-visible");
let value = visible === "1" ? "" : "1";
blockQuote.setAttribute('data-visible', value);
if (isDown) {
setScrollDown();
}
} else if (classes.contains("txt-spoil") || classes.contains("aff-spoil") || classes.contains("masq-spoil")) {
event.preventDefault();
let check = target.closest(".bloc-spoil-jv").getElementsByClassName("open-spoil")[0];
let isDown = isScrollDown();
check.checked = !check.checked;
if (isDown) {
setScrollDown();
}
} else if (classes.contains("jvchat-quote")) {
let bloc = target.closest(".jvchat-message");
let quote = reverseQuote(bloc);
let textarea = document.getElementById("message_topic");
if (isReduced) {
toggleTextarea();
}
insertAtCursor(textarea, quote);
textarea.focus();
} else if (classes.contains("jvchat-edit")) {
let bloc = target.closest(".jvchat-message");
requestEdit(bloc);
} else if (classes.contains("jvchat-delete")) {
let bloc = target.closest(".jvchat-message");
event.stopPropagation();
requestDelete(bloc);
} else if (classes.contains("jvchat-edition-check")) {
let bloc = target.closest(".jvchat-message");
editMessage(bloc);
} else if (classes.contains("jvchat-edition-cancel")) {
let bloc = target.closest(".jvchat-message");
let isDown = isScrollDown();
bloc.getElementsByClassName("jvchat-content")[0].classList.remove("jvchat-hide");
bloc.getElementsByClassName("jvchat-edition")[0].classList.add("jvchat-hide");
if (isDown) {
setScrollDown();
}
}
}
function isScrollDown() {
let element = document.getElementById("jvchat-main");
return element.clientHeight + Math.floor(element.scrollTop) >= element.scrollHeight - 1;
}
function setScrollDown() {
let element = document.getElementById("jvchat-main");
element.scrollTop = element.scrollHeight + 10000;
}
function main() {
addJVChatButton(document);
bindJVChatButton(document);
}
main();