Greasy Fork is available in English.
Améliore la navigation des quotes et des commentaires sur DansTonChat.com (DTC pour les intimes). Miaou... Miaou... MiaouDTC
// ==UserScript== // @name MiaouDTC - DansTonChat.com // @name:fr MiaouDTC - DansTonChat.com // @namespace jlgrall_UserScripts // @version 1.8 // @description Improves browsing of quotes and comments on DansTonChat.com (also known as DTC). Miaou... Miaou... MiaouDTC // @description:fr Améliore la navigation des quotes et des commentaires sur DansTonChat.com (DTC pour les intimes). Miaou... Miaou... MiaouDTC // @homepage https://greasyfork.org/en/scripts/369900-miaoudtc-danstonchat-com // @supportURL https://greasyfork.org/en/scripts/369900-miaoudtc-danstonchat-com/feedback // @contributionURL https://www.tipeee.com/danstonchat // @author jlgrall // @license MIT License // @match https://danstonchat.com/* // @exclude https://danstonchat.com/user/login* // @exclude https://danstonchat.com/myaccount* // @grant none // ==/UserScript== (function($, undefined) { 'use strict'; var $empty = $(); var Miaou = window.Miaou = { // CSS rules: styleContent: [ // GENERAL: 'div.cls-cookie { display: none; }', '.contenu h1.miaouH1Search { cursor: pointer; }', '.item .item-content .decoration a.miaouPseudoLink { display: unset; max-width: unset; }', '.item .item-content .decoration a.miaouPseudoLink:hover { text-decoration: underline; }', // Pseudos: '.item .item-content .decoration.miaouPseudo-similarColor { margin-left: -4px; border-left: 4px solid red; }', // scoreRatio: 'span.miaouScoreRatio { float: left; margin-left: -20px; }', // miaouComments Header: '.miaouComments-header { float: left; background: #ddd; }', '.miaouComments-header > span,', '.miaouComments-header > a { padding: 11px 10px 12px; font-size: 1.1em; _font-weight: bold; display: inline-block; color: #222; }', '.miaouComments-header > a:hover { background: #ccc; }', '@media (max-width: 530px) {', ' .miaouComments-header > a { text-indent: -9999px; line-height: 0; }', // Idea from: https://stackoverflow.com/questions/7####02/how-can-i-replace-text-with-css/22054588#22054588 ' .miaouComments-header > a:after { content: "Comms"; text-indent: 0; line-height: initial; display: block; }', '}', '@media (max-width: 350px) {', ' .miaouComments-header > a { padding: 5px; }', ' .miaouComments-header > a:after { content: "C"; font-size: 1.4em; }', '}', // Toggle between buttons 'a.miaouComments-showComments' and 'a.miaouComments-hideComments': '.miaouItem:not(.miaouComments-hide) a.miaouComments-showComments,', '.miaouItem.miaouComments-hide a.miaouComments-hideComments { display: none; }', // Toggle text 'span.miaouComments-loadingComments': '.miaouItem.miaouComments-hide span.miaouComments-loadingComments { display: none; }', '.miaouItem:not(.miaouComments-hide):not(.miaouComments-loading) span.miaouComments-loadingComments { display: none; }', '.miaouItem.miaouComments-loading a.miaouComments-hideComments { display: none; }', // Highlight available Captain Obvious explanations: '.show_explanation { background: rgba(255, 255, 153, .6); }', '.miaouComments-body .explanation-text { background: rgba(255, 255, 153, .6); padding: 0 6px; }', '.miaouComments-body .explanation-text h2 { margin-top: 0; }', // miaouComments Wrapper and Body: '.miaouComments-wrapper { border-left: 10px solid #ddd; border-bottom: 10px solid #ddd; padding-left: 6px; }', '.miaouComments-body { max-height: 500px; overflow: auto; resize: vertical; padding-top: 6px; }', '.miaouComments-body:empty { display: none; }', '@media (max-height: 660px) { .miaouComments-body { max-height: 74vh; } }', '@media (max-height: 570px) { .miaouComments-body { max-height: 68vh; } }', '@media (max-height: 480px) { .miaouComments-body { max-height: 60vh; } }', '.miaouItem.miaouComments-hide .miaouComments-wrapper { display: none; }', // Linkified quotes: '.item-content a.miaou-linkifiedQuoteRef { display: unset; max-width: unset; color: #8cbd6a; }', '.item-content a.miaou-linkifiedQuoteRef:hover { color: #add095; }', ], // Comments loading states: COMMENTS_UNLOADED: 0, COMMENTS_LOADING: 1, COMMENTS_LOADED: 2, COMMENTS_ERROR: 3, getCommentsStateForItem: function($item) { var state = $item.data('data-miaou-commentsState'); return state === undefined ? Miaou.COMMENTS_UNLOADED : state; }, setCommentsStateForItem: function($item, state) { $item.data('data-miaou-commentsState', state); }, rawPseudoToPseudo: {}, pseudoToColor: {}, pseudoToLink: {}, itemsById: {}, extractItemId: function($item) { // itemId is always a string (it should not be converted to a number) var item = $item[0]; for (var i = 0; i < item.classList.length; i++) { var name = item.classList.item(i); if (name !== 'item') { var match = name.match(/^item(\d{1,9})$/); if (match) return match[1]; } } return undefined; }, initItem: function($item) { var itemId = Miaou.extractItemId($item); var itemURL = ($item.find('div.meta-infos span.comments > a').attr('href') || '').split('#')[0]; var $score = $item.find('span.score'); var scoreMatch = $score.text().match(/(\d{1,6})\s*\/\s*(\d{1,6})/); var scoreRatio = scoreMatch ? (scoreMatch[1] / scoreMatch[2]).toFixed(1) : NaN; var nbComments = parseInt($item.find('span.comments').text(), 10); Miaou.itemsById[itemId] = (Miaou.itemsById[itemId] || $empty).add($item); var $scoreRatio = $('<span class="miaouScoreRatio"> = ' + scoreRatio + '</span>'); $item.data({ 'miaou-itemId': itemId, 'miaou-itemURL': itemURL, 'miaou-nbComments': nbComments, 'miaou-$scoreRatio': $scoreRatio, }); $item.addClass('miaouItem miaouItem' + itemId); Miaou._initItemPseudos($item); $scoreRatio.insertAfter($score); if (Miaou.pageHasItemsList) { Miaou._initItemComments($item); } else { $item.find('.item-content > a:not(.img)').replaceWith(function() { return this.childNodes; }); } return $item; }, _initItemPseudos: function($item) { var pseudoToItem$Els = {}; $item.find('.item-content span.decoration').each(function(i, el) { var $el = $(el); var rawPseudo = $el.text(); var pseudo = Miaou.rawPseudoToPseudo[rawPseudo]; if (!(rawPseudo in Miaou.rawPseudoToPseudo)) { // Previous regex: replace(/^(?:\s|\*|\[[\d:h.]+\]|<[+@]?)+|(?:>|:|dit|\s)+$/g, '') pseudo = rawPseudo .replace(/^(?:\s|\*|\[[\d:h.]+\])+|(?::|dit|\s)+$/g, '') .replace(/^<[+@]?(.*)>$|^\[[+@]?(.*)\]$|^\((.*)\)$/g, '$1$2$3') .trim(); if (pseudo === '') pseudo = undefined; Miaou.rawPseudoToPseudo[rawPseudo] = pseudo; } if (pseudo) { pseudoToItem$Els[pseudo] = (pseudoToItem$Els[pseudo] || $empty).add($el); if (!(pseudo in Miaou.pseudoToColor)) { var match = el.style.backgroundColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); var color = match ? [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10)] : undefined; Miaou.pseudoToColor[pseudo] = color; } var link = Miaou.pseudoToLink[pseudo]; if (!link) { var url = '/search.html?search=' + encodeURIComponent(pseudo); link = Miaou.pseudoToLink[pseudo] = $('<a href="' + url + '" target="_blank" class="miaouPseudoLink"></a>'); } $el.wrapInner(link.clone(true)); } }); // Compare pseudos colors and group them by similarity: var setOfSimilarSets = new Set(); var pseudos = Object.keys(pseudoToItem$Els); for (var i = 0; i < pseudos.length; i++) { var p1 = pseudos[i]; var c1 = Miaou.pseudoToColor[p1]; for (var j = i + 1; j < pseudos.length; j++) { var p2 = pseudos[j]; var c2 = Miaou.pseudoToColor[p2]; var dist = Miaou.colorDistance(c1, c2); if (Miaou.debugColors) { var _c1 = pseudoToItem$Els[p1][0].style.backgroundColor; var _c2 = pseudoToItem$Els[p2][0].style.backgroundColor; console.log( "Color distance: %c" + dist.toFixed(2) + "%c '%c" + p1 + "%c' '%c" + p2 + "%c'", dist < Miaou.minColorDistance ? "color: red" : "", "", "color: black; font-weight: bold; background-color: " + _c1, "", "color: black; font-weight: bold; background-color: " + _c2, "" ); } if (dist < Miaou.minColorDistance) { var foundSet = undefined; for (var similarSet of setOfSimilarSets) { if (similarSet.has(p1) || similarSet.has(p2)) { if (!foundSet) { foundSet = similarSet; } else { // Merge set with previously found set: setOfSimilarSets.delete(similarSet); similarSet.forEach(v => foundSet.add(v)); } similarSet.add(p1).add(p2); } } if (!foundSet) setOfSimilarSets.add(new Set([p1, p2])); } } } // Add additional distinct colors to each set of similar pseudos: setOfSimilarSets.forEach(function(similarSet) { // For pseudos, DTC uses: hsv(X, 15, 95) converted to rgb. // We will use the CSS available: hsl(...) (Note that h is an angle so it wraps around) var bg = Miaou.pseudoToColor[similarSet.values().next().value]; var h = Miaou.rgbToHsl(bg)[0]; var step = 360 / (similarSet.size + 1); for (var pseudo of similarSet) { h += step; pseudoToItem$Els[pseudo].addClass("miaouPseudo-similarColor") .css("border-left-color", "hsl(" + Math.round(h) + ", 75%, 66%)"); } }); return $item; }, _initItemComments: function($item) { var $commentsHeader = $('<div class="miaouComments-header"></div>'); var $commentsWrapper = $('<div class="miaouComments-wrapper"></div>'); var $commentsBody = $('<div class="miaouComments-body"></div>'); var itemId = $item.data('miaou-itemId'); var nbComments = $item.data('miaou-nbComments'); var hrefShowComments = 'javascript:Miaou.showCommentsForItemId(\'' + itemId + '\')'; var hrefHideComments = 'javascript:Miaou.hideCommentsForItemId(\'' + itemId + '\')'; var $showComments = $('<a href="' + hrefShowComments + '" class="miaouComments-showComments">Afficher les commentaires (<span class="miaouComments-nbComments">' + nbComments + '</span>)</a>'); var $hideComments = $('<a href="' + hrefHideComments + '" class="miaouComments-hideComments">Masquer les commentaires (<span class="miaouComments-nbComments">' + nbComments + '</span>)</a>'); var $loadingComments = $('<span class="miaouComments-loadingComments">Chargement des commentaires...</span>'); // Preload comments on mousedown: $showComments.one('mousedown', function() { Miaou.loadCommentsForItemId(itemId); }); $item.data({ 'miaou-$commentsHeader': $commentsHeader, 'miaou-$commentsWrapper': $commentsWrapper, 'miaou-$commentsBody': $commentsBody, }); $item.addClass('miaouComments-hide'); // TODO: rename/change to 'miaouComments-show' instead ? $commentsHeader.append($showComments, $hideComments, $loadingComments); $commentsHeader.insertBefore($item.find('.meta-bar .vote-plus')); $commentsWrapper.append($commentsBody).insertAfter($item.find('div.item-meta')); $item.find('.show_explanation').attr('href', hrefShowComments); Miaou.setCommentsStateForItem($item, Miaou.COMMENTS_UNLOADED); return $item; }, loadCommentsForItemId: function(itemId) { var $items = Miaou.itemsById[itemId] || $empty; if ($items.length === 0) return console.error('Could not find element item with id: ' + itemId); var itemURL = undefined; $items.each(function(i, item) { var $item = $(item); if (Miaou.getCommentsStateForItem($item) === Miaou.COMMENTS_LOADING) return; $item.addClass('miaouComments-loading'); Miaou.setCommentsStateForItem($item, Miaou.COMMENTS_LOADING); itemURL = $item.data('miaou-itemURL'); }); if (!itemURL) return; // Probably already loading. $.get(itemURL, 'html').then(function(data) { var $page = $($.parseHTML(data)).remove('script, style, link'); var $comments = $page.find('.comment-list'); var nbComments = $comments.find('.comment').length; var $explanationText = $page.find('.explanation-text').prependTo($comments); if (nbComments > 0) { $comments = Miaou.linkifyQuoteRefs($comments); return {$comments: $comments, state: Miaou.COMMENTS_LOADED, nbComments: nbComments}; } else if ($page.find('#comments-bloc').length > 0) { $comments = $page.find('#comments-bloc').children(); return {$comments: $comments, state: Miaou.COMMENTS_LOADED, nbComments: 0}; } else { $comments = $.parseHTML('<div class="comment-list">Erreur: commentaires non trouvés.</div>'); return $.Deferred().reject({$comments: $comments, state: Miaou.COMMENTS_ERROR}); } }, function(jqXHR) { var $comments = $.parseHTML('<div class="comment-list">Erreur: status = "' + jqXHR.statusText + '"</div>'); return {$comments: $comments, state: Miaou.COMMENTS_ERROR}; }).always(function(data) { // In case content of page was modified during xhr: var $items = Miaou.itemsById[itemId] || $empty; if ($items.length === 0) return; $items.each(function(i, item) { var $item = $(item); if ('nbComments' in data) { if ($item.data('miaou-nbComments') !== data.nbComments) { $item.data('miaou-nbComments', data.nbComments); $item.find('.miaouComments-nbComments').text(data.nbComments); } } var $commentsBody = $item.data('miaou-$commentsBody'); $commentsBody.empty().append(data.$comments); $item.removeClass('miaouComments-loading'); Miaou.setCommentsStateForItem($item, data.state); }); }); }, showCommentsForItemId: function(itemId) { var $items = Miaou.itemsById[itemId] || $empty; $items.each(function(i, item) { var $item = $(item); var state = Miaou.getCommentsStateForItem($item); if (state === Miaou.COMMENTS_UNLOADED) Miaou.loadCommentsForItemId(itemId); $item.removeClass('miaouComments-hide'); }); }, hideCommentsForItemId: function(itemId) { var $items = Miaou.itemsById[itemId] || $empty; $items.each(function(i, item) { var $item = $(item); $item.addClass('miaouComments-hide'); }); }, toggleCommentsForItemId: function(itemId) { var $items = Miaou.itemsById[itemId] || $empty; if ($items.filter(':not(.miaouComments-hide)').length > 0) { Miaou.hideCommentsForItemId(itemId); } else { Miaou.showCommentsForItemId(itemId); } }, showCommentsForAllItems: function() { for (var itemId in Miaou.itemsById) Miaou.showCommentsForItemId(itemId); }, hideCommentsForAllItems: function() { for (var itemId in Miaou.itemsById) Miaou.hideCommentsForItemId(itemId); }, toggleCommentsForAllItems: function() { for (var itemId in Miaou.itemsById) Miaou.toggleCommentsForItemId(itemId); }, linkifyQuoteRefs: function(node) { if (node.each) { // node is a jQuery object node.each(function(i, node) { Miaou.linkifyQuoteRefs(node); }); } else if (node.hasChildNodes()) { for (var i = 0; i < node.childNodes.length; i++){ Miaou.linkifyQuoteRefs(node.childNodes[i]); } } else if (node.nodeType === Node.TEXT_NODE && node.textContent.indexOf('#') > -1 // Quick test speeds up the process. && !node.parentNode.classList.contains('miaou-linkifiedQuoteRef')) { // Regex to find a DTC quote reference: a '#' followed by up to 9 digits, // and the reference must be separated from other words. var newHTML = node.textContent.replace(/\B#(\d{1,9})\b/g, '<a href="/$1.html" class="miaou-linkifiedQuoteRef">$&</a>'); if (newHTML !== node.textContent) $(node).replaceWith(newHTML); } return node; }, colorDistance: function(c1, c2) { // From: http://www.compuphase.com/cmetric.htm (via: https://stackoverflow.com/questions/5392061/algorithm-to-check-similarity-of-colors/40950076#40950076) var rmean = (c1[0] + c2[0]) / 2; var r = c1[0] - c2[0]; var g = c1[1] - c2[1]; var b = c1[2] - c2[2]; return Math.sqrt((((512+rmean)*r*r)>>8) + 4*g*g + (((767-rmean)*b*b)>>8)); }, // From: https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion/9493060#9493060 /** * Converts an RGB color value to HSL. Conversion formula * adapted from http://en.wikipedia.org/wiki/HSL_color_space. * Assumes r, g, and b are contained in the set [0, 255] and * returns h, s, and l in the set [0, 1]. * * @param {number} r The red color value * @param {number} g The green color value * @param {number} b The blue color value * @return {Array} The HSL representation */ _rgbToHsl: function(r, g, b){ r /= 255, g /= 255, b /= 255; var max = Math.max(r, g, b), min = Math.min(r, g, b); var h, s, l = (max + min) / 2; if(max == min){ h = s = 0; // achromatic }else{ var d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch(max){ case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } return [h, s, l]; }, rgbToHsl: function(c) { var hsl = Miaou._rgbToHsl(c[0], c[1], c[2]); hsl[0] = Math.round(hsl[0] * 360); return hsl; }, }; // MAIN: Miaou.pageHasItemsList = document.querySelector('div.items, div.item-listing, div.comment-listing') !== null; Miaou.$style = $('<style/>', {html: Miaou.styleContent.join('\n'), 'id': 'Miaou.$style'}).appendTo(document.head); if (window.location.pathname.startsWith('/search')) { $('.contenu h1').addClass('miaouH1Search').on('click', function() { $('.widget_search .icon-search').click(); }); } Miaou.minColorDistance = 30; //Miaou.debugColors = true; //Miaou.allowActionLoadAll = true; if (Miaou.pageHasItemsList && Miaou.allowActionLoadAll) { var $showAllComments = $('<a href="javascript:Miaou.showCommentsForAllItems()" class="miaouShowAllComments"><span>Afficher tous les commentaires</span></a>'); $showAllComments.insertAfter('div.contenu a.submit.android'); var $hideAllComments = $('<a href="javascript:Miaou.hideCommentsForAllItems()" class="miaouHideAllComments"><span>Masquer tous les commentaires</span></a>'); $hideAllComments.insertAfter($showAllComments); } $('.item').each(function(i, item) { Miaou.initItem($(item)); }); Miaou.linkifyQuoteRefs($('.item-content, .comment-content')); if (Miaou.pageHasItemsList) { // Remove original .show_explanation click events added in: https://danstonchat.com/themes/danstonchat2016/javascript/theme.js // Needs to be run after theme.js execution, so on document ready: $(function() { $('.show_explanation').off('click'); }); } })(window.jQuery);