// ==UserScript== // @name [HFR] Image Anti HS // @version 0.2.2 // @namespace http://mycrub.info // @description Ajoute une icône anti-hs en haut d'une page d'un topic pour filtrer les messages sans intérêt // @include http://forum.hardware.fr/forum2.php* // @include http://forum.hardware.fr/*/*/*/*-sujet_*_*.htm* // ==/UserScript== var hsImg = "data:image/gif,GIF89a%10%00%10%00%A1%02%00%00%00%00%FF%00%00%FF%FF%FF%FF%FF%FF!%FE%11Created%20with%20GIMP%00!%F9%04%01%0A%00%03%00%2C%00%00%00%00%10%00%10%00%00%02%3B%9C%17%A9y%B7%AF%9A%12%B4F%93%AA%B6%01%25%00%08%20%05%02%8F%18%8Ee%B8%A0%EEH%B5k%0A%CB%2B9%DA..%E8j%D9%5B%FCP%2CFf%A3a%60%8EI%E5%12%B2h%3C%1F%8D%02%00%3B"; var noHsImg = "data:image/gif,GIF89a%10%00%10%00%A1%02%00%00%00%00%80%80%80%FF%FF%FF%FF%FF%FF!%FE%11Created%20with%20GIMP%00!%F9%04%01%0A%00%03%00%2C%00%00%00%00%10%00%10%00%00%02%3D%9C%17%A9y%B7%AF%9A%12%B4F%93%AA%16%0B%25%00%08%20%F5%3Db8~%E1r%9E%9F5%81%A0%1Ar%F1%1CP%B2%9D%E1u%CB%02%A8N)Hk%17%99l%60%18%A5%E6%D2%84t%1A%1EH%A3%00%00%3B"; var minLength = null; var blWords = null; var topicId = null; var doubleClickInterval = 250; var lastClickTime = 0; var clickedImage; /** * Parses processes the whole page */ var processPage = function () { var startTime = new Date().valueOf(); var cmpt = 0; var root = document.getElementById('mesdiscussions'); var messages = getElementByXpath('//table[@class="messagetable"]', root); if (isInFilter()) { injectRemoveFilterButton(); for(var i = 0; i < messages.length; i++) { if (complies(messages[i])) { messages[i].setAttribute("style", null); } else { cmpt++; messages[i].setAttribute("style", "display:none"); } } GM_log(cmpt + " messages filtered in " + (new Date().valueOf() - startTime) + " ms"); } else { injectAddFilterButton(); for(var i = 0; i < messages.length; i++) { messages[i].setAttribute("style", null); } } } /** * Tests if one message complies with the filtering rules of the topic */ var complies = function (message) { return (!filterOnImages() || (filterOnImages() && hasImage(message))) && (!filterOnQuotes() || (filterOnQuotes() && hasQuote(message))) && (!filterOnXQuotes() || (filterOnXQuotes() && hasXQuote(message))) && (!filterOnLength() || (filterOnLength() && hasLength(message))) && (!filterOnWords() || (filterOnWords() && !hasWord(message))) && (!filterOnLinks() || (filterOnLinks() && hasLink(message))) ; } /** * Is this topic configured for filtering on images? */ var filterOnImages = function () { return GM_getValue("ah_images." + getTopicId(), false); } /** * Is this topic configured for filtering on quotes? */ var filterOnQuotes = function () { return GM_getValue("ah_quotes." + getTopicId(), false); } /** * Is this topic configured for filtering on extra-topic quotes? */ var filterOnXQuotes = function () { return GM_getValue("ah_quotes_extra." + getTopicId(), false); } /** * Is this topic configured for filtering on message length? */ var filterOnLength = function () { return GM_getValue("ah_chars." + getTopicId(), false); } /** * Is this topic configured for filtering on blacklisted words? */ var filterOnWords = function () { return GM_getValue("ah_bl_words." + getTopicId(), false); } /** * Is this topic configured for filtering on links? */ var filterOnLinks = function () { return GM_getValue("ah_links." + getTopicId(), false); } /** * Does this message have an external image? */ var hasImage = function (message) { var images = getElementByXpath(".//div[starts-with(@id,'para')]//img[not(starts-with(@src, 'http://forum-images.hardware.fr'))]", message); var quotedImages = getElementByXpath(".//div[starts-with(@id,'para')]//table[@class='citation' or @class='oldcitation' or @class='quote']//img[not(starts-with(@src, 'http://forum-images.hardware.fr'))]", message); return images.length - quotedImages.length > 0; } /** * Does this message have an external link? */ var hasLink = function (message) { var links = getElementByXpath(".//div[starts-with(@id,'para')]//p//a[not(starts-with(@href, 'http://forum.hardware.fr'))]", message); var quotedLinks = getElementByXpath(".//div[starts-with(@id,'para')]//table[@class='citation' or @class='oldcitation' or @class='quote']//p//a[not(starts-with(@href, 'http://forum.hardware.fr'))]", message); return links.length - quotedLinks.length > 0; } /** * Does this message contain at least 1 quote? */ var hasQuote = function (message) { var quotes = getElementByXpath(".//div[starts-with(@id,'para')]//table[@class='citation' or @class='oldcitation' or @class='quote']", message); return quotes.length > 0; } /** * Does this message contain at least 1 extra-topic quote? */ var dbg = 0; var hasXQuote = function (message) { var quotes = getElementByXpath(".//div[starts-with(@id,'para')]//table[@class='citation' or @class='oldcitation' or @class='quote']//tr//td//b[@class='s1']//a", message); for (var i = 0; i < quotes.length; i++) { var href = quotes[i].href; var anchor = href.substring(href.lastIndexOf("#")+1); if (document.getElementsByName(anchor).length == 0) { return true } } return false; } /** * Returns the (unquoted) text of this message */ var getMessageText = function (message) { var pars = getElementByXpath(".//div[starts-with(@id,'para')]/p", message); var text = ""; for (var i = 0 ; i < pars.length ; i++) text += pars[i].textContent + " "; return text; } /** * Is this message long enough? */ var hasLength = function (message) { return getMessageText(message).length >= getMinLength(); } /** * Does this message have 1 blacklisted word? */ var hasWord = function (message) { var blWords = getBlWords(); var text = getMessageText(message); for (var i = 0; i < blWords.length; i++) { if (blWords[i] != null && blWords[i].length > 0) { if (text.indexOf(blWords[i]) >= 0) { return true; } } } return false; } /** * Utility function to retrieve DOM element by XPath */ var getElementByXpath = function (path, element) { var arr = Array(), xpr = document.evaluate(path, element, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null); for (;item = xpr.iterateNext();) arr.push(item); return arr; } /** * Gets the list of filtered topics from persistence */ var getFilteredTopicIds = function () { return GM_getValue("ah_topic_ids", "").split(";"); } /** * Sets the list of filtered topics to persistence */ var setFilteredTopicIds = function (allIds) { GM_setValue("ah_topic_ids", allIds.join(";")); } /** * Returns the topic id as "cat.post" where cat is the category id and post the post id */ var getTopicId = function () { if (topicId == null) { var postId; var catId; var m = new RegExp(".*forum2\\.php\\?.*\\&cat=(\\d*)\\&.*\\&post=(\\d*)\\&.*").exec(document.location.href); if (m != null) { catId = m[1]; postId = m[2]; } else { var searchForm = getElementByXpath('//table[@class="main"]//tr//th//form[@action="/transsearch.php"]', document.body)[0]; postId = getElementByXpath('input[@name="post"]', searchForm)[0].value; catId = getElementByXpath('input[@name="cat"]', searchForm)[0].value; } topicId = catId + "." + postId; } return topicId; } /** * Returns the minimum length of the accepted messages */ var getMinLength = function () { if (minLength == null) { minLength = GM_getValue("ah_chars_length." + getTopicId(), 500); } return minLength; } /** * Returns the list of blacklisted words */ var getBlWords = function () { if (blWords == null) { blWords = GM_getValue("ah_bl_words", "").split("\n"); } return blWords; } /** * Registers this topic in the list of topics that must be filtered */ var addTopicToFilter = function () { if (!isInFilter()) { var id = getTopicId(); var allIds = getFilteredTopicIds(); allIds.push(id); setFilteredTopicIds(allIds); } } /** * Unregisters this topic */ var removeTopicFromFilter = function () { if (isInFilter()) { var allIds = getFilteredTopicIds(); var newIds = new Array(); var id = getTopicId(); for (var i = 0; i < allIds.length; i++) { if(allIds[i] != id) { newIds.push(allIds[i]); } } setFilteredTopicIds(newIds); } } /** * Returns true if the topic has to be filtered */ var isInFilter = function () { return getFilteredTopicIds().indexOf(getTopicId()) != -1; } /** * Returns the topic "toolbar" DOM element */ var getTopicToolbar = function() { return getElementByXpath('//table[@class="main"]//tr//th//div[@class="right"]', document.body)[0]; } /** * Adds a button to the topic "toolbar" that enables the filter in this topic */ var injectAddFilterButton = function () { var newImg = document.createElement('img'); newImg.src = hsImg; newImg.alt = newImg.title = 'Filtrer ce topic (masquer les posts sans images). Double-clic pour configurer.'; newImg.style.cursor = 'pointer'; newImg.style.marginRight = '3px'; newImg.addEventListener('click', function(event) { var clickTime = new Date().getTime(); if (clickTime - lastClickTime > doubleClickInterval) { lastClickTime = clickTime; clickedImage = event.target; setTimeout( function() { if (lastClickTime == clickTime) { addTopicToFilter(); clickedImage.setAttribute("style", "display:none"); if (filterOnImages() || filterOnQuotes() || filterOnXQuotes() || filterOnLength() || filterOnWords() || filterOnLinks()) { processPage(); } else { showConfigDialog(); } } }, doubleClickInterval); } else { lastClickTime = clickTime; showConfigDialog(); } }, false); var newDiv = document.createElement('div'); newDiv.className = 'right'; newDiv.appendChild(newImg); getTopicToolbar().insertBefore(newDiv, toolbar.lastChild); } /** * Adds a button to the topic "toolbar" that disables the filter in this topic */ var injectRemoveFilterButton = function () { var newDiv = document.createElement('div'); var newImg = document.createElement('img'); newImg.src = noHsImg; newImg.alt = newImg.title = 'Ne plus filtrer ce topic (afficher les posts sans images). Double-clic pour configurer.'; newImg.style.cursor = 'pointer'; newImg.style.marginRight = '3px'; newImg.addEventListener('click', function(event) { var clickTime = new Date().getTime(); if (clickTime - lastClickTime > doubleClickInterval) { lastClickTime = clickTime; clickedImage = event.target; setTimeout( function() { if (lastClickTime == clickTime) { removeTopicFromFilter(); clickedImage.setAttribute("style", "display:none"); processPage(); } }, doubleClickInterval); } else { lastClickTime = clickTime; showConfigDialog(); } }, false); newDiv.className = 'right'; newDiv.appendChild(newImg); getTopicToolbar().insertBefore(newDiv, toolbar.lastChild); } function showConfigDialog() { clickedImage.setAttribute("style", "display:none"); cmScript.showConfigWindow(); } /** * Config window */ var cmScript = { backgroundDiv : null, configDiv : null, timer : null, setDivsPosition : function () { cmScript.setBackgroundPosition(); cmScript.setConfigWindowPosition(); }, setBackgroundPosition : function () { cmScript.backgroundDiv.style.width = document.documentElement.clientWidth + 'px'; cmScript.backgroundDiv.style.height = document.documentElement.clientHeight + 'px'; cmScript.backgroundDiv.style.top = window.scrollY + 'px'; }, setConfigWindowPosition : function () { cmScript.configDiv.style.left = (document.documentElement.clientWidth / 2) - (parseInt(cmScript.configDiv.style.width) / 2) + window.scrollX + 'px'; cmScript.configDiv.style.top = (document.documentElement.clientHeight / 2) - (parseInt(cmScript.configDiv.clientHeight) / 2) + window.scrollY + 'px'; }, disableKeys : function (event) { var key = event.which; if (key == 27) { clearInterval(cmScript.timer); cmScript.hideConfigWindow(); } //else if (key == 13) cmScript.validateConfig(); else if (event.altKey || (event.target.nodeName.toLowerCase() != 'input' && key >= 33 && key <= 40)) event.preventDefault(); }, disableTabUp : function (elt) { elt.addEventListener('keydown', function(event) { var key = event.which; if (key == 9 && event.shiftKey) event.preventDefault(); } , false); }, disableTabDown : function (elt) { elt.addEventListener('keydown', function(event) { var key = event.which; if (key == 9 && !event.shiftKey) event.preventDefault(); } , false); }, disableScroll : function () { document.body.style.overflow = 'hidden'; window.addEventListener('keydown', cmScript.disableKeys, false); }, enableScroll : function () { document.body.style.overflow = 'visible'; window.removeEventListener('keydown', cmScript.disableKeys, false); }, alterWindow : function (opening) { if (opening) { // On fige la fenêtre cmScript.disableScroll(); // A chaque resize, repositionnement des divs window.addEventListener('resize', cmScript.setDivsPosition, false); // On cache les iframes de m%$!§ getElementByXpath('//iframe', document.body).forEach(function(iframe){ iframe.style.visibility = 'hidden'; }); } else { cmScript.enableScroll(); window.removeEventListener('resize', cmScript.setDivsPosition, false); getElementByXpath('//iframe', document.body).forEach(function(iframe){ iframe.style.visibility = 'visible'; }); } }, buildBackground : function () { if (!document.getElementById('ah_back')) { cmScript.backgroundDiv = document.createElement("div"); cmScript.backgroundDiv.id = 'ah_back'; cmScript.backgroundDiv.addEventListener('click', function() { clearInterval(cmScript.timer); cmScript.hideConfigWindow(); } , false); cssManager.addCssProperties("#ah_back { display: none; position: absolute; left: 0px; top: 0px; background-color: #242424; z-index: 1001;}"); document.body.appendChild(cmScript.backgroundDiv); } }, buildConfigWindow : function () { if (top.location != self.document.location) { return; } if (!document.getElementById('ah_front')) { cmScript.configDiv = document.createElement("div"); cmScript.configDiv.id = 'ah_front'; cmScript.configDiv.style.width = '400px'; cssManager.addCssProperties("#ah_front { display: none; vertical-align: bottom; position: absolute; background-color: #F7F7F7; z-index: 1003; border: 1px dotted #000; padding: 8px; text-align: left; font-family: Verdana;}"); cssManager.addCssProperties("#ah_front span { font-size: 0.8em;}"); cssManager.addCssProperties("#ah_front select { border: 1px solid black; font-family: Verdana; font-size: 0.75em;}"); cssManager.addCssProperties("#ah_front img { display: block; margin-top: 10px; margin-left: auto; margin-right: auto;}"); cssManager.addCssProperties("#ah_front div { position: absolute; bottom: 8px; right: 8px;}"); cssManager.addCssProperties("#ah_front input[type=image] { margin: 2px; }"); cssManager.addCssProperties("#ah_front label { font-size: 0.8em; }"); var topicId = getTopicId(); var label1 = document.createElement('span'); label1.innerHTML = "