uBlock Origin

extension name.

Pagaliau, efektyvus blokatorius, neapkraunantis nei procesoriaus, nei darbinės atminties.

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

uBlock₀ — Prietaisų skydas

English: uBlock₀ — Dashboard

Nustatymai

appears as tab name in dashboard

Filtrų sąrašai

appears as tab name in dashboard

Mano filtrai

appears as tab name in dashboard

Mano taisyklės

appears as tab name in dashboard

Išimtys

appears as tab name in dashboard

Nuorodos

appears as tab name in dashboard

uBlock₀ — Žurnalas

Title for the logger window

Apie

appears as tab name in dashboard

uBlock₀ — resursų žiūryklė

Title for the asset viewer page

Sudėtingesni nustatymai

Title for the advanced settings page

Spustelėjimas: įjungti/išjungti uBlock₀ šiam puslapiui.

Vald+spustelėjimas: išjungti uBlock₀ tik šiam puslapiui.

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

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

Spustelėkite, kad išjungtumėte uBlock₀ šiai svetainei.

Vald+spustelėkite, kad išjungtumėte uBlock₀ tik šiam puslapiui.

Message to be read by screen readers

Spustelėkite, kad įjungtumėte uBlock₀ šiai svetainei.

Message to be read by screen readers

blokuotos užklausos

English: requests blocked

šiame puslapyje

English: on this page

{{count}} arba {{percent}} %

Example: 15 or 13%

nuo įdiegimo

English: since install

arba

English: or

Atverti prietaisų skydą

English: Click to open the dashboard

Atverti elementų trynimo veikseną

Tooltip for the element-zapper icon in the popup panel

Atverti elementų parinkimo veikseną

English: Enter element picker mode

Atverti žurnalą

Tooltip used for the logger icon in the panel

Perjungti visų iškylančiųjų langų blokavimą šiame puslapyje

Tooltip for the no-popups per-site switch

Spustelėkite visų iškylančių langų blokavimui šioje svetainėje

Spustelėkite visų iškylančių langų neblokavimui šioje svetainėje

Perjungti didelių medijos elementų blokavimą šiame puslapyje

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

Spustelėkite didelių medijos elementų blokavimui šioje svetainėje

Spustelėkite didelių medijos elementų neblokavimui šioje svetainėje

Perjungti kosmetinį filtravimą šiame puslapyje

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

Spustelėkite kosmetinių filtrų išjungimui šioje svetainėje

Spustelėkite kosmetinių filtrų įjungimui šioje svetainėje

Perjungti nuotolinių šriftų blokavimą šiame puslapyje

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

Spustelėkite nutolusių šriftų blokavimui šioje svetainėje

Spustelėkite nutolusių šriftų neblokavimui šioje svetainėje

Spustelėkite JavaScript išjungimui šioje svetainėje

Tooltip for the no-scripting per-site switch

Spustelėkite JavaScript įjungimui šioje svetainėje

Globalios taisyklės: ši skiltis skirta visiems puslapiams taikomoms taisyklėms.

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

Vietinės taisyklės: ši skiltis skirta dabartiniam puslapiui taikomoms taisyklėms.
Vietinės taisyklės nustelbia globalias.

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

Spauskite pakeitimams padaryti pastoviais.

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

Spauskite pakeitimams atstatyti.

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

visi

vaizdai

trečios šalies

3-ios šalies CSS/paveikslai

įterptieji scenarijai

1-os šalies scenarijai

3-ios šalies scenarijai

3-ios šalies rėmeliai

jungtasi prie sričių

appears in popup

{{count}} iš {{total}}

appears in popup

Sukurti

English: Create

Parinkti

English: Pick

Baigti

English: Quit

Peržiūra

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

Tinklo filtrai

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

Kosmetiniai filtrai

English: Cosmetic filters

Spustelėjimas, Vald-spustelėjimas

English: Click, Ctrl-click

Blokuoti elementą

English: Block element

Slėpti blokuotų elementų rezervuotą vietą

English: Hide placeholders of blocked elements

Rodyti blokuotų užklausų skaičių piktogramoje

English: Show the number of blocked requests on the icon

Išjungti paaiškinimus

A checkbox in the Settings pane

Kur tinka, naudoti kontekstinį meniu

English: Make use of context menu where appropriate

Draugiškas neskiriantiems spalvų

English: Color-blind friendly

Įjungti nuotolinės saugyklos palaikymą

Aš esu patyręs naudotojas (privaloma perskaityti)

sudėtingesni nustatymai

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

Išjungti išankstinį gavimą (visiems blokuotų tinklo užklausų prisijungimams išvengti)

English:

Išjungti saitų auditą

English:

Neleisti WebRTC atskleisti vietinio IP adreso

English:

