/*******************************************************************************

    uBlock Origin - a browser extension to block requests.
    Copyright (C) 2017-2018 Raymond Hill

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see {http://www.gnu.org/licenses/}.

    Home: https://github.com/gorhill/uBlock
*/

'use strict';

// Packaging this file is optional: it is not necessary to package it if the
// platform is known to support user stylesheets.

// >>>>>>>> start of HUGE-IF-BLOCK
if ( typeof vAPI === 'object' && vAPI.userStylesheet === undefined ) {

/******************************************************************************/
/******************************************************************************/

vAPI.userStylesheet = {
    style: null,
    styleFixCount: 0,
    css: new Map(),
    disabled: false,
    apply: function() {
    },
    inject: function() {
        this.style = document.createElement('style');
        this.style.disabled = this.disabled;
        const parent = document.head || document.documentElement;
        if ( parent === null ) { return; }
        parent.appendChild(this.style);
        const observer = new MutationObserver(function() {
            if ( this.style === null ) { return; }
            if ( this.style.sheet !== null ) { return; }
            this.styleFixCount += 1;
            if ( this.styleFixCount < 32 ) {
                parent.appendChild(this.style);
            } else {
                observer.disconnect();
            }
        }.bind(this));
        observer.observe(parent, { childList: true });
    },
    add: function(cssText) {
        if ( cssText === '' || this.css.has(cssText) ) { return; }
        if ( this.style === null ) { this.inject(); }
        const sheet = this.style.sheet;
        if ( !sheet ) { return; }
        const i = sheet.cssRules.length;
        sheet.insertRule(cssText, i);
        this.css.set(cssText, sheet.cssRules[i]);
    },
    remove: function(cssText) {
        if ( cssText === '' ) { return; }
        const cssRule = this.css.get(cssText);
        if ( cssRule === undefined ) { return; }
        this.css.delete(cssText);
        if ( this.style === null ) { return; }
        const sheet = this.style.sheet;
        if ( !sheet ) { return; }
        const rules = sheet.cssRules;
        let i = rules.length;
        while ( i-- ) {
            if ( rules[i] !== cssRule ) { continue; }
            sheet.deleteRule(i);
            break;
        }
        if ( rules.length !== 0 ) { return; }
        const style = this.style;
        this.style = null;
        const parent = style.parentNode;
        if ( parent !== null ) {
            parent.removeChild(style);
        }
    },
    toggle: function(state) {
        if ( state === undefined ) { state = this.disabled; }
        if ( state !== this.disabled ) { return; }
        this.disabled = !state;
        if ( this.style !== null ) {
            this.style.disabled = this.disabled;
        }
    }
};

/******************************************************************************/

vAPI.DOMFilterer = class {
    constructor() {
        this.commitTimer = new vAPI.SafeAnimationFrame(this.commitNow.bind(this));
        this.domIsReady = document.readyState !== 'loading';
        this.listeners = [];
        this.excludedNodeSet = new WeakSet();
        this.addedNodes = new Set();
        this.removedNodes = false;

        this.specificSimpleHide = new Set();
        this.specificSimpleHideAggregated = undefined;
        this.addedSpecificSimpleHide = [];
        this.specificComplexHide = new Set();
        this.specificComplexHideAggregated = undefined;
        this.addedSpecificComplexHide = [];
        this.specificOthers = [];
        this.genericSimpleHide = new Set();
        this.genericComplexHide = new Set();
        this.exceptedCSSRules = [];

        this.hideNodeExpando = undefined;
        this.hideNodeBatchProcessTimer = undefined;
        this.hiddenNodeObserver = undefined;
        this.hiddenNodesetToProcess = new Set();
        this.hiddenNodeset = new WeakSet();

        if ( vAPI.domWatcher instanceof Object ) {
            vAPI.domWatcher.addListener(this);
        }

        // https://www.w3.org/community/webed/wiki/CSS/Selectors#Combinators
        this.reCSSCombinators = /[ >+~]/;
    }

    commitNow() {
        this.commitTimer.clear();

        if ( this.domIsReady !== true || vAPI.userStylesheet.disabled ) {
            return;
        }

        // Filterset changed.

        if ( this.addedSpecificSimpleHide.length !== 0 ) {
            //console.time('specific simple filterset changed');
            //console.log('added %d specific simple selectors', this.addedSpecificSimpleHide.length);
            const nodes = document.querySelectorAll(this.addedSpecificSimpleHide.join(','));
            for ( const node of nodes ) {
                this.hideNode(node);
            }
            this.addedSpecificSimpleHide = [];
            this.specificSimpleHideAggregated = undefined;
            //console.timeEnd('specific simple filterset changed');
        }

        if ( this.addedSpecificComplexHide.length !== 0 ) {
            //console.time('specific complex filterset changed');
            //console.log('added %d specific complex selectors', this.addedSpecificComplexHide.length);
            const nodes = document.querySelectorAll(this.addedSpecificComplexHide.join(','));
            for ( const node of nodes ) {
                this.hideNode(node);
            }
            this.addedSpecificComplexHide = [];
            this.specificComplexHideAggregated = undefined;
            //console.timeEnd('specific complex filterset changed');
        }

        // DOM layout changed.

        const domNodesAdded = this.addedNodes.size !== 0;
        const domLayoutChanged = domNodesAdded || this.removedNodes;

        if ( domNodesAdded === false || domLayoutChanged === false ) {
            return;
        }

        //console.log('%d nodes added', this.addedNodes.size);

        if ( this.specificSimpleHide.size !== 0 && domNodesAdded ) {
            //console.time('dom layout changed/specific simple selectors');
            if ( this.specificSimpleHideAggregated === undefined ) {
                this.specificSimpleHideAggregated =
                    Array.from(this.specificSimpleHide).join(',\n');
            }
            for ( const node of this.addedNodes ) {
                if ( node[vAPI.matchesProp](this.specificSimpleHideAggregated) ) {
                    this.hideNode(node);
                }
                const nodes = node.querySelectorAll(this.specificSimpleHideAggregated);
                for ( const node of nodes ) {
                    this.hideNode(node);
                }
            }
            //console.timeEnd('dom layout changed/specific simple selectors');
        }

        if ( this.specificComplexHide.size !== 0 && domLayoutChanged ) {
            //console.time('dom layout changed/specific complex selectors');
            if ( this.specificComplexHideAggregated === undefined ) {
                this.specificComplexHideAggregated =
                    Array.from(this.specificComplexHide).join(',\n');
            }
            const nodes = document.querySelectorAll(this.specificComplexHideAggregated);
            for ( const node of nodes ) {
                this.hideNode(node);
            }
            //console.timeEnd('dom layout changed/specific complex selectors');
        }

        this.addedNodes.clear();
        this.removedNodes = false;
    }

    commit(now) {
        if ( now ) {
            this.commitTimer.clear();
            this.commitNow();
        } else {
            this.commitTimer.start();
        }
    }

    addCSSRule(selectors, declarations, details) {
        if ( selectors === undefined ) { return; }

        if ( details === undefined ) { details = {}; }

        const selectorsStr = Array.isArray(selectors) ?
            selectors.join(',\n') :
            selectors;
        if ( selectorsStr.length === 0 ) { return; }

        vAPI.userStylesheet.add(selectorsStr + '\n{' + declarations + '}');
        this.commit();
        if ( this.hasListeners() ) {
            this.triggerListeners({
                declarative: [ [ selectorsStr, declarations ] ]
            });
        }

        if ( declarations !== 'display:none!important;' ) {
            this.specificOthers.push({
                selectors: selectorsStr,
                declarations: declarations
            });
            return;
        }

        // Do not strongly enforce internal CSS rules.
        if ( details.internal ) { return; }

        const isGeneric= details.lazy === true;
        const isSimple = details.type === 'simple';
        const isComplex = details.type === 'complex';

        if ( isGeneric ) {
            if ( isSimple ) {
                this.genericSimpleHide.add(selectorsStr);
                return;
            }
            if ( isComplex ) {
                this.genericComplexHide.add(selectorsStr);
                return;
            }
        }

        const selectorsArr = Array.isArray(selectors) ?
            selectors :
            selectors.split(',\n');

        if ( isGeneric ) {
            for ( const selector of selectorsArr ) {
                if ( this.reCSSCombinators.test(selector) ) {
                    this.genericComplexHide.add(selector);
                } else {
                    this.genericSimpleHide.add(selector);
                }
            }
            return;
        }

        // Specific cosmetic filters.
        for ( const selector of selectorsArr ) {
            if (
                isComplex ||
                isSimple === false && this.reCSSCombinators.test(selector)
            ) {
                if ( this.specificComplexHide.has(selector) === false ) {
                    this.specificComplexHide.add(selector);
                    this.addedSpecificComplexHide.push(selector);
                }
            } else if ( this.specificSimpleHide.has(selector) === false ) {
                this.specificSimpleHide.add(selector);
                this.addedSpecificSimpleHide.push(selector);
            }
        }
    }

    exceptCSSRules(exceptions) {
        if ( exceptions.length === 0 ) { return; }
        this.exceptedCSSRules.push(...exceptions);
        if ( this.hasListeners() ) {
            this.triggerListeners({ exceptions });
        }
    }

    onDOMCreated() {
        this.domIsReady = true;
        this.addedNodes.clear();
        this.removedNodes = false;
        this.commit();
    }

    onDOMChanged(addedNodes, removedNodes) {
        for ( const node of addedNodes ) {
            this.addedNodes.add(node);
        }
        this.removedNodes = this.removedNodes || removedNodes;
        this.commit();
    }

    addListener(listener) {
        if ( this.listeners.indexOf(listener) !== -1 ) { return; }
        this.listeners.push(listener);
    }

    removeListener(listener) {
        const pos = this.listeners.indexOf(listener);
        if ( pos === -1 ) { return; }
        this.listeners.splice(pos, 1);
    }

    hasListeners() {
        return this.listeners.length !== 0;
    }

    triggerListeners(changes) {
        for ( const listener of this.listeners ) {
            listener.onFiltersetChanged(changes);
        }
    }

    // https://jsperf.com/clientheight-and-clientwidth-vs-getcomputedstyle
    //   Avoid getComputedStyle(), detecting whether a node is visible can be
    //   achieved with clientWidth/clientHeight.
    // https://gist.github.com/paulirish/5d52fb081b3570c81e3a
    //   Do not interleave read-from/write-to the DOM. Write-to DOM
    //   operations would cause the first read-from to be expensive, and
    //   interleaving means that potentially all single read-from operation
    //   would be expensive rather than just the 1st one.
    //   Benchmarking toggling off/on cosmetic filtering confirms quite an
    //   improvement when:
    //   - batching as much as possible handling of all nodes;
    //   - avoiding to interleave read-from/write-to operations.
    //   However, toggling off/on cosmetic filtering repeatedly is not
    //   a real use case, but this shows this will help performance
    //   on sites which try to use inline styles to bypass blockers.
    hideNodeBatchProcess() {
        this.hideNodeBatchProcessTimer.clear();
        const expando = this.hideNodeExpando;
        for ( const node of this.hiddenNodesetToProcess ) {
            if (
                this.hiddenNodeset.has(node) === false ||
                node[expando] === undefined ||
                node.clientHeight === 0 || node.clientWidth === 0
            ) {
                continue;
            }
            let attr = node.getAttribute('style');
            if ( attr === null ) {
                attr = '';
            } else if (
                attr.length !== 0 &&
                attr.charCodeAt(attr.length - 1) !== 0x3B /* ';' */
            ) {
                attr += ';';
            }
            node.setAttribute('style', attr + 'display:none!important;');
        }
        this.hiddenNodesetToProcess.clear();
    }

    hideNodeObserverHandler(mutations) {
        if ( vAPI.userStylesheet.disabled ) { return; }
        const stagedNodes = this.hiddenNodesetToProcess;
        for ( const mutation of mutations ) {
            stagedNodes.add(mutation.target);
        }
        this.hideNodeBatchProcessTimer.start();
    }

    hideNodeInit() {
        this.hideNodeExpando = vAPI.randomToken();
        this.hideNodeBatchProcessTimer =
            new vAPI.SafeAnimationFrame(this.hideNodeBatchProcess.bind(this));
        this.hiddenNodeObserver =
            new MutationObserver(this.hideNodeObserverHandler.bind(this));
        if ( this.hideNodeStyleSheetInjected === false ) {
            this.hideNodeStyleSheetInjected = true;
            vAPI.userStylesheet.add(
                `[${this.hideNodeAttr}]\n{display:none!important;}`
            );
        }
    }

    excludeNode(node) {
        this.excludedNodeSet.add(node);
        this.unhideNode(node);
    }

    unexcludeNode(node) {
        this.excludedNodeSet.delete(node);
    }

    hideNode(node) {
        if ( this.excludedNodeSet.has(node) ) { return; }
        if ( this.hideNodeAttr === undefined ) { return; }
        if ( this.hiddenNodeset.has(node) ) { return; }
        node.hidden = true;
        this.hiddenNodeset.add(node);
        if ( this.hideNodeExpando === undefined ) { this.hideNodeInit(); }
        node.setAttribute(this.hideNodeAttr, '');
        if ( node[this.hideNodeExpando] === undefined ) {
            node[this.hideNodeExpando] =
                node.hasAttribute('style') &&
               (node.getAttribute('style') || '');
        }
        this.hiddenNodesetToProcess.add(node);
        this.hideNodeBatchProcessTimer.start();
        this.hiddenNodeObserver.observe(node, this.hiddenNodeObserverOptions);
    }

    unhideNode(node) {
        if ( this.hiddenNodeset.has(node) === false ) { return; }
        node.hidden = false;
        node.removeAttribute(this.hideNodeAttr);
        this.hiddenNodesetToProcess.delete(node);
        if ( this.hideNodeExpando === undefined ) { return; }
        const attr = node[this.hideNodeExpando];
        if ( attr === false ) {
            node.removeAttribute('style');
        } else if ( typeof attr === 'string' ) {
            node.setAttribute('style', attr);
        }
        node[this.hideNodeExpando] = undefined;
        this.hiddenNodeset.delete(node);
    }

    showNode(node) {
        node.hidden = false;
        const attr = node[this.hideNodeExpando];
        if ( attr === false ) {
            node.removeAttribute('style');
        } else if ( typeof attr === 'string' ) {
            node.setAttribute('style', attr);
        }
    }

    unshowNode(node) {
        node.hidden = true;
        this.hiddenNodesetToProcess.add(node);
    }

    toggle(state, callback) {
        vAPI.userStylesheet.toggle(state);
        const disabled = vAPI.userStylesheet.disabled;
        const nodes = document.querySelectorAll(`[${this.hideNodeAttr}]`);
        for ( const node of nodes ) {
            if ( disabled ) {
                this.showNode(node);
            } else {
                this.unshowNode(node);
            }
        }
        if ( disabled === false && this.hideNodeExpando !== undefined ) {
            this.hideNodeBatchProcessTimer.start();
        }
        if ( typeof callback === 'function' ) {
            callback();
        }
    }

    getAllSelectors_(all) {
        const out = {
            declarative: [],
            exceptions: this.exceptedCSSRules,
        };
        if ( this.specificSimpleHide.size !== 0 ) {
            out.declarative.push([
                Array.from(this.specificSimpleHide).join(',\n'),
                'display:none!important;'
            ]);
        }
        if ( this.specificComplexHide.size !== 0 ) {
            out.declarative.push([
                Array.from(this.specificComplexHide).join(',\n'),
                'display:none!important;'
            ]);
        }
        if ( this.genericSimpleHide.size !== 0 ) {
            out.declarative.push([
                Array.from(this.genericSimpleHide).join(',\n'),
                'display:none!important;'
            ]);
        }
        if ( this.genericComplexHide.size !== 0 ) {
            out.declarative.push([
                Array.from(this.genericComplexHide).join(',\n'),
                'display:none!important;'
            ]);
        }
        if ( all ) {
            out.declarative.push([
                '[' + this.hideNodeAttr + ']',
                'display:none!important;'
            ]);
        }
        for ( const entry of this.specificOthers ) {
            out.declarative.push([ entry.selectors, entry.declarations ]);
        }
        return out;
    }

    getFilteredElementCount() {
        const details = this.getAllSelectors_(true);
        if ( Array.isArray(details.declarative) === false ) { return 0; }
        const selectors = details.declarative.map(entry => entry[0]);
        if ( selectors.length === 0 ) { return 0; }
        return document.querySelectorAll(selectors.join(',\n')).length;
    }

    getAllSelectors() {
        return this.getAllSelectors_(false);
    }
};

vAPI.DOMFilterer.prototype.hiddenNodeObserverOptions = {
    attributes: true,
    attributeFilter: [ 'style' ]
};

/******************************************************************************/
/******************************************************************************/

}
// <<<<<<<< end of HUGE-IF-BLOCK

