Greasy Fork

Greasy Fork is available in English.

Kickass Torrents Enhancements with Ad Remover

Enhances KickassTorrents (KAT) by forcing HTTPS, using a max-width layout, enabling navigation in search results using the arrow keys and removing ads.

当前为 2015-01-07 提交的版本,查看 最新版本

// ==UserScript==
// @name        Kickass Torrents Enhancements with Ad Remover
// @id          kickass-enhancements
// @namespace   urn:uuid:53d8a5b1-8677-481a-8096-939db3c8d0b3
// @version     1.3.2
// @description Enhances KickassTorrents (KAT) by forcing HTTPS, using a max-width layout, enabling navigation in search results using the arrow keys and removing ads.
// @license     MIT; http://opensource.org/licenses/MIT
// @match       http://kickass.so/*
// @match       https://kickass.so/*
// @grant       unsafeWindow
// @run-at      document-start
// ==/UserScript==

"use strict";

(function () {

    var contentContainer;
    var sidebar;
    var prevPageHref;
    var nextPageHref;
    var sidebarToggled;

    (function () {
        // Force HTTPS
        if (/^http:\/\//.test(window.location.href)) {
            // Hide document during redirection
            var style = document.createElement("style");
            style.type = "text/css";
            style.textContent = "* { display: none; }";

            var parent = document.documentElement || document;
            parent.appendChild(style);

            // Redirect to HTTPS
            window.location.replace(window.location.href.replace(/^http:\/\//, "https://"));
            return;
        }

        // Disable pop-up windows
        var code = function (unsafeWindow, exportFunction) {
            // Break the script creating advertisements
            unsafeWindow._scq = {};

            // Prevent opening pop-up windows using window.open()
            unsafeWindow.open = exportFunction(function open() {
                return null;
            }, unsafeWindow);

            // Prevent overwriting
            var HTMLAnchorElement = window.HTMLAnchorElement;

            // Prevent opening pop-up windows using a.click()
            var prevClick = HTMLElement.prototype.click;
            prevClick = prevClick.apply.bind(prevClick);
            unsafeWindow.HTMLElement.prototype.click = exportFunction(function click() {
                if (this instanceof HTMLAnchorElement && this.target != "")
                    return;

                return prevClick(this, arguments);
            }, unsafeWindow);

            // Prevent opening pop-up windows using a.dispatchEvent()
            var prevDispatchEvent = EventTarget.prototype.dispatchEvent;
            prevDispatchEvent = prevDispatchEvent.apply.bind(prevDispatchEvent);
            unsafeWindow.EventTarget.prototype.dispatchEvent = exportFunction(function dispatchEvent(event) {
                if (this instanceof HTMLAnchorElement && this.target != "" && event && event.type == "click")
                    return;

                return prevDispatchEvent(this, arguments);
            }, unsafeWindow);
        };

        if (typeof (unsafeWindow) != "undefined" && typeof (exportFunction) != "undefined" && unsafeWindow !== window) {
            // Execute code in privileged context
            code(unsafeWindow, exportFunction);
        } else {
            // Execute code in non-privileged context
            var script = document.createElement("script");
            script.type = "text/javascript";
            script.textContent = '"use strict";\n(' + code.toString() + ')(window, function (func) { return func; });';

            // Run script before any other script is run
            var parent = document.documentElement || document;
            parent.appendChild(script);
            parent.removeChild(script);
        }

        // Ignore errors in the script creating advertisements
        window.addEventListener("error", onError, true);

        // Prevent advertisements from being created
        if ("onbeforescriptexecute" in document)
            document.addEventListener("beforescriptexecute", onBeforeScriptExecute, true);

        // Continue when DOM is available
        document.addEventListener("DOMContentLoaded", onContentLoaded, true);
    })();

    function onError(event) {
        if (document.readyState != "loading")
            return;

        if (event.filename.replace(/#.*/, "") != window.location.href.replace(/#.*/, ""))
            return;

        var script = document.evaluate("descendant::*[last()]/self::script", document, null, 8 /*ANY_UNORDERED_NODE_TYPE*/, null).singleNodeValue;
        if (!script)
            return;

        // Match the script creating advertisements
        var code = script.textContent;
        if (!/\/\/a\.kickass\.so\//.test(code) || !/\b_scq\b/.test(code))
            return;

        // Prevent error from being logged
        event.preventDefault();
        event.stopPropagation();
    }

    function onBeforeScriptExecute(event) {
        // Ignore external scripts
        var script = event.target;
        if (script.src != "")
            return;

        // Prevent advertisements from being created
        var code = script.textContent;
        if (!/\/\/a\.kickass\.so\//.test(code) || !/\b_scq\b/.test(code))
            return;

        // Prevent script from executing
        event.preventDefault();
        event.stopPropagation();
    }

    function onContentLoaded() {
        document.removeEventListener("DOMContentLoaded", onContentLoaded, true);

        // Force HTTPS for KickassTorrents and Torcache links
        var items = document.querySelectorAll('a[href^="http://kickass.so/"], link[href^="http://kickass.so/"], a[href^="http://torcache.net/"]');
        for (var i = 0; i < items.length; i++) {
            var item = items[i];
            item.href = item.href.replace(/^http:\/\//, "https://")
        }

        // Inline referrer anonymizer (for IMDb links)
        var items = document.querySelectorAll('a[href^="http://anonym.to/?http"], a[href^="http://blankrefer.com/?http"]');
        for (var i = 0; i < items.length; i++) {
            var a = items[i];
            var match = a.href.match(/^http:\/\/(?:anonym\.to|blankrefer\.com)\/\?(https?:\/\/.*)$/);
            if (!match)
                continue;

            var encodedURL = match[1].replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");

            var html = '<html>' +
                '<head>' +
                '<meta http-equiv="refresh" content="0;url=' + encodedURL + '">' +
                '</head>' +
                '</html>';

            // Default charset is US-ASCII
            var charset = "";

            // Specify UTF-8 for non-ASCII characters
            if (/[^\x00-\x7f]/.test(html))
                charset = ";charset=utf-8";

            a.href = "data:text/html" + charset + "," + encodeURIComponent(html);
        }

        // Apply style sheet to support the max-width layout and hide advertisements
        var style = document.createElement("style");
        style.type = "text/css";
        style.textContent = 'body { min-width: 0; }' +
            'footer { width: auto; }' +
            'iframe[style~="visibility:hidden"], iframe[style~="visibility: hidden"] { display: none; }' +
            '.tabs { overflow: visible; }' +
            'ul.tabNavigation li a span { padding-left: 7px; padding-right: 7px; }' +
            '.pages a:not(.blank) { min-width: 15px; padding-left: 7px; padding-right: 7px; }' +
            '.advertising, .partner1Button { display: none; }';
        document.head.appendChild(style);

        // Select original content container
        var mainContent = document.querySelector(".mainpart .doublecelltable td");
        sidebar = document.getElementById("sidebar");
        if (!mainContent && !sidebar)
            mainContent = document.querySelector(".mainpart");

        // Create max-width content container
        if (mainContent) {
            contentContainer = document.createElement("div");
            contentContainer.style.margin = "15px auto 0";
            contentContainer.style.maxWidth = "900px";

            while (mainContent.firstChild)
                contentContainer.appendChild(mainContent.firstChild);

            // Include float elements in parent height
            var clearDiv = document.createElement("div");
            clearDiv.style.clear = "both";
            contentContainer.appendChild(clearDiv);

            mainContent.appendChild(contentContainer);

            // Remove width from nested content containers
            var items = contentContainer.querySelectorAll(':scope > div.margauto[class*="width"]');
            for (var i = 0; i < items.length; i++) {
                var item = items[i];
                if (/(^|\s)width\d+px($|\s)/.test(item.className))
                    item.style.setProperty("width", "auto", "important");
            }
        }

        // Hide advertisements
        var items = document.evaluate("//div[starts-with(@id, '_') and string-length(@id) = 33]", document, null, 6 /*UNORDERED_NODE_SNAPSHOT_TYPE*/, null);
        for (var i = 0; i < items.snapshotLength; i++) {
            var adContainer = items.snapshotItem(i);
            adContainer.style.display = "none";

            // Remove children when added
            var observer = new MutationObserver(function (mutations) {
                mutations.forEach(function (mutation) {
                    var parent = mutation.target;
                    var addedNodes = mutation.addedNodes;

                    for (var i = 0; i < addedNodes.length; i++)
                        parent.removeChild(addedNodes[i]);
                });
            });

            observer.observe(adContainer, { childList: true });
        }

        // Hide single tabs with no content (Sponsored Links)
        var items = document.querySelectorAll(".tabs");
        for (var i = 0; i < items.length; i++) {
            var item = items[i];
            if (!item.querySelector(":scope > :not(.tabNavigation):not(.tabsSeparator)") && document.evaluate("not(descendant::li[2])", item, null, 3 /*BOOLEAN_TYPE*/, null).booleanValue)
                item.style.display = "none";
        }

        // Hide line breaks after hidden elements
        var items = document.querySelectorAll("* + br");
        for (var i = 0; i < items.length; i++) {
            var br = items[i];

            // Ignore line breaks after visible elements
            if (window.getComputedStyle(br.previousElementSibling, null).display != "none")
                continue;

            for (var prevNode = br.previousSibling; prevNode; prevNode = prevNode.previousSibling) {
                switch (prevNode.nodeType) {
                    case 1 /*ELEMENT_NODE*/:
                        // Found previous hidden element
                        br.style.display = "none";
                        break;
                    case 3 /*TEXT_NODE*/:
                        // Ignore white space
                        if (!/\S/.test(prevNode.textContent))
                            continue;
                        break;
                    default:
                        // Ignore comments
                        continue;
                }

                // Found text between the line break and the previous element
                break;
            }
        }

        // Enhance navigation
        var activePages = document.querySelectorAll(".pages a.active");
        for (var i = 0; i < activePages.length; i++) {
            var activePage = activePages[i];
            var parent = activePage.parentNode;

            // Add Prev button
            var prevPage = document.evaluate("preceding-sibling::a[1]", activePage, null, 8 /*ANY_UNORDERED_NODE_TYPE*/, null).singleNodeValue;
            if (prevPage) {
                if (i == 0)
                    prevPageHref = prevPage.href;

                prevPage = prevPage.cloneNode(false);
                prevPage.textContent = "< Prev";
                parent.insertBefore(prevPage, parent.firstElementChild);
            }

            // Add Next button
            var nextPage = document.evaluate("following-sibling::a[1]", activePage, null, 8 /*ANY_UNORDERED_NODE_TYPE*/, null).singleNodeValue;
            if (nextPage) {
                if (i == 0)
                    nextPageHref = nextPage.href;

                nextPage = nextPage.cloneNode(false);
                nextPage.textContent = "Next >";
                parent.insertBefore(nextPage, parent.lastElementChild.nextSibling);
            }
        }

        // Navigate using arrow keys
        if (prevPageHref || nextPageHref)
            window.addEventListener("keypress", onKeyPress, false);

        if (sidebar && contentContainer) {
            // Detect sidebar user interaction
            var items = sidebar.querySelectorAll(".sliderbox");
            for (var i = 0; i < items.length; i++) {
                var section = items[i];

                var sectionHeader = section.querySelector("h3");
                if (!sectionHeader)
                    continue;

                var foldClose = sectionHeader.querySelector(".foldClose");
                if (!foldClose)
                    continue;

                // Enable toggling sidebar section by clicking on the section header
                sectionHeader.style.cursor = "pointer";
                sectionHeader.addEventListener("click", onSectionHeaderClick, false);

                foldClose.addEventListener("click", onFoldCloseClick, false);
            }

            closeSidebarSections();

            // Close sidebar sections again after images are loaded
            window.addEventListener("load", closeSidebarSections, false);

            // Close sidebar sections when window is resized
            window.addEventListener("resize", closeSidebarSections, false);
        }
    }

    function onKeyPress(event) {
        switch (event.key) {
            case "Left":
                if (prevPageHref)
                    window.location.assign(prevPageHref);
                break;
            case "Right":
                if (nextPageHref)
                    window.location.assign(nextPageHref);
                break;
            default:
                return;
        }

        event.preventDefault();
    }

    function onSectionHeaderClick(event) {
        var foldClose = this.querySelector(".foldClose");
        if (!foldClose)
            return;

        // Ignore click event bubbling from foldClose
        if (event.target == foldClose)
            return;

        foldClose.click();
        event.preventDefault();
    }

    function onFoldCloseClick() {
        sidebarToggled = true;
    }

    function closeSidebarSections() {
        // Avoid closing sidebar sections after user interaction
        if (sidebarToggled)
            return;

        var sections = sidebar.querySelectorAll(".sliderbox");
        var i = sections.length - 1;

        // Close sidebar sections while the viewport is shorter than the document and sidebar is higher than content
        while (i >= 0 && window.innerWidth > document.documentElement.clientWidth && sidebar.offsetTop + sidebar.offsetHeight > contentContainer.offsetTop + contentContainer.offsetHeight) {
            var section = sections[i--];
            var foldClose = section.querySelector(".foldClose");
            var block = section.querySelector(".showBlockJS");

            if (foldClose && block) {
                foldClose.classList.add("ka-180");
                block.classList.remove("showBlockJS");
                block.classList.add("hideBlockJS");
            }
        }
    }

})();