Numatytoji elgsena

Šios numatytosios elgsenos gali būti nustelbtos kiekvienam puslapiui atskirai

Išjungti kosmetinius filtrus

Blokuoti medijos elementus didesnius nei {{input}} kB

Blokuoti nuotolinius šriftus

Išjungti JavaScript

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

Blokuoti CSP ataskaitas

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

Naudojama vietos: {{value}} baitų

English: Storage used: {{}} bytes

Paskutinis atkūrimas:

English: Last restore:

Paskutinė atsarginė kopija:

English: Last backup:

Tinklo filtrų ({{netFilterCount}})  ＋ kosmetinių filtrų ({{cosmeticFilterCount}}) iš:

Appears at the top of the _3rd-party filters_ pane

naudojama {{used}} iš {{total}}

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

Automatiškai atnaujinti filtrų sąrašus.

A checkbox in the _3rd-party filters_ pane

Atnaujinti dabar

A button in the in the _3rd-party filters_ pane

Valyti visus podėlius

Analizuoti ir taikyti kosmetinius filtrus.

English: Parse and enforce Adblock+ element hiding filters.

Ši nuostata įjungia Su Adblock Plus suderinamų „elementų slėpimo“ filtrų analizę ir taikymą. Šie filtrai iš esmės yra kosmetiniai, jie naudojami tinklalapio elementams, kurie yra laikomi vaizdiniais nepatogumais ir kurių negalima užblokuoti tinklo užklausomis paremtais filtrais, paslėpti.

Šios nuostatos įjungimas padidina uBlock₀ atminties naudojimą.

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

Ignoruoti daugybinius kosmetinius filtrus.

This will cause uBO to ignore all generic cosmetic filters.

Daugybiniai kosmetiniai filtrai yra tokie kosmetiniai filtrai, kurie taikomi visoms svetainėms.
Nors uBlock₀ juos apdoroja efektyviai, daugybiniai kosmetiniai filtrai vis tiek gali prisidėti prie pamatuojamo atminties ir procesoriaus panaudojimo kai kuriose svetainėse, ypač didelėse ir ilgai gyvuojančiose.
Šio nustatymo įjungimas sumažins atminties ir procesoriaus naudojimą, kurį sukelia svetainės dėl daugybinių kosmetinių filtrų apdorojimo, bei sumažins bendrą uBlock₀ atminties naudojimą.
Rekomenduojama įjungti šį nustatymą mažiau galinguose įrenginiuose.

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

Blokuotų serverių sąrašas

English: Lists of blocked hosts

Taikyti pakeitimus

English: Apply changes

Įtaisyti

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

Reklamos

English: Ads

Privatumas

English: Privacy

Kenksmingos sritys

English: Malware domains

Erzinimas

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

Univarsalūs

English: Multipurpose

Regionai, kalbos

English: Regions, languages

Adaptuoti

English: Custom

Importuoti...

The label for the checkbox used to import external filter lists

Vienas URL eilutėje. Neteisingi URL bus tyliai ignoruoti.

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

Pasenęs.

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

Paskutinis atnaujinimas: {{ago}}.
Ieškoti atnaujinimo.

used as a tooltip for the clock icon beside a list

Atnaujinama...

used as a tooltip for the spinner icon beside a list

Tinklo klaida sutrukdė atnaujinti resursą.

used as a tooltip for error icon beside a list

Vienas filtras eilutėje. Filtras gali būti paprastas serverio adresas, arba su Adblock Plus suderinamas filtras. Eilutės pradėtos <code>!</code> bus ignoruotos.

Short information about how to create custom filters

Importuoti ir papildyti

English: Import and append

Eksportuoti

English: Export

mano-ublock-statiniai-filtrai_{{datetime}}.txt

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

Taikyti pakeitimus

English: Apply changes

Pastovios taisyklės

header

Laikinos taisyklės

header

Atstatyti

This will remove all temporary rules

Pritaikyti

This will persist temporary rules

Redaguoti

Will enable manual-edit mode (textarea)

Įrašyti

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

Atmesti

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

Importuoti iš failo...

Eksportuoti į failą

mano-ublock-dinaminės-taisyklės_{{datetime}}.txt

default file name to use

Dinaminių filtravimo taisyklių sąrašas.

English: List of your dynamic filtering rules.

Taisyklės sintaksė: šaltinis paskirtis tipas veiksmas (dokumentacija).

English: dynamic rule syntax and full documentation.

Baltojo sąrašo direktyvos nurodo, kurioms svetainėms uBlock Origin turėtų būti išjungtas. Vienas įrašas eilutėje. Neteisingos direktyvos bus tyliai ignoruotos ir užkomentuotos.

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

Importuoti ir papildyti

English: Import and append

Eksportuoti

