// ==UserScript==
// @name Tendances Live
// @description Offre plusieurs setups pour l'affichage des topics, permet entre autres de mettre en avant les topics les plus up | Peut rafraîchir la liste des topics | Système Start & Stop | Peut trier la liste par taux de popularité | Peut rafraîchir le compteur de connecté
// @author TendancesLive
// @match http://www.jeuxvideo.com/forums/0-*
// @run-at document-end
// @version 2.6
// @grant none
// @noframes
// ==/UserScript==
/////////////////// OPTIONS ///////////////////////////////////////////////////////
// La vitesse de rafraichissement de la page
// 1 = LENTE, refresh TOUTES LES 10 SECS
// 2 = RAPIDE, refresh TOUTES LES 5 SECS
var VITESSE_F5 = 1;
//// COULEURS TOPICS /////////////////////////////////////////////////////////////
var colorLight = 'rgba(253, 200, 61, 0.2)'; //rgba(r, g, b, opacity) couleur d'un topic devenant populaire
var colorMedium = 'rgba(253, 162, 61, 0.3)'; //rgba(r, g, b, opacity) couleur d'un topic populaire
var colorHeavy = 'rgba(253, 139, 61, 0.4)'; //rgba(r, g, b, opacity) couleur d'un topic très populaire
/////////////////// FIN DES OPTIONS ////////////////////////////////////////////////
// Nombre de secondes avant que les topics en tendance soient mis à jour (qu'une nouvelle page soit téléchargée)
// Ne modifier que si votre page s'affiche en plus de 3 secondes
// Vitesse d'affichage habituelle entre 0 et 400 ms
var timerRefreshTrend = 3;
// Nombre de secondes avant que la requête envoyée pour avoir la page rafraichie soit abandonnée
var connexionTimeOut = timerRefreshTrend;
// NE PAS MODIFIER PLUS BAS ////////////////////////////////////////
if (VITESSE_F5 < 2) {
var timerRefreshPage = 5;
}
else {
var timerRefreshPage = 10;
}
// Met à jour la direction du tri de la page en fonction de la direction de la flèche de tri
var direction = 0;
function updateSortDirection(tab, tabId) {
if (tab.classList.contains('active')) {
if (tab.classList.contains('down')) {
tab.classList.remove('down');
tab.classList.add('up');
direction = 1;
}
else {
direction = 0;
tab.classList.remove('up');
tab.classList.add('down');
}
}
else {
if (tab.classList.contains('down')) {
direction = 0;
}
else {
direction = 1;
}
}
}
// Généralement plus rapide que indexOf() en ce moment
function inArray(needle, haystack) {
var length = haystack.length;
for (var i = 0; i < length; i++) {
if (haystack[i] == needle) return true;
}
return false;
}
// Flags
var areTrendDsp = false;
var areTrendSort = false;
var areTopicsRefr = false;
var areTopicsSort = false;
var areTopicsCol = true;
var isActiveTabRefr = false;
// si la tab active est la 3 || 4, trier les topics en fonction des tendances, sinon mettre l'ordre par défaut
var isActiveTabSort = false;
// si la tab active est la 2 || 3 || 4, colorer les topics en fonction des tendances, sinon mettre la couleur par défaut
var areTopicsColorsMod = false;
var isHoverButton = false;
var isHoverCursor = false;
function States(info) {
var inArr = inArray;
return {
setAreTrendDsp: function(info) { areTrendDsp = inArr(info, [3]); },
setAreTrendSort: function(info) { areTrendSort = inArr(info, [3]); },
setAreTopicsRefr: function(info) { areTopicsRefr = info; },
setAreTopicsSort: function(info) { areTopicsSort = info; },
setIsActiveTabRefr: function(info) { isActiveTabRefr = inArr(info, [1, 2, 3]); },
setIsActiveTabSort: function(info) { isActiveTabSort = inArr(info, [2, 3]); },
setAreTopicsColorsMod: function(info) { areTopicsColorsMod = inArr(info, [1, 2, 3]); },
setAreTopicsCol: function(info) { areTopicsCol = info; },
setIsHoverButton: function(info) { isHoverButton = info; },
setIsHoverCursor: function(info) { isHoverCursor = info; },
setUpdDirection: function(info) { // *lancé avant de rafraichir la classe*
if (inArr(info, [2, 3])) { updateSortDirection(this, info); }
}
};
}
function reverseRun(array, lo, hi) {
hi--;
while (lo < hi) {
var t = array[lo];
array[lo++] = array[hi];
array[hi--] = t;
}
}
function makeAscendingRun(array, lo, hi, compare) {
var runHi = lo + 1;
if (runHi === hi) {
return 1;
}
if (compare(array[runHi++][1].dataset.poids, array[lo][1].dataset.poids) < 0) {
while (runHi < hi && compare(array[runHi][1].dataset.poids, array[runHi - 1][1].dataset.poids) < 0) {
runHi++;
}
reverseRun(array, lo, runHi);
}
else {
while (runHi < hi && compare(array[runHi][1].dataset.poids, array[runHi - 1][1].dataset.poids) >= 0) {
runHi++;
}
}
return runHi - lo;
}
function binaryInsertionSort(array, lo, hi, compare) {
var start = 1;
for (; start < hi; start++) {
var pivot = array[start];
var left = lo;
var right = start;
while (left < right) {
var mid = left + right >>> 1;
if (compare(pivot[1].dataset.poids, array[mid][1].dataset.poids) < 0) {
right = mid;
}
else {
left = mid + 1;
}
}
var n = start - left;
switch (n) {
case 3:
array[left + 3] = array[left + 2];
case 2:
array[left + 2] = array[left + 1];
case 1:
array[left + 1] = array[left];
break;
default:
while (n > 0) {
array[left + n] = array[left + n - 1];
n--;
}
}
array[left] = pivot;
}
}
// tri les topics par poids
function sortByWeight(array) {
var length = array.length;
var compare = direction ? ((x, y) => x - y) : ((x, y) => y - x);
var lo = 0;
var hi = length;
runLength = makeAscendingRun(array, lo, hi, compare);
binaryInsertionSort(array, lo, hi, compare);
}
var topicsTrend = [];
// Refresh le topic de la page rafraichie envoyée (nb message, poids, titre, heure dernier message)
function refreshTopic(topic) {
var flag = 0;
var len = topicsTrend.length;
for (var i = 0; i < len; i++) {
if (topicsTrend[i][0] == topic[0]) {
topic[1] = topicsTrend[i][1];
flag = 1;
break;
}
}
if (flag === 0) {
topic[1].dataset.poids = 1;
}
return topic;
}
// Elements contenant les infos de chaque topics, [0] est l'id, [1] est le node du topic (
)
function ElementListe(id, element) { // Topic
return new Array(id, element);
}
var pageLen = 0;
// recupère les topics sur la page en paramètre (page actuelle ou refresh)
function getTopics(page, trendCall) {
var el = ElementListe;
var refrTopic = refreshTopic;
var inArr = inArray;
var liste = page.getElementsByClassName('topic-list topic-list-admin')[0].cloneNode(true);
var elements = liste.getElementsByTagName('li');
var eleLen = elements.length;
var topics = [];
for (var i = 0; i < eleLen; i++) {
if (!inArr(elements[i].className, ['topic-head', 'ads-middle'])) { // filtre par class name
topics.push(elements[i]);
}
}
var length = topics.length;
pageLen = length; // assigne la taille de la page à une variable globale pour l'utiliser lors de l'affichage
var topicsCurrentPage = [];
var epingles = [];
for (var x = 0; x < length; x++) {
if (topics[x].getElementsByClassName('topic-img')[0].alt == "Topic épinglé") {
// récupère les éléments sur la page actualisée - Met l'id des topics épinglés à 0 car obsolètes ici
epingles.push(el(0, topics[x]));
}
else {
if (!trendCall) {
if (areTrendDsp && areTopicsRefr) {
for (var y = 0; y < topicsTrend.length; y++) {
topicsCurrentPage.push(topicsTrend[y]);
}
break;
}
// récupère les éléments sur la page actualisée et rafraîchit élément(titre, nb messages, poids)
topicsCurrentPage.push(refrTopic(el(topics[x].dataset.id, topics[x])));
}
else { topicsCurrentPage.push(el(topics[x].dataset.id, topics[x])); }
}
}
return [
epingles,
topicsCurrentPage
];
}
// augmente - diminue le poids - met à jour des topics dans la liste des tendances et supprime les topics avec un poids de 0
function processTrend(topicsArray) {
var el = ElementListe;
var topicsTrendTemp = topicsTrend.slice();
var topicsLength = topicsArray.length;
var trendLength = topicsTrend.length;
// add 1 au poids des topics en tendance sur la première page et ajoute les nouveaux topics en tendance
for (var x = 0; x < topicsLength; x++) {
var flag = 0;
if (trendLength > 0) {
for (var y = 0; y < trendLength; y++) {
// si le topic actualisé courant a été trouvé dans la liste des tendances
if (topicsTrendTemp[y][0] == topicsArray[x][0]) {
flag = 1;
var tmp = topicsArray[x][1].cloneNode(true);
// add + 1 au poids des topics en tendance sur la première page actualisée
tmp.dataset.poids = parseInt(topicsTrendTemp[y][1].dataset.poids) + 1;
// remplace le topic par le topic de la liste mis à jour (couleur img et nombre de messages et poids)
topicsTrendTemp[y][1] = tmp;
break;
}
}
}
if (flag === 0) { // si le topic actualisé courant n'a pas été trouvé dans la liste des tendances
var topic = el(topicsArray[x][0], topicsArray[x][1].cloneNode(true));
topic[1].dataset.poids = 1;
topicsTrendTemp.push(topic); // Ajoute les nouveaux topics en tendance
}
}
trendLength = topicsTrendTemp.length;
if (trendLength > 0) {
// remove 1 au poids des topics en tendance pas sur la première page et supprime les anciens topics en tendance
for (var x = 0; x < trendLength; x++) {
flag = 0;
if (topicsTrendTemp[x][1].dataset.poids == 0) {
// supprimé des tendances si trop vieux
topicsTrendTemp.splice(x, 1);
x--;
trendLength--;
}
else {
for (var y = 0; y < topicsLength; y++) {
// si le Trend topic courant a été trouvé dans la liste refresh
if (topicsTrendTemp[x][0] == topicsArray[y][0]) {
flag = 1; // trouvé
break;
}
}
if (flag === 0) { // si le Trend topic courant n'a pas été trouvé dans la liste refresh
if (topicsTrendTemp[x][1].dataset.poids > 0) {
// remove -1 au poids du Trend topic courant
topicsTrendTemp[x][1].dataset.poids = parseInt(topicsTrendTemp[x][1].dataset.poids) - 1;
}
}
}
}
}
return topicsTrendTemp;
}
// dl la page refresh et lance la mise à jour de la liste des tendances
function updateTopicsTrend(page) {
var topicsArray = getTopics(page, true)[1];
topicsTrend = processTrend(topicsArray);
}
// Met à jour le compteur de connectés
function updateNbCo(page) {
var fragment = document.createDocumentFragment();
fragment.appendChild(page.getElementsByClassName('nb-connect-fofo')[0].cloneNode(true));
var element = document.getElementsByClassName('nb-connect-fofo')[0];
element.parentNode.replaceChild(fragment, element);
}
function setCssTopics(topics) {
var len = topics.length;
for (var i = 0; i < len; i++) {
if (!areTopicsCol) {
topics[i][1].classList.remove('light', 'medium', 'heavy', 'basic');
}
else {
if (areTopicsColorsMod) {
topics[i][1].classList.remove('light', 'medium', 'heavy', 'basic');
if (parseInt(topics[i][1].dataset.poids) > 24) {
topics[i][1].classList.add('heavy');
}
else if (parseInt(topics[i][1].dataset.poids) > 16) {
topics[i][1].classList.add('medium');
}
else if (parseInt(topics[i][1].dataset.poids) > 10) {
topics[i][1].classList.add('light');
}
else {
// met tous les background-color de la même couleur par défaut
// - la couleur est prise sur le background-color du deuxième topic de la liste
topics[i][1].classList.add('basic');
}
}
else {
topics[i][1].classList.remove('light', 'medium', 'heavy', 'basic');
}
}
}
}
function jvCake(span)
{
var base16 = '0A12B34C56D78E9F', lien = '', s = span.className.split(' ')[1];
for (var i = 0; i < s.length; i += 2) {
lien += String.fromCharCode(base16.indexOf(s.charAt(i)) * 16 + base16.indexOf(s.charAt(i + 1)));
}
span.innerHTML = "" + span.innerText + "";
}
var hoverCursor = 0;
var topicHead = document.getElementsByClassName('topic-list topic-list-admin')[0].getElementsByTagName("li")[0];
// actualise l'affichage des topics
function displayTopicsCurrentPage(page) {
if (!isActiveTabRefr) {
return;
}
var topicsArray = [];
// Obtention de la liste des topics
if (page == null) {
topicsArray = getTopics(document, false);
}
else {
// met à jour compteur de connecté quand on affiche une liste rafraichir
updateNbCo(page);
topicsArray = getTopics(page, false);
}
// on sépare les topics épinglés des topics normaux
var epingles = topicsArray[0];
var topicsCurrentPage = topicsArray[1];
// si l'onglet tri et affiche seulement la liste des tendances
// ou que
// la liste n'est pas bloquée et que l'onglet courant est trié
// Trier la liste à afficher par poids en tendance
if (((areTrendDsp && areTrendSort) && areTopicsSort) ||
(areTopicsSort && isActiveTabSort)) {
sortByWeight(topicsCurrentPage);
}
// Applique la couleur sur les topics
setCssTopics(topicsCurrentPage);
var ul = document.createElement('ul');
ul.classList.add('topic-list', 'topic-list-admin');
ul.appendChild(topicHead);
// copie les épingles en premier dans la liste qui va être affichée
var lengthPinned = epingles.length;
for (var x = 0; x < lengthPinned; x++) {
jvCake(epingles[x][1].getElementsByClassName('topic-date')[0].getElementsByTagName('span')[0]);
ul.appendChild(epingles[x][1]);
}
// puis copie les topics
var lengthNotPinned = pageLen - lengthPinned;
for (var x = 0; x < lengthNotPinned; x++) {
jvCake(topicsCurrentPage[x][1].getElementsByClassName('topic-date')[0].getElementsByTagName('span')[0]);
ul.appendChild(topicsCurrentPage[x][1]);
}
var fragmentUL = document.createDocumentFragment();
fragmentUL.appendChild(ul.cloneNode(true));
var element = document.getElementsByClassName('topic-list topic-list-admin')[0];
// Affiche
element.parentNode.replaceChild(fragmentUL, element);
// Met en place le blocage du rafraichissement des topics lorsque le curseur est sur la nouvelle liste affichée
var conteneur = document.getElementsByClassName('conteneur-topic-pagi')[0];
conteneur.addEventListener("mouseenter", function(e) { states.setIsHoverCursor(true); }, false);
conteneur.addEventListener("mouseleave", function(e) { states.setIsHoverCursor(false); }, false);
}
function displayRedCross(state) {
var redCross = document.getElementById('red-cross');
if (state) redCross.classList.remove('hidden');
else redCross.classList.add('hidden');
}
var htmlPageRefr = null; // page rafraichie mise en cache jusqu'au prochain dl dans la fonction ci-dessous
function createXHR(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.timeout = connexionTimeOut * 1000;
xhr.ontimeout = function() {
displayRedCross(true)
};
// éviter que les navigateurs chargent une page mise en cache
xhr.setRequestHeader("Cache-Control", "max-age=0,no-cache,no-store,post-check=0,pre-check=0");
xhr.onload = function() {
displayRedCross(false);
var parser = new DOMParser();
var htmlReload = parser.parseFromString(this.responseText, "text/html");
htmlPageRefr = htmlReload;
// Appel la fonction qui va traitée la nouvelle page téléchargée
callback(htmlReload);
};
xhr.send(null);
}
// refresh la liste et le poids des topics en tendance
function refreshTrend() {
createXHR(window.location.href, updateTopicsTrend);
}
function initCSS() {
var fontColor = window.getComputedStyle(document.getElementsByClassName('topic-title')[0]).getPropertyValue('color');
var element = document.getElementsByClassName('topic-list topic-list-admin')[0];
var liste = element.getElementsByTagName("li");
if (liste.length > 2)
var backgroundColor = window.getComputedStyle(liste[2]).getPropertyValue('background-color');
else
var backgroundColor = "#FFF";
document.head.innerHTML = "" + document.head.innerHTML;
}
function processNotHover() {
states.setAreTopicsRefr(true);
states.setAreTopicsSort(true);
if (htmlPageRefr != null) {
displayTopicsCurrentPage(htmlPageRefr);
return;
}
createXHR(window.location.href, displayTopicsCurrentPage);
}
function processHover() {
states.setAreTopicsRefr(false);
states.setAreTopicsSort(false);
displayTopicsCurrentPage(null);
}
var activeTab = 0;
var states = States();
function initUI() {
var cont = document.getElementsByClassName("conteneur-topic-pagi")[0];
var contClone = cont.cloneNode(true);
var childs = contClone.children;
var len = childs.length;
var ul = document.createElement('ul');
var html = "";
for (var x = 0; x < len; x++) {
if (childs[x].className == "topic-list topic-list-admin") {
html += "";
}
html += childs[x].outerHTML;
}
cont.innerHTML = html;
var procNotHover = processNotHover;
var li = document.getElementsByClassName('categoryButtons')[0];
var lenButtons = li.length;
li.addEventListener('click', function(e) {
var elem, evt = e ? e : event;
if (evt.srcElement) {
elem = evt.srcElement;
}
else if (evt.target) {
elem = evt.target;
}
if (elem && ('LI' !== elem.tagName.toUpperCase())) {
return true;
}
var liste = elem.parentNode.getElementsByTagName('li');
activeTab = elem.dataset.id;
var len = liste.length;
states.setUpdDirection.call(elem, activeTab);
states.setAreTrendSort(activeTab);
states.setAreTrendDsp(activeTab);
states.setIsActiveTabSort(activeTab);
states.setIsActiveTabRefr(activeTab);
states.setAreTopicsColorsMod(activeTab);
for (var x = 0; x < len; x++) {
liste[x].classList.remove('active');
}
elem.classList.add('active');
procNotHover();
});
var label = document.getElementById('lock-container');
label.addEventListener('click', function(e) {
var elem, evt = e ? e : event;
if (evt.srcElement) {
elem = evt.srcElement;
}
else if (evt.target) {
elem = evt.target;
}
if (elem && ('INPUT' !== elem.tagName.toUpperCase())) {
return true;
}
if (elem.checked) {
states.setIsHoverButton(false);
}
else {
states.setIsHoverButton(true);
}
});
var label = document.getElementsByClassName('slide')[0];
label.addEventListener('click', function(e) {
var elem, evt = e ? e : event;
if (evt.srcElement) {
elem = evt.srcElement;
}
else if (evt.target) {
elem = evt.target;
}
if (elem && ('INPUT' !== elem.tagName.toUpperCase())) {
return true;
}
if (elem.checked) {
states.setAreTopicsCol(true);
}
else {
states.setAreTopicsCol(false);
}
});
var conteneur = document.getElementsByClassName('conteneur-topic-pagi')[0];
conteneur.addEventListener("mouseenter", function(e) {
states.setIsHoverCursor(true);
}, false);
conteneur.addEventListener("mouseleave", function(e) {
states.setIsHoverCursor(false);
}, false);
refreshTrend();
}
// START
window.onload = function() {
initUI();
initCSS();
var cptPage = 1;
var cptTrend = 1;
var refrTrend = refreshTrend;
var procHover = processHover;
var procNotHover = processNotHover;
setInterval(function() {
if (cptTrend < timerRefreshTrend) { cptTrend++; }
else {
refrTrend();
if (isHoverCursor || isHoverButton) { procHover(); }
cptTrend = 1;
}
if (cptPage < timerRefreshPage) { cptPage++; }
else {
if (!isHoverCursor && !isHoverButton) { procNotHover(); }
cptPage = 1;
}
}, 1000);
};