您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
add a warning message when a spoiler contains hidden link
// ==UserScript== // @name Kaskus Spoiler Alert // @namespace http://userscripts.org/scripts/show/73498 // @description add a warning message when a spoiler contains hidden link // @include *.kaskus.*/thread/* // @include *.kaskus.*/post/* // @include *.kaskus.*/product/* // @include *.kaskus.*/show_post/* // @include *.kaskus.*/group/discussion/* // @include *.kaskus.co.id/thread/* // @include *.kaskus.co.id/post/* // @include *.kaskus.co.id/product/* // @include *.kaskus.co.id/show_post/* // @include *.kaskus.co.id/group/discussion/* // @version 2.66 // @author arifhn // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // ==/UserScript== /** * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. * * * Changelog: * ========== * * 2.6 * - fix new kaskus * - add setting key into greasemonkey/tampermonkey menu * - add 'Show All' menu to 'Thread Tools' * - fix group page * * 2.5 * - fix button width on kaskus beta * * 2.4 * - support livebeta.kaskus.us * * 2.3 * - change shortcut for setting, now use Ctrl+Alt+S * - fix update notification * * 2.2 * - add settings for enable/disable link preview * - add close button to popup box * * 2.1 fix bug on Firefox 3.6.x * * 2.0 * - rewrite source code * - support Firefox 6 - support IDWS * * 1.20 * add button 'Show All' at top & bottom thread. * This button can show/hide all spoiler in current page * * 1.19 * - add browser support Firefox 5.x * - add html tag id (now easy to identify html tag created by KSA) * * 1.18 * fix bug: hidden link not detected if bbcode contains space/text between * URL and SPOILER, sample bbcode: * [URL=http://www.foo.com/#] extra space * [SPOILER=bar]lol[/SPOILER][/URL] * thanks to tuxie.forte * * 1.17 * add new feature: preview URL (title + original link) * * 1.16 * add browser support Firefox 4.x * * 1.15 * - support any standard vbulletin forum (tested on kaskus.us, indoforum.org) * - change all warning messages to english - add script update notification * * 1.14 * support google chrome * * 1.13 * add http://www.kaskus.us/group.php* to @include and * change the script to support it * * 1.12 * fix bug: link alert (too sensitive, now only check domain name) * * 1.11 * - scroll page to closed spoiler after 'Hide All' clicked * - hide button Show/Hide All if post contains 1 spoiler * - add new feature: fake link alert (show info if link text not equal to * link url) * * 1.10 * add http://www.kaskus.us/showpost.php* to @include, * thanks to hermawanadhis * * 1.9 * revert back to 1.7 design with two button ('Show All' and 'Show') and * remove the popup menu * * 1.8 * - move 'Show All' and 'Show Children' button into popup menu * - fixed bug: button label 'Show'/'Hide' * * 1.7 * - change button 'Show All' -> 'Show Children' (open/close child spoiler) * - add button 'Show All' (open/close all spoilers) * * 1.6 * add button 'Show All' to open/close all child spoiler * * 1.5 * exclude kaskus smiley from picture counter * * 1.4 * add new feature: show how many picture and spoiler inside spoiler * * 1.3 * rewrite link detection thanks to "p1nk3d_books" * * 1.2 * fixed bug, hidden link not detected if font color changed thanks to * "p1nk3d_books" * * 1.1 * remove link from spoiler and show the hidden link thanks to "firo sXe" * (kaskusid 650045) * * 1.0 * first release * */ (function() { var script = { putValue: function(key, value) { if(typeof window.localStorage != 'undefined') { localStorage[key] = value; }else { GM_setValue(key, value); } }, getValue: function(key, defValue) { if(typeof window.localStorage != 'undefined') { ret = localStorage[key]; if(ret == null) { ret = defValue; } return ret; }else { return GM_getValue(key, defValue); } return defValue; } }; // returns true if string contains s String.prototype.contains = function(s) { return (this.indexOf(s) != -1); }; // returns true if string starts with s String.prototype.startsWith = function(s) { return (this.substring(0, s.length) == s); }; String.prototype.escapeHTML = function() { return this.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); }; /* * // dont run in frames, prevents detection and misuse of unsafewindow try { * var unsafeWindow = unsafeWindow || window.wrappedJSObject || window; * if(unsafeWindow.frameElement != null) return; } catch(e) {} */ var URL = { // Returns the filename component of the path // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // * example 1: basename('/www/site/home.htm', '.htm'); // * returns 1: // 'home' // * example 2: basename('ecra.php?p=1'); filename : function(path) { var suffix = null; var tailcut = "\\?"; var b = path.replace(/^.*[\/\\]/g, ''); if (typeof (suffix) == 'string' && b.substr(b.length - suffix.length) == suffix) b = b.substr(0, b.length - suffix.length); if (typeof (tailcut) == 'string') b = b.replace(new RegExp(tailcut + ".*$", "g"), ''); return b; }, domain : function(url) { var m = url.match(/(http:\/\/[^?\/]+)/); if (m) { return m[1]; } return null; } }; function getCurrentYPos() { if (document.body && document.body.scrollTop) return document.body.scrollTop; if (document.documentElement && document.documentElement.scrollTop) return document.documentElement.scrollTop; if (window.pageYOffset) return window.pageYOffset; return 0; } function click(elm) { var evt = document.createEvent('MouseEvents'); evt.initEvent('click', true, true); elm.dispatchEvent(evt); } // Utility function for mouseout listener function isChildOf(parent, child) { if (child != null) { while (child.parentNode) { if ((child = child.parentNode) == parent) { return true; } } } return false; } function getElement(q, root, single) { if (root && typeof root == 'string') { root = $(root, null, true); if (!root) { return null; } } root = root || document; if (q[0] == '#') { return root.getElementById(q.substr(1)); } else if (q[0] == '/' || (q[0] == '.' && q[1] == '/')) { if (single) { return document.evaluate(q, root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; } else { var i, r = [], x = document.evaluate(q, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); while ((i = x.iterateNext())) r.push(i); return r; } } else if (q[0] == '.') { return root.getElementsByClassName(q.substr(1)); } return root.getElementsByTagName(q); } function VBLink(el, parent, idx) { this.id = 'KSA-link-' + parent.id + '-' + idx; this.element = el; this.vbPost = parent; this.noPreviewURL = function() { var configLinkPreview = script.getValue("KSA_LINK_PREVIEW", "true"); if(configLinkPreview == 'false') { return true; } var is_ff = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; if (!is_ff) { // disable preview URL on browser other than firefox return true; } var url = this.element.href; var blacklist = new Array('/\.exe$/', '/\.rar$/', '/\.7z$/', '/\.mp3$/', '/\.zip$/', '/^http:\/\/report.kaskusnetworks.com\/index\.php\/laporhansip/'); for ( var i = 0; i < blacklist.length; ++i) { var p = new RegExp(blacklist[i]); if (p.test(url)) { return true; } } var pid = this.vbPost.id.match(/([0-9]+)/)[1]; if (url.contains(pid)) { return true; } if (url.contains('/member.php?')) { return true; } if (this.element.innerHTML .contains('http://static.kaskus.us/images/buttons/viewpost.gif')) { return true; } return false; }; this.showPreview = function(event) { if (event.pageX || event.pageY) { posx = event.pageX; posy = event.pageY; } else if (event.clientX || e.clientY) { posx = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; posy = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; } var top = (posy + 20) + 'px'; var left = (posx - 50) + 'px'; if (!this.contentTitle) { GM_xmlhttpRequest( { vbLink : this, method : 'GET', url : this.element.href, onerror : function(rsp) { this.vbLink.contentTitle = 'Error'; this.vbLink.contentURL = 'Invalid or blocked URL'; VBPage.showPopup(left, top, this.vbLink.contentTitle, this.vbLink.contentURL + '<br/><br/><font color="red">You can disable this popup in Thread Tools menu</font>'); }, onload : function(rsp) { if (rsp.status == 200) { this.vbLink.contentTitle = rsp.finalUrl; this.vbLink.contentURL = rsp.finalUrl; var patt = new RegExp("<title>([^<]*)</title>", "i"); var title = patt.exec(rsp.responseText); if (title) { this.vbLink.contentTitle = title[1]; } } else { this.vbLink.contentTitle = 'Error'; this.vbLink.contentURL = 'Invalid or blocked URL'; } VBPage.showPopup(left, top, this.vbLink.contentTitle, this.vbLink.contentURL + '<br/><br/><font color="red">You can disable this popup in Thread Tools menu</font>'); } }); } else { VBPage.showPopup(left, top, this.contentTitle, this.contentURL + '<br/><br/><font color="red">You can disable this popup in Thread Tools menu</font>'); } }; this.hidePreview = function(event) { var a = this.element; var current_mouse_target = null; if (event.toElement) { current_mouse_target = event.toElement; } else if (event.relatedTarget) { current_mouse_target = event.relatedTarget; } // Code inside this if is executed when leaving the link and it's // children, for good if (a != current_mouse_target && !isChildOf(a, current_mouse_target)) { VBPage.hidePopup(); } }; this.setupKSA = function(index) { var btn = getElement('.//input[@type="button" and @value="Show"]', this.element); if (btn.length > 0) { // kalau ada button 'Show' berarti spoiler // jebakan // move children element of the link to its parent node var achildren = this.element.childNodes; var n = achildren.length; for ( var k = 0; k < n; ++k) { this.element.parentNode.insertBefore(achildren[0], this.element); } // change link title to another text this.element.innerHTML = ' Hidden Link >> ' + this.element.href .escapeHTML(); this.element.style.color = 'red'; this.element.style.textDecoration = 'none'; this.element.id = this.id + '-trap'; btn[0].parentNode.appendChild(this.element); } else { // cek link jebakan var patt = new RegExp("^\s*http:\/\/[^?\/]+", "i"); this.title = patt.exec(this.element.innerHTML.trim()); this.url = patt.exec(this.element.href); // kalo innerHTML starts with http dan gak sama dgn href if (this.title && this.url && this.title.toString() != this.url.toString()) { // buat info link jebakan var info = document.createElement('span'); info.id = this.id + '-info'; info.className = 'smallfont'; info.style.color = 'red'; info.innerHTML = ' Original Link >> ' + this.element.href; if (this.element.nextSibling) { this.element.parentNode.insertBefore(info, this.element.nextSibling); // add info to page } else { this.element.parentNode.appendChild(info); } } if (!this.noPreviewURL()) { this.element.vbLink = this; this.element.setAttribute('linkid', this.id); this.element.addEventListener('mouseover', function(event) { if (this.vbLink) { this.vbLink.showPreview(event); } else { VBPage.getLink(this.getAttribute('linkid')) .showPreview(event); } }, false); this.element.addEventListener('mouseout', function(event) { if (this.vbLink) { this.vbLink.hidePreview(event); } else { VBPage.getLink(this.getAttribute('linkid')) .hidePreview(event); } }, false); } } }; } function VBSpoiler(el, parent, idx) { if (parent != 'none') { this.id = 'KSA-spoiler-' + parent.id + '-' + idx; el.id = 'KSA-spoiler-' + parent.id + '-' + idx; } this.element = el; this.vbPost = parent; this.content = getElement( './/div//div[starts-with(@class,"content_spoiler")]', el, true); // check wether this is top spoiler in current post this.isTopSpoiler = function() { return null;// this.vbPost != 'none'; }; // +++++++++++++++++++++++++++++++++++++++++++++++++++ KSA functions this.showClick = function(str) { if (str == 'Show') { this.btShow.value = 'Hide'; this.btShowAll.value = 'Hide All'; this.content.style.display = ''; } else { this.btShow.value = 'Show'; this.btShowAll.value = 'Show All'; this.content.style.display = 'none'; } var bt = getElement('.//input[@type="button"]', this.element); for ( var i = 0; i < bt.length; ++i) { if (bt[i].value == str && bt[i] != this.btShow && bt[i] != this.btShowAll) { click(bt[i]); } } /* * if(str.contains('Hide') && this.isTopSpoiler) { * this.element.scrollIntoView(true); } */ }; this.setupKSA = function() { this.innerSpoilers = getElement( './/div[contains(@id,"KSA-spoiler-")]', this.element).length; // get button Show var btShowOri = getElement( './/input[@type="button" and @value="Show"]', this.element, true); if(btShowOri) { btShowOri.removeAttribute('onclick'); this.btShow = btShowOri.cloneNode(true); btShowOri.parentNode.replaceChild(this.btShow, btShowOri); }else { var div = getElement('.//div[@class="smallfont"]', this.element, true); this.btShow = document.createElement('input'); this.btShow.value = "Show"; this.btShow.type = 'button'; this.btShow.title = 'Show/Hide spoiler'; div.appendChild(this.btShow); this.content.style.display = 'none'; } this.btShow.vbSpoiler = this; this.btShow.setAttribute('spoilerid', this.id); this.btShow.removeAttribute('onclick'); this.btShow.setAttribute('style', 'margin: 2px; font-size: 10px; padding: 2px; width: 60px !important'); this.btShow.addEventListener('click', function() { if (this.vbSpoiler) { this.vbSpoiler.showClick(this.value); } else { VBPage.getSpoiler(this.getAttribute('spoilerid')) .showClick(this.value); } }, true); // add button Show All this.btShowAll = document.createElement('input'); this.btShowAll.value = "Show All"; this.btShowAll.type = 'button'; this.btShowAll.title = 'Show/Hide all spoilers'; this.btShowAll.setAttribute('style', 'margin: 2px; font-size: 10px; padding: 2px; width: 60px !important'); this.btShowAll.vbPost = this.vbPost; this.btShowAll.setAttribute('postid', this.vbPost.id); this.btShowAll.addEventListener('click', function() { if (this.vbPost) { // substring to remove 'All' this.vbPost.showAllClick(this.value.substring(0, 4)); } else { VBPage.getPost(this.getAttribute('postid')).showAllClick( this.value.substring(0, 4)); } }, true); this.btShow.parentNode.insertBefore(this.btShowAll, this.btShow); var infoMsg = ''; // cek jumlah gambar dalam spoiler var imgs = getElement('.//img', this.element); if (imgs.length > 0) { var maxSize = 0; var idx = -1; var totalGambar = 0; for ( var k = 0; k < imgs.length; ++k) { if (imgs[k].src .indexOf('http://static.kaskus.us/images/smilies') == -1) { // ignore // kaskus // smiley var size = imgs[k].width * imgs[k].height; if (size > 3000) { // ignore icon < 50x50 if (maxSize < size) { maxSize = size; idx = k; } ++totalGambar; } } } if (totalGambar > 0) { infoMsg = totalGambar + ' pics inside (max: ' + imgs[idx].width + 'x' + imgs[idx].height + ') '; } } if (this.innerSpoilers > 0) { if (infoMsg.length > 0) { infoMsg += ', '; } infoMsg += this.innerSpoilers + ' spoiler(s)'; } // tampilkan info if (infoMsg.length > 0) { var info = document.createElement('span'); info.id = this.id + '-info'; info.className = 'smallfont'; info.style.color = 'darkblue'; info.innerHTML = infoMsg; var anchor = getElement('.//div[@class="alt2"]', this.element, true); this.element.insertBefore(info, anchor); } }; } function VBPost(el) { this.id = el.id; this.element = el; // return all spoilers in this post this.getSpoilers = function() { var ret = []; var el = getElement('.//div[@class="spoiler"]', this.element); for ( var i = 0; i < el.length; ++i) { var sp = el[i]; ret.push(new VBSpoiler(sp, this, i)); } return ret; }; this.getSpoiler = function(id) { for ( var i = 0; i < this.spoilers.length; ++i) { if (this.spoilers[i].id == id) { return this.spoilers[i]; } } }; this.getLinks = function() { var ret = []; var el = getElement('.//div[@class="entry"]//a', this.element); for ( var i = 0; i < el.length; ++i) { var l = el[i]; ret.push(new VBLink(l, this, i)); } return ret; }; this.getLink = function(id) { for ( var i = 0; i < this.links.length; ++i) { if (this.links[i].id == id) { return this.links[i]; } } }; this.showAllClick = function(str) { for ( var i = 0; i < this.spoilers.length; ++i) { this.spoilers[i].showClick(str); } /* * if(str.contains('Hide')) { this.element.scrollIntoView(true); } */ }; this.setupKSA = function() { this.spoilers = this.getSpoilers(); for ( var i = 0; i < this.spoilers.length; ++i) { this.spoilers[i].setupKSA(i); } if (this.spoilers.length == 1) { // hide btn 'show all' if there is only 1 spoiler this.spoilers[0].btShowAll.style.display = 'none'; } this.links = this.getLinks(); for ( var i = 0; i < this.links.length; ++i) { this.links[i].setupKSA(i); } }; } // VBulletin page var VBPage = { getPosts : function() { var ret = []; if (location.href.contains('/group/discussion/')) { var el = getElement('.//section[@id="xxxxx-postNumberHere"]'); for ( var i = 0; i < el.length; ++i) { var p = el[i].parentNode.parentNode; p.id = 'post_' + i; ret.push(new VBPost(p)); } } else { var el = getElement('.//*[starts-with(@id,"post")]'); var pattern = new RegExp('^post_?[0-9a-f]+$'); // match post1234abc or // post_1234 for ( var i = 0; i < el.length; ++i) { if (pattern.test(el[i].id)) { ret.push(new VBPost(el[i])); } } } return ret; }, getPost : function(id) { for ( var i = 0; i < this.posts.length; ++i) { if (this.posts[i].id == id) { return this.posts[i]; } } }, getSpoiler : function(id) { var ids = id.split('-'); return this.getPost(ids[2]).getSpoiler(id); }, getLink : function(id) { var ids = id.split('-'); return this.getPost(ids[2]).getLink(id); }, showPopup : function(x, y, title, content) { this.popup.style.top = y; this.popup.style.left = x; this.popup.innerHTML = '<table cellspacing="1" width="350px"><tr><td class="thead"><b>' + title + '</b><div style="float:right"><a id="ksaClosePopup">x</a></div></td></tr><tr><td class="vbmenu_option vbmenu_option_alink">' + content + '</td></tr></table>'; this.popup.style.display = ''; getElement('#ksaClosePopup').addEventListener('click', function() { VBPage.hidePopup(); }, true); }, hidePopup : function() { this.popup.style.display = 'none'; }, showAllClick : function(item) { var str = item.getAttribute('ksa-action'); if(str == null) { str = 'Show'; } for ( var i = 0; i < this.posts.length; ++i) { this.posts[i].showAllClick(str); } var items = getElement('.//a[@ksa-tools-id="ksa-all-spoiler"]'); for(var i = 0; i < items.length; ++i) { if(str == 'Show') { items[i].setAttribute('ksa-action', 'Hide'); items[i].innerHTML = '<i class="fa fa-caret-down"></i> KSA - Hide all spoiler'; }else { items[i].setAttribute('ksa-action', 'Show'); items[i].innerHTML = '<i class="fa fa-caret-down"></i> KSA - Show all spoiler'; } } }, onKeyDown : function(event) { // Ctrl+Alt+S if (event.ctrlKey && event.altKey && event.keyCode == 83) { VBPage.openThreadTools(); } }, // because we only have one setting item then we use 'Thread Tools' menu for it openThreadTools : function() { var threadtools = getElement('.//div[contains(@class,"tools-panel dropdown")]', null, true); if(threadtools) { threadtools.setAttribute('class', threadtools.getAttribute('class') + ' open'); document.documentElement.scrollTop = 0; } }, closeThreadTools : function() { var threadtools = getElement('.//div[contains(@class,"tools-panel dropdown open")]', null, true); if(threadtools) { attr = threadtools.getAttribute('class'); threadtools.setAttribute('class',attr.substring(0, attr.length - 5)); } }, addThreadTools : function(title, icon, id, callback) { var dropdown = getElement('.//ul[@aria-labelledby="thread-tools"]'); if(dropdown && dropdown.length > 0) { for(var i = 0; i < dropdown.length; ++i) { var item = document.createElement('li'); var link = document.createElement('a'); link.href = '#'; link.innerHTML = '<i class="fa '+icon+'"></i>'+title; link.addEventListener('click', callback); link.setAttribute('ksa-tools-id', id); item.appendChild(link); dropdown[i].appendChild(item); } }else { // try to set mobile site layout var tools = getElement('.//div[@class="tools"]', null, true); var item = document.createElement('span'); item.setAttribute('class', 'p-right'); item.innerHTML = ' | '; var link = document.createElement('a'); link.href = '#'; link.innerHTML = '<i class="fa '+icon+'"></i>'+title; link.addEventListener('click', callback); link.setAttribute('ksa-tools-id', id); item.insertBefore(link, item.firstChild); tools.appendChild(item); } }, setupKSA : function() { this.posts = this.getPosts(); for ( var i = 0; i < this.posts.length; ++i) { this.posts[i].setupKSA(); } this.popup = document.createElement('div'); this.popup.setAttribute('class', 'vbmenu_popup'); this.popup.style.display = 'none'; this.popup.style.position = 'absolute'; this.popup.style.top = '50px'; this.popup.style.left = '50px'; this.popup.style.backgroundColor = '#eeeeee'; getElement('body')[0].appendChild(this.popup); // setup show all spoiler this.addThreadTools(' KSA - Show all spoiler', 'fa-caret-down', 'ksa-all-spoiler', function() { VBPage.showAllClick(this); VBPage.closeThreadTools(); }); // add link preview setting to 'Thread Tools' var configLinkPreview = script.getValue("KSA_LINK_PREVIEW", 'true') == 'true'?' KSA - Hide link preview':' KSA - Show link preview'; this.addThreadTools(configLinkPreview, 'fa-gear', 'ksa-link-preview', function() { var config = script.getValue("KSA_LINK_PREVIEW", 'true'); if(config == 'true') { script.putValue("KSA_LINK_PREVIEW", 'false'); }else { script.putValue("KSA_LINK_PREVIEW", 'true'); } location.reload(); }); // add keyboard listener window.addEventListener('keydown', function(e) { VBPage.onKeyDown(e); }, true); if(GM_registerMenuCommand != 'undefined') { GM_registerMenuCommand("KSA Setting", VBPage.openThreadTools); } } }; VBPage.setupKSA(); })();