English: Export

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

Taikyti pakeitimus

English: Apply changes

Tipas

English: Type

Sritis

English: Domain

URL

English: URL

Filtras

English: Filter

Visos

Appears in the logger's tab selector

Užkulisiai

Pretty name for behind-the-scene network requests

Dabartinė kortelė

Appears in the logger's tab selector

Įkelti kortelės turinį iš naujo

Tooltip for the reload button in the logger page

Įjungti / išjungti DOM tyriklį

Tooltip for the DOM inspector button in the logger page

Toggle the popup panel

Tooltip for the popup panel button in the logger page

„uBlock Origin“ vikis: žurnalas

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

Valyti žurnalą

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

Pristabdyti žurnalą (atmesti visus gaunamus duomenis)

Tooltip for the pause button in the logger page

Tęsti žurnalą

Tooltip for the play button in the logger page

Įjungti / išjungti žurnalo filtravimą

Tooltip for the row filterer button in the logger page

filtruoti žurnalo įrašus

Placeholder string for logger output filtering input field

Žurnalo filtravimo nuostatos

Tooltip for the button to bring up logger output filtering options

Ne

A keyword in the built-in row filtering expression

eventful

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

užblokuota

leidžiama

1-oji šalis

3-ioji šalis

Išsamiau

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

Filtras

Label to identify a filter field

Filtrų sąrašas

Label to identify a filter list field

Taisyklė

Label to identify a rule field

Kontekstas

Label to identify a context field (typically a hostname)

Šakninis kontekstas

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

Partyness

Label to identify a field providing partyness information

Tipas

Label to identify the type of an entry

URL

Label to identify the URL of an entry

URL taisyklė

Small header to identify the dynamic URL filtering section

Kontekstas:

Label for the context selector

Tipas:

Label for the type selector

Statinis filtravimas

Small header to identify the static filtering section

{{action}} {{type}} tinklo užklausas, {{br}}kur URL adresas atitinka {{url}} {{br}}ir kurios kyla {{origin}},{{br}}{{importance}} yra atitinkantis išimties filtras.

Used in the static filtering wizard

Blokuoti

Used in the static filtering wizard

Leisti

Used in the static filtering wizard

„{{type}}“ tipo

Used in the static filtering wizard

bet kokio tipo

Used in the static filtering wizard

iš „{{origin}}“

Used in the static filtering wizard

bet kur

Used in the static filtering wizard

išskyrus kai

Used in the static filtering wizard

net jei

Used in the static filtering wizard

Statinis filtras <code>{{filter}}</code> rastas:

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

Statinis filtras <code>{{filter}}</code> nerastas jokiame dabar įjungtame filtrų sąraše

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

Logger entries which do not fulfill all three conditions below will be automatically discarded:

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

Preserve entries from the last {{input}} minutes

A logger setting

Preserve at most {{input}} page loads per tab

A logger setting

Preserve at most {{input}} entries per tab

A logger setting

Use {{input}} lines per entry in vertically expanded mode

A logger setting

Hide columns:

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

{{input}} Time

A label for the time column

{{input}} Filter/rule

A label for the filter or rule column

{{input}} Context

A label for the context column

{{input}} Partyness

A label for the partyness column

Sąrašas

Label for radio-button to pick export format

Lentelė

Neformatuotas

Label for radio-button to pick export text format

Markdown

Pakeitimų žurnalas

Viki

English: project' wiki on GitHub

Palaikymas

A link for where to get support

Klaidų sekimo sistema

Text for a link to official issue tracker

Pirminis tekstas (GPLv3)

English: Source code (GPLv3)

Talkininkai

English: Contributors

Išorinės priklausomybės (suderinamos su „GPLv3“):

Shown in the About pane

Padaryti atsarginę kopiją į failą

Text for button to create a backup of all settings

mano-ublock-atsarginė_kopija_{{datetime}}.txt

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

Atkurti iš failo...

English: Restore from file...

Atkurti numatytuosius nustatymus...

English: Reset to default settings...

Visi jūsų nustatymai bus perrašyti naudojant duomenis iš {{time}} atsarginės kopijos, o uBlock₀ bus perleistas.

Perrašyti visus nustatymus naudojant atsarginės kopijos duomenis?

Message asking user to confirm restore

Nepavyko nuskaityti duomenų arba jie neteisingi

Message to display when an error occurred during restore

Visi jūsų nustatymai bus pašalinti, o uBlock₀ bus perleistas.

Atkurti uBlock₀ pradinius nustatymus?

Message asking user to confirm reset

Tinklo klaida: {{msg}}

English: Network error: {{msg}}

uBlock₀: Pridėti šį URL į jūsų adaptuotų filtrų sąrašą?

Pavadinimas: „{{title}}“
URL: {{url}}

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