/*******************************************************************************

    DO NOT:
    - Remove the following code
    - Add code beyond the following code
    Reason:
    - https://github.com/gorhill/uBlock/pull/3721
    - uBO never uses the return value from injected content scripts

**/

void 0;


if ( this.selectors.size === 0 || this.domIsReady === false ) {

<!--

    uBlock Origin - a browser extension to block requests.
    Copyright (C) 2018-present Raymond Hill

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see {http://www.gnu.org/licenses/}.

    Home: https://github.com/gorhill/uBlock

-->

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display: none;">
    <defs>
    <symbol id="ubo-solid" viewBox="0 0 128 128"><path d="M 64,0 C 40,16 32,16 8,16 c 0,80 0,80 56,112 56,-32 56,-32 56,-112 -24,0 -32,0 -56,-16 z m -32,40 8,0 0,24 c 0,7 1,8 8,8 7,0 8,-1 8,-8 l 0,-4 0,-20 8,0 0,4.054688 C 67.350916,41.522675 71.50202,40 76,40 86.998316,40 96,49.001684 96,60 96,70.998316 86.998316,80 76,80 70.442478,80 65.399719,77.696984 61.765625,74.003906 59.318213,78.076852 54.812381,80 48,80 37,80 32,75 32,64 l 0,-24 z m 44,8 c -6.674796,0 -12,5.325204 -12,12 0,6.674796 5.325204,12 12,12 6.674796,0 12,-5.325204 12,-12 0,-6.674796 -5.325204,-12 -12,-12 z"/></symbol>
    </defs>
</svg>


/*******************************************************************************

    uBlock Origin - a browser extension to block requests.
    Copyright (C) 2014-present Raymond Hill

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see {http://www.gnu.org/licenses/}.

    Home: https://github.com/gorhill/uBlock
*/

'use strict';

/*******************************************************************************

              +--> domCollapser
              |
              |
  domWatcher--+
              |                  +-- domSurveyor
              |                  |
              +--> domFilterer --+-- domLogger
                                 |
                                 +-- domInspector

  domWatcher:
    Watches for changes in the DOM, and notify the other components about these
    changes.

  domCollapser:
    Enforces the collapsing of DOM elements for which a corresponding
    resource was blocked through network filtering.

  domFilterer:
    Enforces the filtering of DOM elements, by feeding it cosmetic filters.

  domSurveyor:
    Surveys the DOM to find new cosmetic filters to apply to the current page.

  domLogger:
    Surveys the page to find and report the injected cosmetic filters blocking
    actual elements on the current page. This component is dynamically loaded
    IF AND ONLY IF uBO's logger is opened.

  If page is whitelisted:
    - domWatcher: off
    - domCollapser: off
    - domFilterer: off
    - domSurveyor: off
    - domLogger: off

  I verified that the code in this file is completely flushed out of memory
  when a page is whitelisted.

  If cosmetic filtering is disabled:
    - domWatcher: on
    - domCollapser: on
    - domFilterer: off
    - domSurveyor: off
    - domLogger: off

  If generic cosmetic filtering is disabled:
    - domWatcher: on
    - domCollapser: on
    - domFilterer: on
    - domSurveyor: off
    - domLogger: on if uBO logger is opened

  If generic cosmetic filtering is enabled:
    - domWatcher: on
    - domCollapser: on
    - domFilterer: on
    - domSurveyor: on
    - domLogger: on if uBO logger is opened

  Additionally, the domSurveyor can turn itself off once it decides that
  it has become pointless (repeatedly not finding new cosmetic filters).

  The domFilterer makes use of platform-dependent user stylesheets[1].

  At time of writing, only modern Firefox provides a custom implementation,
  which makes for solid, reliable and low overhead cosmetic filtering on
  Firefox.

  The generic implementation[2] performs as best as can be, but won't ever be
  as reliable and accurate as real user stylesheets.

  [1] "user stylesheets" refer to local CSS rules which have priority over,
       and can't be overriden by a web page's own CSS rules.
  [2] below, see platformUserCSS / platformHideNode / platformUnhideNode

*/

// Abort execution if our global vAPI object does not exist.
//   https://github.com/chrisaljoudi/uBlock/issues/456
//   https://github.com/gorhill/uBlock/issues/2029

 // >>>>>>>> start of HUGE-IF-BLOCK
if ( typeof vAPI === 'object' && !vAPI.contentScript ) {

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

vAPI.contentScript = true;

/******************************************************************************/
/******************************************************************************/
/*******************************************************************************

  The purpose of SafeAnimationFrame is to take advantage of the behavior of
  window.requestAnimationFrame[1]. If we use an animation frame as a timer,
  then this timer is described as follow:

  - time events are throttled by the browser when the viewport is not visible --
    there is no point for uBO to play with the DOM if the document is not
    visible.
  - time events are micro tasks[2].
  - time events are synchronized to monitor refresh, meaning that they can fire
    at most 1/60 (typically).

  If a delay value is provided, a plain timer is first used. Plain timers are
  macro-tasks, so this is good when uBO wants to yield to more important tasks
  on a page. Once the plain timer elapse, an animation frame is used to trigger
  the next time at which to execute the job.

  [1] https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
  [2] https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

*/

// https://github.com/gorhill/uBlock/issues/2147

vAPI.SafeAnimationFrame = function(callback) {
    this.fid = this.tid = undefined;
    this.callback = callback;
};

vAPI.SafeAnimationFrame.prototype = {
    start: function(delay) {
        if ( delay === undefined ) {
            if ( this.fid === undefined ) {
                this.fid = requestAnimationFrame(( ) => { this.onRAF(); } );
            }
            if ( this.tid === undefined ) {
                this.tid = vAPI.setTimeout(( ) => { this.onSTO(); }, 20000);
            }
            return;
        }
        if ( this.fid === undefined && this.tid === undefined ) {
            this.tid = vAPI.setTimeout(( ) => { this.macroToMicro(); }, delay);
        }
    },
    clear: function() {
        if ( this.fid !== undefined ) {
            cancelAnimationFrame(this.fid);
            this.fid = undefined;
        }
        if ( this.tid !== undefined ) {
            clearTimeout(this.tid);
            this.tid = undefined;
        }
    },
    macroToMicro: function() {
        this.tid = undefined;
        this.start();
    },
    onRAF: function() {
        if ( this.tid !== undefined ) {
            clearTimeout(this.tid);
            this.tid = undefined;
        }
        this.fid = undefined;
        this.callback();
    },
    onSTO: function() {
        if ( this.fid !== undefined ) {
            cancelAnimationFrame(this.fid);
            this.fid = undefined;
        }
        this.tid = undefined;
        this.callback();
    },
};

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

// https://github.com/uBlockOrigin/uBlock-issues/issues/552
//   Listen and report CSP violations so that blocked resources through CSP
//   are properly reported in the logger.

{
    const events = new Set();
    let timer;

    const send = function() {
        vAPI.messaging.send(
            'scriptlets',
            {
                what: 'securityPolicyViolation',
                type: 'net',
                docURL: document.location.href,
                violations: Array.from(events),
            },
            response => {
                if ( response === true ) { return; }
                stop();
            }
        );
        events.clear();
    };

    const sendAsync = function() {
        if ( timer !== undefined ) { return; }
        timer = self.requestIdleCallback(
            ( ) => { timer = undefined; send(); },
            { timeout: 2000 }
        );
    };

    const listener = function(ev) {
        if ( ev.isTrusted !== true ) { return; }
        if ( ev.disposition !== 'enforce' ) { return; }
        events.add(JSON.stringify({
            url: ev.blockedURL || ev.blockedURI,
            policy: ev.originalPolicy,
            directive: ev.effectiveDirective || ev.violatedDirective,
        }));
        sendAsync();
    };

    const stop = function() {
        events.clear();
        if ( timer !== undefined ) {
            self.cancelIdleCallback(timer);
            timer = undefined;
        }
        document.removeEventListener('securitypolicyviolation', listener);
        vAPI.shutdown.remove(stop);
    };

    document.addEventListener('securitypolicyviolation', listener);
    vAPI.shutdown.add(stop);

    // We need to call at least once to find out whether we really need to
    // listen to CSP violations.
    sendAsync();
}

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

vAPI.domWatcher = (function() {

    const addedNodeLists = [];
    const removedNodeLists = [];
    const addedNodes = [];
    const ignoreTags = new Set([ 'br', 'head', 'link', 'meta', 'script', 'style' ]);
    const listeners = [];

    let domIsReady = false,
        domLayoutObserver,
        listenerIterator = [], listenerIteratorDirty = false,
        removedNodes = false,
        safeObserverHandlerTimer;

    const safeObserverHandler = function() {
        //console.time('dom watcher/safe observer handler');
        let i = addedNodeLists.length,
            j = addedNodes.length;
        while ( i-- ) {
            const nodeList = addedNodeLists[i];
            let iNode = nodeList.length;
            while ( iNode-- ) {
                const node = nodeList[iNode];
                if ( node.nodeType !== 1 ) { continue; }
                if ( ignoreTags.has(node.localName) ) { continue; }
                if ( node.parentElement === null ) { continue; }
                addedNodes[j++] = node;
            }
        }
        addedNodeLists.length = 0;
        i = removedNodeLists.length;
        while ( i-- && removedNodes === false ) {
            const nodeList = removedNodeLists[i];
            let iNode = nodeList.length;
            while ( iNode-- ) {
                if ( nodeList[iNode].nodeType !== 1 ) { continue; }
                removedNodes = true;
                break;
            }
        }
        removedNodeLists.length = 0;
        //console.timeEnd('dom watcher/safe observer handler');
        if ( addedNodes.length === 0 && removedNodes === false ) { return; }
        for ( const listener of getListenerIterator() ) {
            listener.onDOMChanged(addedNodes, removedNodes);
        }
        addedNodes.length = 0;
        removedNodes = false;
    };

    // https://github.com/chrisaljoudi/uBlock/issues/205
    // Do not handle added node directly from within mutation observer.
    const observerHandler = function(mutations) {
        //console.time('dom watcher/observer handler');
        let i = mutations.length;
        while ( i-- ) {
            const mutation = mutations[i];
            let nodeList = mutation.addedNodes;
            if ( nodeList.length !== 0 ) {
                addedNodeLists.push(nodeList);
            }
            nodeList = mutation.removedNodes;
            if ( nodeList.length !== 0 ) {
                removedNodeLists.push(nodeList);
            }
        }
        if ( addedNodeLists.length !== 0 || removedNodes ) {
            safeObserverHandlerTimer.start(
                addedNodeLists.length < 100 ? 1 : undefined
            );
        }
        //console.timeEnd('dom watcher/observer handler');
    };

    const startMutationObserver = function() {
        if ( domLayoutObserver !== undefined || !domIsReady ) { return; }
        domLayoutObserver = new MutationObserver(observerHandler);
        domLayoutObserver.observe(document.documentElement, {
            //attributeFilter: [ 'class', 'id' ],
            //attributes: true,
            childList: true,
            subtree: true
        });
        safeObserverHandlerTimer = new vAPI.SafeAnimationFrame(safeObserverHandler);
        vAPI.shutdown.add(cleanup);
    };

    const stopMutationObserver = function() {
        if ( domLayoutObserver === undefined ) { return; }
        cleanup();
        vAPI.shutdown.remove(cleanup);
    };

    const getListenerIterator = function() {
        if ( listenerIteratorDirty ) {
            listenerIterator = listeners.slice();
            listenerIteratorDirty = false;
        }
        return listenerIterator;
    };

    const addListener = function(listener) {
        if ( listeners.indexOf(listener) !== -1 ) { return; }
        listeners.push(listener);
        listenerIteratorDirty = true;
        if ( domIsReady !== true ) { return; }
        listener.onDOMCreated();
        startMutationObserver();
    };

    const removeListener = function(listener) {
        const pos = listeners.indexOf(listener);
        if ( pos === -1 ) { return; }
        listeners.splice(pos, 1);
        listenerIteratorDirty = true;
        if ( listeners.length === 0 ) {
            stopMutationObserver();
        }
    };

    const cleanup = function() {
        if ( domLayoutObserver !== undefined ) {
            domLayoutObserver.disconnect();
            domLayoutObserver = null;
        }
        if ( safeObserverHandlerTimer !== undefined ) {
            safeObserverHandlerTimer.clear();
            safeObserverHandlerTimer = undefined;
        }
    };

    const start = function() {
        domIsReady = true;
        for ( const listener of getListenerIterator() ) {
            listener.onDOMCreated();
        }
        startMutationObserver();
    };

    return { start, addListener, removeListener };
})();

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

vAPI.matchesProp = (( ) => {
    const docElem = document.documentElement;
    if ( typeof docElem.matches !== 'function' ) {
        if ( typeof docElem.mozMatchesSelector === 'function' ) {
            return 'mozMatchesSelector';
        } else if ( typeof docElem.webkitMatchesSelector === 'function' ) {
            return 'webkitMatchesSelector';
        } else if ( typeof docElem.msMatchesSelector === 'function' ) {
            return 'msMatchesSelector';
        }
    }
    return 'matches';
})();

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

vAPI.injectScriptlet = function(doc, text) {
    if ( !doc ) { return; }
    let script;
    try {
        script = doc.createElement('script');
        script.appendChild(doc.createTextNode(text));
        (doc.head || doc.documentElement).appendChild(script);
    } catch (ex) {
    }
    if ( script ) {
        if ( script.parentNode ) {
            script.parentNode.removeChild(script);
        }
        script.textContent = '';
    }
};

/******************************************************************************/
/******************************************************************************/
/*******************************************************************************

  The DOM filterer is the heart of uBO's cosmetic filtering.

  DOMBaseFilterer: platform-specific
  |
  |
  +---- DOMFilterer: adds procedural cosmetic filtering

*/

vAPI.DOMFilterer = (function() {

    // 'P' stands for 'Procedural'

    const PSelectorHasTextTask = class {
        constructor(task) {
            let arg0 = task[1], arg1;
            if ( Array.isArray(task[1]) ) {
                arg1 = arg0[1]; arg0 = arg0[0];
            }
            this.needle = new RegExp(arg0, arg1);
        }
        exec(input) {
            const output = [];
            for ( const node of input ) {
                if ( this.needle.test(node.textContent) ) {
                    output.push(node);
                }
            }
            return output;
        }
    };

    const PSelectorIfTask = class {
        constructor(task) {
            this.pselector = new PSelector(task[1]);
        }
        exec(input) {
            const output = [];
            for ( const node of input ) {
                if ( this.pselector.test(node) === this.target ) {
                    output.push(node);
                }
            }
            return output;
        }
    };
    PSelectorIfTask.prototype.target = true;

    const PSelectorIfNotTask = class extends PSelectorIfTask {
        constructor(task) {
            super(task);
            this.target = false;
        }
    };

    const PSelectorMatchesCSSTask = class {
        constructor(task) {
            this.name = task[1].name;
            let arg0 = task[1].value, arg1;
            if ( Array.isArray(arg0) ) {
                arg1 = arg0[1]; arg0 = arg0[0];
            }
            this.value = new RegExp(arg0, arg1);
        }
        exec(input) {
            const output = [];
            for ( const node of input ) {
                const style = window.getComputedStyle(node, this.pseudo);
                if ( style === null ) { return null; } /* FF */
                if ( this.value.test(style[this.name]) ) {
                    output.push(node);
                }
            }
            return output;
        }
    };
    PSelectorMatchesCSSTask.prototype.pseudo = null;

    const PSelectorMatchesCSSAfterTask = class extends PSelectorMatchesCSSTask {
        constructor(task) {
            super(task);
            this.pseudo = ':after';
        }
    };

    const PSelectorMatchesCSSBeforeTask = class extends PSelectorMatchesCSSTask {
        constructor(task) {
            super(task);
            this.pseudo = ':before';
        }
    };

    const PSelectorNthAncestorTask = class {
        constructor(task) {
            this.nth = task[1];
        }
        exec(input) {
            const output = [];
            for ( let node of input ) {
                let nth = this.nth;
                for (;;) {
                    node = node.parentElement;
                    if ( node === null ) { break; }
                    nth -= 1;
                    if ( nth !== 0 ) { continue; }
                    output.push(node);
                    break;
                }
            }
            return output;
        }
    };

    const PSelectorSpathTask = class {
        constructor(task) {
            this.spath = task[1];
        }
        exec(input) {
            const output = [];
            for ( let node of input ) {
                const parent = node.parentElement;
                if ( parent === null ) { continue; }
                let pos = 1;
                for (;;) {
                    node = node.previousElementSibling;
                    if ( node === null ) { break; }
                    pos += 1;
                }
                const nodes = parent.querySelectorAll(
                    ':scope > :nth-child(' + pos + ')' + this.spath
                );
                for ( const node of nodes ) {
                    output.push(node);
                }
            }
            return output;
        }
    };

    const PSelectorWatchAttrs = class {
        constructor(task) {
            this.observer = null;
            this.observed = new WeakSet();
            this.observerOptions = {
                attributes: true,
                subtree: true,
            };
            const attrs = task[1];
            if ( Array.isArray(attrs) && attrs.length !== 0 ) {
                this.observerOptions.attributeFilter = task[1];
            }
        }
        // TODO: Is it worth trying to re-apply only the current selector?
        handler() {
            const filterer =
                vAPI.domFilterer && vAPI.domFilterer.proceduralFilterer;
            if ( filterer instanceof Object ) {
                filterer.onDOMChanged([ null ]);
            }
        }
        exec(input) {
            if ( input.length === 0 ) { return input; }
            if ( this.observer === null ) {
                this.observer = new MutationObserver(this.handler);
            }
            for ( const node of input ) {
                if ( this.observed.has(node) ) { continue; }
                this.observer.observe(node, this.observerOptions);
                this.observed.add(node);
            }
            return input;
        }
    };

    const PSelectorXpathTask = class {
        constructor(task) {
            this.xpe = document.createExpression(task[1], null);
            this.xpr = null;
        }
        exec(input) {
            const output = [];
            for ( const node of input ) {
                this.xpr = this.xpe.evaluate(
                    node,
                    XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
                    this.xpr
                );
                let j = this.xpr.snapshotLength;
                while ( j-- ) {
                    const node = this.xpr.snapshotItem(j);
                    if ( node.nodeType === 1 ) {
                        output.push(node);
                    }
                }
            }
            return output;
        }
    };

    const PSelector = class {
        constructor(o) {
            if ( PSelector.prototype.operatorToTaskMap === undefined ) {
                PSelector.prototype.operatorToTaskMap = new Map([
                    [ ':has', PSelectorIfTask ],
                    [ ':has-text', PSelectorHasTextTask ],
                    [ ':if', PSelectorIfTask ],
                    [ ':if-not', PSelectorIfNotTask ],
                    [ ':matches-css', PSelectorMatchesCSSTask ],
                    [ ':matches-css-after', PSelectorMatchesCSSAfterTask ],
                    [ ':matches-css-before', PSelectorMatchesCSSBeforeTask ],
                    [ ':not', PSelectorIfNotTask ],
                    [ ':nth-ancestor', PSelectorNthAncestorTask ],
                    [ ':spath', PSelectorSpathTask ],
                    [ ':watch-attrs', PSelectorWatchAttrs ],
                    [ ':xpath', PSelectorXpathTask ],
                ]);
            }
            this.budget = 200; // I arbitrary picked a 1/5 second
            this.raw = o.raw;
            this.cost = 0;
            this.lastAllowanceTime = 0;
            this.selector = o.selector;
            this.tasks = [];
            const tasks = o.tasks;
            if ( !tasks ) { return; }
            for ( const task of tasks ) {
                this.tasks.push(new (this.operatorToTaskMap.get(task[0]))(task));
            }
        }
        prime(input) {
            const root = input || document;
            if ( this.selector !== '' ) {
                return root.querySelectorAll(this.selector);
            }
            return [ root ];
        }
        exec(input) {
            let nodes = this.prime(input);
            for ( const task of this.tasks ) {
                if ( nodes.length === 0 ) { break; }
                nodes = task.exec(nodes);
            }
            return nodes;
        }
        test(input) {
            const nodes = this.prime(input);
            const AA = [ null ];
            for ( const node of nodes ) {
                AA[0] = node;
                let aa = AA;
                for ( const task of this.tasks ) {
                    aa = task.exec(aa);
                    if ( aa.length === 0 ) { break; }
                }
                if ( aa.length !== 0 ) { return true; }
            }
            return false;
        }
    };
    PSelector.prototype.operatorToTaskMap = undefined;

    const DOMProceduralFilterer = function(domFilterer) {
        this.domFilterer = domFilterer;
        this.domIsReady = false;
        this.domIsWatched = false;
        this.mustApplySelectors = false;
        this.selectors = new Map();
        this.hiddenNodes = new Set();
    };

    DOMProceduralFilterer.prototype = {

        addProceduralSelectors: function(aa) {
            const addedSelectors = [];
            let mustCommit = this.domIsWatched;
            for ( let i = 0, n = aa.length; i < n; i++ ) {
                const raw = aa[i];
                const o = JSON.parse(raw);
                if ( o.style ) {
                    this.domFilterer.addCSSRule(o.style[0], o.style[1]);
                    mustCommit = true;
                    continue;
                }
                if ( o.pseudoclass ) {
                    this.domFilterer.addCSSRule(
                        o.raw,
                        'display:none!important;'
                    );
                    mustCommit = true;
                    continue;
                }
                if ( o.tasks ) {
                    if ( this.selectors.has(raw) === false ) {
                        const pselector = new PSelector(o);
                        this.selectors.set(raw, pselector);
                        addedSelectors.push(pselector);
                        mustCommit = true;
                    }
                    continue;
                }
            }
            if ( mustCommit === false ) { return; }
            this.mustApplySelectors = this.selectors.size !== 0;
            this.domFilterer.commit();
            if ( this.domFilterer.hasListeners() ) {
                this.domFilterer.triggerListeners({
                    procedural: addedSelectors
                });
            }
        },

        commitNow: function() {
            if ( this.selectors.size === 0 || this.domIsReady === false ) {
                return;
            }

            this.mustApplySelectors = false;

            //console.time('procedural selectors/dom layout changed');

            // https://github.com/uBlockOrigin/uBlock-issues/issues/341
            //   Be ready to unhide nodes which no longer matches any of
            //   the procedural selectors.
            const toRemove = this.hiddenNodes;
            this.hiddenNodes = new Set();

            let t0 = Date.now();

            for ( const entry of this.selectors ) {
                const pselector = entry[1];
                const allowance = Math.floor((t0 - pselector.lastAllowanceTime) / 2000);
                if ( allowance >= 1 ) {
                    pselector.budget += allowance * 50;
                    if ( pselector.budget > 200 ) { pselector.budget = 200; }
                    pselector.lastAllowanceTime = t0;
                }
                if ( pselector.budget <= 0 ) { continue; }
                const nodes = pselector.exec();
                const t1 = Date.now();
                pselector.budget += t0 - t1;
                if ( pselector.budget < -500 ) {
                    console.info('uBO: disabling %s', pselector.raw);
                    pselector.budget = -0x7FFFFFFF;
                }
                t0 = t1;
                for ( const node of nodes ) {
                    this.domFilterer.hideNode(node);
                    this.hiddenNodes.add(node);
                }
            }

            for ( const node of toRemove ) {
                if ( this.hiddenNodes.has(node) ) { continue; }
                this.domFilterer.unhideNode(node);
            }
            //console.timeEnd('procedural selectors/dom layout changed');
        },

        createProceduralFilter: function(o) {
            return new PSelector(o);
        },

        onDOMCreated: function() {
            this.domIsReady = true;
            this.domFilterer.commitNow();
        },

        onDOMChanged: function(addedNodes, removedNodes) {
            if ( this.selectors.size === 0 ) { return; }
            this.mustApplySelectors =
                this.mustApplySelectors ||
                addedNodes.length !== 0 ||
                removedNodes;
            this.domFilterer.commit();
        }
    };

    const DOMFilterer = class extends vAPI.DOMFilterer {
        constructor() {
            super();
            this.exceptions = [];
            this.proceduralFilterer = new DOMProceduralFilterer(this);
            this.hideNodeAttr = undefined;
            this.hideNodeStyleSheetInjected = false;
            if ( vAPI.domWatcher instanceof Object ) {
                vAPI.domWatcher.addListener(this);
            }
        }

        commitNow() {
            super.commitNow();
            this.proceduralFilterer.commitNow();
        }

        addProceduralSelectors(aa) {
            this.proceduralFilterer.addProceduralSelectors(aa);
        }

        createProceduralFilter(o) {
            return this.proceduralFilterer.createProceduralFilter(o);
        }

        getAllSelectors() {
            const out = super.getAllSelectors();
            out.procedural = Array.from(this.proceduralFilterer.selectors.values());
            return out;
        }

        getAllExceptionSelectors() {
            return this.exceptions.join(',\n');
        }

        onDOMCreated() {
            if ( super.onDOMCreated instanceof Function ) {
                super.onDOMCreated();
            }
            this.proceduralFilterer.onDOMCreated();
        }

        onDOMChanged() {
            if ( super.onDOMChanged instanceof Function ) {
                super.onDOMChanged(arguments);
            }
            this.proceduralFilterer.onDOMChanged.apply(
                this.proceduralFilterer,
                arguments
            );
        }
    };

    return DOMFilterer;
})();

vAPI.domFilterer = new vAPI.DOMFilterer();

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

vAPI.domCollapser = (function() {
    const messaging = vAPI.messaging;
    const toCollapse = new Map();
    const src1stProps = {
        embed: 'src',
        iframe: 'src',
        img: 'src',
        object: 'data'
    };
    const src2ndProps = {
        img: 'srcset'
    };
    const tagToTypeMap = {
        embed: 'object',
        iframe: 'sub_frame',
        img: 'image',
        object: 'object'
    };

    let resquestIdGenerator = 1,
        processTimer,
        cachedBlockedSet,
        cachedBlockedSetHash,
        cachedBlockedSetTimer,
        toProcess = [],
        toFilter = [],
        netSelectorCacheCount = 0;

    const cachedBlockedSetClear = function() {
        cachedBlockedSet =
        cachedBlockedSetHash =
        cachedBlockedSetTimer = undefined;
    };

    // https://github.com/chrisaljoudi/uBlock/issues/174
    //   Do not remove fragment from src URL
    const onProcessed = function(response) {
        if ( !response ) { // This happens if uBO is disabled or restarted.
            toCollapse.clear();
            return;
        }

        const targets = toCollapse.get(response.id);
        if ( targets === undefined ) { return; }
        toCollapse.delete(response.id);
        if ( cachedBlockedSetHash !== response.hash ) {
            cachedBlockedSet = new Set(response.blockedResources);
            cachedBlockedSetHash = response.hash;
            if ( cachedBlockedSetTimer !== undefined ) {
                clearTimeout(cachedBlockedSetTimer);
            }
            cachedBlockedSetTimer = vAPI.setTimeout(cachedBlockedSetClear, 30000);
        }
        if ( cachedBlockedSet === undefined || cachedBlockedSet.size === 0 ) {
            return;
        }
        const selectors = [];
        const iframeLoadEventPatch = vAPI.iframeLoadEventPatch;
        let netSelectorCacheCountMax = response.netSelectorCacheCountMax;

        for ( const target of targets ) {
            const tag = target.localName;
            let prop = src1stProps[tag];
            if ( prop === undefined ) { continue; }
            let src = target[prop];
            if ( typeof src !== 'string' || src.length === 0 ) {
                prop = src2ndProps[tag];
                if ( prop === undefined ) { continue; }
                src = target[prop];
                if ( typeof src !== 'string' || src.length === 0 ) { continue; }
            }
            if ( cachedBlockedSet.has(tagToTypeMap[tag] + ' ' + src) === false ) {
                continue;
            }
            // https://github.com/chrisaljoudi/uBlock/issues/399
            // Never remove elements from the DOM, just hide them
            target.style.setProperty('display', 'none', 'important');
            target.hidden = true;
            // https://github.com/chrisaljoudi/uBlock/issues/1048
            // Use attribute to construct CSS rule
            if ( netSelectorCacheCount <= netSelectorCacheCountMax ) {
                const value = target.getAttribute(prop);
                if ( value ) {
                    selectors.push(tag + '[' + prop + '="' + value + '"]');
                    netSelectorCacheCount += 1;
                }
            }
            if ( iframeLoadEventPatch !== undefined ) {
                iframeLoadEventPatch(target);
            }
        }

        if ( selectors.length !== 0 ) {
            messaging.send(
                'contentscript',
                {
                    what: 'cosmeticFiltersInjected',
                    type: 'net',
                    hostname: window.location.hostname,
                    selectors: selectors
                }
            );
        }
    };

    const send = function() {
        processTimer = undefined;
        toCollapse.set(resquestIdGenerator, toProcess);
        const msg = {
            what: 'getCollapsibleBlockedRequests',
            id: resquestIdGenerator,
            frameURL: window.location.href,
            resources: toFilter,
            hash: cachedBlockedSetHash
        };
        messaging.send('contentscript', msg, onProcessed);
        toProcess = [];
        toFilter = [];
        resquestIdGenerator += 1;
    };

    const process = function(delay) {
        if ( toProcess.length === 0 ) { return; }
        if ( delay === 0 ) {
            if ( processTimer !== undefined ) {
                clearTimeout(processTimer);
            }
            send();
        } else if ( processTimer === undefined ) {
            processTimer = vAPI.setTimeout(send, delay || 20);
        }
    };

    const add = function(target) {
        toProcess[toProcess.length] = target;
    };

    const addMany = function(targets) {
        for ( const target of targets ) {
            add(target);
        }
    };

    const iframeSourceModified = function(mutations) {
        for ( const mutation of mutations ) {
            addIFrame(mutation.target, true);
        }
        process();
    };
    const iframeSourceObserver = new MutationObserver(iframeSourceModified);
    const iframeSourceObserverOptions = {
        attributes: true,
        attributeFilter: [ 'src' ]
    };

    // The injected scriptlets are those which were injected in the current
    // document, from within `bootstrapPhase1`, and which scriptlets are
    // selectively looked-up from:
    // https://github.com/uBlockOrigin/uAssets/blob/master/filters/resources.txt
    const primeLocalIFrame = function(iframe) {
        if ( vAPI.injectedScripts ) {
            vAPI.injectScriptlet(iframe.contentDocument, vAPI.injectedScripts);
        }
    };

    // https://github.com/gorhill/uBlock/issues/162
    // Be prepared to deal with possible change of src attribute.
    const addIFrame = function(iframe, dontObserve) {
        if ( dontObserve !== true ) {
            iframeSourceObserver.observe(iframe, iframeSourceObserverOptions);
        }
        const src = iframe.src;
        if ( src === '' || typeof src !== 'string' ) {
            primeLocalIFrame(iframe);
            return;
        }
        if ( src.startsWith('http') === false ) { return; }
        toFilter.push({ type: 'sub_frame', url: iframe.src });
        add(iframe);
    };

    const addIFrames = function(iframes) {
        for ( const iframe of iframes ) {
            addIFrame(iframe);
        }
    };

    const onResourceFailed = function(ev) {
        if ( tagToTypeMap[ev.target.localName] !== undefined ) {
            add(ev.target);
            process();
        }
    };

    const domWatcherInterface = {
        onDOMCreated: function() {
            if ( vAPI instanceof Object === false ) { return; }
            if ( vAPI.domCollapser instanceof Object === false ) {
                if ( vAPI.domWatcher instanceof Object ) {
                    vAPI.domWatcher.removeListener(domWatcherInterface);
                }
                return;
            }
            // Listener to collapse blocked resources.
            // - Future requests not blocked yet
            // - Elements dynamically added to the page
            // - Elements which resource URL changes
            // https://github.com/chrisaljoudi/uBlock/issues/7
            // Preferring getElementsByTagName over querySelectorAll:
            //   http://jsperf.com/queryselectorall-vs-getelementsbytagname/145
            const elems = document.images ||
                          document.getElementsByTagName('img');
            for ( const elem of elems ) {
                if ( elem.complete ) {
                    add(elem);
                }
            }
            addMany(document.embeds || document.getElementsByTagName('embed'));
            addMany(document.getElementsByTagName('object'));
            addIFrames(document.getElementsByTagName('iframe'));
            process(0);

            document.addEventListener('error', onResourceFailed, true);

            vAPI.shutdown.add(function() {
                document.removeEventListener('error', onResourceFailed, true);
                if ( processTimer !== undefined ) {
                    clearTimeout(processTimer);
                }
            });
        },
        onDOMChanged: function(addedNodes) {
            if ( addedNodes.length === 0 ) { return; }
            for ( const node of addedNodes ) {
                if ( node.localName === 'iframe' ) {
                    addIFrame(node);
                }
                if ( node.childElementCount === 0 ) { continue; }
                const iframes = node.getElementsByTagName('iframe');
                if ( iframes.length !== 0 ) {
                    addIFrames(iframes);
                }
            }
            process();
        }
    };

    if ( vAPI.domWatcher instanceof Object ) {
        vAPI.domWatcher.addListener(domWatcherInterface);
    }

    return { add, addMany, addIFrame, addIFrames, process };
})();

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

vAPI.domSurveyor = (function() {
    const messaging = vAPI.messaging;
    const queriedIds = new Set();
    const queriedClasses = new Set();
    const maxSurveyNodes = 65536;
    const maxSurveyTimeSlice = 4;
    const maxSurveyBuffer = 64;

    let domFilterer,
        hostname = '',
        surveyCost = 0;

    const pendingNodes = {
        nodeLists: [],
        buffer: [
            null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null,
            null, null, null, null, null, null, null, null,
        ],
        j: 0,
        accepted: 0,
        iterated: 0,
        stopped: false,
        add: function(nodes) {
            if ( nodes.length === 0 || this.accepted >= maxSurveyNodes ) {
                return;
            }
            this.nodeLists.push(nodes);
            this.accepted += nodes.length;
        },
        next: function() {
            if ( this.nodeLists.length === 0 || this.stopped ) { return 0; }
            const nodeLists = this.nodeLists;
            let ib = 0;
            do {
                const nodeList = nodeLists[0];
                let j = this.j;
                let n = j + maxSurveyBuffer - ib;
                if ( n > nodeList.length ) {
                    n = nodeList.length;
                }
                for ( let i = j; i < n; i++ ) {
                    this.buffer[ib++] = nodeList[j++];
                }
                if ( j !== nodeList.length ) {
                    this.j = j;
                    break;
                }
                this.j = 0;
                this.nodeLists.shift();
            } while ( ib < maxSurveyBuffer && nodeLists.length !== 0 );
            this.iterated += ib;
            if ( this.iterated >= maxSurveyNodes ) {
                this.nodeLists = [];
                this.stopped = true;
                //console.info(`domSurveyor> Surveyed a total of ${this.iterated} nodes. Enough.`);
            }
            return ib;
        },
        hasNodes: function() {
            return this.nodeLists.length !== 0;
        },
    };

    // Extract all classes/ids: these will be passed to the cosmetic
    // filtering engine, and in return we will obtain only the relevant
    // CSS selectors.
    const reWhitespace = /\s/;

    // https://github.com/gorhill/uBlock/issues/672
    // http://www.w3.org/TR/2014/REC-html5-20141028/infrastructure.html#space-separated-tokens
    // http://jsperf.com/enumerate-classes/6

    const surveyPhase1 = function() {
        //console.time('dom surveyor/surveying');
        const t0 = performance.now();
        const rews = reWhitespace;
        const ids = [];
        const classes = [];
        const nodes = pendingNodes.buffer;
        const deadline = t0 + maxSurveyTimeSlice;
        let qids = queriedIds;
        let qcls = queriedClasses;
        let processed = 0;
        for (;;) {
            const n = pendingNodes.next();
            if ( n === 0 ) { break; }
            for ( let i = 0; i < n; i++ ) {
                const node = nodes[i]; nodes[i] = null;
                let v = node.id;
                if ( typeof v === 'string' && v.length !== 0 ) {
                    v = v.trim();
                    if ( qids.has(v) === false && v.length !== 0 ) {
                        ids.push(v); qids.add(v);
                    }
                }
                let vv = node.className;
                if ( typeof vv === 'string' && vv.length !== 0 ) {
                    if ( rews.test(vv) === false ) {
                        if ( qcls.has(vv) === false ) {
                            classes.push(vv); qcls.add(vv);
                        }
                    } else {
                        vv = node.classList;
                        let j = vv.length;
                        while ( j-- ) {
                            const v = vv[j];
                            if ( qcls.has(v) === false ) {
                                classes.push(v); qcls.add(v);
                            }
                        }
                    }
                }
            }
            processed += n;
            if ( performance.now() >= deadline ) { break; }
        }
        const t1 = performance.now();
        surveyCost += t1 - t0;
        //console.info(`domSurveyor> Surveyed ${processed} nodes in ${(t1-t0).toFixed(2)} ms`);
        // Phase 2: Ask main process to lookup relevant cosmetic filters.
        if ( ids.length !== 0 || classes.length !== 0 ) {
            messaging.send(
                'contentscript',
                {
                    what: 'retrieveGenericCosmeticSelectors',
                    hostname: hostname,
                    ids: ids,
                    classes: classes,
                    exceptions: domFilterer.exceptions,
                    cost: surveyCost
                },
                surveyPhase3
            );
        } else {
            surveyPhase3(null);
        }
        //console.timeEnd('dom surveyor/surveying');
    };

    const surveyTimer = new vAPI.SafeAnimationFrame(surveyPhase1);

    // This is to shutdown the surveyor if result of surveying keeps being
    // fruitless. This is useful on long-lived web page. I arbitrarily
    // picked 5 minutes before the surveyor is allowed to shutdown. I also
    // arbitrarily picked 256 misses before the surveyor is allowed to
    // shutdown.
    let canShutdownAfter = Date.now() + 300000,
        surveyingMissCount = 0;

    // Handle main process' response.

    const surveyPhase3 = function(response) {
        const result = response && response.result;
        let mustCommit = false;

        if ( result ) {
            let selectors = result.simple;
            if ( Array.isArray(selectors) && selectors.length !== 0 ) {
                domFilterer.addCSSRule(
                    selectors,
                    'display:none!important;',
                    { type: 'simple' }
                );
                mustCommit = true;
            }
            selectors = result.complex;
            if ( Array.isArray(selectors) && selectors.length !== 0 ) {
                domFilterer.addCSSRule(
                    selectors,
                    'display:none!important;',
                    { type: 'complex' }
                );
                mustCommit = true;
            }
            selectors = result.injected;
            if ( typeof selectors === 'string' && selectors.length !== 0 ) {
                domFilterer.addCSSRule(
                    selectors,
                    'display:none!important;',
                    { injected: true }
                );
                mustCommit = true;
            }
            selectors = result.excepted;
            if ( Array.isArray(selectors) && selectors.length !== 0 ) {
                domFilterer.exceptCSSRules(selectors);
            }
        }

        if ( pendingNodes.stopped === false ) {
            if ( pendingNodes.hasNodes() ) {
                surveyTimer.start(1);
            }
            if ( mustCommit ) {
                surveyingMissCount = 0;
                canShutdownAfter = Date.now() + 300000;
                return;
            }
            surveyingMissCount += 1;
            if ( surveyingMissCount < 256 || Date.now() < canShutdownAfter ) {
                return;
            }
        }

        //console.info('dom surveyor shutting down: too many misses');

        surveyTimer.clear();
        vAPI.domWatcher.removeListener(domWatcherInterface);
        vAPI.domSurveyor = null;
    };

    const domWatcherInterface = {
        onDOMCreated: function() {
            if (
                vAPI instanceof Object === false ||
                vAPI.domSurveyor instanceof Object === false ||
                vAPI.domFilterer instanceof Object === false
            ) {
                if ( vAPI instanceof Object ) {
                    if ( vAPI.domWatcher instanceof Object ) {
                        vAPI.domWatcher.removeListener(domWatcherInterface);
                    }
                    vAPI.domSurveyor = null;
                }
                return;
            }
            //console.time('dom surveyor/dom layout created');
            domFilterer = vAPI.domFilterer;
            pendingNodes.add(document.querySelectorAll('[id],[class]'));
            surveyTimer.start();
            //console.timeEnd('dom surveyor/dom layout created');
        },
        onDOMChanged: function(addedNodes) {
            if ( addedNodes.length === 0 ) { return; }
            //console.time('dom surveyor/dom layout changed');
            let i = addedNodes.length;
            while ( i-- ) {
                const node = addedNodes[i];
                pendingNodes.add([ node ]);
                if ( node.childElementCount === 0 ) { continue; }
                pendingNodes.add(node.querySelectorAll('[id],[class]'));
            }
            if ( pendingNodes.hasNodes() ) {
                surveyTimer.start(1);
            }
            //console.timeEnd('dom surveyor/dom layout changed');
        }
    };

    const start = function(details) {
        if ( vAPI.domWatcher instanceof Object === false ) { return; }
        hostname = details.hostname;
        vAPI.domWatcher.addListener(domWatcherInterface);
    };

    return { start };
})();

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

// Bootstrapping allows all components of the content script to be launched
// if/when needed.

vAPI.bootstrap = (function() {

    const bootstrapPhase2 = function() {
        // This can happen on Firefox. For instance:
        // https://github.com/gorhill/uBlock/issues/1893
        if ( window.location === null ) { return; }
        if ( vAPI instanceof Object === false ) { return; }

        vAPI.messaging.send(
            'contentscript',
            { what: 'shouldRenderNoscriptTags' }
        );

        if ( vAPI.domWatcher instanceof Object ) {
            vAPI.domWatcher.start();
        }

        // Element picker works only in top window for now.
        if (
            window !== window.top ||
            vAPI.domFilterer instanceof Object === false
        ) {
            return;
        }

        // To send mouse coordinates to main process, as the chrome API fails
        // to provide the mouse position to context menu listeners.
        // https://github.com/chrisaljoudi/uBlock/issues/1143
        // Also, find a link under the mouse, to try to avoid confusing new tabs
        // as nuisance popups.
        // Ref.: https://developer.mozilla.org/en-US/docs/Web/Events/contextmenu

        const onMouseClick = function(ev) {
            let elem = ev.target;
            while ( elem !== null && elem.localName !== 'a' ) {
                elem = elem.parentElement;
            }
            vAPI.messaging.send(
                'contentscript',
                {
                    what: 'mouseClick',
                    x: ev.clientX,
                    y: ev.clientY,
                    url: elem !== null && ev.isTrusted !== false ? elem.href : ''
                }
            );
        };

        document.addEventListener('mousedown', onMouseClick, true);

        // https://github.com/gorhill/uMatrix/issues/144
        vAPI.shutdown.add(function() {
            document.removeEventListener('mousedown', onMouseClick, true);
        });
    };

    // https://github.com/uBlockOrigin/uBlock-issues/issues/403
    //   If there was a spurious port disconnection -- in which case the
    //   response is expressly set to `null`, rather than undefined or
    //   an object -- let's stay around, we may be given the opportunity
    //   to try bootstrapping again later.

    const bootstrapPhase1 = function(response) {
        if ( response === null ) { return; }
        vAPI.bootstrap = undefined;

        // cosmetic filtering engine aka 'cfe'
        const cfeDetails = response && response.specificCosmeticFilters;
        if ( !cfeDetails || !cfeDetails.ready ) {
            vAPI.domWatcher = vAPI.domCollapser = vAPI.domFilterer =
            vAPI.domSurveyor = vAPI.domIsLoaded = null;
            return;
        }

        if ( response.noCosmeticFiltering ) {
            vAPI.domFilterer = null;
            vAPI.domSurveyor = null;
        } else {
            const domFilterer = vAPI.domFilterer;
            if ( response.noGenericCosmeticFiltering || cfeDetails.noDOMSurveying ) {
                vAPI.domSurveyor = null;
            }
            domFilterer.exceptions = cfeDetails.exceptionFilters;
            domFilterer.hideNodeAttr = cfeDetails.hideNodeAttr;
            domFilterer.hideNodeStyleSheetInjected =
                cfeDetails.hideNodeStyleSheetInjected === true;
            domFilterer.addCSSRule(
                cfeDetails.declarativeFilters,
                'display:none!important;'
            );
            domFilterer.addCSSRule(
                cfeDetails.highGenericHideSimple,
                'display:none!important;',
                { type: 'simple', lazy: true }
            );
            domFilterer.addCSSRule(
                cfeDetails.highGenericHideComplex,
                'display:none!important;',
                { type: 'complex', lazy: true }
            );
            domFilterer.addCSSRule(
                cfeDetails.injectedHideFilters,
                'display:none!important;',
                { injected: true }
            );
            domFilterer.addProceduralSelectors(cfeDetails.proceduralFilters);
            domFilterer.exceptCSSRules(cfeDetails.exceptedFilters);
        }

        if ( cfeDetails.networkFilters.length !== 0 ) {
            vAPI.userStylesheet.add(
                cfeDetails.networkFilters + '\n{display:none!important;}');
        }

        vAPI.userStylesheet.apply();

        // Library of resources is located at:
        // https://github.com/gorhill/uBlock/blob/master/assets/ublock/resources.txt
        if ( response.scriptlets ) {
            vAPI.injectScriptlet(document, response.scriptlets);
            vAPI.injectedScripts = response.scriptlets;
        }

        if ( vAPI.domSurveyor instanceof Object ) {
            vAPI.domSurveyor.start(cfeDetails);
        }

        // https://github.com/chrisaljoudi/uBlock/issues/587
        // If no filters were found, maybe the script was injected before
        // uBlock's process was fully initialized. When this happens, pages
        // won't be cleaned right after browser launch.
        if (
            typeof document.readyState === 'string' &&
            document.readyState !== 'loading'
        ) {
            bootstrapPhase2();
        } else {
            document.addEventListener(
                'DOMContentLoaded',
                bootstrapPhase2,
                { once: true }
            );
        }
    };

    return function() {
        vAPI.messaging.send(
            'contentscript',
            {
                what: 'retrieveContentScriptParameters',
                url: window.location.href,
                isRootFrame: window === window.top,
                charset: document.characterSet,
            },
            bootstrapPhase1
        );
    };
})();

// This starts bootstrap process.
vAPI.bootstrap();

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

}
// <<<<<<<< end of HUGE-IF-BLOCK


## INSTALL

### Chromium

- Download and unzip `ublock0.chromium.zip` ([latest release desirable](https://github.com/gorhill/uBlock/releases)).
- Rename the unzipped directory to `ublock`
    - When you later update manually, replace the **content** of the `ublock` folder with the **content** of the latest zipped version.
    - This will ensure that all the extension settings will be preserved
    - As long as the extension loads **from same folder path from which it was originally installed**, all your settings will be preserved.
- Go to chromium/chrome *Extensions*.
- Click to check *Developer mode*.
- Click *Load unpacked extension...*.
- In the file selector dialog:
    - Select the directory `ublock` which was created above.
    - Click *Open*.

The extension will now be available in your chromium/chromium-based browser.

Remember that you have to update manually also. For some users, updating manually is actually an advantage because:
- You can update when **you** want
- If ever a new version sucks, you can easily just re-install the previous one

### Firefox

Compatible with Firefox 52 and beyond.

#### For stable release version

This works only if you set `xpinstall.signatures.required` to `false` in `about:config`.[see "Add-on signing in Firefox"](https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox)

- Download `ublock0.firefox.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)).
    - Right-click and choose _"Save As..."_.
- Drag and drop the previously downloaded `ublock0.firefox.xpi` into Firefox

#### For beta version

- Click on `ublock0.firefox.signed.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)).

#### Location of uBO settings

On Linux, the settings are saved in a JSON file located at `~/.mozilla/firefox/[profile name]/browser-extension-data/uBlock0@raymondhill.net/storage.js`.

When you uninstall the extension, Firefox deletes that file, so all your settings are lost when you uninstall.

### Firefox legacy

Compatible with Firefox 24 to Firefox 56.

- Download `ublock0.firefox-legacy.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)).
    - Right-click and select "Save Link As..."
- Drag and drop the previously downloaded `ublock0.firefox-legacy.xpi` into Firefox

With Firefox 43 and beyond, you may need to toggle the setting `xpinstall.signatures.required` to `false` in `about:config`.[see "Add-on signing in Firefox"](https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox)

Your uBlock Origin settings are kept intact even after you uninstall the addon.

On Linux, the settings are saved in a SQlite file located at `~/.mozilla/firefox/[profile name]/extension-data/ublock0.sqlite`.

On Windows, the settings are saved in a SQlite file located at `%APPDATA%\Mozilla\Firefox\Profiles\[profile name]\extension-data\ublock0.sqlite`.

### Build instructions (for developers)

- Clone [uBlock](https://github.com/gorhill/uBlock) and [uAssets](https://github.com/uBlockOrigin/uAssets) repositories in the same parent directory
    - `git clone https://github.com/gorhill/uBlock.git`
    - `git clone https://github.com/uBlockOrigin/uAssets.git`
- Set path to uBlock: `cd uBlock`
- The official version of uBO is in the `master` branch
    - `git checkout master`
- Build the plugin:
    - Chromium: `./tools/make-chromium.sh`
    - Firefox webext: `./tools/make-firefox.sh all`
    - Firefox legacy:
        - `git checkout firefox-legacy`
        - `./tools/make-firefox-legacy.sh all`
- Load the result of the build into your browser:
    - Chromium: load the unpacked extension folder `/uBlock/dist/build/uBlock0.chromium/` in Chromium to use the extension.
    - Firefox: drag-and-drop `/uBlock/dist/build/uBlock0.firefox.xpi` or `/uBlock/dist/build/uBlock0.firefox-legacy.xpi` into Firefox.


uBlock Origin

extension name.

Nareszcie skuteczny bloker charakteryzujący się niskim użyciem procesora i pamięci.

this will be in the Chrome web store: must be 132 characters or less

uBlock₀ – Panel sterowania

English: uBlock₀ — Dashboard

Ustawienia

appears as tab name in dashboard

Listy filtrów

appears as tab name in dashboard

Moje filtry

appears as tab name in dashboard

Moje reguły

appears as tab name in dashboard

Biała lista

appears as tab name in dashboard

Skróty

appears as tab name in dashboard

uBlock₀ – Rejestrator

Title for the logger window

O rozszerzeniu

appears as tab name in dashboard

uBlock₀ – Podgląd zasobu

Title for the asset viewer page

Ustawienia zaawansowane

Title for the advanced settings page

Kliknij, aby włączyć/wyłączyć uBlock₀ na tej witrynie.

Kliknij z wciśniętym klawiszem Ctrl, aby włączyć/wyłączyć uBlock₀ tylko na tej stronie.

English: Click: disable/enable uBlock₀ for this site.

Ctrl+click: disable uBlock₀ only on this page.

Kliknij, aby wyłączyć uBlock₀ dla tej witryny.

Kliknij z wciśniętym klawiszem Ctrl, aby wyłączyć uBlock₀ tylko dla tej strony.

Message to be read by screen readers

Kliknij, aby włączyć uBlock₀ dla tej witryny.

Message to be read by screen readers

zablokowane żądania

English: requests blocked

na tej stronie

English: on this page

{{count}} ({{percent}}%)

Example: 15 or 13%

od instalacji

English: since install

lub

English: or

Otwórz panel sterowania

English: Click to open the dashboard

Przejdź do trybu usuwania elementów

Tooltip for the element-zapper icon in the popup panel

Przejdź do trybu zaznaczania elementów

English: Enter element picker mode

Otwórz rejestrator

Tooltip used for the logger icon in the panel

Włącz/wyłącz blokowanie wyskakujących okien na tej witrynie

Tooltip for the no-popups per-site switch

Kliknij, aby zablokować wszystkie wyskakujące okna na tej witrynie

Kliknij, aby nie blokować wyskakujących okien na tej witrynie

Włącz/wyłącz blokowanie dużych elementów multimedialnych na tej witrynie

Tooltip for the no-large-media per-site switch

Kliknij, aby zablokować duże elementy multimedialne na tej witrynie

Kliknij, aby nie blokować dużych elementów multimedialnych na tej witrynie

Włącz/wyłącz kosmetyczne filtrowanie na tej witrynie

Tooltip for the no-cosmetic-filtering per-site switch

Kliknij, aby wyłączyć filtrowanie kosmetyczne na tej witrynie

Kliknij, aby włączyć filtrowanie kosmetyczne na tej witrynie

Włącz/wyłącz blokowanie zdalnych czcionek na tej witrynie

Tooltip for the no-remote-fonts per-site switch

Kliknij, aby zablokować zdalne czcionki na tej witrynie

Kliknij, aby nie blokować zdalnych czcionek na tej witrynie

Kliknij, aby wyłączyć JavaScript na tej witrynie

Tooltip for the no-scripting per-site switch

Kliknij, aby JavaScript nie był już wyłączony na tej witrynie

Reguły globalne. W tej kolumnie znajdują się reguły stosowane na wszystkich witrynach.

Tooltip when hovering the top-most cell of the global-rules column.

Reguły lokalne. W tej kolumnie znajdują się reguły stosowane tylko na bieżącej witrynie.
Reguły lokalne zastępują reguły globalne.

Tooltip when hovering the top-most cell of the local-rules column.

Kliknij, aby zastosować zmiany na stałe.

Tooltip when hovering over the padlock in the dynamic filtering pane.

Kliknij, aby odrzucić zmiany.

Tooltip when hovering over the eraser in the dynamic filtering pane.

wszystko

obrazki

domeny zewnętrzne

zewnętrzne CSS/obrazki

zagnieżdżone skrypty

skrypty z tej domeny

skrypty z domen zewnętrznych

ramki z domen zewnętrznych

podłączone domeny

appears in popup

{{count}} z {{total}}

appears in popup

Utwórz

English: Create

Wybierz

English: Pick

Zamknij

English: Quit

Podgląd

Element picker preview mode: will cause the elements matching the current filter to be removed from the page

Filtry sieciowe

English: header for a type of filter in the element picker dialog

Filtry kosmetyczne

English: Cosmetic filters

Klik, Ctrl + klik

English: Click, Ctrl-click

Zablokuj element

English: Block element

Ukrywaj symbole zastępcze zablokowanych elementów

English: Hide placeholders of blocked elements

Wyświetlaj liczbę zablokowanych żądań na ikonie rozszerzenia

English: Show the number of blocked requests on the icon

Nie wyświetlaj dymków podpowiedzi

A checkbox in the Settings pane

Używaj menu kontekstowego, gdzie to możliwe

English: Make use of context menu where appropriate

Interfejs przyjazny osobom z zaburzeniami widzenia kolorów

English: Color-blind friendly

Włącz obsługę zapisu ustawień w chmurze

Jestem zaawansowanym użytkownikiem (Wymagana lektura)

Ustawienia zaawansowane

For the tooltip of a link which gives access to advanced settings

Wyłącz wstępne pobieranie (by uniknąć połączeń z zablokowanymi zapytaniami sieciowymi)

English:

Wyłącz śledzenie kliknięć odnośników

English:

Zapobiegaj ujawnianiu lokalnego adresu IP poprzez interfejs WebRTC

English:

Działanie domyślne

Te ustawienia mogą być zastąpione przez ustawienia danej witryny

Wyłącz filtrowanie kosmetyczne

Blokuj elementy multimedialne większe niż {{input}} KiB

Blokuj zdalne czcionki

Wyłącz JavaScript

The default state for the per-site no-scripting switch

Blokuj raporty CSP

background information: https://github.com/gorhill/uBlock/issues/3150

Użycie pamięci masowej: {{value}} bajtów

English: Storage used: {{}} bytes

Ostatnie przywrócenie wykonano:

English: Last restore:

Ostatnią kopię zapasową wykonano:

English: Last backup:

Filtry sieciowe: {{netFilterCount}} + filtry kosmetyczne: {{cosmeticFilterCount}} z:

Appears at the top of the _3rd-party filters_ pane

użytych {{used}} z {{total}}

Appears aside each filter list in the _3rd-party filters_ pane

Automatycznie aktualizuj listy filtrów

A checkbox in the _3rd-party filters_ pane

Aktualizuj teraz

A button in the in the _3rd-party filters_ pane

Wyczyść całą pamięć podręczną

Przetwarzaj i stosuj filtry kosmetyczne

English: Parse and enforce Adblock+ element hiding filters.

Włączenie tej funkcji skutkuje przetwarzaniem i używaniem filtrów kompatybilnych z dodatkiem Adblock Plus służących do ukrywania elementów. Są to filtry kosmetyczne i służą do ukrycia elementów uważanych za wizualnie uciążliwe, których nie można zablokować przez filtry sieciowe.

Włączenie tej funkcji powoduje zwiększenie użycia pamięci przez uBlock₀.

Describes the purpose of the 'Parse and enforce cosmetic filters' feature.

Ignoruj ogólne filtry kosmetyczne

This will cause uBO to ignore all generic cosmetic filters.

Ogólne filtry kosmetyczne, to filtry które mają być zastosowane na wszystkich stronach internetowych.
Choć dobrze obsługiwane przez uBlock₀ mogą na niektórych stronach, szczególnie dużych i otwartych przez dłuższy czas, przyczyniać się do obciążenia procesora i pamięci operacyjnej.
Włącznie tej funkcji wyeliminuje obciążenie procesora i pamięci operacyjnej powodowane stosowaniem ogólnych filtrów kosmetycznych, a także zmniejszy zapotrzebowanie na pamięć samego uBlocka₀.
Zaleca się włączenie tej funkcji na mniej wydajnych urządzeniach.

Describes the purpose of the 'Ignore generic cosmetic filters' feature.

Listy zablokowanych hostów

English: Lists of blocked hosts

Zastosuj zmiany

English: Apply changes

Wbudowane

Header for the uBlock filters section in 'Filter lists pane'

Reklamy

English: Ads

Prywatność

English: Privacy

Domeny ze złośliwym oprogramowaniem

English: Malware domains

Elementy irytujące

The header identifying the filter lists in the category 'annoyances'

Wielofunkcyjne

English: Multipurpose

Regiony, języki

English: Regions, languages

Własne

English: Custom

Importuj...

The label for the checkbox used to import external filter lists

Jeden adres URL na linię. Błędne adresy URL będą pomijane.

Short information about how to use the textarea to import external filter lists by URL

Nieaktualna.

used as a tooltip for the out-of-date icon beside a list

Zaktualizowano: {{ago}}.
Kliknij, aby wymusić aktualizację.

used as a tooltip for the clock icon beside a list

Aktualizowanie...

used as a tooltip for the spinner icon beside a list

Błąd sieci uniemożliwił aktualizację zasobów.

used as a tooltip for error icon beside a list

Jeden filtr na linię. Filtrem może być nazwa hosta lub filtr kompatybilny z dodatkiem Adblock Plus. Wiersze poprzedzone <code>!</code> będą pomijane.

Short information about how to create custom filters

Importuj i dołącz

English: Import and append

Eksportuj

English: Export

ublock-statyczne-filtry_{{datetime}}.txt

English: my-ublock-static-filters_{{datetime}}.txt

Zastosuj zmiany

English: Apply changes

Stałe reguły

header

Tymczasowe reguły

header

Przywróć

This will remove all temporary rules

Zatwierdź

This will persist temporary rules

Edytuj

Will enable manual-edit mode (textarea)

Zapisz

Will save manually-edited content and exit manual-edit mode

Odrzuć

Will discard manually-edited content and exit manual-edit mode

Importuj z pliku…

Eksportuj do pliku…

ublock-dynamiczne-reguly_{{datetime}}.txt

default file name to use

Lista reguł dynamicznego filtrowania.

English: List of your dynamic filtering rules.

Składnia reguły: źródło cel typ akcja (pełna dokumentacja).

English: dynamic rule syntax and full documentation.

Dyrektywy białej listy wskazują, na których stronach uBlock Origin powinien zostać wyłączony. Jeden wpis na linię. Nieprawidłowe wpisy zostaną bez powiadomienia zignorowane i wykomentowane.

English: An overview of the content of the dashboard's Whitelist pane.

Importuj i dołącz

English: Import and append

Eksportuj

English: Export

English: my-ublock-whitelist_{{datetime}}.txt

Zastosuj zmiany

English: Apply changes

Typ

English: Type

Domena

English: Domain

Adres URL

English: URL

Filtr

English: Filter

Wszystkie

Appears in the logger's tab selector

Bez kart

Pretty name for behind-the-scene network requests

Aktywna karta

Appears in the logger's tab selector

Przeładuj zawartość karty

Tooltip for the reload button in the logger page

Przełącz inspektor DOM

Tooltip for the DOM inspector button in the logger page

Przełącz panel popup

Tooltip for the popup panel button in the logger page

uBlock Origin wiki: Rejestrator (ang.)

Tooltip for the top-right info label in the logger page

Wyczyść rejestr

Tooltip for the eraser in the logger page; used to blank the content of the logger

Pauzuj rejestrowanie (porzuć wszystkie przychodzące dane)

Tooltip for the pause button in the logger page

Wznów rejestrowanie

Tooltip for the play button in the logger page

Przełącz filtrowanie rejestru

Tooltip for the row filterer button in the logger page

filtruj zawartość rejestru

Placeholder string for logger output filtering input field

Opcje filtrowania rejestru

Tooltip for the button to bring up logger output filtering options

Nie

A keyword in the built-in row filtering expression

akcje

A keyword in the built-in row filtering expression: all items corresponding to uBO doing something (blocked, allowed, redirected, etc.)

zablokowane

dozwolone

własna domena

domeny trzecie

Szczegóły

Small header to identify the 'Details' pane for a specific logger entry

Filtr

Label to identify a filter field

Lista filtrów

Label to identify a filter list field

Reguła

Label to identify a rule field

Kontekst

Label to identify a context field (typically a hostname)

Kontekst główny

Label to identify a root context field (typically a hostname)

Lokalność

Label to identify a field providing partyness information

Typ

Label to identify the type of an entry

Adres URL

Label to identify the URL of an entry

Reguła URL

Small header to identify the dynamic URL filtering section

Kontekst:

Label for the context selector

Typ:

Label for the type selector

Filtr statyczny

Small header to identify the static filtering section

{{action}} żądania sieciowe {{type}}, których adres URL jest zgodny z: {{url}} {{br}}i pochodzi {{origin}}, {{importance}} filtr znajduje się na liście wyjątków.

Used in the static filtering wizard

Blokuj

Used in the static filtering wizard

Zezwalaj na

Used in the static filtering wizard

typu „{{type}}”

Used in the static filtering wizard

każdego typu

Used in the static filtering wizard

z „{{origin}}”

Used in the static filtering wizard

z dowolnego miejsca

Used in the static filtering wizard

z wyjątkiem, gdy

Used in the static filtering wizard

nawet, jeśli

Used in the static filtering wizard

Filtr statyczny <code>{{filter}}</code> znajdujący się w:

Below this sentence, the filter list(s) in which the filter was found

Filtr statyczny <code>{{filter}}</code> nie został znaleziony w aktualnie włączonych listach filtrów

Message to show when a filter cannot be found in any filter lists

Wpisy rejestru nie spełniające wszystkich trzech warunków będą automatycznie porzucane:

Logger setting: A sentence to describe the purpose of the settings below

Zachowuj wpisy z ostatnich {{input}} minut

A logger setting

Zachowuj nie więcej niż {{input}} przeładowań strony na kartę

A logger setting

Zachowuj nie więcej niż {{input}} wpisów na kartę

A logger setting

Użyj {{input}} linie na wpis w widoku poszerzonym

A logger setting

Ukryj kolumny:

Logger settings: a sentence to describe the purpose of the checkboxes below

{{input}} Czas

A label for the time column

{{input}} Filtr/reguła

A label for the filter or rule column

{{input}} Kontekst

A label for the context column

{{input}} Lokalność

A label for the partyness column

Lista

Label for radio-button to pick export format

Tabela

Bez formatowania

Label for radio-button to pick export text format

Markdown

Informacje o wydaniu

Instrukcja obsługi

English: project' wiki on GitHub

Pomoc techniczna

A link for where to get support

Zgłaszanie błędów

Text for a link to official issue tracker

Kod źródłowy (GPLv3)

English: Source code (GPLv3)

Współtwórcy

English: Contributors

Zewnętrzne zależności (kompatybilne z GPLv3):

Shown in the About pane

Utwórz kopię zapasową…

Text for button to create a backup of all settings

English: my-ublock-backup_{{datetime}}.txt

Przywróć z pliku…

English: Restore from file...

Przywróć ustawienia domyślne…

English: Reset to default settings...

Wszystkie twoje ustawienia zostaną zastąpione ustawieniami z kopii stworzonej {{time}}. Po zakończeniu uBlock₀ zostanie uruchomiony ponownie.

Przywrócić ustawienia z kopii zapasowej?

Message asking user to confirm restore

Danych nie można odczytać lub są nieprawidłowe

Message to display when an error occurred during restore

Wszystkie twoje ustawienia zostaną usunięte i uBlock₀ zostanie ponownie uruchomiony.

Czy chcesz przywrócić ustawienia domyślne?

Message asking user to confirm reset

Błąd sieci: {{msg}}

English: Network error: {{msg}}

uBlock₀: Dodać następujący URL do twojej listy filtrów?

Tytuł: "{{title}}"
Adres: {{url}}

English: The message seen by the user to confirm subscription to a ABP filter list

minutę temu

English: a minute ago

{{value}} min. temu

English: {{value}} minutes ago

godzinę temu

English: an hour ago

{{value}} godz. temu

English: {{value}} hours ago

wczoraj

English: a day ago

{{value}} dni temu

English: {{value}} days ago

Panel sterowania

Firefox/Fennec-specific: Show Dashboard

Pokaż rejestrator

Firefox/Fennec-specific: Show Logger

wyłączony

Firefox-specific: appears as 'uBlock₀ (off)'

uBlock Origin zablokował wczytywanie następującej strony:

English: uBlock₀ has prevented the following page from loading:

Powodem zablokowania jest filtr

English: Because of the following filter

bez parametrów

label to be used for the parameter-less URL: https://cloud.githubusercontent.com/assets/585534/9832014/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png

Filtr znajduje się na liście:

English: List of filter list names follows

Wstecz

English: Go back

Zamknij to okno

English: Close this window

Wyłącz rygorystyczne blokowanie dla {{hostname}}

English: Disable strict blocking for {{hostname}} ...

Tymczasowo

English: Temporarily

Na stałe

English: Permanently

Eksportuj do chmury

tooltip

Importuj z chmury

tooltip

Importuj z chmury i połącz z aktualnymi ustawieniami

tooltip

Nazwa urządzenia:

used as a prompt for the user to provide a custom device name

Uwaga! Te ustawienia zmieniasz na własne ryzyko.

A warning to users at the top of 'Advanced settings' page

Wyślij

for generic 'Submit' buttons

Zastosuj zmiany

for generic 'Apply changes' buttons

Przywróć

for generic 'Revert' buttons

bajty

Tymczasowo zezwalaj na wyświetlanie dużych elementów multimedialnych

A context menu entry, present when large media elements have been blocked on the current site

Wprowadź skrót

Placeholder string for input field used to capture a keyboard shortcut

Przełącz przewijanie synchroniczne

Tooltip for the button used to lock scrolling between the views in the 'My rules' pane

Kopiuj do schowka

Label for buttons used to copy something to the clipboard

Ten wpis musi być ostatni

so we dont need to deal with comma for last entry


#!/usr/bin/env bash
#
# This script assumes a linux environment

echo "*** uBlock0.thunderbird: Creating web store package"
echo "*** uBlock0.thunderbird: Copying files"

BLDIR=dist/build
DES="$BLDIR"/uBlock0.thunderbird
rm -rf $DES
mkdir -p $DES

bash ./tools/make-assets.sh $DES

cp -R src/css                           $DES/
cp -R src/img                           $DES/
cp -R src/js                            $DES/
cp -R src/lib                           $DES/
cp -R src/_locales                      $DES/
cp -R $DES/_locales/nb                  $DES/_locales/no
cp src/*.html                           $DES/
cp platform/chromium/*.js               $DES/js/
cp platform/chromium/*.html             $DES/
cp platform/chromium/*.json             $DES/
cp LICENSE.txt                          $DES/

cp platform/thunderbird/manifest.json      $DES/
cp platform/thunderbird/vapi-webrequest.js $DES/js/
cp platform/firefox/vapi-usercss.js        $DES/js/

echo "*** uBlock0.thunderbird: concatenating content scripts"
cat $DES/js/vapi-usercss.js > /tmp/contentscript.js
echo >> /tmp/contentscript.js
grep -v "^'use strict';$" $DES/js/vapi-usercss.real.js >> /tmp/contentscript.js
echo >> /tmp/contentscript.js
grep -v "^'use strict';$" $DES/js/contentscript.js >> /tmp/contentscript.js
mv /tmp/contentscript.js $DES/js/contentscript.js
rm $DES/js/vapi-usercss.js
rm $DES/js/vapi-usercss.real.js
rm $DES/js/vapi-usercss.pseudo.js

# Firefox/webext-specific
rm $DES/img/icon_128.png

echo "*** uBlock0.thunderbird: Generating web accessible resources..."
cp -R src/web_accessible_resources $DES/
python3 tools/import-war.py $DES/

echo "*** uBlock0.thunderbird: Generating meta..."
python tools/make-firefox-meta.py $DES/

if [ "$1" = all ]; then
    echo "*** uBlock0.thunderbird: Creating package..."
    pushd $DES > /dev/null
    zip ../$(basename $DES).xpi -qr *
    popd > /dev/null
elif [ -n "$1" ]; then
    echo "*** uBlock0.thunderbird: Creating versioned package..."
    pushd $DES > /dev/null
    zip ../$(basename $DES).xpi -qr *
    popd > /dev/null
    mv "$BLDIR"/uBlock0.thunderbird.xpi "$BLDIR"/uBlock0_"$1".thunderbird.xpi
fi

echo "*** uBlock0.thunderbird: Package done."