RU AdList JS Fixes

try to take over the world!

Från och med 2017-06-16. Se den senaste versionen.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         RU AdList JS Fixes
// @namespace    ruadlist_js_fixes
// @version      20170616.0
// @description  try to take over the world!
// @author       lainverse & dimisa
// @match        *://*/*
// @grant        unsafeWindow
// @grant        window.close
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';
    var win = (unsafeWindow || window),
        // http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
        isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0,
        isChrome = !!window.chrome && !!window.chrome.webstore,
        isSafari = (Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0 ||
                    (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window.safari || safari.pushNotification)),
        isFirefox = typeof InstallTrigger !== 'undefined',
        inIFrame = (win.self !== win.top),
        _getAttribute = Element.prototype.getAttribute,
        _setAttribute = Element.prototype.setAttribute,
        _de = document.documentElement,
        _appendChild = Document.prototype.appendChild.bind(_de),
        _removeChild = Document.prototype.removeChild.bind(_de);

    // NodeList iterator polyfill (mostly for Safari)
    // https://jakearchibald.com/2014/iterators-gonna-iterate/
    if (!NodeList.prototype[Symbol.iterator]) {
        NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
    }

    // Options
    var opts = {
        'useWSIFunc': useWSI
    };
    (function(){
        function optsCall(callback) {
            // Register event listener
            var key = "optsCallEvent_" + Math.random().toString(36).substr(2),
                cb = callback.func.bind(callback.name);
            window.addEventListener(key, cb, false);
            // Generate and dispatch synthetic event
            var ev = document.createEvent("HTMLEvents");
            ev.initEvent(key, true, false);
            window.dispatchEvent(ev);
            // Remove listener
            window.removeEventListener(key, cb, false);
        }
        function initOptsHandler() {
            /*jshint validthis:true */
            opts[this] = GM_getValue(this, true);
            if(opts[this]) {
                opts[this+'Func']();
            }
        }
        optsCall({
            func: initOptsHandler,
            name: 'useWSI'
        });
        // show options page
        function openOptions() {
            var ovl = document.createElement('div'),
                inner = document.createElement('div');
            ovl.style = (
                'position: fixed;'+
                'top:0; left:0;'+
                'bottom: 0; right: 0;'+
                'background: rgba(0,0,0,0.85);'+
                'z-index: 2147483647;'+
                'padding: 5em'
            );
            inner.style = (
                'background: whitesmoke;'+
                'font-size: 10pt;'+
                'color: black;'+
                'padding: 1em'
            );
            inner.textContent = 'JS Fixes Options: (reload page to apply)';
            inner.appendChild(document.createElement('br'));
            inner.appendChild(document.createElement('br'));
            ovl.addEventListener('click', function(e){
                if (e.target === ovl) {
                    ovl.parentNode.removeChild(ovl);
                    e.preventDefault();
                }
                e.stopPropagation();
            }, false);
            // append checkbox with label function
            function addCheckbox(optName, optLabel) {
                var c = document.createElement('input'),
                    l = document.createElement('label');
                c.type = 'checkbox';
                c.id = optName;
                optsCall({
                    func:function(){
                        c.checked = GM_getValue(this);
                    },
                    name:optName
                });
                c.addEventListener('click', function(e) {
                    optsCall({
                        func:function(){
                            GM_setValue(this, e.target.checked);
                            opts[this] = e.target.checked;
                        },
                        name:optName
                    });
                }, true);
                l.textContent = optLabel;
                l.setAttribute('for', optName);
                inner.appendChild(c);
                inner.appendChild(l);
                inner.appendChild(document.createElement('br'));
            }
            // append checkboxes
            addCheckbox('useWSI', 'Use WebSocket filter. Disable if experience problems with WebSocket connections.');
            document.body.appendChild(ovl);
            ovl.appendChild(inner);
        }
        // monitor keys pressed for Ctrl+Alt+Shift+J > s > f code
        var opPos = 0, opKey = 'Jsf', isNotKey;
        document.addEventListener('keydown', function(e) {
            isNotKey = (e.key.length > 1);
            if ((e.key === opKey[opPos] || isNotKey) &&
                (!!opPos || e.altKey && e.ctrlKey)) {
                opPos += (isNotKey ? 0 : 1);
                e.stopPropagation();
                e.preventDefault();
            } else {
                opPos = 0;
            }
            if (opPos === opKey.length) {
                opPos = 0;
                openOptions();
            }
        }, false);
    })();

    // Special wrapper script to run scripts designed to override standard DOM functions
    // In Firefox appends supplied script to a page to make it run in page context and let
    // page content access overridden functions. In other browsers just run it as-is.
    function scriptLander(func, prepend) {
        if (!isFirefox) {
            func();
            return;
        }
        var s = document.createElement('script');
        s.textContent = '(function(){var win=window;' + (
            prepend && prepend.join('') || ''
        ) + '!' + func + '();})();';
        _appendChild(s);
        _removeChild(s);
    }

    // Creates and return protected style (unless protection is manually disabled).
    // Protected style will re-add itself on removal and remaind enabled on attempt to disable it.
    function createStyle(rules, props, skip_protect) {
        var prop = '';

        function _createGetterSetter(obj) {
            return {
                get: function() {return obj;}, //pretend to be empty
                set: function() {},
                enumerable: true
            };
        }
        function _protect(style) {
            win.stts = style.sheet;
            Object.defineProperty(style, 'sheet', {
                value: null,
                enumerable: true
            });
            Object.defineProperty(style, 'disabled', {
                get: function() {return true;}, //pretend to be disabled
                set: function() {},
                enumerable: true
            });
            (new MutationObserver(function() {
                _removeChild(style);
            })).observe(style, {childList: true});
        }

        function _create() {
            var style = _appendChild(document.createElement('style'));
            style.type = 'text/css';
            Object.assign(style, props);
            function insertRule(rule) {
                try {
                    style.sheet.insertRule(rule, 0);
                } catch (e) {
                    console.error(e);
                }
            }
            if (typeof rules === 'string') {
                insertRule(rules);
            } else {
                rules.forEach(insertRule);
            }
            if (!skip_protect) {
                _protect(style);
            }
            return style;
        }

        var style = _create();
        if (skip_protect) {
            return style;
        }

        (new MutationObserver(function(ms){
            var m, node,
                resolveInANewContext = function(resolve){
                    setTimeout(function(resolve){
                        resolve(_create());
                    }, 0, resolve);
                },
                setStyle = function(st){
                    style = st;
                };
            for (m of ms) {
                for (node of m.removedNodes) {
                    if (node === style) {
                        (new Promise(resolveInANewContext))
                            .then(setStyle);
                    }
                }
            }
        })).observe(_de, {childList:true});

        return style;
    }

    // https://greatest.deepsurf.us/scripts/19144-websuckit/
    function useWSI() {
        // check does browser support Proxy and WebSocket
        if (typeof Proxy !== 'function' ||
            typeof WebSocket !== 'function') {
            return;
        }

        function getWrappedCode(removeSelf) {
            var text = getWrappedCode.toString()+WSI.toString();
            text = (
                '(function(){"use strict";'+
                text.replace(/\/\/[^\r\n]*/g,'').replace(/[\s\r\n]+/g,' ')+
                '(new WSI(self||window)).init();'+
                '})();\n'+
                (removeSelf?'var s = document.currentScript; if (s) {s.parentNode.removeChild(s);}':'')
            );
            return text;
        }

        function WSI(win, safeWin) {
            safeWin = safeWin || win;
            var masks = [], filter;
            for (filter of [// blacklist
                '||185.87.50.147^',
                '||10root25.website^', '||24video.xxx^',
                '||adlabs.ru^', '||adspayformymortgage.win^', '||aviabay.ru^',
                '||bgrndi.com^', '||brokeloy.com^',
                '||cnamerutor.ru^',
                '||docfilms.info^', '||dreadfula.ru^',
                '||et-code.ru^',
                '||franecki.net^', '||film-doma.ru^',
                '||free-torrent.org^', '||free-torrent.pw^',
                '||free-torrents.org^', '||free-torrents.pw^',
                '||game-torrent.info^', '||gocdn.ru^',
                '||hdkinoshka.com^', '||hghit.com^', '||hindcine.net^',
                '||kiev.ua^', '||kinotochka.net^',
                '||kinott.com^', '||kinott.ru^', '||kuveres.com^',
                '||lepubs.com^', '||luxadv.com^', '||luxup.ru^', '||luxupcdna.com^',
                '||mail.ru^', '||marketgid.com^', '||mixadvert.com^', '||mxtads.com^',
                '||nickhel.com^',
                '||oconner.biz^', '||oconner.link^', '||octoclick.net^', '||octozoon.org^',
                '||pkpojhc.com^',
                '||psma01.com^', '||psma02.com^', '||psma03.com^',
                '||recreativ.ru^', '||redtram.com^', '||regpole.com^', '||rootmedia.ws^', '||ruttwind.com^',
                '||skidl.ru^',
                '||torvind.com^', '||traffic-media.co^', '||trafmag.com^',
                '||webadvert-gid.ru^', '||webadvertgid.ru^',
                '||xxuhter.ru^',
                '||yuiout.online^',
                '||zoom-film.ru^'
            ]) {
                masks.push(new RegExp(
                    filter.replace(/([\\\/\[\].*+?(){}$])/g, '\\$1')
                    .replace(/\^(?!$)/g,'\\.?[^\\w%._-]')
                    .replace(/\^$/,'\\.?([^\\w%._-]|$)')
                    .replace(/^\|\|/,'^(ws|http)s?:\\/+([^\/.]+\\.)*'),
                    'i'));
            }

            function isBlocked(url) {
                for (var mask of masks) {
                    if (mask.test(url)) {
                        return true;
                    }
                }
                return false;
            }

            var realWebSocket = win.WebSocket;
            function wsGetter (target, name) {
                try {
                    if (typeof realWebSocket.prototype[name] === 'function') {
                        if (name === 'close' || name === 'send') { // send also closes connection
                            target.readyState = realWebSocket.CLOSED;
                        }
                        return (
                            function fake() {
                                console.log('[WSI] Invoked function "'+name+'"', '| Tracing', (new Error()));
                                return;
                            }
                        );
                    }
                    if (typeof realWebSocket.prototype[name] === 'number') {
                        return realWebSocket[name];
                    }
                } catch(ignore) {}
                return target[name];
            }

            function createWebSocketWrapper(target) {
                return new Proxy(realWebSocket, {
                    construct: function (target, args) {
                        var url = args[0];
                        console.log('[WSI] Opening socket on ' + url + ' \u2026');
                        if (isBlocked(url)) {
                            console.log("[WSI] Blocked.");
                            return new Proxy({
                                url: url,
                                readyState: realWebSocket.OPEN
                            }, {
                                get: wsGetter
                            });
                        }
                        return new target(args[0], args[1]);
                    }
                });
            }

            function WorkerWrapper() {
                var realWorker = win.Worker;
                win.Worker = function Worker() {
                    var isBlobURL = /^blob:/i,
                        resourceURI = arguments[0],
                        deepLogMode = false,
                        xhr = null,
                        _callbacks = new WeakMap(),
                        _worker = null,
                        _onevs = { names: ['onmessage', 'onerror'] },
                        _actions = [],
                        /*jshint validthis:true */
                        _self = this;

                    function log() {
                        if (deepLogMode) {
                            console.log.apply(this, arguments);
                        }
                    }

                    function callbackWrapper(func) {
                        if (typeof func !== 'function') {
                            return undefined;
                        }
                        return (
                            function callback() {
                                func.apply(_self, arguments);
                            }
                        );
                    }

                    function updateWorker() {
                        var action, name, args;
                        for ([action, name, args] of _actions) {
                            log(_worker, action, name, args);
                            if (action === 'set') {
                                _worker[name] = callbackWrapper(args);
                            }
                            if (action === 'call') {
                                _worker[name].apply(_worker, args);
                            }
                        }
                        _actions.length = 0;
                        log('Applied buffered actions.');
                    }

                    function createGetSet(prop) {
                        return {
                            get: function() {
                                return _onevs[prop];
                            },
                            set: function(val) {
                                _onevs[prop] = val;
                                if (_worker) {
                                    _worker[prop] = callbackWrapper(val);
                                } else {
                                    _actions.push(['set', prop, val]);
                                    log('Stored into buffer:', arguments);
                                }
                                return val;
                            },
                            enumerable: true
                        };
                    }
                    for (var _onev of _onevs.names) {
                        Object.defineProperty(_self, _onev, createGetSet(_onev));
                    }

                    _self.postMessage = function(){
                        if (_worker) {
                            _worker.postMessage.apply(_worker, arguments);
                        } else {
                            _actions.push(['call', 'postMessage', arguments]);
                            log('Stored into buffer:', arguments);
                        }
                    };
                    _self.terminate = function(){
                        if (_worker) {
                            _worker.terminate();
                        } else {
                            _actions.push(['call','terminate', arguments]);
                            log('Stored into buffer:', arguments);
                        }
                    };
                    _self.addEventListener = function(event, callback, other) {
                        if (typeof callback !== 'function') {
                            return;
                        }
                        if (!_callbacks.has(callback)) {
                            _callbacks.set(callback, callbackWrapper(callback));
                        }
                        arguments[1] = _callbacks.get(callback);
                        if (_worker) {
                            _worker.addEventListener.apply(_worker, arguments);
                        } else {
                            _actions.push(['call', 'addEventListener', arguments]);
                            log('Stored into buffer:', arguments);
                        }
                    };
                    _self.removeEventListener = function(event, callback, other){
                        if (typeof callback !== 'function' || !_callbacks.has(callback)) {
                            return;
                        }
                        arguments[1] = _callbacks.get(callback);
                        _callbacks.delete(callback);
                        if (_worker) {
                            _worker.removeEventListener.apply(_worker, arguments);
                        } else {
                            _actions.push(['call', 'removeEventListener', arguments]);
                            log('Stored into buffer:', arguments);
                        }
                    };

                    if (!isBlobURL.test(resourceURI)) {
                        _worker = new realWorker(resourceURI);
                        return; // not a blob, no need to wrap
                    }

                    xhr = new XMLHttpRequest();
                    xhr.responseType = 'blob';
                    try {
                        xhr.open('GET', resourceURI, true);
                    } catch(ignore) {
                        _worker = new realWorker(resourceURI);
                        return; // failed to open connection, unable to continue wrapping procedure
                    }
                    (new Promise(function(resolve, reject){
                        if (xhr.readyState !== XMLHttpRequest.OPENED) {
                            // connection wasn't opened, unable to continue wrapping procedure
                            return reject();
                        }
                        xhr.onload = function(){
                            if (this.status === 200) {
                                var reader = new FileReader();
                                reader.addEventListener("loadend", function() {
                                    resolve(new realWorker(URL.createObjectURL(
                                        new Blob([getWrappedCode(false)+this.result])
                                    )));
                                });
                                reader.readAsText(this.response);
                            }
                        };
                        xhr.onerror = function(e) {
                            reject(e);
                        };
                        xhr.send();
                    })).then(function(val) {
                        _worker = val;
                        updateWorker();
                    }).catch(function(e) {
                        // connection were blocked by CSP or something else triggered error event on xhr object
                        // it should be safe to create worker with original URI now and let it dispatch error event
                        _worker = new realWorker(resourceURI);
                        updateWorker();
                    });

                    if (deepLogMode) {
                        return new Proxy(_self, {
                            get: function(target, prop) {
                                console.log('Worker _get_', prop);
                                return target[prop];
                            },
                            set: function(target, prop, val) {
                                console.log('Worker _set_', prop, '_to_', val);
                                target[prop] = val;
                                return val;
                            }
                        });
                    }
                }.bind(safeWin);
            }

            function CreateElementWrapper() {
                var realCreateElement = Document.prototype.createElement,
                    _addEventListener = Element.prototype.addEventListener,
                    code = encodeURIComponent('<scr'+'ipt>'+getWrappedCode(true)+'</scr'+'ipt>\n'),
                    isDataURL = /^data:/i,
                    isBlobURL = /^blob:/i;

                function frameRewrite(e) {
                    var f = e.target,
                        w = f.contentWindow;
                    if (!f.src || (w && isBlobURL.test(f.src))) {
                        w.WebSocket = createWebSocketWrapper();
                    }
                    if (isDataURL.test(f.src) && f.src.indexOf(code) < 0) {
                        f.src = f.src.replace(',',',' + code);
                    }
                }

                var scriptMap = new WeakMap();
                scriptMap.isBlocked = isBlocked;
                var onErrorWrapper = {
                    set: function(val) {
                        if (scriptMap.has(this)) {
                            this.removeEventListener('error', scriptMap.get(this).wrp, false);
                            scriptMap.delete(this);
                        }
                        if (!val || typeof val !== 'function') {
                            return val;
                        }
                        scriptMap.set(this, {
                            org: val,
                            wrp: function() {
                                if (scriptMap.isBlocked(this.src)) {
                                    console.log('[WSI] Blocked "onerror" callback from', this);
                                    return;
                                }
                                scriptMap.get(this).org.apply(this, arguments);
                            }
                        });
                        this.addEventListener('error', scriptMap.get(this).wrp, false);
                        return val;
                    },
                    get: function() {
                        return scriptMap.has(this) ? scriptMap.get(this).org : null;
                    },
                    enumerable: true
                };
                function wrappedCreateElement(name) {
                    /*jshint validthis:true */
                    var el = realCreateElement.apply(this, arguments);
                    if (el.tagName === 'IFRAME') {
                        _addEventListener.call(el, 'load', frameRewrite, false);
                    }
                    if (el.tagName === 'SCRIPT') {
                        Object.defineProperty(el, 'onerror', onErrorWrapper);
                    }
                    return el;
                }
                Document.prototype.createElement = wrappedCreateElement;

                document.addEventListener('DOMContentLoaded', function(){
                    for (var ifr of document.querySelectorAll('IFRAME')) {
                        _addEventListener.call(ifr, 'load', frameRewrite, false);
                    }
                }, false);
            }

            this.init = function() {
                win.WebSocket = createWebSocketWrapper();
                if (!(/firefox/i.test(navigator.userAgent))) {
                    WorkerWrapper(); // skip WorkerWrapper in Firefox
                }
                if (typeof document !== 'undefined') {
                    CreateElementWrapper();
                }
            };
        }

        if (isFirefox) {
            var script = document.createElement('script');
            script.appendChild(document.createTextNode(getWrappedCode(true)));
            document.head.insertBefore(script, document.head.firstChild);
            return; //we don't want to call functions on page from here in Fx, so exit
        }

        (new WSI((unsafeWindow||self||window),(self||window))).init();
    }

    if (!isFirefox) { // scripts for non-Firefox browsers

        // https://greatest.deepsurf.us/scripts/14720-it-s-not-important
        (function(){
            var imptt = /((display|(margin|padding)(-top|-bottom)?)\s*:[^;!]*)!\s*important/ig,
                ret_b = function(a,b){return b;},
                _toLowerCase = String.prototype.toLowerCase,
                protectedNodes = new WeakSet();

            function unimportanter(node) {
                var style = (node.nodeType === Node.ELEMENT_NODE) ?
                    _getAttribute.call(node, 'style') : null;
                if (!style || !imptt.test(style) || node.style.display === 'none') {
                    return false; // get out if we have nothing to do here
                }
                if (node.nodeName === 'IFRAME' && node.src &&
                    node.src.slice(0,17) === 'chrome-extension:') {
                    return false; // Web of Trust uses this method to add their frame
                }
                protectedNodes.add(node);
                _setAttribute.call(node, 'style',
                                   style.replace(imptt, ret_b));
                return true;
            }

            function logger(log) {
                if (log) {
                    console.log('Some page elements became a bit less important.');
                }
            }

            (new MutationObserver(function(mutations) {
                setTimeout(function(ms) {
                    var log = false, m, node;
                    for (m of ms) {
                        for (node of m.addedNodes) {
                            log = unimportanter(node) | log;
                        }
                    }
                    logger(log);
                }, 0, mutations);
            })).observe(document, {
                childList : true,
                subtree : true
            });

            Element.prototype.setAttribute = function setAttribute(name, value) {
                "[native code]";
                var replaced = value;
                if (_toLowerCase.call(name) === 'style' && protectedNodes.has(this)) {
                    replaced = value.replace(imptt, ret_b);
                    logger(replaced !== value);
                }
                return _setAttribute.call(this, name, replaced);
            };

            win.addEventListener ("load", function(){
                var log = false, imp;
                for (imp of document.querySelectorAll('[style*="!"]')) {
                    log = unimportanter(imp) | log;
                }
                logger(log);
            }, false);
        })();

    }

    if (/^https?:\/\/(mail\.yandex\.|music\.yandex\.|news\.yandex\.|(www\.)?yandex\.[^\/]+\/(yand)?search[\/?])/i.test(win.location.href)) {
        // https://greatest.deepsurf.us/en/scripts/809-no-yandex-ads
        (function(){
            var adWords = ['Яндекс.Директ','Реклама','Ad'],
                genericAdSelectors = (
                    '.serp-adv__head + .serp-item,'+
                    '#adbanner,'+
                    '.serp-adv,'+
                    '.b-spec-adv,'+
                    'div[class*="serp-adv__"]:not(.serp-adv__found):not(.serp-adv__displayed)'
                );
            function remove(node) {
                node.parentNode.removeChild(node);
            }
            // Generic ads removal and fixes
            function removeGenericAds() {
                var s = document.querySelector('.serp-header');
                if (s) {
                    s.style.marginTop='0';
                }
                for (s of document.querySelectorAll(genericAdSelectors)) {
                    remove(s);
                }
            }
            // Search ads
            function removeSearchAds() {
                var s, item, l;
                for (s of document.querySelectorAll('.t-construct-adapter__legacy')) {
                    item = s.querySelector('.organic__subtitle');
                    l = window.getComputedStyle(item, ':after').content;
                    if (item && adWords.indexOf(l.replace(/"/g,'')) > -1) {
                        remove(s);
                        console.log('Ads removed.');
                    }
                }
            }
            // News ads
            function removeNewsAds() {
                for (var s of document.querySelectorAll(
                    '.page-content__left > *,'+
                    '.page-content__right > *:not(.page-content__col),'+
                    '.page-content__right > .page-content__col > *'
                )) {
                    if (s.textContent.indexOf(adWords[0]) > -1 ||
                        (s.clientHeight < 15 && s.classList.contains('rubric'))) {
                        remove(s);
                        console.log('Ads removed.');
                    }
                }
            }
            // Music ads
            function removeMusicAds() {
                for (var s of document.querySelectorAll('.ads-block')) {
                    remove(s);
                }
            }
            // Mail ads
            function removeMailAds() {
                var slice = Array.prototype.slice,
                    nodes = slice.call(document.querySelectorAll('.ns-view-folders')),
                    node, len, cls;
                for (node of nodes) {
                    if (!len || len > node.classList.length) {
                        len = node.classList.length;
                    }
                }
                node = nodes.pop();
                while (node) {
                    if (node.classList.length > len) {
                        for (cls of slice.call(node.classList)) {
                            if (cls.indexOf('-') === -1) {
                                remove(node);
                                break;
                            }
                        }
                    }
                    node = nodes.pop();
                }
            }
            // News fixes
            function removePageAdsClass() {
                if (document.body.classList.contains("b-page_ads_yes")){
                    document.body.classList.remove("b-page_ads_yes");
                    console.log('Page ads class removed.');
                }
            }
            // Function to attach an observer to monitor dynamic changes on the page
            function pageUpdateObserver(func, obj, params) {
                if (obj) {
                    (new MutationObserver(func)).observe(obj,(params || {childList:true, subtree:true}));
                }
            }
            // Cleaner
            document.addEventListener ('DOMContentLoaded', function() {
                removeGenericAds();
                if (win.location.hostname.search(/^mail\./i) === 0) {
                    pageUpdateObserver(function(ms, o){
                        var aside = document.querySelector('.mail-Layout-Aside');
                        if (aside) {
                            o.disconnect();
                            pageUpdateObserver(removeMailAds, aside);
                        }
                    }, document.querySelector('BODY'));
                    removeMailAds();
                } else if (win.location.hostname.search(/^music\./i) === 0) {
                    pageUpdateObserver(removeMusicAds, document.querySelector('.sidebar'));
                    removeMusicAds();
                } else if (win.location.hostname.search(/^news\./i) === 0) {
                    pageUpdateObserver(removeNewsAds, document.querySelector('BODY'));
                    pageUpdateObserver(removePageAdsClass, document.body, {attributes:true, attributesFilter:['class']});
                    removeNewsAds();
                    removePageAdsClass();
                } else {
                    pageUpdateObserver(removeSearchAds, document.querySelector('.main__content'));
                    removeSearchAds();
                }
            });
        })();
    }
    // Yandex Link Tracking
    if (/^https?:\/\/([^.]+\.)*yandex\.[^\/]+/i.test(win.location.href)) {
        // Partially based on https://greatest.deepsurf.us/en/scripts/22737-remove-yandex-redirect
        (function(){
            var link, selectors = (
                'A[onmousedown*="/jsredir"],'+
                'A[data-vdir-href],'+
                'A[data-counter]'
            );
            function removeTrackingAttributes(link) {
                link.removeAttribute('onmousedown');
                if (link.hasAttribute('data-vdir-href')) {
                    link.removeAttribute('data-vdir-href');
                    link.removeAttribute('data-orig-href');
                }
                if (link.hasAttribute('data-counter')) {
                    link.removeAttribute('data-counter');
                    link.removeAttribute('data-bem');
                }
            }
            function removeTracking(scope) {
                for (link of scope.querySelectorAll(selectors)) {
                    removeTrackingAttributes(link);
                }
            }
            document.addEventListener('DOMContentLoaded', function(e) {
                removeTracking(e.target);
            });
            (new MutationObserver(function(ms) {
                var m, node;
                for (m of ms) {
                    for (node of m.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            if (node.tagName === 'A' && node.matches(selectors)) {
                                removeTrackingAttributes(node);
                            } else {
                                removeTracking(node);
                            }
                        }
                    }
                }
            })).observe(_de, {childList: true, subtree: true});
        })();
        return; //skip fixes for other sites
    }

    // https://greatest.deepsurf.us/en/scripts/21937-moonwalk-hdgo-kodik-fix v0.8 (adapted)
    document.addEventListener ('DOMContentLoaded', function() {
        var tmp;
        function log (e) {
            console.log('Moonwalk&HDGo&Kodik FIX: ' + e + ' player in ' + win.location.href);
        }
        if (win.adv_enabled !== undefined && win.condition_detected !== undefined) { // moonwalk
            log('Moonwalk');
            if (win.adv_enabled) {
                win.adv_enabled = false;
            }
            win.condition_detected = false;
            if (win.MXoverrollCallback) {
                document.addEventListener('click', function catcher(e){
                    e.stopPropagation();
                    win.MXoverrollCallback.call(window);
                    document.removeEventListener('click', catcher, true);
                }, true);
            }
        } else if (win.stat_url !== undefined && win.is_html5 !== undefined && win.is_wp8 !== undefined) { // hdgo
            log('HDGo');
            document.body.onclick = null;
            tmp = document.querySelector('#swtf');
            if (tmp) {
                tmp.style.display = 'none';
            }
            if (win.banner_second !== undefined) {
                win.banner_second = 0;
            }
            if (win.$banner_ads !== undefined) {
                win.$banner_ads = false;
            }
            if (win.$new_ads !== undefined) {
                win.$new_ads = false;
            }
            if (win.createCookie !== undefined) {
                win.createCookie('popup','true','999');
            }
            if (win.canRunAds !== undefined && win.canRunAds !== true) {
                win.canRunAds = true;
            }
        } else if (win.MXoverrollCallback && win.iframeSearch !== undefined) { // kodik
            log('Kodik');
            tmp = document.querySelector('.play_button');
            if (tmp) {
                tmp.onclick = win.MXoverrollCallback.bind(window);
            }
            win.IsAdBlock = false;
        }
    }, false);

    // Automated protection against specific circumvention method based on unwrapping various functions,
    // hiding ads in the Shadow DOM and injecting iFrames with ads. Previously this code were known as
    // apiBreaker since it were breaking Shadow DOM and onerror/onload API on specific domains. This
    // version should be safe enough to run on majority of sites without actually breaking them.
    scriptLander(function() {
        var blacklist = new WeakMap(),
            replacer, func;
        /* Wrap functions used to attach shadow root to a node */
        replacer = function (func) {
            return function() {
                blacklist.set(this, true);
                return func.apply(this, arguments);
            };
        };
        for (func of ['createShadowRoot', 'attachShadow']) {
            if (func in Element.prototype) {
                Element.prototype[func] = replacer(Element.prototype[func]);
            }
        }
        /* Wrap functions used to insert/append elements to check for IFRAME objects */
        replacer = function (func) {
            return function(el, par) {
                if (el.tagName === 'IFRAME' &&
                    ((typeof par === 'object' && blacklist.get(par))/* ||
                                 el.style.display === 'none'*/)) {
                    console.log('Blocked suspicious', func.name, arguments);
                    return null;
                }
                return func.apply(this, arguments);
            };
        };
        for (func of [/*'appendChild', */'insertBefore']) {
            Object.defineProperty(Element.prototype, func, {
                value: replacer(Element.prototype[func]), enumerable: true
            });
        }
    });

    // === Helper functions ===

    // function to search and remove nodes by content
    // selector - standard CSS selector to define set of nodes to check
    // words - regular expression to check content of the suspicious nodes
    // params - object with multiple extra parameters:
    //   .log - display log in the console
    //   .hide - set display to none instead of removing from the page
    //   .parent - parent node to remove if content is found in the child node
    //   .siblings - number of simling nodes to remove (excluding text nodes)
    function scRemove(e) {
        e.parentNode.removeChild(e);
    }
    function scHide(e) {
        var s = _getAttribute.call(e, 'style') || '',
            h = ';display:none!important;';
        if (s.indexOf(h) < 0) {
            _setAttribute.call(e, 'style', s+h);
        }
    }
    function scissors (selector, words, scope, params) {
        if (params.log) console.log('[s] starting with', selector, words, scope, JSON.stringify(params));
        var remFunc = (params.hide ? scHide : scRemove),
            iterFunc = (params.siblings > 0 ?
                        'nextSibling' :
                        'previousSibling'),
            toRemove = [],
            siblings,
            node;
        for (node of scope.querySelectorAll(selector)) {
            if (params.log) console.log('[s] found node', node);
            if (params.parent) {
                while(node !== scope && !(node.matches(params.parent))) {
                    node = node.parentNode;
                }
                if (params.log) console.log('[s] moving to parent node', node);
            }
            if (words.test(node.innerHTML) || !node.childNodes.length) {
                // drill up to the specified parent node if required
                if (node === scope) {
                    if (params.log) console.log('[s] oops, we don\'t want to remove our scope!');
                    break;
                }
                if (toRemove.indexOf(node) === -1) {
                    if (params.log) console.log('[s] adding node into list for removal');
                    toRemove.push(node);
                    // add multiple nodes if defined more than one sibling
                    siblings = Math.abs(params.siblings) || 0;
                    while (siblings) {
                        node = node[iterFunc];
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            if (params.log) console.log('[s] adding sibling node', node);
                            toRemove.push(node);
                            siblings -= 1; //count only element nodes
                        } else if (!params.hide) {
                            if (params.log) console.log('[s] adding sibling node', node);
                            toRemove.push(node);
                        }
                    }
                } else {
                    if (params.log) console.log('[s] node already marked for removal');
                }
            } else {
                if (params.log) console.log('[s] word test failed, proceed to the next node');
            }
        }
        if (params.log) console.log('[s] proceed with', (params.hide?'hide':'removal'), 'of', toRemove);
        for (node of toRemove) {
            remFunc(node);
        }
        return toRemove.length;
    }

    // function to perform multiple checks if ads inserted with a delay
    // by default does 30 checks withing a 3 seconds unless nonstop mode specified
    // also does 1 extra check when a page completely loads
    // selector and words - passed dow to scissors
    // params - object with multiple extra parameters:
    //   .log - display log in the console
    //   .root - selector to narrow down scope to scan;
    //   .observe - if true then check will be performed continuously;
    // Other parameters passed down to scissors.
    function gardener(selector, words, params) {
        params = params || {};
        if (params.log) console.log('[g] starting with', selector, words, JSON.stringify(params));
        var scope = document,
            nonstop = false;
        // narrow down scope to a specific element
        if (params.root) {
            scope = scope.querySelector(params.root);
            if (!scope) {// exit if the root element is not present on the page
                return 0;
            }
            if (params.log) console.log('[g] scope', scope);
        }
        // add observe mode if required
        if (params.observe) {
            if (typeof MutationObserver === 'function') {
                (new MutationObserver(function(ms){
                    for (var m of ms) {
                        if (m.addedNodes.length) {
                            scissors(selector, words, scope, params);
                        }
                    }
                })).observe(scope, {childList:true, subtree: true});
                if (params.log) console.log('[g] observer enabled');
            } else {
                nonstop = true;
                if (params.log) console.log('[g] nonstop mode enabled');
            }
        }
        // wait for a full page load to do one extra cut
        win.addEventListener('load',function(){
            if (params.log) console.log('[g] onload cleanup');
            scissors(selector, words, scope, params);
        });
        // do multiple cuts until ads removed
        function cut(sci, s, w, sc, p, i) {
            if (i > 0) {
                i -= 1;
            }
            if (i && !sci(s, w, sc, p)) {
                setTimeout(cut, 100, sci, s, w, sc, p, i);
            }
        }
        cut(scissors, selector, words, scope, params, (nonstop ? -1 : 30));
    }

    // Helper function to close background tab if site opens itself in a new tab and then
    // loads a 3rd-party page in the background one (thus performing background redirect).
    function preventBackgroundRedirect() {
        // create "cose_me" event to call high-level window.close()
        var key = Math.random().toString(36).substr(2);
        window.addEventListener('close_me_'+key, function(e) {
            window.close();
        });

        // window.open wrapper
        function pbrLander() {
            var orgOpen = window.open.bind(window),
                idx = String.prototype.indexOf,
                event = new CustomEvent("close_me_%key%", {});
            function closeWindow(){
                // site went to a new tab and attempts to unload
                // call for high-level close through event
                window.dispatchEvent(event);
            }
            // window.open wrapper
            function open(){
                console.log(arguments, window.location.host);
                if (arguments[0] &&
                    (idx.call(arguments[0], window.location.host) > -1 ||
                     idx.call(arguments[0], '://') === -1)) {
                    window.addEventListener('unload', closeWindow, true);
                }
                orgOpen.apply(window, arguments);
            }
            window.open = open.bind(window);
            // Node.createElement wrapper to prevent click-dispatch in Google Chrome and similar browsers
            var realCreateElement = Document.prototype.createElement;
            function wrappedCreateElement(name) {
                /*jshint validthis:true */
                var el = realCreateElement.apply(this, arguments);
                if (el.tagName === 'A') {
                    el.addEventListener('click', function(e){
                        if (!e.target.parentNode || !e.isTrusted) {
                            window.addEventListener('unload', closeWindow, true);
                        }
                    }, false);
                }
                return el;
            }
            Document.prototype.createElement = wrappedCreateElement;
            console.log("Background redirect prevention enabled.");
        }

        // land wrapper on the page
        var script = document.createElement('script');
        script.appendChild(document.createTextNode('('+pbrLander.toString().replace(/%key%/g,key)+')();'));
        document.head.insertBefore(script, document.head.firstChild);
        script.parentNode.removeChild(script);
    }

    // Function to catch and block various methods to open a new window with 3rd-party content.
    // Some advertisement networks went way past simple window.open call to circumvent default popup protection.
    // This funciton blocks window.open, ability to restore original window.open from an IFRAME object,
    // ability to perform an untrusted (not initiated by user) click on a link, click on a link without a parent
    // node or simply a link with piece of javascript code in the HREF attribute.
    function preventPopups() {
        if (inIFrame) {
            var i = -1, val;
            do {
                i++;
                val = GM_getValue('forbid.popups.'+i);
            } while(val & val !== win.location.href);
            GM_setValue('forbid.popups.'+i, win.location.href);
            win.top.postMessage('forbid.popups.'+i,'*');
            return;
        }

        scriptLander(function() {
            var _createElement = Document.prototype.createElement,
                _appendChild = Element.prototype.appendChild;

            function open() {
                '[native code]';
                console.log('Site attempted to open a new window', arguments);
                function nil(){}
                return {
                    document: {
                        write: nil,
                        writeln: nil
                    }
                };
            }

            function redefineOpen(obj) {
                Object.defineProperty(obj, 'open', {
                    get: function () {
                        return open;
                    },
                    set: function (val) {
                        console.log('Site attempted to change window.open');
                        return val;
                    },
                    enumerable: true
                });
            }
            redefineOpen(win);

            Document.prototype.createElement = function createElement(name) {
                /*jshint validthis:true */
                var el = _createElement.apply(this, arguments);
                if (el.tagName === 'A') {
                    el.addEventListener('click', function(e) {
                        if (!e.target.parentNode || !e.isTrusted ||
                            (e.target.href && e.target.href.toLowerCase().indexOf('javascript') > -1)) {
                            e.preventDefault();
                            console.log('Blocked suspicious click event', e, 'on', e.target);
                        }
                    }, false);
                }
                if (el.tagName === 'IFRAME') {
                    el.addEventListener('load', function(){
                        try {
                            redefineOpen(this.contentWindow);
                        } catch(ignore) {}
                    }, false);
                }
                return el;
            };

            Element.prototype.appendChild = function appendChild() {
                /*jshint validthis:true */
                var child = _appendChild.apply(this, arguments);
                if (child && child.nodeType === Node.ELEMENT_NODE && child.tagName === 'IFRAME') {
                    try {
                        redefineOpen(child.contentWindow);
                    } catch(ignore) {}
                }
                return child;
            };
            console.log('Popup prevention enabled.');
        });
    }
    // External listener for case when site known to open popups were loaded in iframe
    // It will sandbox any iframe which will send message 'forbid.popups' (preventPopups sends it)
    // Some sites replace frame's window.location with data-url to run in clean context
    (function(){
        var popWindows = new WeakSet();
        if (!inIFrame) {
            window.addEventListener('message', function(e){
                if (e.data.slice(0,13) === 'forbid.popups' && !popWindows.has(e.source)) {
                    var src = GM_getValue(e.data);
                    if (src) {
                        GM_deleteValue(e.data);
                    }
                    popWindows.add(e.source); // remember window of iframe with suspected domain
                    for (var frame of document.querySelectorAll('iframe')) {
                        if (frame.contentWindow === e.source) {
                            if (frame.hasAttribute('sandbox')) {
                                // remove allow-popups if frame already sandboxed
                                frame.sandbox.remove('allow-popups');
                            } else {
                                // set sandbox mode for troublesome frame and allow scripts and forms
                                frame.setAttribute('sandbox','allow-forms allow-scripts');
                            }
                            console.log('Disallowed popups from iframe', frame);
                            // reload frame content to apply restrictions
                            if (!src) {
                                src = frame.src;
                                console.log('Unable to get current iframe location, reloading from src', src);
                            } else {
                                console.log('Reloading iframe with URL', src);
                            }
                            frame.src = 'about:blank';
                            frame.src = src;
                        }
                    }
                }
            }, false);
        }
    })();

    // Currently unused piece of code developed to prevent site from registering serviceWorker
    // and uninstall any existing instances of serivceWorker in case there is one already.
    /* Commented out since not used
    function forbidServiceWorker() {
        if (!("serviceWorker" in navigator)) {
            return;
        }
        var svr = navigator.serviceWorker.ready;
        Object.defineProperty(navigator, 'serviceWorker', {
            value: {
                register: function(){
                    console.log('Registration of serviceWorker ' + arguments[0] + ' blocked.');
                    return new Promise(function(){});
                },
                ready: new Promise(function(){}),
                addEventListener:function(){}
            }
        });
        document.addEventListener('DOMContentLoaded', function() {
            if (!svr) {
                return;
            }
            svr.then(function(sw) {
                console.log('Found existing serviceWorker:', sw);
                console.log('Attempting to unregister...');
                sw.unregister().then(function() {
                    console.log('Unregistered! :)');
                }).catch(function(err) {
                    console.log('Unregistration failed. :(', err);
                    console.log('Try to remove it manually:');
                    console.log(' 1. Open: chrome://serviceworker-internals/ (Google Chrome and alike) or about:serviceworkers (Mozilla Firefox) in a new tab.');
                    console.log(' 2. Search there for one with "'+document.domain+'" in the name.');
                    console.log(' 3. Use buttons in the same block with service you found to stop it and uninstall/unregister.');
                });
            }).catch(function(err) {
                console.log("Lol, it failed on it's own. -_-", err);
            });
        }, false);
    }
    /**/

    // Currently obsolete code developed to prevent error and load calls on objects supposed to load resources
    // from the internet like IMG or IFRAME, but missing SRC/HREF attribute. Usually tricks like this are used
    // to unwrap wrapped functions to be able to load ads.
    /* Commented out since not used
    function errorAndLoadEventsFilter() {
        var toString = Function.prototype.toString,
            addEventListener = Element.prototype.addEventListener,
            removeEventListener = Element.prototype.removeEventListener,
            hasAttribute = Element.prototype.hasAttribute,
            evtMap = new WeakMap();
        Element.prototype.addEventListener = function(evt, func, capt) {
            if (evt === 'error' || evt === 'load') {
                if (!evtMap.get(func)) {
                    evtMap.set(func, function() {
                        if (hasAttribute.call(this, 'src') ||
                            hasAttribute.call(this, 'href')) {
                            func.apply(this, arguments);
                        } else {
                            console.log('Blocked', evt, 'handler', toString.call(func), 'on', this);
                        }
                    });
                }
            }
            addEventListener.call(this, evt, (evtMap.get(func) || func), capt);
        };
        Element.prototype.removeEventListener = function(evt, func, capt) {
            removeEventListener.call(this, evt, (evtMap.get(func) || func), capt);
        };
        Object.defineProperty(HTMLElement.prototype, 'onload', {
            set: function(func) {
                if(evtMap.has(this)) {
                    if (evtMap.get(this).onload) {
                        Element.prototype.removeEventListener.call(this, 'load', evtMap.get(this).onload, false);
                    }
                    evtMap.get(this).onload = func;
                } else {
                    evtMap.set(this, { onload: func });
                }
                if (func) {
                    Element.prototype.addEventListener.call(this, 'load', func, false);
                }
                return func;
            },
            get: function() {
                return evtMap.has(this) ? evtMap.get(this).onload : null;
            }
        });
        Object.defineProperty(HTMLElement.prototype, 'onerror', {
            set: function(func) {
                if (evtMap.has(this)) {
                    evtMap.get(this).onerror = func;
                } else {
                    evtMap.set(this, { onerror: func });
                }
                if (func) {
                    console.log('Blocked error handler', toString.call(func), 'on', this);
                }
                return func;
            },
            get: function() {
                return evtMap.has(this) ? evtMap.get(this).onerror : null;
            }
        });
    }
    /**/

    // === Scripts for specific domains ===

    var scripts = {};
    // prevent popups and redirects block
    var preventPopupsNow = { 'now': preventPopups },
        preventBackgroundRedirectNow = { 'now': preventBackgroundRedirect };
    // Popups
    scripts['biqle.ru'] = preventPopupsNow;
    scripts['chaturbate.com'] = preventPopupsNow;
    scripts['dfiles.ru'] = preventPopupsNow;
    scripts['hentaiz.org'] = preventPopupsNow;
    scripts['mirrorcreator.com'] = preventPopupsNow;
    scripts['online-multy.ru'] = preventPopupsNow;
    scripts['openload.co'] = preventPopupsNow;
    scripts['radikal.ru'] = preventPopupsNow;
    scripts['seedoff.cc'] = preventPopupsNow;
    scripts['seedoff.tv'] = preventPopupsNow;
    scripts['tapochek.net'] = preventPopupsNow;
    scripts['thepiratebay.org'] = preventPopupsNow;
    scripts['torseed.net'] = preventPopupsNow;
    scripts['unionpeer.com'] = preventPopupsNow;
    scripts['zippyshare.com'] = preventPopupsNow;
    // Background redirects
    scripts['mediafire.com'] = preventBackgroundRedirectNow;
    scripts['megapeer.org'] = preventBackgroundRedirectNow;
    scripts['megapeer.ru'] = preventBackgroundRedirectNow;
    scripts['perfectgirls.net'] = preventBackgroundRedirectNow;
    scripts['turbobit.net'] = preventBackgroundRedirectNow;

    // other
    scripts['4pda.ru'] = {
        'now': function() {
            // https://greatest.deepsurf.us/en/scripts/14470-4pda-unbrender
            var isForum = document.location.href.search('/forum/') !== -1,
                hStyle;

            function remove(n) {
                if (n) {
                    n.parentNode.removeChild(n);
                }
            }

            function afterClean() {
                hStyle.disabled = true;
                remove(hStyle);
            }

            function beforeClean() {
                // attach styles before document displayed
                hStyle = createStyle([
                    'html { overflow-y: scroll }',
                    'section[id] {'+(
                        'position: absolute;'+
                        'width: 100%'
                    )+'}',
                    'article + aside * { display: none !important }',
                    '#header + div:after {'+(
                        'content: "";'+
                        'position: fixed;'+
                        'top: 0;'+
                        'left: 0;'+
                        'width: 100%;'+
                        'height: 100%;'+
                        'background-color: #E6E7E9'
                    )+'}',
                    // http://codepen.io/Beaugust/pen/DByiE
                    '@keyframes spin { 100% { transform: rotate(360deg) } }',
                    'article + aside:after {'+(
                        'content: "";'+
                        'position: absolute;'+
                        'width: 150px;'+
                        'height: 150px;'+
                        'top: 150px;'+
                        'left: 50%;'+
                        'margin-top: -75px;'+
                        'margin-left: -75px;'+
                        'box-sizing: border-box;'+
                        'border-radius: 100%;'+
                        'border: 10px solid rgba(0, 0, 0, 0.2);'+
                        'border-top-color: rgba(0, 0, 0, 0.6);'+
                        'animation: spin 2s infinite linear'
                    )+'}'
                ], {id:'ubrHider'}, true);

                // display content of a page if time to load a page is more than 2 seconds to avoid
                // blocking access to a page if it is loading for too long or stuck in a loading state
                setTimeout(2000, afterClean);
            }

            createStyle([
                '#nav .use-ad { display: block !important }',
                'article:not(.post) + article:not(#id),'+
                'html:not(#id)>body:not(#id) a[target="_blank"] img[height="90"] { display: none !important }'
            ]);

            if (!isForum) {
                beforeClean();
            }

            // save links to non-overridden functions to use later
            var protectedElems;
            // protect/hide changed attributes in case site attempt to restore them
            function styleProtector(eventMode) {
                var isStyleText = function(t){ return t === 'style'; },
                    returnUndefined = function(){},
                    protectedElems = new WeakMap();
                function protoOverride(element, functionName, isStyleCheck, returnIfProtected) {
                    var oF = element.prototype[functionName], r;
                    element.prototype[functionName] = function() {
                        if (protectedElems.has(this) && isStyleCheck(arguments[0])) {
                            return returnIfProtected(this, arguments);
                        }
                        r = oF.apply(this, arguments);
                        return r;
                    };
                }
                protoOverride(Element, 'removeAttribute', isStyleText, returnUndefined);
                protoOverride(Element, 'hasAttribute', isStyleText, function(_this) {
                    return protectedElems.get(_this) !== null;
                });
                protoOverride(Element, 'setAttribute', isStyleText, function(_this, args) {
                    protectedElems.set(_this, args[1]);
                });
                protoOverride(Element, 'getAttribute', isStyleText, function(_this) {
                    return protectedElems.get(_this);
                });
                if (eventMode) {
                    var e = document.createEvent('Event');
                    e.initEvent('protoOverride', false, false);
                    window.protectedElems = protectedElems;
                    window.dispatchEvent(e);
                } else {
                    return protectedElems;
                }
            }
            if (isFirefox) {
                var s = document.createElement('script');
                s.textContent = '(' + styleProtector.toString() + ')(true);';
                window.addEventListener('protoOverride', function protoOverrideCallback(e){
                    if (win.protectedElems) {
                        protectedElems = win.protectedElems;
                        delete win.protectedElems;
                    }
                    document.removeEventListener('protoOverride', protoOverrideCallback, true);
                }, true);
                _appendChild(s);
                _removeChild(s);
            } else {
                protectedElems = styleProtector(false);
            }

            // clean a page
            window.addEventListener('DOMContentLoaded', function(){
                var rem, si, itm;
                function width(){ return window.innerWidth||_de.clientWidth||document.body.clientWidth||0; }
                function height(){ return window.innerHeight||_de.clientHeight||document.body.clientHeight||0; }


                if (isForum) {
                    si = document.querySelector('#logostrip');
                    if (si) {
                        remove(si.parentNode.nextSibling);
                    }
                }

                if (document.location.href.search('/forum/dl/') !== -1) {
                    document.body.setAttribute('style', (document.body.getAttribute('style')||'')+
                                               ';background-color:black!important');
                    for (itm of document.querySelectorAll('body>div')) {
                        if (!itm.querySelector('.dw-fdwlink')) {
                            remove(itm);
                        }
                    }
                }

                if (isForum) { // Do not continue if it's a forum
                    return;
                }

                si = document.querySelector('#header');
                if (si) {
                    rem = si.previousSibling;
                    while (rem) {
                        si = rem.previousSibling;
                        remove(rem);
                        rem = si;
                    }
                }

                for (itm of document.querySelectorAll('#nav li[class]')) {
                    if (itm && itm.querySelector('a[href^="/tag/"]')) {
                        remove(itm);
                    }
                }

                var style, result, fakeStyle,
                    fakeStyles = new WeakMap(),
                    styleProxy = {
                        get: function(target, prop) {
                            fakeStyle = fakeStyles.get(target);
                            if (fakeStyle.hasOwnProperty(prop)) {
                                return fakeStyle[prop];
                            } else {
                                return target[prop];
                            }
                        },
                        set: function(target, prop, value) {
                            fakeStyle = fakeStyles.get(target);
                            if (fakeStyle.hasOwnProperty(prop)) {
                                fakeStyle[prop] = value;
                            } else {
                                target[prop] = value;
                            }
                            return value;
                        }
                    };
                for (itm of document.querySelectorAll('DIV, A')) {
                    if (itm.tagName ==='DIV' && itm.offsetWidth > 0.95 * width() && itm.offsetHeight > 0.85 * height()) {
                        style = window.getComputedStyle(itm, null);
                        result = [];
                        if (style.backgroundImage !== 'none') {
                            result.push('background-image:none!important');
                        }
                        if (style.backgroundColor !== 'transparent' &&
                            style.backgroundColor !== 'rgba(0, 0, 0, 0)') {
                            result.push('background-color:transparent!important');
                        }
                        if (result.length) {
                            if (itm.getAttribute('style')) {
                                result.unshift(itm.getAttribute('style'));
                            }
                            fakeStyles.set(itm.style, {
                                'backgroundImage': itm.style.backgroundImage,
                                'backgroundColor': itm.style.backgroundColor
                            });
                            try {
                                Object.defineProperty(itm, 'style', {
                                    value: new Proxy(itm.style, styleProxy),
                                    enumerable: true
                                });
                            } catch (e) {
                                console.log('Unable to protect style property.', e);
                            }
                            if (protectedElems) {
                                protectedElems.set(itm, _getAttribute.call(itm, 'style'));
                            }
                            _setAttribute.call(itm, 'style', result.join(';'));
                        }
                    }
                    if (itm.tagName ==='A' && (itm.offsetWidth > 0.95 * width() || itm.offsetHeight > 0.85 * height())) {
                        if (protectedElems) {
                            protectedElems.set(itm, _getAttribute.call(itm, 'style'));
                        }
                        _setAttribute.call(itm, 'style', 'display:none!important');
                    }
                }

                for (itm of document.querySelectorAll('ASIDE>DIV')) {
                    if ( ((itm.querySelector('script, iframe, a[href*="/ad/www/"]') ||
                           itm.querySelector('img[src$=".gif"]:not([height="0"]), img[height="400"]')) &&
                          !itm.classList.contains('post') ) || !itm.childNodes.length ) {
                        remove(itm);
                    }
                }

                document.body.setAttribute('style', (document.body.getAttribute('style')||'')+';background-color:#E6E7E9!important');

                // display content of the page
                afterClean();
            });
        }
    };

    scripts['allmovie.pro'] = function() {
        // pretend to be Android to make site use different played for ads
        if (isSafari) {
            return;
        }
        Object.defineProperty(navigator, 'userAgent', {
            get: function(){ return 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19'; },
            enumerable: true
        });
    };
    scripts['rufilmtv.org'] = scripts['allmovie.pro'];

    scripts['anidub-online.ru'] = function() {
        var script = document.createElement('script');
        script.type = "text/javascript";
        script.innerHTML = "function ogonekstart1() {}";
        document.getElementsByTagName('head')[0].appendChild(script);

        var style = document.createElement('style');
        style.type = 'text/css';
        style.appendChild(document.createTextNode('.background {background: none!important;}'));
        style.appendChild(document.createTextNode('.background > script + div, .background > script ~ div:not([id]):not([class]) + div[id][class] {display:none!important}'));
        document.head.appendChild(style);
    };
    scripts['online.anidub.com'] = scripts['anidub-online.ru'];

    scripts['fs.to'] = function() {
        function skipClicker(i) {
            if (!i) {
                return;
            }
            var skip = document.querySelector('.b-aplayer-banners__close');
            if (skip) {
                skip.click();
            } else {
                setTimeout(skipClicker, 100, i-1);
            }
        }
        setTimeout(skipClicker, 100, 30);

        createStyle([
            '.l-body-branding *,'+
            '.b-styled__item-central,'+
            '.b-styled__content-right,'+
            '.b-styled__section-central,'+
            'div[id^="adsProxy-"]'+
            '{display:none!important}',
            'body {background-image:url(data:image/png;base64,'+
            'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACX'+
            'BIWXMAAC4jAAAuIwF4pT92AAAADUlEQVR42mOQUdL5DwACMgFq'+
            'BC3ttwAAAABJRU5ErkJggg==)!important}'
        ]);

        if (/\/(view_iframe|iframeplayer)\//i.test(document.location.pathname)) {
            var p = document.querySelector('#player:not([preload="auto"])'),
                m = document.querySelector('.main'),
                adStepper = function(p) {
                    if (p.currentTime < p.duration) {
                        p.currentTime += 1;
                    }
                },
                adSkipper = function(f, p) {
                    f.click();
                    p.waitAfterSkip = false;
                    p.longerSkipper = false;
                    console.log('Пропустили.');
                },
                cl = function(p) {
                    var faster = document.querySelector('.b-aplayer__html5-desktop-skip'),
                        series = document.querySelector('.b-aplayer__actions-series');

                    function clickSelected() {
                        var s = document.querySelector('.b-aplayer__popup-series-episodes .selected a');
                        if (s) {
                            s.click();
                        }
                    }

                    if ((!faster || faster.style.display !== 'block') && series && !p.seriesClicked) {
                        series.click();
                        p.seriesClicked = true;
                        p.longerSkipper = true;
                        setTimeout(clickSelected, 1000);
                        p.pause();
                    }

                    function skipListener() {
                        if (p.waitAfterSkip) {
                            console.log('В процессе пропуска…');
                            return;
                        }
                        p.pause();
                        if (!p.classList.contains('m-hidden')) {
                            p.classList.add('m-hidden');
                        }
                        if (faster && p.currentTime &&
                            win.getComputedStyle(faster).display === 'block' &&
                            !faster.querySelector('.b-aplayer__html5-desktop-skip-timer')) {
                            p.waitAfterSkip = true;
                            setTimeout(adSkipper, (p.longerSkipper?3:1)*1000, faster, p);
                            console.log('Доступен быстрый пропуск…');
                        } else {
                            setTimeout(adStepper, 1000, p);
                        }
                    }

                    p.addEventListener('timeupdate', skipListener, false);
                };
            if (p.nodeName === 'VIDEO') {
                cl(p);
            } else {
                (new MutationObserver(function (ms) {
                    var m, node;
                    for (m of ms) {
                        for (node of m.addedNodes) {
                            if (node.id === 'player' &&
                                node.nodeName === 'VIDEO' &&
                                _getAttribute.call(node, 'preload') !== 'auto') {
                                cl(node);
                            }
                        }
                    }
                })).observe(m, {childList: true});
            }
        }
    };
    scripts['brb.to'] = scripts['fs.to'];
    scripts['cxz.to'] = scripts['fs.to'];

    scripts['drive2.ru'] = function() {
        gardener('.c-block:not([data-metrika="recomm"]),.o-grid__item', />Реклама<\//i);
    };

    scripts['fishki.net'] = function() {
        gardener('.drag_list > .drag_element, .list-view > .paddingtop15, .post-wrap', /543769|Новости\sпартнеров/);
    };

    scripts['gidonline.club'] = {
        'now': function() {
            createStyle('.tray > div[style] {display: none!important}');
        }
    };
    scripts['gidonlinekino.com'] = scripts['gidonline.club'];

    scripts['hdgo.cc'] = {
        'now': function(){
            (new MutationObserver(function(ms) {
                var m, node;
                for (m of ms) {
                    for (node of m.addedNodes) {
                        if (node.tagName === 'SCRIPT' && _getAttribute(node, 'onerror') !== null) {
                            node.removeAttribute('onerror');
                        }
                    }
                }
            })).observe(document, {childList:true, subtree: true});
        }
    };
    scripts['couber.be'] = scripts['hdgo.cc'];
    scripts['46.30.43.38'] = scripts['hdgo.cc'];

    scripts['gismeteo.ru'] = {
        'DOMContentLoaded': function() {
            gardener('div > a[target^="_"]', /Яндекс\.Директ/i, { root: 'body', observe: true, parent: 'div[class*="frame"]' });
        }
    };

    scripts['hdrezka.me'] = {
        'now': function() {
            Object.defineProperty(win, 'fuckAdBlock', {
                value: {
                    onDetected: function() {
                        console.log('Pretending to be an ABP detector.');
                    }
                }
            });
            Object.defineProperty(win, 'ab', {
                value: false,
                enumerable: true
            });
        },
        'DOMContentLoaded': function() {
            gardener('div[id][onclick][onmouseup][onmousedown]', /onmouseout/i);
        }
    };

    scripts['imageban.ru'] = {
        'now': preventBackgroundRedirect,
        'DOMContentLoaded': function() {
            win.addEventListener('unload', function() {
                if (!window.location.hash) {
                    window.location.replace(window.location+'#');
                } else {
                    window.location.hash = '';
                }
            }, true);
        }
    };

    scripts['e.mail.ru'] = function() {
        /*gardener('#LEGO :not([data-mnemo])>.js-href[data-id]',
                 /data:image.*>Реклама<|>Реклама<.*\/\/favicon\./i,
                 {root:'#LEGO', observe: true, parent:'div[id][class]'});*/
    };

    scripts['mail.ru'] = {
        'now': function() {
            // Trick to prevent mail.ru from removing 3rd-party styles
            scriptLander(function(){
                Object.defineProperty(Object.prototype, 'restoreVisibility', {
                    get: function() { return function(){}; },
                    set: function() {}
                });
            });
        }
    };

    scripts['megogo.net'] = {
        'now': function() {
            Object.defineProperty(win, "adBlock", {
                value : false,
                enumerable : true
            });
            Object.defineProperty(win, "showAdBlockMessage", {
                value : function () {},
                enumerable : true
            });
        }
    };

    scripts['naruto-base.su'] = function() {
        gardener('div[id^="entryID"],.block', /href="http.*?target="_blank"/i);
    };

    scripts['overclockers.ru'] = {
        'now': function() {
            createStyle('.fixoldhtml {display:block!important}');
            if (!isChrome && !isOpera) {
                return; // Looks like my code works only in Chrome-like browsers
            }
            var noContentYet = true;
            function jWrap() {
                var _$ = win.$, _e = _$.extend;
                win.$ = function() {
                    var _ret = _$.apply(window, arguments);
                    if (_ret[0] === document.body) {
                        _ret.html = function() {
                            console.log('Anti-adblock prevented.');
                        };
                    }
                    return _ret;
                };
                win.$.extend = function() {
                    return _e.apply(_$, arguments);
                };
                win.jQuery = win.$;
            }
            (function jReady() {
                if (!win.$ && noContentYet) {
                    setTimeout(jReady, 0);
                } else {
                    jWrap();
                }
            })();
            document.addEventListener ('DOMContentLoaded', function(){
                noContentYet = false;
            }, false);
        }
    };
    scripts['forums.overclockers.ru'] = {
        'now': function() {
            createStyle('.needblock {position: fixed; left: -10000px}');
            Object.defineProperty(win, 'adblck', {
                value: 'no',
                enumerable: true
            });
        }
    };

    scripts['pb.wtf'] = function() {
        createStyle('.reques,#result,tbody.row1:not([id]) {display: none !important}');
        // image in the slider in the header
        gardener('a[href^="/ex"],a[href$="=="]', /img/i, {root:'.release-navbar', observe:true, parent:'div'});
        // ads in blocks on the page
        gardener('a[href^="/topic/234257"]', /Как\sразместить/i, {siblings:-1, root:'#main_content', observe:true, parent:'span[style]'});
        // line above topic content
        gardener('.re_top1', /./, {root:'#main_content', parent:'.hidden-sm'});
    };
    scripts['piratbit.org'] = scripts['pb.wtf'];
    scripts['piratbit.ru'] = scripts['pb.wtf'];

    scripts['pikabu.ru'] = function() {
        gardener('.story', /story__sponsor|story__gag|profile\/ads"/i, {root: '.inner_wrap', observe: true});
    };

    scripts['rp5.ru'] = function() {
        createStyle('#bannerBottom {display: none!important}');
        var co = document.querySelector('#content'), i, nodes;
        if (!co) {
            return;
        }
        nodes = co.parentNode.childNodes;
        i = nodes.length;
        while (i--) {
            if (nodes[i] !== co) {
                nodes[i].parentNode.removeChild(nodes[i]);
            }
        }
    };
    scripts['rp5.by'] = scripts['rp5.ru'];
    scripts['rp5.kz'] = scripts['rp5.ru'];
    scripts['rp5.ua'] = scripts['rp5.ru'];

    scripts['rustorka.com'] = {
        'now': function() {
            createStyle('.header > div:not(.head-block) a, #sidebar1 img, #logo img {opacity:0!important}', {
                id: 'tempHidingStyles'
            }, true);
            preventPopups();
        },
        'DOMContentLoaded': function() {
            for (var o of document.querySelectorAll('IMG, A')) {
                if ((o.clientWidth === 728 && o.clientHeight === 90) ||
                    (o.clientWidth === 300 && o.clientHeight === 250)) {
                    while (o && o.tagName !== 'A') {
                        o = o.parentNode;
                    }
                    if (o) {
                        _setAttribute.call(o, 'style', 'display: none !important');
                    }
                }
            }
            var s = document.querySelector('#tempHidingStyles');
            s.parentNode.removeChild(s);
        }
    };
    scripts['rumedia.ws'] = scripts['rustorka.com'];

    scripts['sport-express.ru'] = function() {
        gardener('.js-relap__item',/>Реклама\s+<\//, {root:'.container', observe: true});
    };

    scripts['sports.ru'] = function() {
        gardener('.aside-news-list__item', /aside-news-list__advert/i, {root:'.columns-layout__left', observe: true});
        gardener('.material-list__item', /Реклама/i, {root:'.columns-layout', observe: true});
        // extra functionality: shows/hides panel at the top depending on scroll direction
        createStyle([
            '.user-panel__fixed { transition: top 0.2s ease-in-out!important; }',
            '.user-panel-up { top: -40px!important }'
        ], {id: 'userPanelSlide'}, false);
        (function lookForPanel() {
            var panel = document.querySelector('.user-panel__fixed');
            if (!panel) {
                setTimeout(lookForPanel, 100);
            } else {
                window.addEventListener('wheel', function(e){
                    if (e.deltaY > 0 && !panel.classList.contains('user-panel-up')) {
                        panel.classList.add('user-panel-up');
                    } else
                        if (e.deltaY < 0 && panel.classList.contains('user-panel-up')) {
                            panel.classList.remove('user-panel-up');
                        }
                }, false);
            }
        })();
    };

    scripts['www.ukr.net'] = scripts['sinoptik.com.ru'];

    scripts['vk.com'] = function() {
        gardener('div[data-post-id]', /wall_marked_as_ads/, {root: '#page_wall_posts', observe: true});
    };

    scripts['yap.ru'] = function() {
        gardener('form > table[id^="p_row_"]:nth-of-type(2)', /member1438|Administration/);
        gardener('.icon-comments', /member1438|Administration|\/go\/\?http/, {parent:'tr', siblings:-2});
    };
    scripts['yaplakal.com'] = scripts['yap.ru'];

    scripts['rambler.ru'] = {
        'now': function() {
            scriptLander(function() {
                var _createElement = Document.prototype.createElement,
                    loadMap = new WeakMap();
                Document.prototype.createElement = function createElement(name) {
                    /*jshint validthis:true */
                    var el = _createElement.apply(this, arguments);
                    if (el.tagName === 'LINK') {
                        Object.defineProperty(el, 'onload', {
                            get: function() {
                                return loadMap.get(loadMap.get(this));
                            },
                            set: function(func) {
                                var wrap = loadMap.get(this),
                                    isContent = /\{\s*content\s*:\s*"[^"]+"/i;
                                if (wrap) {
                                    this.removeEventListener('load', wrap, false);
                                    loadMap.remove(wrap);
                                    loadMap.remove(this);
                                }
                                wrap = function(e) {
                                    if (e.target && e.target.sheet && e.target.sheet.cssRules &&
                                        e.target.sheet.cssRules[0] && e.target.sheet.cssRules[0].cssText &&
                                        isContent.test(e.target.sheet.cssRules[0].cssText)) {
                                        console.log('Blocked "onload" for', e.target.href);
                                        return false;
                                    }
                                    return func.apply(this, arguments);
                                };
                                loadMap.set(this, wrap);
                                loadMap.set(wrap, func);
                                this.addEventListener('load', wrap, false);
                            },
                            enumberable: true
                        });
                    }
                    return el;
                };
            });
        }
    };

    scripts['reactor.cc'] = {
        'now': function() {
            win.open = (function(){ throw new Error('Redirect prevention.'); }).bind(window);
        },
        'click': function(e) {
            var node = e.target;
            if (node.nodeType === Node.ELEMENT_NODE &&
                node.style.position === 'absolute' &&
                node.style.zIndex > 0)
                node.parentNode.removeChild(node);
        },
        'DOMContentLoaded': function() {
            var words = new RegExp(
                'блокировщика рекламы'
                .split('')
                .map(function(e){return e+'[\u200b\u200c\u200d]*';})
                .join('')
                .replace(' ', '\\s*')
                .replace(/[аоре]/g, function(e){return ['[аa]','[оo]','[рp]','[еe]']['аоре'.indexOf(e)];}),
                'i'),
                can;
            function deeper(spider) {
                var c, l, n;
                if (words.test(spider.innerText)) {
                    if (spider.nodeType === Node.TEXT_NODE) {
                        return true;
                    }
                    c = spider.childNodes;
                    l = c.length;
                    n = 0;
                    while(l--) {
                        if (deeper(c[l]), can) {
                            n++;
                        }
                    }
                    if (n > 0 && n === c.length && spider.offsetHeight < 750) {
                        can.push(spider);
                    }
                    return false;
                }
                return true;
            }
            function probe(){
                if (words.test(document.body.innerText)) {
                    can = [];
                    deeper(document.body);
                    var i = can.length, spider;
                    while(i--) {
                        spider = can[i];
                        if (spider.offsetHeight > 10 && spider.offsetHeight < 750) {
                            _setAttribute.call(spider, 'style', 'background:none!important');
                        }
                    }
                }
            }
            (new MutationObserver(probe)).observe(document,{childList:true, subtree:true});
        }
    };
    scripts['joyreactor.cc'] = scripts['reactor.cc'];
    scripts['pornreactor.cc'] = scripts['reactor.cc'];

    scripts['auto.ru'] = function() {
        var words = /Реклама|Яндекс.Директ|yandex_ad_/;
        var userAdsListAds = [
            '.listing-list > .listing-item',
            '.listing-item_type_fixed.listing-item'
        ];
        var catalogAds = [
            'div[class*="layout_catalog-inline"]',
            'div[class$="layout_horizontal"]'
        ];
        var otherAds = [
            '.advt_auto',
            '.sidebar-block',
            '.pager-listing + div[class]',
            '.card > div[class][style]',
            '.sidebar > div[class]',
            '.main-page__section + div[class]',
            '.listing > tbody'];
        gardener(userAdsListAds.join(','), words, {root:'.listing-wrap', observe:true});
        gardener(catalogAds.join(','), words, {root:'.catalog__page,.content__wrapper', observe:true});
        gardener(otherAds.join(','), words);
    };

    scripts['rsload.net'] = {
        'load': function() {
            var dis = document.querySelector('label[class*="cb-disable"]');
            if (dis) {
                dis.click();
            }
        },
        'click': function(e) {
            var t = e.target;
            if (t && t.href && (/:\/\/\d+\.\d+\.\d+\.\d+\//.test(t.href))) {
                t.href = t.href.replace('://','://rsload.net:rsload.net@');
            }
        }
    };

    var domain = document.domain, name;
    while (domain.indexOf('.') !== -1) {
        if (scripts.hasOwnProperty(domain)) {
            if (typeof scripts[domain] === 'function') {
                document.addEventListener ('DOMContentLoaded', scripts[domain], false);
            }
            for (name in scripts[domain]) {
                if (name !== 'now') {
                    (name === 'load' ? window : document)
                        .addEventListener (name, scripts[domain][name], false);
                } else {
                    scripts[domain][name]();
                }
            }
        }
        domain = domain.slice(domain.indexOf('.') + 1);
    }
})();