prieš minutę

English: a minute ago

prieš {{value}} min.

English: {{value}} minutes ago

prieš valandą

English: an hour ago

prieš {{value}} val.

English: {{value}} hours ago

prieš dieną

English: a day ago

prieš {{value}} d.

English: {{value}} days ago

Rodyti prietaisų skydą

Firefox/Fennec-specific: Show Dashboard

Rodyti žurnalą

Firefox/Fennec-specific: Show Logger

išjungta

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

uBlock Origin neleido įkelti šio puslapio:

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

Dėl šio filtro

English: Because of the following filter

be parametrų

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

Rasta:

English: List of filter list names follows

Grįžti

English: Go back

Užverti langą

English: Close this window

Išjungti griežtą {{hostname}} blokavimą

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

Laikinai

English: Temporarily

Negrįžtamai

English: Permanently

Eksportuoti į nuotolinę saugyklą

tooltip

Importuoti iš nuotolinės saugyklos

tooltip

Importuoti iš nuotolinės saugyklos ir sulieti su dabartiniais nustatymais

tooltip

Šio įrenginio vardas:

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

Įspėjimas! Keiskite šiuos sudėtingesnius nustatymus savo rizika.

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

Pateikti

for generic 'Submit' buttons

Taikyti pakeitimus

for generic 'Apply changes' buttons

Atstatyti

for generic 'Revert' buttons

baitai

Laikinai leisti didelius medijos elementus

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

Įveskite nuorodą

Placeholder string for input field used to capture a keyboard shortcut

Toggle locked scrolling

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

Kopijuoti į iškarpinę

Label for buttons used to copy something to the clipboard

This entry must be the last one

so we dont need to deal with comma for last entry


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

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

DES=dist/build/uBlock0.chromium
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 src/*.html               $DES/
cp platform/chromium/*.js   $DES/js/
cp platform/chromium/*.html $DES/
cp platform/chromium/*.json $DES/
cp LICENSE.txt              $DES/

echo "*** uBlock0.chromium: 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/vapi-usercss.pseudo.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

# Chrome store-specific
cp -R $DES/_locales/nb $DES/_locales/no

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

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

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

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


extension name.

終於出現了，一個高效率的阻擋器，使用不多的 CPU 及記憶體資源。

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

uBlock₀ — 控制台

English: uBlock₀ — Dashboard

設定

appears as tab name in dashboard

過濾規則清單

appears as tab name in dashboard

自訂靜態過濾規則

appears as tab name in dashboard

自訂動態過濾規則

appears as tab name in dashboard

白名單

appears as tab name in dashboard

快速鍵

appears as tab name in dashboard

uBlock₀ — 記錄器

Title for the logger window

關於

appears as tab name in dashboard

uBlock₀ — 資源檢視器

Title for the asset viewer page

進階設定

Title for the advanced settings page

點擊：對此網站 停用/啟用 uBlock₀ 。

Ctrl + 點擊：僅在此頁面停用 uBlock₀ 。

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

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

點擊：對此網站停用 uBlock₀ 。

Ctrl + 點擊：僅在此頁面停用 uBlock₀ 。

Message to be read by screen readers

點擊以在此網站啟用 uBlock₀ 。

Message to be read by screen readers

已阻擋連線請求

English: requests blocked

在此頁面

English: on this page

{{count}} 或 {{percent}}%

Example: 15 or 13%

自安裝後

English: since install

或

English: or

開啟控制台

English: Click to open the dashboard

進入元素臨時移除模式

Tooltip for the element-zapper icon in the popup panel

進入元素選擇器模式

English: Enter element picker mode

開啟記錄器

Tooltip used for the logger icon in the panel

切換是否阻擋此網站的所有彈出型視窗

Tooltip for the no-popups per-site switch

點擊以封鎖此網站所有彈窗

點擊以停止封鎖此網站所有彈窗

切換是否封鎖此網站的大型媒體元素

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

點擊以封鎖此網站的大型媒體元素

點擊以停止封鎖此網站的大型媒體元素

切換是否在此網站過濾網頁元素

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

點擊以停用此網站的網頁元素過濾

點擊以啟用此網站的網頁元素過濾

切換是否封鎖此網站的遠端字體

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

點擊以封鎖此網站的遠端字體

點擊以停止封鎖此網站的遠端字體

點擊以禁用此網站的所有腳本

Tooltip for the no-scripting per-site switch

點擊以停止禁用此網站的所有腳本

全域規則：此欄位的規則會套用至所有網站。

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

區域規則：此欄位的規則僅會套用至目前網站。
區域規則會覆蓋過全域規則。

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

點擊此處讓變更永久生效。

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

點擊以撤銷變更。

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

全部

圖片

第三方

第三方階層式樣式表 (CSS)／圖片

內聯腳本

第一方腳本

第三方腳本

第三方框架

已連結域名

appears in popup

{{count}} / {{total}}

appears in popup

建立

English: Create

選擇

English: Pick

放棄

English: Quit

預覽

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

網址過濾規則

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

元素隱藏過濾規則

English: Cosmetic filters

點擊，按住 Ctrl 鍵點擊

English: Click, Ctrl-click

阻擋元素

English: Block element

隱藏已阻擋元素的佔位元素

English: Hide placeholders of blocked elements

在圖示上顯示被阻擋的連線請求的數量

English: Show the number of blocked requests on the icon

關閉提示文字功能

A checkbox in the Settings pane

當適用時改進右鍵選單

English: Make use of context menu where appropriate

使用對色盲友善的色彩

English: Color-blind friendly

啟用雲端儲存空間的支援

我是進階使用者（必讀資訊）

進階設定

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

關閉預讀功能（避免連線至被阻擋的網域）

English:

停用超連結監測

English:

預防 WebRTC 洩漏本地 IP 地址

English:

預設行為

這些預設行為可再依各網站調整

停用元素隱藏過濾規則

封鎖超過 {{input}} KB 的媒體元素

封鎖遠端字體

禁用網頁腳本

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

封鎖 CSP 報告

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

儲存空間：已使用 {{value}} 位元組

English: Storage used: {{}} bytes

上次還原：

English: Last restore:

上次備份：

English: Last backup:

目前已使用 {{netFilterCount}} 個網址過濾規則 ＋ {{cosmeticFilterCount}} 個元素隱藏過濾規則：

Appears at the top of the _3rd-party filters_ pane

在 {{total}} 個中，已使用 {{used}} 個

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

自動更新過濾規則

A checkbox in the _3rd-party filters_ pane

立即更新

A button in the in the _3rd-party filters_ pane

清除所有快取

解析並套用元素隱藏過濾規則

English: Parse and enforce Adblock+ element hiding filters.

啟用此選項後，您就可以使用 與 Adblock Plus 相容的「元素隱藏」過濾規則。這些過濾規則可用來隱藏網頁中礙眼，卻又不能被網路請求的過濾引擎所阻擋的元素。

啟用這項功能後將增加 uBlock₀  的記憶體使用量。

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

忽略通用元素隱藏過濾規則

This will cause uBO to ignore all generic cosmetic filters.

一般元素過濾規則是指會套用於所有網站的規則。
雖然 uBlock₀ 會有效率地處理這些規則，但這些規則還是可能會在某些網頁中造成許多多餘的記憶體與 CPU 使用率，尤其是在較長又較舊的網頁。
開啟此選項後將可避免處理一般元素過濾規則時，所造成多餘的記憶體與 CPU 使用率，也能降低 uBlock₀ 本身的記憶體用量。
建議您在效能較差的裝置中開啟此選項。

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

已阻擋域名的列表

English: Lists of blocked hosts

套用變更

English: Apply changes

內建

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

廣告

English: Ads

隱私

English: Privacy

惡意域名

English: Malware domains

惱人的

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

多用途

English: Multipurpose

地區、語言

English: Regions, languages

自訂

English: Custom

匯入⋯

The label for the checkbox used to import external filter lists

每行一個網址。無效網址將被忽略。

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

过久未更新。

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

最後更新：{{ago}}。
點擊此處以要求更新。

used as a tooltip for the clock icon beside a list

更新中…

used as a tooltip for the spinner icon beside a list

因網路錯誤無法更新資源。

used as a tooltip for error icon beside a list

每行一個過濾規則。規則可以單純是主機名稱，或是 Adblock Plus 相容格式的過濾規則，以 <code>!</code> 開頭的行將被忽略。

Short information about how to create custom filters

匯入並加入

English: Import and append

匯出

English: Export

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

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

套用變更

English: Apply changes

永久規則

header

臨時規則

header

還原

This will remove all temporary rules

提交

This will persist temporary rules

編輯

Will enable manual-edit mode (textarea)

儲存

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

捨棄

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

從檔案匯入…

匯出至檔案…

my-ublock-dynamic-rules_{{datetime}}.txt

default file name to use

您的動態過濾規則清單。

English: List of your dynamic filtering rules.

規則語法：來源主機名稱 目標主機名稱 連線請求類型 操作（完整說明）。

English: dynamic rule syntax and full documentation.

白名單中的規則適用的頁面不會被 uBlock₀ 過濾或阻擋。每行一個規則。無效的規則將被忽略。

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

匯入並加入

English: Import and append

匯出

English: Export

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

套用變更

English: Apply changes

類型

English: Type

域名

English: Domain

網址

English: URL

過濾規則

English: Filter

全部

Appears in the logger's tab selector

背景網路連線請求

Pretty name for behind-the-scene network requests

目前分頁

Appears in the logger's tab selector

重新載入分頁內容

Tooltip for the reload button in the logger page

切換 DOM 檢測器

Tooltip for the DOM inspector button in the logger page

開閉彈出式面板

Tooltip for the popup panel button in the logger page

uBlock Origin wiki：記錄器

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

清除記錄

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

暫停記錄（丟棄所有傳入資料）

Tooltip for the pause button in the logger page

取消暫停記錄

Tooltip for the play button in the logger page

開閉記錄篩選

Tooltip for the row filterer button in the logger page

篩選記錄條目

Placeholder string for logger output filtering input field

記錄篩選設定

Tooltip for the button to bring up logger output filtering options

非

A keyword in the built-in row filtering expression

已套用規則

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

已封鎖

已允許

第一方

第三方

詳細資料

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

過濾規則

Label to identify a filter field

過濾規則清單

Label to identify a filter list field

規則

Label to identify a rule field

上下文

Label to identify a context field (typically a hostname)

根上下文

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

第一方/第三方

Label to identify a field providing partyness information

類型

Label to identify the type of an entry

網址

Label to identify the URL of an entry

網址規則

Small header to identify the dynamic URL filtering section

上下文：

Label for the context selector

類型：

Label for the type selector

靜態過濾規則

Small header to identify the static filtering section

當網址符合 {{url}}，{{br}}並且來自 {{origin}} 時，{{br}}{{action}} {{type}} 的網路請求，{{br}}{{importance}} 已有符合的過濾例外規則。

Used in the static filtering wizard

封鎖

Used in the static filtering wizard

允許

Used in the static filtering wizard

{{type}} 類型

Used in the static filtering wizard

任何類型

Used in the static filtering wizard

來自「{{origin}}」

Used in the static filtering wizard

來自任何地方

Used in the static filtering wizard

除非

Used in the static filtering wizard

就算

Used in the static filtering wizard

在下列清單中找到靜態過濾規則 <code>{{filter}}</code>：

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

無法在任何啟用的過濾清單中，找到靜態過濾規則 <code>{{filter}}</code>

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

不符合以下任一狀況的記錄將會被自動清除：

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

最多保留 {{input}} 分鐘以內的記錄

A logger setting

每個分頁最多保留 {{input}} 次內容加載產生的記錄

A logger setting

每個分頁最多保留 {{input}} 條記錄

A logger setting

在垂直延展模式中每條記錄顯示 {{input}} 行

A logger setting

隱藏欄位：

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

{{input}} 時間

A label for the time column

{{input}} 過濾規則

A label for the filter or rule column

{{input}} 上下文

A label for the context column

{{input}} 第一方/第三方

A label for the partyness column

清單

Label for radio-button to pick export format

表格

純文字

Label for radio-button to pick export text format

Markdown

更新日誌

Wiki

English: project' wiki on GitHub

支援網站

A link for where to get support

問題報告

Text for a link to official issue tracker

程式原始碼（GPLv3）

English: Source code (GPLv3)

貢獻者

English: Contributors

外部相依套件（與 GPLv3 相容）：

Shown in the About pane

備份到檔案…

Text for button to create a backup of all settings

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

從檔案還原…

English: Restore from file...

重設為預設設定…

English: Reset to default settings...

您所有的設定將會被 {{time}} 的備份資料覆蓋，並將重新啟動 uBlock₀。

您確定要用備份資料蓋過目前的所有設定嗎？

Message asking user to confirm restore

無法讀取資料，或資料無效

Message to display when an error occurred during restore

您所有的設定都將被移除，並將重新啟動 uBlock₀。

您確定要將 uBlock₀ 回復為原廠設定？

Message asking user to confirm reset

網路錯誤：{{msg}}

English: Network error: {{msg}}

uBlock₀：確定要新增下列網址至自訂過濾規則清單？

標題：「{{title}}」
網址：{{url}}

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

1 分鐘前

English: a minute ago

{{value}} 分鐘前

English: {{value}} minutes ago

1 小時前

English: an hour ago

{{value}} 小時前

English: {{value}} hours ago

1 天前

English: a day ago

{{value}} 天前

English: {{value}} days ago

顯示控制台

Firefox/Fennec-specific: Show Dashboard

顯示記錄器

Firefox/Fennec-specific: Show Logger

關閉

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

uBlock₀ 已防止下列的頁面載入：

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

因為下列過濾規則

English: Because of the following filter

不帶參數

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

在下列清單找到：

English: List of filter list names follows

返回

English: Go back

關閉此視窗

English: Close this window

停止針對 {{hostname}} 的嚴格封鎖

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

暫時

English: Temporarily

永久

English: Permanently

匯出至雲端儲存空間

tooltip

從雲端儲存空間匯入

tooltip

自雲端匯入，並與目前設定合併

tooltip

此裝置的名稱：

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

警告！修改進階設定時請自負風險。

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

送出

for generic 'Submit' buttons

套用變更

for generic 'Apply changes' buttons

還原

for generic 'Revert' buttons

位元組

暫時允許大型媒體元素

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

輸入捷徑

Placeholder string for input field used to capture a keyboard shortcut

切換是否啟用同步捲動

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

複製到剪貼簿

Label for buttons used to copy something to the clipboard

此條目須為最後一個

so we dont need to deal with comma for last entry


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

    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


#domInspector {
    display: none;
    }
#inspectors.dom #domInspector {
    display: flex;
    }
#domInspector .permatoolbar .highlightMode.invert {
    transform: rotate(180deg);
    }
#domInspector .vscrollable {
    overflow-x: auto;
    }
#domInspector > ul:first-of-type {
    padding-left: 0.5em;
    }
#domInspector ul {
    background-color: #fff;
    margin: 0;
    padding-left: 1em;
    }
#domInspector li {
    list-style-type: none;
    white-space: nowrap;
    }
#domInspector li.isCosmeticHide,
#domInspector li.isCosmeticHide ul,
#domInspector li.isCosmeticHide li {
    background-color: #fee;
    }
#domInspector li > * {
    font-size: 0.8rem;
    display: inline-block;
    line-height: 1.2;
    margin-right: 1em;
    vertical-align: middle;
    }
#domInspector li > span {
    color: #aaa;
    }
#domInspector li > span:first-child {
    color: #000;
    cursor: default;
    font-size: 1rem;
    margin-right: 0;
    opacity: 0.5;
    padding: 0 4px 0 1px;
    visibility: hidden;
    }
#domInspector li > span:first-child:hover {
    opacity: 1;
    }
#domInspector li > *:last-child {
    margin-right: 0;
    }
#domInspector li > span:first-child:before {
    content: '\a0';
    }
#domInspector li.branch > span:first-child:before {
    content: '\25b8';
    visibility: visible;
    }
#domInspector li.branch.show > span:first-child:before {
    content: '\25be';
    }
#domInspector li.branch.hasCosmeticHide > span:first-child:before {
    color: red;
    }
#domInspector li > code {
    cursor: pointer;
    font-family: monospace;
    overflow: hidden;
    text-overflow: ellipsis;
    }
#domInspector li > code.off {
    text-decoration: line-through;
    }
#domInspector li > code.filter {
    color: red;
    }

#domInspector li > ul {
    display: block;
    }
#domInspector li:not(.hasCosmeticHide):not(.isCosmeticHide):not(.show) > ul {
    display: none;
    }

#domInspector:not(.vExpanded) li:not(.hasCosmeticHide):not(.isCosmeticHide) {
    display: none;
    }
#domInspector #domTree > li {
    display: block;
    }
#domInspector:not(.vExpanded) ul {
    display: block;
    }
#domInspector li > ul > li:not(.hasCosmeticHide):not(.isCosmeticHide) {
    display: none;
    }
#domInspector li.show > ul > li:not(.hasCosmeticHide):not(.isCosmeticHide) {
    display: block;
    }
#domInspector li:not(.hasCosmeticHide):not(.isCosmeticHide) {
    display: block;
    }
#domInspector.hCompact li > code:first-of-type {
    max-width: 12em;
    }

#cosmeticFilteringDialog .dialog {
    text-align: center;
    }
#cosmeticFilteringDialog .dialog textarea {
    height: 40vh;
    white-space: pre;
    word-wrap: normal;
    }


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

    lz4-block-codec-any.js
        A wrapper to instanciate a wasm- and/or js-based LZ4 block
        encoder/decoder.
    Copyright (C) 2018 Raymond Hill

    BSD-2-Clause License (http://www.opensource.org/licenses/bsd-license.php)

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are
    met:

    1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above
    copyright notice, this list of conditions and the following disclaimer
    in the documentation and/or other materials provided with the
    distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    Home: https://github.com/gorhill/lz4-wasm

    I used the same license as the one picked by creator of LZ4 out of respect
    for his creation, see https://lz4.github.io/lz4/

*/

'use strict';

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

(function(context) {                    // >>>> Start of private namespace

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

const wd = (function() {
    let url = document.currentScript.src;
    let match = /[^\/]+$/.exec(url);
    return match !== null ?
        url.slice(0, match.index) :
        '';
})();

const removeScript = function(script) {
    if ( !script ) { return; }
    if ( script.parentNode === null ) { return; }
    script.parentNode.removeChild(script);
};

const createInstanceWASM = function() {
    if ( context.LZ4BlockWASM instanceof Function ) {
        const instance = new context.LZ4BlockWASM();
        return instance.init().then(ok => ok ? instance : null);
    }
    if ( context.LZ4BlockWASM === null ) {
        return Promise.resolve(null);
    }
    return new Promise(resolve => {
        const script = document.createElement('script');
        script.src = wd + 'lz4-block-codec-wasm.js';
        script.addEventListener('load', ( ) => {
            if ( context.LZ4BlockWASM instanceof Function === false ) {
                context.LZ4BlockWASM = null;
                resolve(null);
                return;
            }
            const instance = new context.LZ4BlockWASM();
            instance.init().then(ok => { resolve(ok ? instance : null); });
        });
        script.addEventListener('error', ( ) => {
            context.LZ4BlockWASM = null;
            resolve(null);
        });
        document.head.appendChild(script);
        removeScript(script);
    });
};

const createInstanceJS = function() {
    if ( context.LZ4BlockJS instanceof Function ) {
        const instance = new context.LZ4BlockJS();
        return instance.init().then(ok => ok ? instance : null);
    }
    if ( context.LZ4BlockJS === null ) {
        return Promise.resolve(null);
    }
    return new Promise(resolve => {
        const script = document.createElement('script');
        script.src = wd + 'lz4-block-codec-js.js';
        script.addEventListener('load', ( ) => {
            if ( context.LZ4BlockJS instanceof Function === false ) {
                context.LZ4BlockJS = null;
                resolve(null);
                return;
            }
            const instance = new context.LZ4BlockJS();
            instance.init().then(ok => { resolve(ok ? instance : null); });
        });
        script.addEventListener('error', ( ) => {
            context.LZ4BlockJS = null;
            resolve(null);
        });
        document.head.appendChild(script);
        removeScript(script);
    });
};

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

context.lz4BlockCodec = {
    createInstance: function(flavor) {
        let instantiator;
        if ( flavor === 'wasm' ) {
            instantiator = createInstanceWASM;
        } else if ( flavor === 'js' ) {
            instantiator = createInstanceJS;
        } else {
            instantiator = createInstanceWASM || createInstanceJS;
        }
        return (instantiator)().then(instance => {
            if ( instance ) { return instance; }
            if ( flavor === undefined ) {
                return createInstanceJS();
            }
            return null;
        });
    },
    reset: function() {
        context.LZ4BlockWASM = undefined;
        context.LZ4BlockJS = undefined;
    }
};

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

})(this || self);                       // <<<< End of private namespace

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


An efficient blocker: easy on memory and CPU footprint, and yet can load and enforce thousands more filters than other popular blockers out there.

Illustrated overview of its efficiency: https://github.com/gorhill/uBlock/wiki/uBlock-vs.-ABP:-efficiency-compared

Usage: The big power button in the popup is to permanently disable/enable uBlock for the current web site. It applies to the current web site only, it is not a global power button.

***

Flexible, it's more than an "ad blocker": it can also read and create filters from hosts files.

Out of the box, these lists of filters are loaded and enforced:

- EasyList
- Peter Lowe’s Ad server list
- EasyPrivacy
- Malware domains

More lists are available for you to select if you wish:

- Fanboy’s Enhanced Tracking List
- Dan Pollock’s hosts file
- hpHosts’s Ad and tracking servers
- MVPS HOSTS
- Spam404
- And many others

Of course, the more filters enabled, the higher the memory footprint. Yet, even after adding Fanboy's two extra lists, hpHosts’s Ad and tracking servers, uBlock still has a lower memory footprint than other very popular blockers out there.

Also, be aware that selecting some of these extra lists may lead to higher likelihood of web site breakage -- especially those lists which are normally used as hosts file.

***

Without the preset lists of filters, this extension is nothing. So if ever you really do want to contribute something, think about the people working hard to maintain the filter lists you are using, which were made available to use by all for free.

***

Free.
Open source with public license (GPLv3)
For users by users.

Contributors @ Github: https://github.com/gorhill/uBlock/graphs/contributors
Contributors @ Crowdin: https://crowdin.net/project/ublock

***

It's quite an early version, keep this in mind when you review.

Project change log:
https://github.com/gorhill/uBlock/releases


onDOMChanged: function(addedNodes, removedNodes) {

if ( this.selectors.size === 0 ) { return; }


/**
    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
*/

#results {
    font-family: mono;
    font-size: 90%;
    white-space: pre;
    }


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

    uBlock Origin - a browser extension to block requests.
    Copyright (C) 2015-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';

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

if ( typeof vAPI === 'object' && vAPI.domFilterer ) {
    vAPI.domFilterer.toggle(true);
}

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

    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;