aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlessio Vanni <vannilla@firemail.cc>2019-02-19 21:06:09 +0100
committerAlessio Vanni <vannilla@firemail.cc>2019-02-19 21:06:09 +0100
commitfe2f8acc8210c2ddead4621797b47106a9b38f5b (patch)
tree5fb103d45d7e4345f56fc068ce8173b82fa7051f
downloadematrix-fe2f8acc8210c2ddead4621797b47106a9b38f5b.tar.lz
ematrix-fe2f8acc8210c2ddead4621797b47106a9b38f5b.tar.xz
ematrix-fe2f8acc8210c2ddead4621797b47106a9b38f5b.zip
Fork uMatrix
Pretty much just changing the name and the copyright.
-rw-r--r--LICENSE.txt675
-rw-r--r--about.html58
-rw-r--r--asset-viewer.html21
-rw-r--r--assets/assets.json85
-rw-r--r--background.html34
-rw-r--r--bootstrap.js246
-rw-r--r--chrome.manifest54
-rw-r--r--cloud-ui.html21
-rw-r--r--css/cloud-ui.css104
-rw-r--r--css/common.css118
-rw-r--r--css/dashboard-common.css83
-rw-r--r--css/dashboard.css67
-rw-r--r--css/fonts/Roboto_Condensed/LICENSE.txt202
-rw-r--r--css/fonts/Roboto_Condensed/RobotoCondensed-Bold.ttfbin0 -> 141796 bytes
-rw-r--r--css/fonts/Roboto_Condensed/RobotoCondensed-Light.ttfbin0 -> 141384 bytes
-rw-r--r--css/fonts/Roboto_Condensed/RobotoCondensed-Regular.ttfbin0 -> 140396 bytes
-rwxr-xr-xcss/fonts/fontawesome-webfont.ttfbin0 -> 112160 bytes
-rw-r--r--css/hosts-files.css142
-rw-r--r--css/legacy-toolbar-button.css46
-rw-r--r--css/logger-ui.css233
-rw-r--r--css/popup.css691
-rw-r--r--css/raw-settings.css18
-rw-r--r--css/user-rules.css162
-rw-r--r--dashboard.html32
-rw-r--r--frameModule.js357
-rw-r--r--frameScript.js73
-rw-r--r--hosts-files.html58
-rw-r--r--icon.pngbin0 -> 8953 bytes
-rw-r--r--img/browsericons/icon19-0.pngbin0 -> 817 bytes
-rw-r--r--img/browsericons/icon19-1.pngbin0 -> 830 bytes
-rw-r--r--img/browsericons/icon19-10.pngbin0 -> 840 bytes
-rw-r--r--img/browsericons/icon19-11.pngbin0 -> 814 bytes
-rw-r--r--img/browsericons/icon19-12.pngbin0 -> 829 bytes
-rw-r--r--img/browsericons/icon19-13.pngbin0 -> 825 bytes
-rw-r--r--img/browsericons/icon19-14.pngbin0 -> 808 bytes
-rw-r--r--img/browsericons/icon19-15.pngbin0 -> 822 bytes
-rw-r--r--img/browsericons/icon19-16.pngbin0 -> 808 bytes
-rw-r--r--img/browsericons/icon19-17.pngbin0 -> 792 bytes
-rw-r--r--img/browsericons/icon19-18.pngbin0 -> 761 bytes
-rw-r--r--img/browsericons/icon19-19.pngbin0 -> 750 bytes
-rw-r--r--img/browsericons/icon19-2.pngbin0 -> 836 bytes
-rw-r--r--img/browsericons/icon19-3.pngbin0 -> 836 bytes
-rw-r--r--img/browsericons/icon19-4.pngbin0 -> 827 bytes
-rw-r--r--img/browsericons/icon19-5.pngbin0 -> 837 bytes
-rw-r--r--img/browsericons/icon19-6.pngbin0 -> 833 bytes
-rw-r--r--img/browsericons/icon19-7.pngbin0 -> 823 bytes
-rw-r--r--img/browsericons/icon19-8.pngbin0 -> 818 bytes
-rw-r--r--img/browsericons/icon19-9.pngbin0 -> 830 bytes
-rw-r--r--img/browsericons/icon19-off.pngbin0 -> 777 bytes
-rw-r--r--img/browsericons/icon38-off.pngbin0 -> 2255 bytes
-rw-r--r--img/cloud.pngbin0 -> 5017 bytes
-rw-r--r--img/icon_16.pngbin0 -> 656 bytes
-rw-r--r--img/icon_64.pngbin0 -> 5483 bytes
-rw-r--r--img/matrix-group-hide.pngbin0 -> 253 bytes
-rw-r--r--img/matrix-group-hline.pngbin0 -> 178 bytes
-rw-r--r--img/matrix-group-show.pngbin0 -> 257 bytes
-rw-r--r--img/permanent-black-small-cb.pngbin0 -> 159 bytes
-rw-r--r--img/permanent-black-small.pngbin0 -> 243 bytes
-rw-r--r--img/permanent-white-small-cb.pngbin0 -> 151 bytes
-rw-r--r--img/permanent-white-small.pngbin0 -> 246 bytes
-rw-r--r--install.rdf452
-rw-r--r--js/about.js146
-rw-r--r--js/asset-viewer.js46
-rw-r--r--js/assets.js911
-rw-r--r--js/background.js246
-rw-r--r--js/browsercache.js65
-rw-r--r--js/cloud-ui.js214
-rw-r--r--js/contentscript-start.js97
-rw-r--r--js/contentscript.js541
-rw-r--r--js/cookies.js552
-rw-r--r--js/dashboard-common.js41
-rw-r--r--js/dashboard.js56
-rw-r--r--js/hosts-files.js391
-rw-r--r--js/httpsb.js212
-rw-r--r--js/i18n.js209
-rw-r--r--js/liquid-dict.js203
-rw-r--r--js/logger-ui.js908
-rw-r--r--js/logger.js93
-rw-r--r--js/main-blocked.js176
-rw-r--r--js/matrix.js873
-rw-r--r--js/messaging.js963
-rw-r--r--js/pagestats.js274
-rw-r--r--js/polyfill.js96
-rw-r--r--js/popup.js1538
-rw-r--r--js/profiler.js63
-rw-r--r--js/raw-settings.js116
-rw-r--r--js/settings.js195
-rw-r--r--js/start.js108
-rw-r--r--js/storage.js615
-rw-r--r--js/tab.js710
-rw-r--r--js/traffic.js444
-rw-r--r--js/udom.js729
-rw-r--r--js/uritools.js537
-rw-r--r--js/user-rules.js341
-rw-r--r--js/usersettings.js59
-rw-r--r--js/utils.js106
-rw-r--r--js/vapi-background.js3466
-rw-r--r--js/vapi-client.js226
-rw-r--r--js/vapi-common.js192
-rw-r--r--js/vapi-popup.js23
-rw-r--r--js/xal.js72
-rw-r--r--lib/publicsuffixlist.js369
-rw-r--r--lib/punycode.js530
-rw-r--r--lib/yamd5.js402
-rw-r--r--locale/am/messages.properties179
-rw-r--r--locale/ar/messages.properties179
-rw-r--r--locale/bg/messages.properties179
-rw-r--r--locale/bn/messages.properties179
-rw-r--r--locale/ca/messages.properties179
-rw-r--r--locale/cs/messages.properties179
-rw-r--r--locale/da/messages.properties179
-rw-r--r--locale/de/messages.properties179
-rw-r--r--locale/el/messages.properties179
-rw-r--r--locale/en/messages.properties179
-rw-r--r--locale/eo/messages.properties179
-rw-r--r--locale/es/messages.properties179
-rw-r--r--locale/et/messages.properties179
-rw-r--r--locale/fa/messages.properties179
-rw-r--r--locale/fi/messages.properties179
-rw-r--r--locale/fil/messages.properties179
-rw-r--r--locale/fr/messages.properties179
-rw-r--r--locale/gu/messages.properties179
-rw-r--r--locale/he/messages.properties179
-rw-r--r--locale/hi/messages.properties179
-rw-r--r--locale/hr/messages.properties179
-rw-r--r--locale/hu/messages.properties179
-rw-r--r--locale/id/messages.properties179
-rw-r--r--locale/it/messages.properties179
-rw-r--r--locale/ja/messages.properties179
-rw-r--r--locale/kn/messages.properties179
-rw-r--r--locale/ko/messages.properties179
-rw-r--r--locale/lt/messages.properties179
-rw-r--r--locale/lv/messages.properties179
-rw-r--r--locale/ml/messages.properties179
-rw-r--r--locale/mr/messages.properties179
-rw-r--r--locale/ms/messages.properties179
-rw-r--r--locale/nb/messages.properties179
-rw-r--r--locale/nl/messages.properties179
-rw-r--r--locale/pl/messages.properties179
-rw-r--r--locale/pt-BR/messages.properties179
-rw-r--r--locale/pt-PT/messages.properties179
-rw-r--r--locale/ro/messages.properties179
-rw-r--r--locale/ru/messages.properties179
-rw-r--r--locale/sk/messages.properties179
-rw-r--r--locale/sl/messages.properties179
-rw-r--r--locale/sr/messages.properties179
-rw-r--r--locale/sv/messages.properties179
-rw-r--r--locale/sw/messages.properties179
-rw-r--r--locale/ta/messages.properties179
-rw-r--r--locale/te/messages.properties179
-rw-r--r--locale/th/messages.properties179
-rw-r--r--locale/tr/messages.properties179
-rw-r--r--locale/uk/messages.properties179
-rw-r--r--locale/vi/messages.properties179
-rw-r--r--locale/zh-CN/messages.properties179
-rw-r--r--locale/zh-TW/messages.properties179
-rw-r--r--logger-ui.html55
-rw-r--r--main-blocked.html138
-rw-r--r--options.xul9
-rw-r--r--popup.html115
-rw-r--r--raw-settings.html27
-rw-r--r--settings.html118
-rw-r--r--user-rules.html52
163 files changed, 32008 insertions, 0 deletions
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..6b156fe
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,675 @@
+GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {one line to give the program's name and a brief idea of what it does.}
+ Copyright (C) {year} {name of author}
+
+ 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/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ {project} Copyright (C) {year} {fullname}
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/about.html b/about.html
new file mode 100644
index 0000000..1c9c5c5
--- /dev/null
+++ b/about.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>uMatrix — About</title>
+<link rel="stylesheet" type="text/css" href="css/common.css">
+<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
+<style>
+ul {
+ list-style-type: none;
+}
+</style>
+</head>
+
+<body>
+
+<h2>uMatrix <span id="aboutVersion"></span></h2>
+<ul>
+ <li><span id="aboutStorageUsed"></span><br>
+ <li>&nbsp;
+ <li><span data-i18n="aboutChangelog"></span><br>
+ <li><a href="https://github.com/gorhill/uMatrix/wiki"><span data-i18n="aboutDoc"></span></a><br>
+ <li><span data-i18n="aboutPermissions"></span><br>
+ <li><a href="https://github.com/gorhill/uMatrix" data-i18n="aboutCode"></a><br>
+ <li><a href="https://github.com/gorhill/uMatrix/issues" data-i18n="aboutIssues"></a><br>
+ <li><span data-i18n="aboutContributors"></span>
+ <ul>
+ <li><span data-i18n="aboutCodeContributors"></span> <a href="https://github.com/gorhill/uMatrix/graphs/contributors">uMatrix, <a href="https://github.com/gorhill/httpswitchboard/graphs/contributors">HTTP Switchboard</a>
+ <li><span data-i18n="aboutIssueContributors"></span> <a href="https://github.com/gorhill/uMatrix/issues?q=is%3Aissue">uMatrix</a>, <a href="https://github.com/gorhill/httpswitchboard/issues?q=is%3Aissue">HTTP Switchboard</a>
+ <li><span data-i18n="aboutTranslationContributors"></span> <a href="https://github.com/gorhill/uMatrix/wiki/Translation-work-contributors">Crowdin</a>
+ </ul>
+</ul>
+
+<h2 data-i18n="aboutUserDataHeader"></h2>
+<div>
+ <p><button type="button" id="backupUserDataButton" data-i18n="aboutBackupButton"></button>
+ <button type="button" id="restoreUserDataButton" data-i18n="aboutRestoreButton"></button>
+ <input id="restoreFilePicker" type="file" accept="text/plain" style="display:none;">
+ <p style="margin-left: 2em;" data-i18n="aboutOr">
+ <p><button type="button" id="resetUserDataButton" data-i18n="aboutResetButton"></button>
+</div>
+
+<span style="display: none;" data-i18n="aboutStorageUsed"></span>
+<span style="display: none;" data-i18n="aboutBackupFilename"></span>
+<span style="display: none;" data-i18n="aboutRestoreConfirm"></span>
+<span style="display: none;" data-i18n="aboutRestoreError"></span>
+<span style="display: none;" data-i18n="aboutResetConfirm"></span>
+
+
+<script src="js/vapi-common.js"></script>
+<script src="js/vapi-client.js"></script>
+<script src="js/udom.js"></script>
+<script src="js/i18n.js"></script>
+<script src="js/dashboard-common.js"></script>
+<script src="js/about.js"></script>
+
+</body>
+</html>
diff --git a/asset-viewer.html b/asset-viewer.html
new file mode 100644
index 0000000..314e6c6
--- /dev/null
+++ b/asset-viewer.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title>uMatrix — Asset viewer</title>
+<style>
+#content {
+ font: 12px monospace;
+ white-space: pre;
+ }
+</style>
+</head>
+<body>
+<div id="content"></div>
+<script src="js/vapi-common.js"></script>
+<script src="js/vapi-client.js"></script>
+<script src="js/udom.js"></script>
+<script src="js/asset-viewer.js"></script>
+</body>
+</html>
diff --git a/assets/assets.json b/assets/assets.json
new file mode 100644
index 0000000..5b8c33a
--- /dev/null
+++ b/assets/assets.json
@@ -0,0 +1,85 @@
+{
+ "assets.json": {
+ "content": "internal",
+ "updateAfter": 13,
+ "contentURL": [
+ "https://raw.githubusercontent.com/gorhill/uMatrix/master/assets/assets.json",
+ "assets/assets.json"
+ ]
+ },
+ "public_suffix_list.dat": {
+ "content": "internal",
+ "updateAfter": 19,
+ "contentURL": [
+ "https://publicsuffix.org/list/public_suffix_list.dat",
+ "assets/thirdparties/publicsuffix.org/list/public_suffix_list.dat",
+ "assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat"
+ ]
+ },
+ "malware-0": {
+ "content": "filters",
+ "group": "malware",
+ "title": "Malware Domain List",
+ "contentURL": [
+ "https://www.malwaredomainlist.com/hostslist/hosts.txt",
+ "assets/thirdparties/www.malwaredomainlist.com/hostslist/hosts.txt"
+ ]
+ },
+ "malware-1": {
+ "content": "filters",
+ "group": "malware",
+ "title": "Malware domains",
+ "contentURL": [
+ "https://mirror.cedia.org.ec/malwaredomains/justdomains",
+ "https://mirror1.malwaredomains.com/files/justdomains",
+ "assets/thirdparties/mirror1.malwaredomains.com/files/justdomains",
+ "assets/thirdparties/mirror1.malwaredomains.com/files/justdomains.txt"
+ ],
+ "supportURL": "http://www.malwaredomains.com/"
+ },
+ "dpollock-0": {
+ "content": "filters",
+ "group": "multipurpose",
+ "updateAfter": 11,
+ "title": "Dan Pollock’s hosts file",
+ "contentURL": [
+ "http://someonewhocares.org/hosts/hosts",
+ "assets/thirdparties/someonewhocares.org/hosts/hosts.txt"
+ ],
+ "supportURL": "http://someonewhocares.org/hosts/"
+ },
+ "hphosts": {
+ "content": "filters",
+ "group": "multipurpose",
+ "updateAfter": 11,
+ "title": "hpHosts’ Ad and tracking servers",
+ "contentURL": [
+ "https://hosts-file.net/.%5Cad_servers.txt",
+ "assets/thirdparties/hosts-file.net/ad_servers.txt"
+ ],
+ "supportURL": "https://hosts-file.net/"
+ },
+ "mvps-0": {
+ "content": "filters",
+ "group": "multipurpose",
+ "updateAfter": 11,
+ "title": "MVPS HOSTS",
+ "contentURL": [
+ "http://winhelp2002.mvps.org/hosts.txt",
+ "assets/thirdparties/winhelp2002.mvps.org/hosts.txt"
+ ],
+ "supportURL": "http://winhelp2002.mvps.org/"
+ },
+ "plowe-0": {
+ "content": "filters",
+ "group": "multipurpose",
+ "updateAfter": 13,
+ "title": "Peter Lowe’s Ad and tracking server list",
+ "contentURL": [
+ "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext",
+ "assets/thirdparties/pgl.yoyo.org/as/serverlist",
+ "assets/thirdparties/pgl.yoyo.org/as/serverlist.txt"
+ ],
+ "supportURL": "https://pgl.yoyo.org/adservers/"
+ }
+}
diff --git a/background.html b/background.html
new file mode 100644
index 0000000..a6e8a1e
--- /dev/null
+++ b/background.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>uMatrix</title>
+</head>
+<body>
+<script src="js/polyfill.js"></script>
+<script src="lib/punycode.js"></script>
+<script src="lib/publicsuffixlist.js"></script>
+<script src="js/vapi-common.js"></script>
+<script src="js/vapi-background.js"></script>
+<script src="js/vapi-cachestorage.js"></script><!-- Optional -->
+<script src="js/background.js"></script>
+<script src="js/xal.js"></script>
+<script src="js/usersettings.js"></script>
+<script src="js/liquid-dict.js"></script>
+<script src="js/matrix.js"></script>
+<script src="js/utils.js"></script>
+<script src="js/assets.js"></script>
+<script src="js/httpsb.js"></script>
+<script src="js/uritools.js"></script>
+<script src="js/cookies.js"></script>
+<script src="js/logger.js"></script>
+<script src="js/messaging.js"></script>
+<script src="js/profiler.js"></script>
+<script src="js/storage.js"></script>
+<script src="js/pagestats.js"></script>
+<script src="js/tab.js"></script>
+<script src="js/traffic.js"></script>
+<script src="js/browsercache.js"></script>
+<script src="js/start.js"></script>
+</body>
+</html>
diff --git a/bootstrap.js b/bootstrap.js
new file mode 100644
index 0000000..513f3aa
--- /dev/null
+++ b/bootstrap.js
@@ -0,0 +1,246 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global ADDON_UNINSTALL, APP_SHUTDOWN */
+/* exported startup, shutdown, install, uninstall */
+
+'use strict';
+
+/******************************************************************************/
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+// Accessing the context of the background page:
+// var win = Services.appShell.hiddenDOMWindow.document.querySelector('iframe[src*=umatrix]').contentWindow;
+
+let windowlessBrowser = null;
+let windowlessBrowserPL = null;
+let bgProcess = null;
+let version;
+const hostName = 'umatrix';
+const restartListener = {
+ get messageManager() {
+ return Cc['@mozilla.org/parentprocessmessagemanager;1']
+ .getService(Ci.nsIMessageListenerManager);
+ },
+
+ receiveMessage: function() {
+ shutdown();
+ startup();
+ }
+};
+
+/******************************************************************************/
+
+// https://github.com/gorhill/uBlock/issues/2493
+// Fix by https://github.com/gijsk
+// imported from https://github.com/gorhill/uBlock/pull/2497
+
+function startup(data/*, reason*/) {
+ if ( data !== undefined ) {
+ version = data.version;
+ }
+
+ // Already started?
+ if ( bgProcess !== null ) {
+ return;
+ }
+
+ waitForHiddenWindow();
+}
+
+function createBgProcess(parentDocument) {
+ bgProcess = parentDocument.documentElement.appendChild(
+ parentDocument.createElementNS('http://www.w3.org/1999/xhtml', 'iframe')
+ );
+ bgProcess.setAttribute(
+ 'src',
+ 'chrome://' + hostName + '/content/background.html#' + version
+ );
+
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#addMessageListener%28%29
+ // "If the same listener registers twice for the same message, the
+ // "second registration is ignored."
+ restartListener.messageManager.addMessageListener(
+ hostName + '-restart',
+ restartListener
+ );
+}
+
+function getWindowlessBrowserFrame(appShell) {
+ windowlessBrowser = appShell.createWindowlessBrowser(true);
+ windowlessBrowser.QueryInterface(Ci.nsIInterfaceRequestor);
+ let webProgress = windowlessBrowser.getInterface(Ci.nsIWebProgress);
+ let XPCOMUtils = Cu.import('resource://gre/modules/XPCOMUtils.jsm', null).XPCOMUtils;
+ windowlessBrowserPL = {
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsIWebProgressListener,
+ Ci.nsIWebProgressListener2,
+ Ci.nsISupportsWeakReference
+ ]),
+ onStateChange: function(wbp, request, stateFlags, status) {
+ if ( !request ) { return; }
+ if ( stateFlags & Ci.nsIWebProgressListener.STATE_STOP ) {
+ webProgress.removeProgressListener(windowlessBrowserPL);
+ windowlessBrowserPL = null;
+ createBgProcess(windowlessBrowser.document);
+ }
+ }
+ };
+ webProgress.addProgressListener(windowlessBrowserPL, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
+ windowlessBrowser.document.location = "data:application/vnd.mozilla.xul+xml;charset=utf-8,<window%20id='" + hostName + "-win'/>";
+}
+
+function waitForHiddenWindow() {
+ let appShell = Cc['@mozilla.org/appshell/appShellService;1']
+ .getService(Ci.nsIAppShellService);
+
+ let isReady = function() {
+ var hiddenDoc;
+
+ try {
+ hiddenDoc = appShell.hiddenDOMWindow &&
+ appShell.hiddenDOMWindow.document;
+ } catch (ex) {
+ }
+
+ // Do not test against `loading`: it does appear `readyState` could be
+ // undefined if looked up too early.
+ if ( !hiddenDoc || hiddenDoc.readyState !== 'complete' ) {
+ return false;
+ }
+
+ // In theory, it should be possible to create a windowless browser
+ // immediately, without waiting for the hidden window to have loaded
+ // completely. However, in practice, on Windows this seems to lead
+ // to a broken Firefox appearance. To avoid this, we only create the
+ // windowless browser here. We'll use that rather than the hidden
+ // window for the actual background page (windowless browsers are
+ // also what the webextension implementation in Firefox uses for
+ // background pages).
+ let { Services } = Cu.import('resource://gre/modules/Services.jsm', null);
+ if ( Services.vc.compare(Services.appinfo.platformVersion, '27') >= 0 ) {
+ getWindowlessBrowserFrame(appShell);
+ } else {
+ createBgProcess(hiddenDoc);
+ }
+ return true;
+ };
+
+ if ( isReady() ) {
+ return;
+ }
+
+ // https://github.com/gorhill/uBlock/issues/749
+ // Poll until the proper environment is set up -- or give up eventually.
+ // We poll frequently early on but relax poll delay as time pass.
+
+ let tryDelay = 5;
+ let trySum = 0;
+ // https://trac.torproject.org/projects/tor/ticket/19438
+ // Try for a longer period.
+ let tryMax = 600011;
+ let timer = Cc['@mozilla.org/timer;1']
+ .createInstance(Ci.nsITimer);
+
+ let checkLater = function() {
+ trySum += tryDelay;
+ if ( trySum >= tryMax ) {
+ timer = null;
+ return;
+ }
+ timer.init(timerObserver, tryDelay, timer.TYPE_ONE_SHOT);
+ tryDelay *= 2;
+ if ( tryDelay > 503 ) {
+ tryDelay = 503;
+ }
+ };
+
+ var timerObserver = {
+ observe: function() {
+ timer.cancel();
+ if ( isReady() ) {
+ timer = null;
+ } else {
+ checkLater();
+ }
+ }
+ };
+
+ checkLater();
+}
+
+/******************************************************************************/
+
+function shutdown(data, reason) {
+ if ( reason === APP_SHUTDOWN ) {
+ return;
+ }
+
+ if ( bgProcess !== null ) {
+ bgProcess.parentNode.removeChild(bgProcess);
+ bgProcess = null;
+ }
+
+ if ( windowlessBrowser !== null ) {
+ // close() does not exist for older versions of Firefox.
+ if ( typeof windowlessBrowser.close === 'function' ) {
+ windowlessBrowser.close();
+ }
+ windowlessBrowser = null;
+ windowlessBrowserPL = null;
+ }
+
+ if ( data === undefined ) {
+ return;
+ }
+
+ // Remove the restartObserver only when the extension is being disabled
+ restartListener.messageManager.removeMessageListener(
+ hostName + '-restart',
+ restartListener
+ );
+}
+
+/******************************************************************************/
+
+function install() {
+ // https://bugzil.la/719376
+ Cc['@mozilla.org/intl/stringbundle;1']
+ .getService(Ci.nsIStringBundleService)
+ .flushBundles();
+}
+
+/******************************************************************************/
+
+function uninstall(data, aReason) {
+ if ( aReason !== ADDON_UNINSTALL ) {
+ return;
+ }
+ // To cleanup vAPI.localStorage in vapi-common.js, aka
+ // "extensions.umatrix.*" in `about:config`.
+ Cu.import('resource://gre/modules/Services.jsm', null)
+ .Services.prefs.getBranch('extensions.' + hostName + '.')
+ .deleteBranch('');
+}
+
+/******************************************************************************/
diff --git a/chrome.manifest b/chrome.manifest
new file mode 100644
index 0000000..fc28b4b
--- /dev/null
+++ b/chrome.manifest
@@ -0,0 +1,54 @@
+content umatrix ./
+
+locale umatrix en ./locale/en/
+locale umatrix am ./locale/am/
+locale umatrix ar ./locale/ar/
+locale umatrix bg ./locale/bg/
+locale umatrix bn ./locale/bn/
+locale umatrix ca ./locale/ca/
+locale umatrix cs ./locale/cs/
+locale umatrix da ./locale/da/
+locale umatrix de ./locale/de/
+locale umatrix el ./locale/el/
+locale umatrix eo ./locale/eo/
+locale umatrix es ./locale/es/
+locale umatrix et ./locale/et/
+locale umatrix fa ./locale/fa/
+locale umatrix fi ./locale/fi/
+locale umatrix fil ./locale/fil/
+locale umatrix fr ./locale/fr/
+locale umatrix gu ./locale/gu/
+locale umatrix he ./locale/he/
+locale umatrix hi ./locale/hi/
+locale umatrix hr ./locale/hr/
+locale umatrix hu ./locale/hu/
+locale umatrix id ./locale/id/
+locale umatrix it ./locale/it/
+locale umatrix ja ./locale/ja/
+locale umatrix kn ./locale/kn/
+locale umatrix ko ./locale/ko/
+locale umatrix lt ./locale/lt/
+locale umatrix lv ./locale/lv/
+locale umatrix ml ./locale/ml/
+locale umatrix mr ./locale/mr/
+locale umatrix ms ./locale/ms/
+locale umatrix nb ./locale/nb/
+locale umatrix nl ./locale/nl/
+locale umatrix pl ./locale/pl/
+locale umatrix pt-BR ./locale/pt-BR/
+locale umatrix pt-PT ./locale/pt-PT/
+locale umatrix ro ./locale/ro/
+locale umatrix ru ./locale/ru/
+locale umatrix sk ./locale/sk/
+locale umatrix sl ./locale/sl/
+locale umatrix sr ./locale/sr/
+locale umatrix sv ./locale/sv/
+locale umatrix sw ./locale/sw/
+locale umatrix ta ./locale/ta/
+locale umatrix te ./locale/te/
+locale umatrix th ./locale/th/
+locale umatrix tr ./locale/tr/
+locale umatrix uk ./locale/uk/
+locale umatrix vi ./locale/vi/
+locale umatrix zh-CN ./locale/zh-CN/
+locale umatrix zh-TW ./locale/zh-TW/
diff --git a/cloud-ui.html b/cloud-ui.html
new file mode 100644
index 0000000..3053d7a
--- /dev/null
+++ b/cloud-ui.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title></title>
+</head>
+<body>
+<button id="cloudPush" type="button" title="cloudPush"></button>
+<span data-i18n="cloudNoData"></span>
+<button id="cloudPull" type="button" title="cloudPull" disabled></button>
+<button id="cloudPullAndMerge" type="button" title="cloudPullAndMerge" disabled></button>
+<p id="cloudError"><span></span></p>
+<span id="cloudCog" class="fa">&#xf013;</span>
+<div id="cloudOptions">
+ <div>
+ <p><label data-i18n="cloudDeviceNamePrompt"></label> <input id="cloudDeviceName" type="text" value="">
+ <p><button id="cloudOptionsSubmit" type="button" data-i18n="genericSubmit"></button>
+ </div>
+</div>
+</body>
+</html>
diff --git a/css/cloud-ui.css b/css/cloud-ui.css
new file mode 100644
index 0000000..609b3cd
--- /dev/null
+++ b/css/cloud-ui.css
@@ -0,0 +1,104 @@
+#cloudWidget {
+ background: url("../img/cloud.png") hsl(216, 100%, 93%);
+ border-radius: 3px;
+ margin: 0.5em 0;
+ padding: 1em 1em 0 1em;
+ position: relative;
+ }
+#cloudWidget.hide {
+ display: none;
+ }
+#cloudWidget > button {
+ display: inline-block;
+ font-family: FontAwesome;
+ font-size: 160%;
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ padding: 0.2em 0.25em 0.1em 0.25em;
+ position: relative;
+ vertical-align: baseline;
+ }
+#cloudWidget > button[disabled] {
+ visibility: hidden;
+ }
+#cloudWidget > button.error {
+ color: red;
+ }
+#cloudPush:after {
+ content: '\f0ee';
+ }
+#cloudPull:before,
+#cloudPullAndMerge:before {
+ content: '\f0ed';
+}
+#cloudPullAndMerge {
+ margin: 0 0.25em;
+ }
+#cloudPullAndMerge:after {
+ content: '\f067';
+ font-size: 50%;
+ position: absolute;
+ right: 0;
+ top: 10%;
+ }
+#cloudWidget > span {
+ color: gray;
+ display: inline-block;
+ font-size: 90%;
+ margin: 0 1em;
+ padding: 0;
+ vertical-align: bottom;
+ white-space: pre;
+ }
+#cloudError {
+ color: red;
+ margin: 0;
+ padding: 0.5em 0;
+ }
+#cloudError > span {
+ font-size: x-small;
+ }
+#cloudWidget > #cloudCog {
+ cursor: pointer;
+ display: inline-block;
+ font-size: 110%;
+ margin: 0;
+ opacity: 0.5;
+ padding: 4px;
+ position: absolute;
+ top: 0;
+ }
+body[dir="ltr"] #cloudWidget > #cloudCog {
+ right: 0;
+ }
+body[dir="rtl"] #cloudWidget > #cloudCog {
+ left: 0;
+ }
+#cloudWidget > #cloudCog:hover {
+ opacity: 1;
+ }
+#cloudWidget > #cloudOptions {
+ align-items: center;
+ -webkit-align-items: center;
+ background-color: rgba(0, 0, 0, 0.75);
+ bottom: 0;
+ display: none;
+ justify-content: center;
+ -webkit-justify-content: center;
+ left: 0;
+ position: fixed;
+ right: 0;
+ top: 0;
+ z-index: 2000;
+ }
+#cloudWidget > #cloudOptions.show {
+ display: flex;
+ display: -webkit-flex;
+ }
+#cloudWidget > #cloudOptions > div {
+ background-color: white;
+ border-radius: 3px;
+ padding: 1em;
+ text-align: center;
+ }
diff --git a/css/common.css b/css/common.css
new file mode 100644
index 0000000..225aa30
--- /dev/null
+++ b/css/common.css
@@ -0,0 +1,118 @@
+@font-face {
+ font-family: 'httpsb';
+ font-style: normal;
+ font-weight: normal;
+ src: local('httpsb'), url(fonts/Roboto_Condensed/RobotoCondensed-Regular.ttf) format('truetype');
+}
+@font-face {
+ font-family: 'httpsb';
+ font-style: normal;
+ font-weight: bold;
+ src: local('httpsb'), url(fonts/Roboto_Condensed/RobotoCondensed-Bold.ttf) format('truetype');
+}
+@font-face {
+ font-family: 'httpsb';
+ font-style: normal;
+ font-weight: 100;
+ src: local('httpsb'), url(fonts/Roboto_Condensed/RobotoCondensed-Light.ttf) format('truetype');
+}
+@font-face {
+ font-family: 'FontAwesome';
+ src: url('fonts/fontawesome-webfont.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+.fa {
+ font-family: FontAwesome;
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ vertical-align: baseline;
+ display: inline-block;
+ }
+
+body[dir="ltr"] {
+ direction: ltr;
+ }
+body[dir="rtl"] {
+ direction: rtl;
+ }
+
+/* http://stackoverflow.com/questions/2011142/how-to-change-the-style-of-title-attribute-inside-the-anchor-tag?answertab=votes */
+*[data-i18n-tip] {
+ position: relative;
+ cursor: pointer;
+ }
+*[data-i18n-tip]:after {
+ content: "";
+ opacity: 0;
+ }
+*[data-i18n-tip]:hover:after {
+ background-color: #fffffa;
+ border: 1px solid gray;
+ border-radius: 3px;
+ box-shadow: 1px 1px 3px gray;
+ color: black;
+ content: attr(data-tip);
+ font: 12px sans-serif;
+ line-height: 140%;
+ min-width: 25vw;
+ opacity: 1;
+ padding: 4px 6px;
+ pointer-events: none;
+ position: absolute;
+ text-align: start;
+ top: 110%;
+ -webkit-transition: opacity 0.15s 0.5s;
+ transition: opacity 0.15s 0.5s;
+ white-space: pre-line;
+ z-index: 20;
+ }
+body[dir="ltr"] .tip-anchor-left[data-i18n-tip]:hover:after,
+body[dir="rtl"] .tip-anchor-right[data-i18n-tip]:hover:after {
+ left: -3vw;
+ }
+body[dir="ltr"] .tip-anchor-right[data-i18n-tip]:hover:after,
+body[dir="rtl"] .tip-anchor-left[data-i18n-tip]:hover:after {
+ right: -3vw;
+ }
+button.custom {
+ padding: 0.6em 1em;
+ border: 1px solid transparent;
+ border-color: #ccc #ccc #bbb #bbb;
+ border-radius: 3px;
+ background-color: hsl(216, 0%, 75%);
+ background-image: linear-gradient(#f2f2f2, #dddddd);
+ background-repeat: repeat-x;
+ color: #000;
+ opacity: 0.8;
+ }
+button.custom:hover {
+ opacity: 1.0;
+ }
+button.custom.important {
+ padding: 0.6em 1em;
+ border: 1px solid transparent;
+ border-color: #ffcc7f #ffcc7f hsl(36, 100%, 73%);
+ border-radius: 3px;
+ background-color: hsl(36, 100%, 75%);
+ background-image: linear-gradient(#ffdca8, #ffcc7f);
+ background-repeat: repeat-x;
+ color: #222;
+ opacity: 0.8;
+ }
+button.custom.important:hover {
+ opacity: 1.0;
+ }
+button.custom.disabled,
+button.custom[disabled] {
+ border-color: #ddd #ddd hsl(36, 0%, 85%);
+ background-color: hsl(36, 0%, 72%);
+ background-image: linear-gradient(#f2f2f2, #dddddd);
+ color: #666;
+ opacity: 0.6;
+ pointer-events: none;
+ }
+code {
+ font-size: 90%;
+ }
diff --git a/css/dashboard-common.css b/css/dashboard-common.css
new file mode 100644
index 0000000..59e24e4
--- /dev/null
+++ b/css/dashboard-common.css
@@ -0,0 +1,83 @@
+body {
+ background-color: #fff;
+ color: #000;
+ margin: 0;
+ padding: 0 0.5em 0 0.5em;
+ font: 15px/1.4 sans-serif;
+ }
+body > *:first-child {
+ margin-top: 0;
+ }
+h2, h3 {
+ margin: 1em 0;
+ font-family: sans-serif;
+ }
+h2 {
+ font-size: 18px;
+ }
+h2:nth-of-type(1) {
+ margin-top: 0;
+ }
+h3 {
+ font-size: 16px;
+ }
+h2 + * {
+ padding: 0;
+ }
+html.ltr h2 + * {
+ margin: 0 0 0 1em;
+ }
+html.rtl h2 + * {
+ margin: 0 1em 0 0;
+ }
+a {
+ text-decoration: none;
+ }
+button {
+ padding: 0.3em 0.5em;
+ }
+input[disabled] + label {
+ color: gray;
+ }
+.para {
+ width: 40em;
+ }
+
+.whatisthis {
+ margin: 0 0 0 8px;
+ border: 0;
+ font-family: FontAwesome;
+ padding: 0 0 4px 0;
+ cursor: pointer;
+ opacity: 0.4;
+ }
+.whatisthis:before {
+ content: '\f059';
+ }
+.whatisthis:hover {
+ opacity: 1.0;
+ }
+.whatisthis-expandable {
+ background-color: #F8F8F8;
+ border: 1px dotted #aaa;
+ display: none;
+ font-size: smaller;
+ margin: 0.5em 0 1em 1.25em;
+ padding: 0.5em;
+ white-space: pre-line;
+ }
+.whatisthis-expandable > p {
+ margin-top: 1em;
+ margin-bottom: 0;
+ }
+.whatisthis-expandable > p:first-child {
+ margin-top: 0;
+ }
+.whatisthis-expandable.whatisthis-expanded {
+ display: block;
+ }
+.warn {
+ margin: 0;
+ padding: 5px;
+ background-color: #FEDAE0;
+ }
diff --git a/css/dashboard.css b/css/dashboard.css
new file mode 100644
index 0000000..0b728ca
--- /dev/null
+++ b/css/dashboard.css
@@ -0,0 +1,67 @@
+body {
+ margin: 0;
+ border: 0;
+ padding: 0;
+ font: 15px sans-serif;
+ position: relative;
+ width: 100vw;
+ height: 100vh;
+ overflow: hidden;
+ }
+#dashboard-nav {
+ margin: 0;
+ border: 0;
+ padding: 0;
+ position: absolute;
+ top: 0;
+ width: 100vw;
+ height: 50px;
+ z-index: 10;
+ }
+#dashboard-nav-widgets {
+ margin: 0;
+ border-bottom: 1px solid #ccc;
+ padding: 4px 0 0 0;
+ white-space: nowrap;
+ background-color: white;
+ }
+#dashboard-nav-widgets span {
+ padding: 0 0.5em;
+ font-size: larger;
+ }
+.tabButton {
+ margin: 0;
+ border: 1px solid #ccc;
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
+ padding: 4px;
+ display: inline-block;
+ position: relative;
+ top: 1px;
+ color: black;
+ background-color: #eee;
+ font: inherit;
+ cursor: pointer;
+ text-decoration: none;
+ }
+.tabButton:focus {
+ outline: 0;
+ }
+.tabButton:active,.tabButton:visited {
+ color: inherited;
+ }
+.tabButton.selected {
+ border-bottom: 1px solid white;
+ background-color: white;
+ }
+iframe {
+ margin: 0;
+ border: 0;
+ padding: 0;
+ background-color: transparent;
+ overflow: auto;
+ position: absolute;
+ top: 50px;
+ width: 100%;
+ height: calc(100% - 50px);
+ }
diff --git a/css/fonts/Roboto_Condensed/LICENSE.txt b/css/fonts/Roboto_Condensed/LICENSE.txt
new file mode 100644
index 0000000..75b5248
--- /dev/null
+++ b/css/fonts/Roboto_Condensed/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/css/fonts/Roboto_Condensed/RobotoCondensed-Bold.ttf b/css/fonts/Roboto_Condensed/RobotoCondensed-Bold.ttf
new file mode 100644
index 0000000..48dd635
--- /dev/null
+++ b/css/fonts/Roboto_Condensed/RobotoCondensed-Bold.ttf
Binary files differ
diff --git a/css/fonts/Roboto_Condensed/RobotoCondensed-Light.ttf b/css/fonts/Roboto_Condensed/RobotoCondensed-Light.ttf
new file mode 100644
index 0000000..a6e368d
--- /dev/null
+++ b/css/fonts/Roboto_Condensed/RobotoCondensed-Light.ttf
Binary files differ
diff --git a/css/fonts/Roboto_Condensed/RobotoCondensed-Regular.ttf b/css/fonts/Roboto_Condensed/RobotoCondensed-Regular.ttf
new file mode 100644
index 0000000..65bf32a
--- /dev/null
+++ b/css/fonts/Roboto_Condensed/RobotoCondensed-Regular.ttf
Binary files differ
diff --git a/css/fonts/fontawesome-webfont.ttf b/css/fonts/fontawesome-webfont.ttf
new file mode 100755
index 0000000..96a3639
--- /dev/null
+++ b/css/fonts/fontawesome-webfont.ttf
Binary files differ
diff --git a/css/hosts-files.css b/css/hosts-files.css
new file mode 100644
index 0000000..656535a
--- /dev/null
+++ b/css/hosts-files.css
@@ -0,0 +1,142 @@
+@keyframes spin {
+ 0% { transform: rotate(0deg); -webkit-transform: rotate(0deg); }
+ 12.5% { transform: rotate(45deg); -webkit-transform: rotate(45deg); }
+ 25% { transform: rotate(90deg); -webkit-transform: rotate(90deg); }
+ 37.5% { transform: rotate(135deg); -webkit-transform: rotate(135deg); }
+ 50% { transform: rotate(180deg); -webkit-transform: rotate(180deg); }
+ 67.5% { transform: rotate(225deg); -webkit-transform: rotate(225deg); }
+ 75% { transform: rotate(270deg); -webkit-transform: rotate(270deg); }
+ 87.5% { transform: rotate(315deg); -webkit-transform: rotate(315deg); }
+ }
+ul {
+ padding: 0;
+ list-style-type: none;
+ }
+ul#options {
+ margin-top: 0;
+ }
+ul#options li {
+ margin-bottom: 0.5em;
+ }
+ul#lists {
+ margin: 0.5em 0 0 0;
+ padding: 0;
+ }
+li.listEntry {
+ margin: 0 auto 0 auto;
+ padding: 0.2em 0;
+ }
+body[dir="ltr"] li.listEntry {
+ margin-left: 1em;
+ margin-right: 0em;
+ }
+body[dir="rtl"] li.listEntry {
+ margin-left: 0em;
+ margin-right: 1em;
+ }
+li.listEntry > * {
+ margin-right: 0.5em;
+ text-indent: 0;
+ unicode-bidi: embed;
+ }
+li.listEntry > a:nth-of-type(2) {
+ font-size: 13px;
+ opacity: 0.5;
+ }
+li.listEntry.toRemove > input[type="checkbox"] {
+ visibility: hidden;
+ }
+li.listEntry.toRemove > a.content {
+ text-decoration: line-through;
+ }
+li.listEntry > .fa {
+ color: inherit;
+ display: none;
+ font-size: 110%;
+ opacity: 0.5;
+ vertical-align: baseline;
+ }
+li.listEntry > a.fa:hover {
+ opacity: 1;
+ }
+li.listEntry.support > a.support {
+ display: inline-block;
+ }
+li.listEntry > a.remove,
+li.listEntry > a.remove:visited {
+ color: darkred;
+ }
+li.listEntry.external > a.remove {
+ display: inline-block;
+ }
+li.listEntry.mustread > a.mustread {
+ display: inline-block;
+ }
+li.listEntry.mustread > a.mustread:hover {
+ color: mediumblue;
+ }
+li.listEntry > .counts {
+ display: none;
+ font-size: smaller;
+}
+li.listEntry > input[type="checkbox"]:checked ~ .counts {
+ display: inline;
+}
+li.listEntry span.status {
+ color: #444;
+ cursor: default;
+ display: none;
+}
+li.listEntry span.status:hover {
+ opacity: 1;
+ }
+li.listEntry span.unsecure {
+ color: darkred;
+ }
+li.listEntry.unsecure > input[type="checkbox"]:checked ~ span.unsecure {
+ display: inline-block;
+ }
+li.listEntry span.failed {
+ color: darkred;
+ }
+li.listEntry.failed span.failed {
+ display: inline-block;
+ }
+li.listEntry span.cache {
+ cursor: pointer;
+ }
+li.listEntry.cached:not(.obsolete) > input[type="checkbox"]:checked ~ span.cache {
+ display: inline-block;
+ }
+li.listEntry span.obsolete {
+ color: hsl(36, 100%, 40%);
+ }
+body:not(.updating) li.listEntry.obsolete > input[type="checkbox"]:checked ~ span.obsolete {
+ display: inline-block;
+ }
+li.listEntry span.updating {
+ transform-origin: 50% 46%;
+ }
+body.updating li.listEntry.obsolete > input[type="checkbox"]:checked ~ span.updating {
+ animation: spin 1s step-start infinite;
+ display: inline-block;
+ }
+.dim {
+ opacity: 0.5;
+ }
+#externalLists {
+ margin: 2em auto 0 auto;
+ }
+body[dir="ltr"] #externalListsDiv {
+ margin-left: 1em;
+ }
+body[dir="rtl"] #externalListsDiv {
+ margin-right: 1em;
+ }
+#externalHostsFiles {
+ box-sizing: border-box;
+ font-size: smaller;
+ width: 100%;
+ height: 12em;
+ white-space: pre;
+ }
diff --git a/css/legacy-toolbar-button.css b/css/legacy-toolbar-button.css
new file mode 100644
index 0000000..a6408ee
--- /dev/null
+++ b/css/legacy-toolbar-button.css
@@ -0,0 +1,46 @@
+#umatrix-legacy-button {
+ list-style-image: url('../img/browsericons/icon19-19.png');
+}
+#umatrix-legacy-button.off {
+ list-style-image: url('../img/browsericons/icon19-off.png');
+}
+
+toolbar[iconsize="small"] #umatrix-legacy-button {
+ list-style-image: url('../img/browsericons/icon19-19.png');
+}
+toolbar[iconsize="small"] #umatrix-legacy-button.off {
+ list-style-image: url('../img/browsericons/icon19-off.png');
+}
+#umatrix-legacy-button[badge]::before {
+ background: #000;
+ color: #fff;
+ content: attr(badge);
+ font: bold 10px sans-serif;
+ margin-top: -2px;
+ padding: 0 2px;
+ position: fixed;
+}
+/* This hack required because if the before content changes it de-pops the
+ popup (without firing any events). So just hide it instead. Note, can't
+ actually *hide* it, or the same thing happens.
+**/
+#umatrix-legacy-button[badge=""]::before {
+ padding: 0;
+}
+
+/* Override off state when in palette */
+toolbarpaletteitem #umatrix-legacy-button.off {
+ list-style-image: url('../img/browsericons/icon19-12.png');
+}
+
+/* Override badge when in palette */
+toolbarpaletteitem #umatrix-legacy-button[badge]::before {
+ content: none;
+}
+
+/* Prevent pale moon from showing the arrow underneath the button */
+/* https://github.com/chrisaljoudi/uBlock/issues/1449#issuecomment-112112761 */
+#umatrix-legacy-button .toolbarbutton-menu-dropmarker {
+ display: none;
+ -moz-box-orient: horizontal;
+}
diff --git a/css/logger-ui.css b/css/logger-ui.css
new file mode 100644
index 0000000..120fc73
--- /dev/null
+++ b/css/logger-ui.css
@@ -0,0 +1,233 @@
+body {
+ background-color: white;
+ border: 0;
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ color: black;
+ margin: 0;
+ overflow-x: hidden;
+ padding: 0;
+ width: 100%;
+ }
+#toolbar {
+ background-color: white;
+ border: 0;
+ border-bottom: 1px solid #ccc;
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ left: 0;
+ margin: 0;
+ padding: 0.5em 1em;
+ position: fixed;
+ top: 0;
+ width: 100%;
+ z-index: 10;
+ }
+#toolbar .button {
+ background-color: white;
+ border: none;
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ cursor: pointer;
+ display: inline-block;
+ font-size: 150%;
+ margin: 0;
+ padding: 8px;
+ }
+#toolbar .button.disabled {
+ opacity: 0.2;
+ pointer-events: none;
+ }
+#toolbar .button:hover {
+ background-color: #eee;
+ }
+#toolbar > div {
+ white-space: nowrap;
+ }
+#toolbar > div:first-of-type {
+ font-size: 120%;
+ }
+#toolbar > div > * {
+ vertical-align: middle;
+ }
+#pageSelector {
+ width: 28em;
+ padding: 0.2em 0;
+ }
+body #compactViewToggler.button:before {
+ content: '\f102';
+ }
+body.compactView #compactViewToggler.button:before {
+ content: '\f103';
+ }
+#filterButton {
+ opacity: 0.25;
+ }
+body.f #filterButton {
+ opacity: 1;
+ }
+#filterInput.bad {
+ background-color: #fee;
+ }
+#maxEntries {
+ margin: 0 2em;
+ }
+input:focus {
+ background-color: #ffe;
+ }
+#content {
+ font-family: "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
+ font-size: 13px;
+ width: 100%;
+ }
+
+#content table {
+ border: 0;
+ border-collapse: collapse;
+ direction: ltr;
+ table-layout: fixed;
+ width: 100%;
+ }
+#content table > colgroup > col:nth-of-type(1) {
+ width: 4.6em;
+ }
+#content table > colgroup > col:nth-of-type(2) {
+ width: 2.2em;
+ }
+#content table > colgroup > col:nth-of-type(3) {
+ width: 2.2em;
+ }
+#content table > colgroup > col:nth-of-type(4) {
+ width: 5.4em;
+ }
+#content table > colgroup > col:nth-of-type(5) {
+ width: calc(100% - 14.4em);
+ }
+#content table tr {
+ background-color: #fafafa;
+ }
+body.f table tr.f {
+ display: none;
+ }
+#content table tr:nth-of-type(2n+1) {
+ background-color: #eee;
+ }
+
+#content table tr.cat_info {
+ color: #00f;
+ }
+#content table tr.blocked {
+ color: #f00;
+ }
+#content table tr.doc {
+ background-color: #666;
+ color: white;
+ text-align: center;
+ }
+
+body #content td {
+ border: 1px solid #ccc;
+ min-width: 0.5em;
+ padding: 3px;
+ vertical-align: top;
+ white-space: normal;
+ word-break: break-all;
+ word-wrap: break-word;
+ }
+#content table tr td:first-of-type {
+ border-left: none;
+ }
+#content table tr td:last-of-type {
+ border-right: none;
+ }
+body.compactView #content tr:not(.vExpanded) td {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+#content table tr td:nth-of-type(1) {
+ cursor: default;
+ text-align: right;
+ white-space: nowrap;
+ }
+#content table tr td:nth-of-type(2) {
+ text-align: center;
+ white-space: nowrap;
+ }
+#content table tr.tab_bts > td:nth-of-type(2):before {
+ content: '\f070';
+ font: 1em FontAwesome;
+ }
+#content table tr.tab:not(.canMtx) {
+ opacity: 0.3;
+ }
+#content table tr.tab:not(.canMtx):hover {
+ opacity: 0.7;
+ }
+#content table tr.tab:not(.canMtx) > td:nth-of-type(2):before {
+ content: '\f00d';
+ font: 1em FontAwesome;
+ }
+body:not(.popupOn) #content table tr.canMtx td:nth-of-type(2) {
+ cursor: zoom-in;
+ }
+body:not(.popupOn) #content table tr.canMtx td:nth-of-type(2):hover {
+ background: #ccc;
+ }
+#content table tr.cat_net td:nth-of-type(3) {
+ font: 12px monospace;
+ text-align: center;
+ white-space: nowrap;
+ }
+#content table tr.cat_net td:nth-of-type(5) {
+ }
+#content table tr.cat_net td:nth-of-type(5) > span > * {
+ opacity: 0.6;
+ }
+#content table tr.cat_net td:nth-of-type(5) > span > b:first-of-type {
+ opacity: 1;
+ }
+
+#popupContainer {
+ background: white;
+ border: 1px solid gray;
+ display: none;
+ overflow: hidden;
+ position: fixed;
+ right: 1em;
+ top: 0;
+ z-index: 200;
+ }
+body.popupOn #popupContainer {
+ display: block;
+ }
+#popupContainer > div {
+ background: #888;
+ border: 0;
+ }
+#popupContainer > div {
+ text-align: right;
+ }
+#popupContainer > div > span {
+ color: #ccc;
+ cursor: pointer;
+ display: inline-block;
+ font: 14px FontAwesome;
+ padding: 3px;
+ }
+#popupContainer > div > span:hover {
+ color: white;
+ }
+#popupContainer > iframe {
+ border: 0;
+ padding: 0;
+ margin: 0;
+ width: 100%;
+ }
+#popupContainer.hide {
+ width: 6em !important;
+ }
+#popupContainer.hide > iframe {
+ display: none;
+ }
diff --git a/css/popup.css b/css/popup.css
new file mode 100644
index 0000000..b5a468b
--- /dev/null
+++ b/css/popup.css
@@ -0,0 +1,691 @@
+body {
+ background-color: white;
+ border: 0;
+ float: left;
+ font: 14px httpsb,sans-serif;
+ margin: 0;
+ min-height: 16em;
+ min-width: 32em;
+ opacity: 1;
+ overflow-x: hidden;
+ overflow-y: auto;
+ padding: 0;
+ position: relative;
+ }
+*:focus {
+ outline: none;
+ }
+a {
+ color: inherit;
+ text-decoration: none;
+ }
+
+#version {
+ font-size: 10px;
+ font-weight: normal;
+ }
+#gotoDashboard {
+ background-color: #444;
+ border: 0;
+ color: #bbb;
+ cursor: pointer;
+ display: block;
+ font-size: 12px;
+ line-height: 12px;
+ margin: 0;
+ padding: 3px 0;
+ position: relative;
+ text-align: center;
+ }
+#gotoDashboard > span:last-of-type {
+ opacity: 0.5;
+ position: absolute;
+ }
+body[dir="ltr"] #gotoDashboard > span:last-of-type {
+ left: 3px;
+ }
+body[dir="rtl"] #gotoDashboard > span:last-of-type {
+ right: 3px;
+ }
+
+.paneHead {
+ background-color: white;
+ left: 0;
+ padding: 0;
+ position: fixed;
+ right: 0;
+ top: 0;
+ z-index: 100;
+ }
+.paneContent {
+ padding-top: 5.5em;
+ }
+
+
+.paneHead > a:first-child {
+ background-color: #444;
+ border: 0;
+ border-bottom: 1px solid white;
+ color: #bbb;
+ cursor: pointer;
+ display: block;
+ font-family: sans-serif;
+ line-height: 12px;
+ margin: 0;
+ padding: 2px 0;
+ text-align: center;
+ }
+#toolbarContainer {
+ display: flex;
+ justify-content: space-between;
+}
+.toolbar {
+ border: 0;
+ display: inline-flex;
+ margin: 0;
+ padding: 0;
+ }
+
+body .toolbar button {
+ background-color: white;
+ border: 0;
+ color: black;
+ cursor: pointer;
+ margin: 0;
+ padding: 0.2em 0.2em 0.1em 0.2em;
+ position: relative;
+ }
+body .toolbar button:hover {
+ background-color: #eee;
+ }
+body .toolbar button.disabled {
+ color: #ccc;
+ }
+body .toolbar button.fa {
+ font: 1.7em FontAwesome;
+ min-width: 1.4em;
+ }
+#mtxSwitch_matrix-off.switchTrue {
+ color: #a00;
+ }
+
+#mtxSwitches > li {
+ align-items: center;
+ color: #888;
+ display: flex;
+ }
+#mtxSwitches > li.switchTrue {
+ color: #000;
+ }
+#mtxSwitches > li > svg {
+ display: inline;
+ height: 1em;
+ margin-right: 0.4em;
+ width: 1.5em;
+ }
+#mtxSwitches > li > svg * {
+ fill-opacity: 1;
+ opacity: 1;
+ stroke: none;
+ }
+#mtxSwitches > li.relevant > svg .dot {
+ fill: #aaa;
+ }
+#mtxSwitches > li.switchTrue.relevant > svg .dot {
+ fill: #eee;
+ }
+#mtxSwitches > li > svg .off,
+#mtxSwitches > li.switchTrue > svg .on,
+#mtxSwitches > li.relevant > svg .dot {
+ display: block;
+ }
+#mtxSwitches > li > svg .on,
+#mtxSwitches > li > svg .dot,
+#mtxSwitches > li.switchTrue > svg .off {
+ display: none;
+ }
+#mtxSwitches > li > span[data-i18n] {
+ flex-grow: 1;
+ }
+#mtxSwitches > li > a {
+ color: #000;
+ opacity: 0;
+ }
+#mtxSwitches > li:hover > a {
+ opacity: 0.1;
+ }
+#mtxSwitches > li > a:hover {
+ opacity: 0.8;
+ }
+
+.dropdown-menu-capture {
+ background-color: transparent;
+ border: 0;
+ bottom: 0;
+ display: none;
+ left: 0;
+ margin: 0;
+ padding: 0;
+ position: fixed;
+ right: 0;
+ top: 0;
+ z-index: 300;
+ }
+.dropdown-menu-capture.show {
+ display: block;
+ }
+.dropdown-menu {
+ border: 0;
+ display: inline-block;
+ margin: 0;
+ padding: 3px 0 0 0;
+ position: absolute;
+ white-space: normal;
+ }
+.dropdown-menu > ul {
+ margin: 0;
+ border: 0;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ padding: 0;
+ background-color: white;
+ list-style-type: none;
+ }
+.dropdown-menu > ul > li.dropdown-menu-entry {
+ border: 0;
+ color: black;
+ cursor: pointer;
+ margin: 0;
+ padding: 0.2em 0.25em;
+ white-space: nowrap;
+ }
+.dropdown-menu > ul > li.dropdown-menu-entry:hover {
+ background: #eee;
+ }
+.dropdown-menu > ul > li.dropdown-menu-entry-divider {
+ border-top: 1px solid #ccc;
+ margin: 0.5em 0;
+ }
+.dropdown-menu.show {
+ display: block;
+ }
+
+#buttonReload {
+ margin-left: 1em;
+ }
+
+button > span.badge {
+ padding: 1px 1px;
+ display: inline-block;
+ font-size: 40%;
+ position: absolute;
+ right: 1px;
+ bottom: 1px;
+ color: #000;
+ background-color: rgba(240,240,240,0.75)
+ }
+button.disabled > span.badge {
+ display: none;
+ }
+#buttonPresets + .dropdown-menu {
+ position: fixed;
+ left: 10vw;
+ width: 80vw;
+ }
+.presetInfo {
+ margin: 0.25em 0.5em;
+ text-align: center;
+ }
+.presetEntry {
+ margin: 0.25em 0.25em;
+ border-radius: 3px;
+ padding: 0.5em;
+ display: inline-block;
+ cursor: pointer;
+ background-color: #eee;
+ }
+.presetEntry .fa {
+ margin-right: 0.25em;
+ font-size: 110%;
+ }
+.presetEntry:hover {
+ background-color: #80e2ff;
+ }
+#presetMore > *:first-child {
+ margin: 0;
+ padding: 0;
+ text-align: center;
+ color: #888;
+ cursor: pointer;
+ font-size: 13px;
+ }
+#presetMore > *:first-child + div {
+ margin: 0.25em 0 0 0;
+ padding: 0.25em 0 0 0;
+ display: none;
+ text-align: center;
+ }
+#presetMore > *:first-child + div.show {
+ display: block;
+ }
+#presetMore > *:first-child + div > * {
+ vertical-align: middle;
+ }
+#presetMoreRecipe {
+ border: 1px solid #aaa;
+ width: 75%;
+ height: 4em;
+ overflow: hidden;
+ resize: none;
+ font-size: 10px;
+ color: #888;
+ }
+#presetMoreRecipe.bad {
+ border: 1px solid #fcc;
+ color: #aaa;
+ }
+#presetMoreWrite.bad {
+ visibility: hidden;
+ }
+
+/* I think this is obsolete */
+.dropdown-menu > li > a > i {
+ padding: 0 6px;
+ font-size: 1.2em;
+ }
+
+body .toolbar .scope {
+ background-color: #ccc;
+ border: 1px solid #ccc;
+ box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ display: inline-flex;
+ color: white;
+ margin: 0;
+ padding: 1px;
+ cursor: pointer;
+ }
+body .toolbar .scope > span {
+ align-items: center;
+ display: inline-flex;
+ }
+body .toolbar .scope > span > span {
+ pointer-events: none;
+ white-space: nowrap;
+ }
+body .toolbar #specificScope {
+ direction: ltr;
+ justify-content: flex-end;
+ width: 16em;
+ }
+body .toolbar #specificScope.on {
+ background-color: #24c;
+ border-color: #24c;
+ }
+body .toolbar #specificScope > span {
+ background-color: #ccc;
+ justify-content: flex-end;
+ }
+body .toolbar #specificScope > span.on {
+ background-color: #24c;
+ }
+body .toolbar #specificScope > span:first-of-type:not(.on):hover,
+body .toolbar #specificScope > span:first-of-type:not(.on):hover ~ span:not(.on),
+body .toolbar #specificScope > span:not(.on) + span:not(.on):hover,
+body .toolbar #specificScope > span:not(.on) + span:not(.on):hover ~ span:not(.on) {
+ background-color: #999;
+ }
+body .toolbar #specificScope > span:first-of-type:not(.on):hover ~ span,
+body .toolbar #specificScope > span:not(.on) + span:not(.on):hover ~ span,
+body .toolbar #specificScope > span.on + span:hover,
+body .toolbar #specificScope > span.on + span:hover ~ span {
+ background-color: #139;
+ }
+body .toolbar #specificScope > span:first-of-type {
+ flex: 1;
+ }
+body .toolbar #globalScope {
+ justify-content: center;
+ margin-left: 1px;
+ width: 1.8em;
+ }
+body .toolbar #globalScope.on {
+ background-color: #000;
+ border-color: #000;
+ }
+body .toolbar #globalScope:not(.on):hover {
+ background-color: #999;
+ border-color: #999;
+ }
+body .toolbar .scopeRel {
+ color: #24c;
+ }
+body.globalScope .toolbar .scopeRel {
+ color: #000;
+ }
+body.globalScope .toolbar .scopeRel.disabled {
+ color: #ccc;
+ }
+
+.matrix {
+ text-align: left;
+ }
+.matRow {
+ white-space: nowrap;
+ }
+.matCell {
+ margin: 1px 1px 0 0;
+ border: 1px dotted rgba(0,0,0,0.2);
+ padding: 6px 1px 3px 1px;
+ display: inline-block;
+ box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ width: 2.6em;
+ white-space: nowrap;
+ text-align: center;
+ line-height: 110%;
+ position: relative;
+ }
+
+#matHead {
+ border-top: 1px dotted #ccc;
+ padding-top: 1px;
+ margin: 1px 0 0 0;
+ }
+.paneHead .matCell:nth-child(2) {
+ letter-spacing: -0.3px;
+ }
+
+.paneContent .matrix .matRow > .matCell:first-child {
+ font-weight: 100;
+ }
+.paneContent .matrix .matRow > .matCell:first-child > b {
+ font-weight: normal;
+ }
+
+/* RFC 3987 Internationalized Resource Identifiers (IRIs) -- 4.4 */
+.matrix .matRow > .matCell:first-child {
+ direction: ltr;
+ text-align: right;
+ unicode-bidi: embed;
+ width: 16em;
+ }
+.matrix .matGroup.g4 .matRow.ro > .matCell:first-child {
+ direction: inherit;
+ }
+.matrix .matRow.l2 > .matCell:first-child {
+ margin-left: 1px;
+ width: calc(16em - 1px);
+ }
+.matrix .matRow > .matCell:hover {
+ border-style: solid;
+ }
+.matrix .matGroup .matSection {
+ margin: 2px 0 0 0;
+ border: 0;
+ padding: 0;
+ /* background-color: rgba(0,0,0,0.05); */
+ }
+.matrix .matGroup.g0 .matSection:first-child {
+ margin-top: 0;
+ }
+.matrix .matGroup.g4 .matSection:first-child {
+ margin-top: 0;
+ }
+/* Collapsing of domains */
+.matrix .matSection .matRow.meta {
+ display: none;
+ }
+.matrix .matSection.collapsible.collapsed .matRow.meta {
+ display: block;
+ }
+.matrix .matSection.collapsible.collapsed .matRow.l1:not(.meta) {
+ display: none;
+ }
+.matrix .matSection.collapsible.collapsed .matRow.l2.collapsible {
+ display: none;
+ }
+
+/* Collapsing of blacklisted */
+.matrix .g4Meta {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ height: 6px;
+ background: url('../img/matrix-group-hide.png') no-repeat center top,
+ url('../img/matrix-group-hline.png') repeat-x center top 3px;
+ opacity: 0.2;
+ cursor: pointer;
+ }
+.matrix .g4Meta:hover {
+ opacity: 0.4;
+ }
+.matrix .g4Meta.g4Collapsed {
+ background: url('../img/matrix-group-show.png') no-repeat center top,
+ url('../img/matrix-group-hline.png') repeat-x center top 3px;
+ }
+.matrix .g4Meta.g4Collapsed ~ .matSection {
+ display: none;
+ }
+body.powerOff .matrix .g4Meta.g4Collapsed ~ .matSection {
+ display: block;
+ }
+.matrix .g4Meta ~ .matRow.ro {
+ display: none;
+ }
+.matrix .g4Meta.g4Collapsed ~ .matRow.ro {
+ display: block;
+ }
+body.powerOff .matrix .g4Meta.g4Collapsed ~ .matRow.ro {
+ display: none;
+ }
+.matrix .matGroup .g4Meta + *,.matrix .matGroup .g4Meta + * + * {
+ margin-top: 0;
+ padding-top: 0;
+ }
+
+/* Cell coloring */
+.t81 {
+ color: white;
+ background-color: #c00;
+ }
+.t82 {
+ color: white;
+ background-color: #080;
+ }
+.t1 {
+ border-color: #debaba;
+ color: black;
+ background-color: #f8d0d0;
+ }
+.t2 {
+ border-color: #bad6ba;
+ color: black;
+ background-color: #d0f0d0;
+ }
+.matCell.p81 {
+ background-image: url('../img/permanent-black-small.png');
+ background-repeat: no-repeat;
+ background-position: -1px -1px;
+ }
+.matCell.p82 {
+ background-image: url('../img/permanent-white-small.png');
+ background-repeat: no-repeat;
+ background-position: -1px -1px;
+ }
+
+/* Cell coloring for color blind-friendly (hopefully) */
+body.colorblind .t81 {
+ color: white;
+ background-color: rgb(0, 19, 110);
+ }
+body.colorblind .t82 {
+ border-color: rgb(255, 194, 57);
+ color: black;
+ background-color: rgb(255, 194, 57);
+ }
+body.colorblind .t1 {
+ border-color: rgba(0, 19, 110, 0.3);
+ color: black;
+ background-color: rgba(0, 19, 110, 0.2);
+ }
+body.colorblind .t2 {
+ border-color: rgba(255, 194, 57, 0.3);
+ color: black;
+ background-color: rgba(255, 194, 57, 0.2);
+ }
+body.colorblind .matCell.p81 {
+ background-image: url('../img/permanent-black-small-cb.png');
+ }
+body.colorblind .matCell.p82 {
+ background-image: url('../img/permanent-white-small-cb.png');
+ }
+
+
+.matRow.rw .matCell {
+ cursor: pointer;
+ }
+body.powerOff .matRow.rw .matCell {
+ cursor: auto;
+ opacity: 0.6;
+ }
+
+.top {
+ font-weight: bold;
+ }
+
+#cellHotspots {
+ margin: 0;
+ border: 0;
+ padding: 0;
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 10;
+ }
+#whitelist, #blacklist {
+ margin: 0;
+ border: 0;
+ padding: 0;
+ position: absolute;
+ left: 0;
+ width: 100%;
+ height: 50%;
+ background: transparent;
+ }
+#whitelist {
+ top: 0;
+ }
+#blacklist {
+ top: 50%;
+ }
+body.powerOff #whitelist, body.powerOff #blacklist {
+ display: none;
+ }
+.rw .matCell.t1 #whitelist:hover {
+ background-color: #080;
+ opacity: 0.25;
+ }
+body.colorblind .rw .matCell.t1 #whitelist:hover,
+body.colorblind .rw .matCell.t2 #whitelist:hover {
+ background-color: rgb(255, 194, 57);
+ opacity: 0.6;
+ }
+.rw .matCell.t2 #whitelist:hover {
+ background-color: #080;
+ opacity: 0.25;
+ }
+.matCell.t81 #whitelist:hover {
+ background-color: transparent;
+ }
+.matCell.t82 #whitelist:hover {
+ background-color: transparent;
+ }
+.rw .matCell.t1 #blacklist:hover {
+ background-color: #c00;
+ opacity: 0.25;
+ }
+body.colorblind .rw .matCell.t1 #blacklist:hover,
+body.colorblind .rw .matCell.t2 #blacklist:hover {
+ background-color: rgb(0, 19, 110);
+ opacity: 0.4;
+ }
+.rw .matCell.t2 #blacklist:hover {
+ background-color: #c00;
+ opacity: 0.25;
+ }
+.matCell.t81 #blacklist:hover {
+ background-color: transparent;
+ }
+.matCell.t82 #blacklist:hover {
+ background-color: transparent;
+ }
+#domainOnly {
+ margin: 0;
+ border: 1px solid gray;
+ border-radius: 3px;
+ padding: 0 1px;
+ position: absolute;
+ font-size: 1.1em;
+ left: 20%;
+ bottom: -20%;
+ display: none;
+ color: black;
+ background-color: white;
+ opacity: 0.25;
+ z-index: 10000;
+ cursor: pointer;
+ }
+.matSection #domainOnly .fa:before {
+ content: '\f106';
+ }
+.matSection.collapsed #domainOnly .fa:before {
+ content: '\f107';
+ }
+.matSection.collapsible .matRow.l1 .matCell:nth-of-type(1):hover #domainOnly {
+ display: block;
+ }
+#matHead #domainOnly .fa:before {
+ content: '\f106';
+ }
+#matHead.collapsed #domainOnly .fa:before {
+ content: '\f107';
+ }
+#matHead.collapsible .matRow .matCell:nth-of-type(1):hover #domainOnly {
+ display: block;
+ }
+#domainOnly:hover {
+ opacity: 1;
+ }
+
+/* No data was found for the tab */
+
+body.noTabFound .paneHead,
+body.noTabFound .paneContent {
+ display: none;
+ }
+body.noTabFound #noTabFound {
+ align-items: center;
+ color: gray;
+ display: flex;
+ font-size: xx-large;
+ height: 100vh;
+ justify-content: center;
+ }
+
+/* Mobile-friendly rules */
+
+body.hConstrained {
+ overflow-x: auto;
+ }
+body.hConstrained .paneHead {
+ left: auto;
+ position: absolute;
+ right: auto;
+ width: 100%;
+ }
+body[data-touch="true"] .matCell {
+ line-height: 200%;
+ }
diff --git a/css/raw-settings.css b/css/raw-settings.css
new file mode 100644
index 0000000..4d9e49d
--- /dev/null
+++ b/css/raw-settings.css
@@ -0,0 +1,18 @@
+body {
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+ justify-content: space-between;
+ }
+p {
+ margin: 0.5em 0;
+ }
+textarea {
+ box-sizing: border-box;
+ flex-grow: 1;
+ resize: none;
+ text-align: left;
+ white-space: pre;
+ width: 100%;
+ word-wrap: normal;
+ }
diff --git a/css/user-rules.css b/css/user-rules.css
new file mode 100644
index 0000000..8142edb
--- /dev/null
+++ b/css/user-rules.css
@@ -0,0 +1,162 @@
+div > p:first-child {
+ margin-top: 0;
+ }
+div > p:last-child {
+ margin-bottom: 0;
+ }
+#diff {
+ border: 0;
+ margin: 0;
+ padding: 0;
+ white-space: nowrap;
+}
+#diff > .pane {
+ border: 0;
+ box-sizing: box-border;
+ display: inline-block;
+ margin: 0;
+ padding: 0;
+ position: relative;
+ vertical-align: top;
+ white-space: normal;
+ width: calc(50% - 2px);
+ }
+#diff > .pane > div {
+ padding: 0 0 1em 0;
+ text-align: center;
+ }
+#diff > .pane > div > span {
+ float: left;
+ }
+body[dir="ltr"] #revertButton:after {
+ content: '\2009\f061';
+ font-family: FontAwesome;
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ vertical-align: baseline;
+ display: inline-block;
+ }
+body[dir="rtl"] #revertButton:after {
+ content: '\2009\f060';
+ font-family: FontAwesome;
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ vertical-align: baseline;
+ display: inline-block;
+ }
+body[dir="ltr"] #commitButton:before {
+ content: '\f060\2009';
+ font-family: FontAwesome;
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ vertical-align: baseline;
+ display: inline-block;
+ }
+body[dir="rtl"] #commitButton:before {
+ content: '\f061\2009';
+ font-family: FontAwesome;
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ vertical-align: baseline;
+ display: inline-block;
+ }
+#revertButton,
+#commitButton,
+#diff.edit #editEnterButton {
+ opacity: 0.25;
+ pointer-events: none;
+ }
+#editStopButton,
+#editCancelButton {
+ display: none;
+ }
+#diff.dirty:not(.edit) #revertButton,
+#diff.dirty:not(.edit) #commitButton {
+ opacity: 1;
+ pointer-events: auto;
+ }
+#diff.edit #editStopButton,
+#diff.edit #editCancelButton {
+ display: initial;
+ }
+#diff.edit #importButton,
+#diff.edit #exportButton {
+ display: none;
+ }
+#diff ul {
+ border: 0;
+ border-top: 1px solid #eee;
+ list-style-type: none;
+ margin: 0;
+ overflow: hidden;
+ padding: 1em 0 0 0;
+ }
+#diff ul,
+#diff textarea {
+ font: 12px/1.8 monospace;
+ }
+#diff.edit .right ul {
+ visibility: hidden;
+ }
+#diff .left {
+ padding: 0 0 0 0;
+ }
+#diff .right > ul {
+ color: #888;
+ }
+#diff li {
+ background-color: white;
+ direction: ltr;
+ padding: 0;
+ text-align: left;
+ white-space: nowrap;
+ }
+#diff li:nth-of-type(2n+0) {
+ background-color: #eee;
+ }
+#diff .right li {
+ cursor: pointer;
+ }
+#diff .right li:hover {
+ background-color: #ffc;
+ color: #000;
+ }
+#diff .right li.notLeft {
+ color: #000;
+ }
+#diff .right li.notLeft:hover {
+ text-decoration: line-through;
+ }
+#diff .right li.notRight {
+ color: #000;
+ }
+#diff .right li.toRemove {
+ color: #000;
+ text-decoration: line-through;
+ }
+#diff textarea {
+ border: 0;
+ border-top: 1px solid #eee;
+ direction: ltr;
+ height: 100%;
+ left: 0;
+ margin: 0;
+ overflow: hidden;
+ overflow-y: auto;
+ padding: 1em 0 0 0;
+ position: absolute;
+ resize: none;
+ visibility: hidden;
+ white-space: pre;
+ width: 100%;
+ }
+#diff.edit textarea {
+ visibility: visible;
+ }
+.hidden {
+ display: none;
+ }
diff --git a/dashboard.html b/dashboard.html
new file mode 100644
index 0000000..752fecf
--- /dev/null
+++ b/dashboard.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<link rel="shortcut icon" type="image/png" href="img/icon_16.png">
+<title data-i18n="dashboardPageName"></title>
+<link href='css/dashboard.css' rel='stylesheet' type='text/css'>
+<link href='css/common.css' rel='stylesheet' type='text/css'>
+</head>
+
+<body>
+<div id="dashboard-nav">
+ <div id="dashboard-nav-widgets">
+ <span>uMatrix</span>
+ <a class="tabButton" id="settings" href="#settings" data-dashboard-panel-url="settings.html" data-i18n="settingsPageName"></a>
+ <a class="tabButton" id="user-rules" href="#user-rules" data-dashboard-panel-url="user-rules.html" data-i18n="userRulesPageName"></a>
+ <a class="tabButton" id="hosts-files" href="#hosts-files" data-dashboard-panel-url="hosts-files.html" data-i18n="ubiquitousRulesPageName"></a>
+ <a class="tabButton" id="raw-settings" href="#raw-settings" data-dashboard-panel-url="raw-settings.html" data-i18n="rawSettingsPageName"></a>
+ <a class="tabButton" id="about" href="#about" data-dashboard-panel-url="about.html" data-i18n="aboutPageName"></a>
+ </div>
+</div>
+
+<iframe src=""></iframe>
+
+<script src="js/vapi-common.js"></script>
+<script src="js/vapi-client.js"></script>
+<script src="js/udom.js"></script>
+<script src="js/i18n.js"></script>
+<script src="js/dashboard.js"></script>
+</body>
+</html>
diff --git a/frameModule.js b/frameModule.js
new file mode 100644
index 0000000..46d9377
--- /dev/null
+++ b/frameModule.js
@@ -0,0 +1,357 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 The µBlock authors
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global Components */
+
+'use strict';
+
+/******************************************************************************/
+
+// https://github.com/gorhill/uBlock/issues/800#issuecomment-146580443
+this.EXPORTED_SYMBOLS = ['contentObserver', 'LocationChangeListener'];
+
+const {interfaces: Ci, utils: Cu} = Components;
+const {Services} = Cu.import('resource://gre/modules/Services.jsm', null);
+const {XPCOMUtils} = Cu.import('resource://gre/modules/XPCOMUtils.jsm', null);
+
+const hostName = Services.io.newURI(Components.stack.filename, null, null).host;
+
+// Cu.import('resource://gre/modules/Console.jsm');
+
+/******************************************************************************/
+
+const getMessageManager = function(win) {
+ let iface = win
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell)
+ .sameTypeRootTreeItem
+ .QueryInterface(Ci.nsIDocShell)
+ .QueryInterface(Ci.nsIInterfaceRequestor);
+
+ try {
+ return iface.getInterface(Ci.nsIContentFrameMessageManager);
+ } catch (ex) {
+ // This can throw. It appears `shouldLoad` can be called *after* a
+ // tab has been closed. For example, a case where this happens all
+ // the time (FF38):
+ // - Open twitter.com (assuming you have an account and are logged in)
+ // - Close twitter.com
+ // There will be an exception raised when `shouldLoad` is called
+ // to process a XMLHttpRequest with URL `https://twitter.com/i/jot`
+ // fired from `https://twitter.com/`, *after* the tab is closed.
+ // In such case, `win` is `about:blank`.
+ }
+ return null;
+};
+
+/******************************************************************************/
+
+var contentObserver = {
+ classDescription: 'content-policy for ' + hostName,
+ classID: Components.ID('{c84283d4-9975-41b7-b1a4-f106af56b51d}'),
+ contractID: '@' + hostName + '/content-policy;1',
+ ACCEPT: Ci.nsIContentPolicy.ACCEPT,
+ MAIN_FRAME: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ contentBaseURI: 'chrome://' + hostName + '/content/js/',
+ cpMessageName: hostName + ':shouldLoad',
+ uniqueSandboxId: 1,
+ modernFirefox:
+ Services.vc.compare(Services.appinfo.platformVersion, '44') > 0 && (
+ Services.appinfo.ID === '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}' ||
+ Services.appinfo.ID === '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}'
+ ),
+
+ get componentRegistrar() {
+ return Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+ },
+
+ get categoryManager() {
+ return Components.classes['@mozilla.org/categorymanager;1']
+ .getService(Ci.nsICategoryManager);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsIFactory,
+ Ci.nsIObserver,
+ Ci.nsIContentPolicy,
+ Ci.nsISupportsWeakReference
+ ]),
+
+ createInstance: function(outer, iid) {
+ if ( outer ) {
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ }
+
+ return this.QueryInterface(iid);
+ },
+
+ register: function() {
+ Services.obs.addObserver(this, 'document-element-inserted', true);
+
+ if ( !this.modernFirefox ) {
+ this.componentRegistrar.registerFactory(
+ this.classID,
+ this.classDescription,
+ this.contractID,
+ this
+ );
+ this.categoryManager.addCategoryEntry(
+ 'content-policy',
+ this.contractID,
+ this.contractID,
+ false,
+ true
+ );
+ }
+ },
+
+ unregister: function() {
+ Services.obs.removeObserver(this, 'document-element-inserted');
+
+ if ( !this.modernFirefox ) {
+ this.componentRegistrar.unregisterFactory(
+ this.classID,
+ this
+ );
+ this.categoryManager.deleteCategoryEntry(
+ 'content-policy',
+ this.contractID,
+ false
+ );
+ }
+ },
+
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentPolicy
+ // https://bugzil.la/612921
+ shouldLoad: function(type, location, origin, context) {
+ if ( Services === undefined || !context ) {
+ return this.ACCEPT;
+ }
+
+ if ( !location.schemeIs('http') && !location.schemeIs('https') ) {
+ return this.ACCEPT;
+ }
+
+ var contextWindow;
+ if ( type === this.MAIN_FRAME ) {
+ contextWindow = context.contentWindow || context;
+ } else if ( type === this.SUB_FRAME ) {
+ contextWindow = context.contentWindow;
+ } else {
+ contextWindow = (context.ownerDocument || context).defaultView;
+ }
+
+ // https://github.com/gorhill/uMatrix/issues/706
+ if ( !contextWindow ) {
+ return this.ACCEPT;
+ }
+
+ // The context for the toolbar popup is an iframe element here,
+ // so check context.top instead of context
+ if ( !contextWindow.top || !contextWindow.location ) {
+ return this.ACCEPT;
+ }
+
+ let messageManager = getMessageManager(contextWindow);
+ if ( messageManager === null ) {
+ return this.ACCEPT;
+ }
+
+ let details = {
+ rawType: type,
+ url: location.asciiSpec
+ };
+
+ if ( typeof messageManager.sendRpcMessage === 'function' ) {
+ // https://bugzil.la/1092216
+ messageManager.sendRpcMessage(this.cpMessageName, details);
+ } else {
+ // Compatibility for older versions
+ messageManager.sendSyncMessage(this.cpMessageName, details);
+ }
+
+ return this.ACCEPT;
+ },
+
+ initContentScripts: function(win, sandbox) {
+ let messager = getMessageManager(win);
+ let sandboxId = hostName + ':sb:' + this.uniqueSandboxId++;
+
+ if ( sandbox ) {
+ let sandboxName = [
+ win.location.href.slice(0, 100),
+ win.document.title.slice(0, 100)
+ ].join(' | ');
+
+ // https://github.com/gorhill/uMatrix/issues/325
+ // "Pass sameZoneAs to sandbox constructor to make GCs cheaper"
+ sandbox = Cu.Sandbox([win], {
+ sameZoneAs: win.top,
+ sandboxName: sandboxId + '[' + sandboxName + ']',
+ sandboxPrototype: win,
+ wantComponents: false,
+ wantXHRConstructor: false
+ });
+
+ sandbox.injectScript = function(script) {
+ Services.scriptloader.loadSubScript(script, sandbox);
+ };
+ }
+ else {
+ sandbox = win;
+ }
+
+ sandbox._sandboxId_ = sandboxId;
+ sandbox.sendAsyncMessage = messager.sendAsyncMessage;
+
+ sandbox.addMessageListener = function(callback) {
+ if ( sandbox._messageListener_ ) {
+ sandbox.removeMessageListener();
+ }
+
+ sandbox._messageListener_ = function(message) {
+ callback(message.data);
+ };
+
+ messager.addMessageListener(
+ sandbox._sandboxId_,
+ sandbox._messageListener_
+ );
+ messager.addMessageListener(
+ hostName + ':broadcast',
+ sandbox._messageListener_
+ );
+ };
+
+ sandbox.removeMessageListener = function() {
+ try {
+ messager.removeMessageListener(
+ sandbox._sandboxId_,
+ sandbox._messageListener_
+ );
+ messager.removeMessageListener(
+ hostName + ':broadcast',
+ sandbox._messageListener_
+ );
+ } catch (ex) {
+ // It throws sometimes, mostly when the popup closes
+ }
+
+ sandbox._messageListener_ = null;
+ };
+
+ return sandbox;
+ },
+
+ observe: function(doc) {
+ let win = doc.defaultView;
+ if ( !win ) {
+ return;
+ }
+
+ let loc = win.location;
+ if ( !loc ) {
+ return;
+ }
+
+ // https://github.com/gorhill/uBlock/issues/260
+ // TODO: We may have to skip more types, for now let's be
+ // conservative, i.e. let's not test against `text/html`.
+ if ( doc.contentType.lastIndexOf('image/', 0) === 0 ) {
+ return;
+ }
+
+ if ( loc.protocol !== 'http:' && loc.protocol !== 'https:' && loc.protocol !== 'file:' ) {
+ if ( loc.protocol === 'chrome:' && loc.host === hostName ) {
+ this.initContentScripts(win);
+ }
+
+ // What about data: and about:blank?
+ return;
+ }
+
+ let lss = Services.scriptloader.loadSubScript;
+ let sandbox = this.initContentScripts(win, true);
+
+ // Can throw with attempts at injecting into non-HTML document.
+ // Example: https://a.pomf.se/avonjf.webm
+ try {
+ lss(this.contentBaseURI + 'vapi-client.js', sandbox);
+ lss(this.contentBaseURI + 'contentscript-start.js', sandbox);
+ } catch (ex) {
+ return; // don't further try to inject anything
+ }
+
+ let docReady = (e) => {
+ let doc = e.target;
+ doc.removeEventListener(e.type, docReady, true);
+ lss(this.contentBaseURI + 'contentscript-end.js', sandbox);
+ };
+
+ if ( doc.readyState === 'loading') {
+ doc.addEventListener('DOMContentLoaded', docReady, true);
+ } else {
+ docReady({ target: doc, type: 'DOMContentLoaded' });
+ }
+ }
+};
+
+/******************************************************************************/
+
+const locationChangedMessageName = hostName + ':locationChanged';
+
+var LocationChangeListener = function(docShell) {
+ if ( !docShell ) {
+ return;
+ }
+
+ var requestor = docShell.QueryInterface(Ci.nsIInterfaceRequestor);
+ var ds = requestor.getInterface(Ci.nsIWebProgress);
+ var mm = requestor.getInterface(Ci.nsIContentFrameMessageManager);
+
+ if ( ds && mm && typeof mm.sendAsyncMessage === 'function' ) {
+ this.docShell = ds;
+ this.messageManager = mm;
+ ds.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
+ }
+};
+
+LocationChangeListener.prototype.QueryInterface = XPCOMUtils.generateQI([
+ 'nsIWebProgressListener',
+ 'nsISupportsWeakReference'
+]);
+
+LocationChangeListener.prototype.onLocationChange = function(webProgress, request, location, flags) {
+ if ( !webProgress.isTopLevel ) {
+ return;
+ }
+ this.messageManager.sendAsyncMessage(locationChangedMessageName, {
+ url: location.asciiSpec,
+ flags: flags,
+ });
+};
+
+/******************************************************************************/
+
+contentObserver.register();
+
+/******************************************************************************/
diff --git a/frameScript.js b/frameScript.js
new file mode 100644
index 0000000..3fb6ad1
--- /dev/null
+++ b/frameScript.js
@@ -0,0 +1,73 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 The µBlock authors
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/******************************************************************************/
+
+var locationChangeListener; // Keep alive while frameScript is alive
+
+(function() {
+
+'use strict';
+
+/******************************************************************************/
+
+let {contentObserver, LocationChangeListener} = Components.utils.import(
+ Components.stack.filename.replace('Script', 'Module'),
+ null
+);
+
+let injectContentScripts = function(win) {
+ if ( !win || !win.document ) {
+ return;
+ }
+
+ contentObserver.observe(win.document);
+
+ if ( win.frames && win.frames.length ) {
+ let i = win.frames.length;
+ while ( i-- ) {
+ injectContentScripts(win.frames[i]);
+ }
+ }
+};
+
+let onLoadCompleted = function() {
+ removeMessageListener('umatrix-load-completed', onLoadCompleted);
+ injectContentScripts(content);
+};
+
+addMessageListener('umatrix-load-completed', onLoadCompleted);
+
+if ( docShell ) {
+ let Ci = Components.interfaces;
+ let wp = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
+ let dw = wp.DOMWindow;
+ if ( dw === dw.top ) {
+ locationChangeListener = new LocationChangeListener(docShell);
+ }
+}
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
diff --git a/hosts-files.html b/hosts-files.html
new file mode 100644
index 0000000..c5043c2
--- /dev/null
+++ b/hosts-files.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>uMatrix — Hosts files</title>
+<link rel="stylesheet" type="text/css" href="css/common.css">
+<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
+<link rel="stylesheet" type="text/css" href="css/hosts-files.css">
+</head>
+
+<body>
+
+<p data-i18n="hostsFilesPrompt"></p>
+<p>
+ <button id="buttonApply" class="custom important reloadAll disabled" data-i18n="hostsFilesApplyChanges"></button>
+ <button id="buttonUpdate" class="custom important reloadAll disabled" data-i18n="hostsFilesUpdateNow"></button>
+ <button id="buttonPurgeAll" class="custom disabled" data-i18n="hostsFilesPurgeAll"></button>
+</p>
+<ul id="options">
+ <li><input type="checkbox" id="autoUpdate"><label data-i18n="hostsFilesAutoUpdatePrompt" for="autoUpdate"></label>
+ <li><span id="listsOfBlockedHostsPrompt"></span>
+</ul>
+<ul id="lists">
+</ul>
+
+<div id="externalLists">
+<p data-i18n="hostsFilesExternalListsHint" style="margin: 0 0 0.25em 0; font-size: 13px;"></p>
+<textarea id="externalHostsFiles" dir="ltr" spellcheck="false"></textarea>
+</div>
+
+<div id="templates" style="display: none;">
+ <ul>
+ <li class="listEntry">
+ <input type="checkbox"><!--
+ --><a class="content" type="text/plain" target="_blank" href=""></a>&#8203;<!--
+ --><a class="fa support" href="" target="_blank">&#xf015;</a>&#8203;<!--
+ --><a class="fa remove" href="">&#xf014;</a>&#8203;<!--
+ --><a class="fa mustread" href="" target="_blank">&#xf05a;</a>&#8203;<!--
+  --><span class="fa status unsecure" title="http">&#xf13e;</span>&#8203;<!--
+ --><span class="counts dim"></span>&#8203;<!--
+  --><span class="fa status obsolete" title="hostsFilesExternalListObsolete">&#xf071;</span>&#8203;<!--
+  --><span class="fa status cache">&#xf017;</span>&#8203;<!--
+  --><span class="fa status updating">&#xf110;</span>&#8203;<!--
+  --><span class="fa status failed">&#xf06a;</span>
+ </li>
+ </ul>
+</div>
+
+<script src="js/vapi-common.js"></script>
+<script src="js/vapi-client.js"></script>
+<script src="js/udom.js"></script>
+<script src="js/i18n.js"></script>
+<script src="js/dashboard-common.js"></script>
+<script src="js/hosts-files.js"></script>
+
+</body>
+</html>
+
diff --git a/icon.png b/icon.png
new file mode 100644
index 0000000..3f1ead8
--- /dev/null
+++ b/icon.png
Binary files differ
diff --git a/img/browsericons/icon19-0.png b/img/browsericons/icon19-0.png
new file mode 100644
index 0000000..da3bb57
--- /dev/null
+++ b/img/browsericons/icon19-0.png
Binary files differ
diff --git a/img/browsericons/icon19-1.png b/img/browsericons/icon19-1.png
new file mode 100644
index 0000000..d3aea61
--- /dev/null
+++ b/img/browsericons/icon19-1.png
Binary files differ
diff --git a/img/browsericons/icon19-10.png b/img/browsericons/icon19-10.png
new file mode 100644
index 0000000..96c0cfa
--- /dev/null
+++ b/img/browsericons/icon19-10.png
Binary files differ
diff --git a/img/browsericons/icon19-11.png b/img/browsericons/icon19-11.png
new file mode 100644
index 0000000..9412e32
--- /dev/null
+++ b/img/browsericons/icon19-11.png
Binary files differ
diff --git a/img/browsericons/icon19-12.png b/img/browsericons/icon19-12.png
new file mode 100644
index 0000000..a7f46c8
--- /dev/null
+++ b/img/browsericons/icon19-12.png
Binary files differ
diff --git a/img/browsericons/icon19-13.png b/img/browsericons/icon19-13.png
new file mode 100644
index 0000000..556c74c
--- /dev/null
+++ b/img/browsericons/icon19-13.png
Binary files differ
diff --git a/img/browsericons/icon19-14.png b/img/browsericons/icon19-14.png
new file mode 100644
index 0000000..dcf0c7c
--- /dev/null
+++ b/img/browsericons/icon19-14.png
Binary files differ
diff --git a/img/browsericons/icon19-15.png b/img/browsericons/icon19-15.png
new file mode 100644
index 0000000..8a86409
--- /dev/null
+++ b/img/browsericons/icon19-15.png
Binary files differ
diff --git a/img/browsericons/icon19-16.png b/img/browsericons/icon19-16.png
new file mode 100644
index 0000000..124bd8d
--- /dev/null
+++ b/img/browsericons/icon19-16.png
Binary files differ
diff --git a/img/browsericons/icon19-17.png b/img/browsericons/icon19-17.png
new file mode 100644
index 0000000..c913b41
--- /dev/null
+++ b/img/browsericons/icon19-17.png
Binary files differ
diff --git a/img/browsericons/icon19-18.png b/img/browsericons/icon19-18.png
new file mode 100644
index 0000000..0288d92
--- /dev/null
+++ b/img/browsericons/icon19-18.png
Binary files differ
diff --git a/img/browsericons/icon19-19.png b/img/browsericons/icon19-19.png
new file mode 100644
index 0000000..500242b
--- /dev/null
+++ b/img/browsericons/icon19-19.png
Binary files differ
diff --git a/img/browsericons/icon19-2.png b/img/browsericons/icon19-2.png
new file mode 100644
index 0000000..46e3495
--- /dev/null
+++ b/img/browsericons/icon19-2.png
Binary files differ
diff --git a/img/browsericons/icon19-3.png b/img/browsericons/icon19-3.png
new file mode 100644
index 0000000..4730ef6
--- /dev/null
+++ b/img/browsericons/icon19-3.png
Binary files differ
diff --git a/img/browsericons/icon19-4.png b/img/browsericons/icon19-4.png
new file mode 100644
index 0000000..da44b73
--- /dev/null
+++ b/img/browsericons/icon19-4.png
Binary files differ
diff --git a/img/browsericons/icon19-5.png b/img/browsericons/icon19-5.png
new file mode 100644
index 0000000..a4303ae
--- /dev/null
+++ b/img/browsericons/icon19-5.png
Binary files differ
diff --git a/img/browsericons/icon19-6.png b/img/browsericons/icon19-6.png
new file mode 100644
index 0000000..5507d36
--- /dev/null
+++ b/img/browsericons/icon19-6.png
Binary files differ
diff --git a/img/browsericons/icon19-7.png b/img/browsericons/icon19-7.png
new file mode 100644
index 0000000..7b3980c
--- /dev/null
+++ b/img/browsericons/icon19-7.png
Binary files differ
diff --git a/img/browsericons/icon19-8.png b/img/browsericons/icon19-8.png
new file mode 100644
index 0000000..cee49d9
--- /dev/null
+++ b/img/browsericons/icon19-8.png
Binary files differ
diff --git a/img/browsericons/icon19-9.png b/img/browsericons/icon19-9.png
new file mode 100644
index 0000000..afa60d7
--- /dev/null
+++ b/img/browsericons/icon19-9.png
Binary files differ
diff --git a/img/browsericons/icon19-off.png b/img/browsericons/icon19-off.png
new file mode 100644
index 0000000..036a7a3
--- /dev/null
+++ b/img/browsericons/icon19-off.png
Binary files differ
diff --git a/img/browsericons/icon38-off.png b/img/browsericons/icon38-off.png
new file mode 100644
index 0000000..1c03c65
--- /dev/null
+++ b/img/browsericons/icon38-off.png
Binary files differ
diff --git a/img/cloud.png b/img/cloud.png
new file mode 100644
index 0000000..6c78dde
--- /dev/null
+++ b/img/cloud.png
Binary files differ
diff --git a/img/icon_16.png b/img/icon_16.png
new file mode 100644
index 0000000..3773ae1
--- /dev/null
+++ b/img/icon_16.png
Binary files differ
diff --git a/img/icon_64.png b/img/icon_64.png
new file mode 100644
index 0000000..3901388
--- /dev/null
+++ b/img/icon_64.png
Binary files differ
diff --git a/img/matrix-group-hide.png b/img/matrix-group-hide.png
new file mode 100644
index 0000000..d6e618e
--- /dev/null
+++ b/img/matrix-group-hide.png
Binary files differ
diff --git a/img/matrix-group-hline.png b/img/matrix-group-hline.png
new file mode 100644
index 0000000..395610e
--- /dev/null
+++ b/img/matrix-group-hline.png
Binary files differ
diff --git a/img/matrix-group-show.png b/img/matrix-group-show.png
new file mode 100644
index 0000000..295ddab
--- /dev/null
+++ b/img/matrix-group-show.png
Binary files differ
diff --git a/img/permanent-black-small-cb.png b/img/permanent-black-small-cb.png
new file mode 100644
index 0000000..de45e78
--- /dev/null
+++ b/img/permanent-black-small-cb.png
Binary files differ
diff --git a/img/permanent-black-small.png b/img/permanent-black-small.png
new file mode 100644
index 0000000..3cc3df2
--- /dev/null
+++ b/img/permanent-black-small.png
Binary files differ
diff --git a/img/permanent-white-small-cb.png b/img/permanent-white-small-cb.png
new file mode 100644
index 0000000..a774fd3
--- /dev/null
+++ b/img/permanent-white-small-cb.png
Binary files differ
diff --git a/img/permanent-white-small.png b/img/permanent-white-small.png
new file mode 100644
index 0000000..aa00073
--- /dev/null
+++ b/img/permanent-white-small.png
Binary files differ
diff --git a/install.rdf b/install.rdf
new file mode 100644
index 0000000..ff873d7
--- /dev/null
+++ b/install.rdf
@@ -0,0 +1,452 @@
+<?xml version="1.0" encoding="utf-8"?>
+<r:RDF xmlns:r="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.mozilla.org/2004/em-rdf#">
+ <r:Description about="urn:mozilla:install-manifest">
+ <id>uMatrix@raymondhill.net</id>
+ <version>1.3.2</version>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ <creator>Raymond Hill</creator>
+ <developer>Deathamns</developer>
+ <developer>Alex Vallat</developer>
+ <type>2</type>
+ <bootstrap>true</bootstrap>
+ <multiprocessCompatible>true</multiprocessCompatible>
+ <optionsType>2</optionsType>
+
+ <localized><r:Description>
+ <locale>am</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>ar</locale>
+ <name>uMatrix</name>
+ <description>قم بالاشارة ثم انقر لكي تقوم بسماح او منع اي اتصال يجري عن طريق المتصفح. يمكنك منع السكربتات، أطر، اعلانات، موقع مثل الفيسبوك، الخ.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>bg</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>bn</locale>
+ <name>uMatrix</name>
+ <description>আপনার ব্রাউজার দ্বারা করা যে কোন শ্রেণীর অনুরোধ অনুমতি/নিষেধ করতে পয়েন্ট ও ক্লিক করুন। স্ক্রিপ্ট, আইফ্রেম, বিজ্ঞাপন, ফেসবুক, ইত্যাদি ব্লক এটি ব্যবহার করুন।</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>ca</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>cs</locale>
+ <name>uMatrix</name>
+ <description>Umožňuje cíleně povolovat/blokovat spojení z vašeho prohlížeče pouhým kliknutím. Můžete jím blokovat skripty, reklamy, Facebook, …</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>da</locale>
+ <name>uMatrix</name>
+ <description>Peg og klik for at forbyde/tillade bestemte former for anmodninger. Brug det til at blokere scripts, iframes, annoncer, Facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>de</locale>
+ <name>uMatrix</name>
+ <description>Kontrolliere alle Anfragen deines Browsers durch einfaches Point&amp;Click. Blockiere damit Skripte, IFrames, Werbung, Facebook usw.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>el</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>eo</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>es</locale>
+ <name>uMatrix</name>
+ <description>Prohíbe/permite cualquier petición del navegador. Bloquea scripts, iframes, anuncios, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>et</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>fa</locale>
+ <name>uMatrix</name>
+ <description>اشاره و کلیک کنید تا هر کلاسی از درخواست‌هایی که توسط مرورگر ایجاد می‌شوند را منع کنید یا اجازه دهید. برای سد راه کردن اسکریپت‌ها، iframeها، تبلیغات، فیس‌بوک و غیره از آن استفاده کنید.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>fi</locale>
+ <name>uMatrix</name>
+ <description>Osoita &amp; klikkaa estääksesi/salliaksesi mitä tahansa selaimesi tekemiä luokan pyyntöjä. Käytä sitä komentosarjojen, iframesin, mainosten, facebookin -estämiseen jne.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>fil</locale>
+ <name>uMatrix</name>
+ <description>Ituro at i-click ang pagbawalan / pahintulutan ang anumang uri ng mga kahilingan na ginawa ng iyong browser. Gamitin ito upang i-block ang mga script, iframe, ad, facebook, atbp.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>fr</locale>
+ <name>uMatrix</name>
+ <description>Pointez-et-cliquez pour interdire/autoriser toute sorte de requêtes effectuées par le navigateur à l'aide d'un tableau de données.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>gu</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>he</locale>
+ <name>uMatrix</name>
+ <description>כוון &amp; לחץ על מנת לחסום/להרשות כל סוג של בקשות שיצאו מהדפדפן שלך. השתמש בזה על מנת לחסום סקריפטים, מסגרות, פרסומות, פייסבוק, וכו'.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>hi</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>hr</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>hu</locale>
+ <name>uMatrix</name>
+ <description>Tiltsd le, vagy engedélyezd egy kattintással a böngésző által létrehozott lekéréseket csoportosítva – szkriptek, iframe-ek, stb.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>id</locale>
+ <name>uMatrix</name>
+ <description>Tunjuk &amp; klik untuk larang/izinkan permintaan yang dibuat peramban anda. Gunakan untuk memblokir skrip, iframe, iklan, dll.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>it</locale>
+ <name>uMatrix</name>
+ <description>Punta &amp; clicca per bloccare/consentire qualunque categoria di richieste fatte dal browser.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>ja</locale>
+ <name>uMatrix</name>
+ <description>マウス一つでどんな種類のリクエストでも拒否/許可することができます。スクリプト、Iframe、広告、Facebookなどをブロックします。</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>kn</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>ko</locale>
+ <name>uMatrix</name>
+ <description>클릭 한번으로 모든 종류의 요청에 대한 거부 / 허용을 설정 할 수 있습니다. 스크립트, Iframe, 광고, Facebook 등을 차단합니다.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>lt</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>lv</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>ml</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>mr</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>ms</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>nb</locale>
+ <name>uMatrix</name>
+ <description>Pek og klikk for å forby/tillate enhver forespørselsklasse fra nettleseren din. Bruk det til å blokkere skript, iframe-rammer, reklame, Facebook, osv.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>nl</locale>
+ <name>uMatrix</name>
+ <description>Beslis zelf welke aanvragen door uw browser mogen worden gedaan. Blokkeer scripts, iframes, advertenties, Facebook en meer.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>pl</locale>
+ <name>uMatrix</name>
+ <description>Kliknij by za/odblokować jakikolwiek połączenia wykonywane przez przeglądarkę. Blokuj skrypty, ramki, reklamy, fecebooka itp.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>pt-BR</locale>
+ <name>uMatrix</name>
+ <description>Apontar &amp; clicar para proibir/permitir quaisquer pedidos feitos pelo seu navegador. Bloqueia scripts, iframes, anúncios, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>pt-PT</locale>
+ <name>uMatrix</name>
+ <description>Aponte &amp; clique para proibir/permitir pedidos feitos pelo seu browser. Use-o para bloquear scripts, iframes, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>ro</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>ru</locale>
+ <name>uMatrix</name>
+ <description>Интерактивный блокировщик любых типов запросов браузера. Позволяет запретить загрузку скриптов, фреймов, плагинов, рекламы и пр.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>sk</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>sl</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>sr</locale>
+ <name>uMatrix</name>
+ <description>Забраните или дозволите било коју врсту захтева од стране прегледача. Корисно за блокирање скрипти, реклама, друштвених мрежа и др.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>sv</locale>
+ <name>uMatrix</name>
+ <description>Klicka runt för att förbjuda eller tillåta förfrågningsklasser din webbläsare skapat. Använd detta för att blockera skript, annonser, Facebook, med mera.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>sw</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>ta</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>te</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>th</locale>
+ <name>uMatrix</name>
+ <description>Point &amp; click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>tr</locale>
+ <name>uMatrix</name>
+ <description>İşaret edip tıklayarak tarayıcının yaptığı herhangi bir istek sınıfı yasaklanabilir veya buna izin verilebilir.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>uk</locale>
+ <name>uMatrix</name>
+ <description>Натисніть для блокування будь якого класу запитів. Використовуйте для блокування скриптів, банерів, соцмереж, тощо.</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>vi</locale>
+ <name>uMatrix</name>
+ <description>Chỉ cần click để chặn/cho phép mọi loại yêu cầu của trình duyệt. Dùng để chặn script, iframe, quảng cáo, facebook,...</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>zh-CN</locale>
+ <name>uMatrix</name>
+ <description>通过点击开关达到控制浏览器中的各种网页请求。借此拦截网页脚本、嵌套页面、媒体广告、社交网站追踪等行为。</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <localized><r:Description>
+ <locale>zh-TW</locale>
+ <name>uMatrix</name>
+ <description>通過點選開關達到控制瀏覽器中的各種網頁請求。藉此阻擋網頁指令、嵌套頁面、媒體廣告、社交網站追踪等行爲。</description>
+ <creator>Raymond Hill</creator>
+ <homepageURL>https://github.com/gorhill/uMatrix</homepageURL>
+ </r:Description></localized>
+
+ <!-- Firefox -->
+ <targetApplication>
+ <r:Description>
+ <id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</id>
+ <minVersion>24.0</minVersion>
+ <maxVersion>56.*</maxVersion>
+ </r:Description>
+ </targetApplication>
+
+ <!-- SeaMonkey -->
+ <targetApplication>
+ <r:Description>
+ <id>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</id>
+ <minVersion>2.24</minVersion>
+ <maxVersion>*</maxVersion>
+ </r:Description>
+ </targetApplication>
+
+ <!-- Pale Moon -->
+ <targetApplication>
+ <r:Description>
+ <id>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</id>
+ <minVersion>26.0</minVersion>
+ <maxVersion>27.*</maxVersion>
+ </r:Description>
+ </targetApplication>
+ </r:Description>
+</r:RDF>
diff --git a/js/about.js b/js/about.js
new file mode 100644
index 0000000..acaffa3
--- /dev/null
+++ b/js/about.js
@@ -0,0 +1,146 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global uDom */
+
+'use strict';
+
+/******************************************************************************/
+
+uDom.onLoad(function() {
+
+/******************************************************************************/
+
+var backupUserDataToFile = function() {
+ var userDataReady = function(userData) {
+ vAPI.download({
+ 'url': 'data:text/plain,' + encodeURIComponent(JSON.stringify(userData, null, 2)),
+ 'filename': uDom('[data-i18n="aboutBackupFilename"]').text()
+ });
+ };
+
+ vAPI.messaging.send('about.js', { what: 'getAllUserData' }, userDataReady);
+};
+
+/******************************************************************************/
+
+function restoreUserDataFromFile() {
+ var validateBackup = function(s) {
+ var userData = null;
+ try {
+ userData = JSON.parse(s);
+ }
+ catch (e) {
+ userData = null;
+ }
+ if ( userData === null ) {
+ return null;
+ }
+ if (
+ typeof userData !== 'object' ||
+ typeof userData.version !== 'string' ||
+ typeof userData.when !== 'number' ||
+ typeof userData.settings !== 'object' ||
+ typeof userData.rules !== 'string' ||
+ typeof userData.hostsFiles !== 'object'
+ ) {
+ return null;
+ }
+ return userData;
+ };
+
+ var fileReaderOnLoadHandler = function() {
+ var userData = validateBackup(this.result);
+ if ( !userData ) {
+ window.alert(uDom('[data-i18n="aboutRestoreError"]').text());
+ return;
+ }
+ var time = new Date(userData.when);
+ var msg = uDom('[data-i18n="aboutRestoreConfirm"]').text()
+ .replace('{{time}}', time.toLocaleString());
+ var proceed = window.confirm(msg);
+ if ( proceed ) {
+ vAPI.messaging.send(
+ 'about.js',
+ { what: 'restoreAllUserData', userData: userData }
+ );
+ }
+ };
+
+ var file = this.files[0];
+ if ( file === undefined || file.name === '' ) {
+ return;
+ }
+ if ( file.type.indexOf('text') !== 0 ) {
+ return;
+ }
+ var fr = new FileReader();
+ fr.onload = fileReaderOnLoadHandler;
+ fr.readAsText(file);
+}
+
+/******************************************************************************/
+
+var startRestoreFilePicker = function() {
+ var input = document.getElementById('restoreFilePicker');
+ // Reset to empty string, this will ensure an change event is properly
+ // triggered if the user pick a file, even if it is the same as the last
+ // one picked.
+ input.value = '';
+ input.click();
+};
+
+/******************************************************************************/
+
+var resetUserData = function() {
+ var proceed = window.confirm(uDom('[data-i18n="aboutResetConfirm"]').text());
+ if ( proceed ) {
+ vAPI.messaging.send('about.js', { what: 'resetAllUserData' });
+ }
+};
+
+/******************************************************************************/
+
+(function() {
+ var renderStats = function(details) {
+ document.getElementById('aboutVersion').textContent = details.version;
+ var template = uDom('[data-i18n="aboutStorageUsed"]').text();
+ var storageUsed = '?';
+ if ( typeof details.storageUsed === 'number' ) {
+ storageUsed = details.storageUsed.toLocaleString();
+ }
+ document.getElementById('aboutStorageUsed').textContent =
+ template.replace('{{storageUsed}}', storageUsed);
+ };
+ vAPI.messaging.send('about.js', { what: 'getSomeStats' }, renderStats);
+})();
+
+/******************************************************************************/
+
+uDom('#backupUserDataButton').on('click', backupUserDataToFile);
+uDom('#restoreUserDataButton').on('click', startRestoreFilePicker);
+uDom('#restoreFilePicker').on('change', restoreUserDataFromFile);
+uDom('#resetUserDataButton').on('click', resetUserData);
+
+/******************************************************************************/
+
+});
diff --git a/js/asset-viewer.js b/js/asset-viewer.js
new file mode 100644
index 0000000..97a1fb8
--- /dev/null
+++ b/js/asset-viewer.js
@@ -0,0 +1,46 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+/******************************************************************************/
+
+(function() {
+
+ var onAssetContentReceived = function(details) {
+ document.getElementById('content').textContent =
+ details && (details.content || '');
+ };
+
+ var q = window.location.search;
+ var matches = q.match(/^\?url=([^&]+)/);
+ if ( !matches || matches.length !== 2 ) {
+ return;
+ }
+
+ vAPI.messaging.send(
+ 'asset-viewer.js',
+ { what : 'getAssetContent', url: matches[1] },
+ onAssetContentReceived
+ );
+
+})();
diff --git a/js/assets.js b/js/assets.js
new file mode 100644
index 0000000..c3fb85f
--- /dev/null
+++ b/js/assets.js
@@ -0,0 +1,911 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2013-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+/******************************************************************************/
+
+µMatrix.assets = (function() {
+
+/******************************************************************************/
+
+var reIsExternalPath = /^(?:[a-z-]+):\/\//,
+ errorCantConnectTo = vAPI.i18n('errorCantConnectTo'),
+ noopfunc = function(){};
+
+var api = {
+};
+
+/******************************************************************************/
+
+var observers = [];
+
+api.addObserver = function(observer) {
+ if ( observers.indexOf(observer) === -1 ) {
+ observers.push(observer);
+ }
+};
+
+api.removeObserver = function(observer) {
+ var pos;
+ while ( (pos = observers.indexOf(observer)) !== -1 ) {
+ observers.splice(pos, 1);
+ }
+};
+
+var fireNotification = function(topic, details) {
+ var result;
+ for ( var i = 0; i < observers.length; i++ ) {
+ if ( observers[i](topic, details) === false ) {
+ result = false;
+ }
+ }
+ return result;
+};
+
+/******************************************************************************/
+
+api.fetchText = function(url, onLoad, onError) {
+ var actualUrl = reIsExternalPath.test(url) ? url : vAPI.getURL(url);
+
+ if ( typeof onError !== 'function' ) {
+ onError = onLoad;
+ }
+
+ // https://github.com/gorhill/uMatrix/issues/15
+ var onResponseReceived = function() {
+ this.onload = this.onerror = this.ontimeout = null;
+ // xhr for local files gives status 0, but actually succeeds
+ var details = {
+ url: url,
+ content: '',
+ statusCode: this.status || 200,
+ statusText: this.statusText || ''
+ };
+ if ( details.statusCode < 200 || details.statusCode >= 300 ) {
+ return onError.call(null, details);
+ }
+ // consider an empty result to be an error
+ if ( stringIsNotEmpty(this.responseText) === false ) {
+ return onError.call(null, details);
+ }
+ // we never download anything else than plain text: discard if response
+ // appears to be a HTML document: could happen when server serves
+ // some kind of error page I suppose
+ var text = this.responseText.trim();
+ if ( text.startsWith('<') && text.endsWith('>') ) {
+ return onError.call(null, details);
+ }
+ details.content = this.responseText;
+ return onLoad.call(null, details);
+ };
+
+ var onErrorReceived = function() {
+ this.onload = this.onerror = this.ontimeout = null;
+ µMatrix.logger.writeOne('', 'error', errorCantConnectTo.replace('{{msg}}', actualUrl));
+ onError.call(null, { url: url, content: '' });
+ };
+
+ // Be ready for thrown exceptions:
+ // I am pretty sure it used to work, but now using a URL such as
+ // `file:///` on Chromium 40 results in an exception being thrown.
+ var xhr = new XMLHttpRequest();
+ try {
+ xhr.open('get', actualUrl, true);
+ xhr.timeout = 30000;
+ xhr.onload = onResponseReceived;
+ xhr.onerror = onErrorReceived;
+ xhr.ontimeout = onErrorReceived;
+ xhr.responseType = 'text';
+ xhr.send();
+ } catch (e) {
+ onErrorReceived.call(xhr);
+ }
+};
+
+/*******************************************************************************
+
+ TODO(seamless migration):
+ This block of code will be removed when I am confident all users have
+ moved to a version of uBO which does not require the old way of caching
+ assets.
+
+ api.listKeyAliases: a map of old asset keys to new asset keys.
+
+ migrate(): to seamlessly migrate the old cache manager to the new one:
+ - attempt to preserve and move content of cached assets to new locations;
+ - removes all traces of now obsolete cache manager entries in cacheStorage.
+
+ This code will typically execute only once, when the newer version of uBO
+ is first installed and executed.
+
+**/
+
+api.listKeyAliases = {
+ "assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat": "public_suffix_list.dat",
+ "assets/thirdparties/hosts-file.net/ad-servers": "hphosts",
+ "assets/thirdparties/www.malwaredomainlist.com/hostslist/hosts.txt": "malware-0",
+ "assets/thirdparties/mirror1.malwaredomains.com/files/justdomains": "malware-1",
+ "assets/thirdparties/pgl.yoyo.org/as/serverlist": "plowe-0",
+ "assets/thirdparties/someonewhocares.org/hosts/hosts": "dpollock-0",
+ "assets/thirdparties/winhelp2002.mvps.org/hosts.txt": "mvps-0"
+};
+
+var migrate = function(callback) {
+ var entries,
+ moveCount = 0,
+ toRemove = [];
+
+ var countdown = function(change) {
+ moveCount -= (change || 0);
+ if ( moveCount !== 0 ) { return; }
+ vAPI.cacheStorage.remove(toRemove);
+ saveAssetCacheRegistry();
+ callback();
+ };
+
+ var onContentRead = function(oldKey, newKey, bin) {
+ var content = bin && bin['cached_asset_content://' + oldKey] || undefined;
+ if ( content ) {
+ assetCacheRegistry[newKey] = {
+ readTime: Date.now(),
+ writeTime: entries[oldKey]
+ };
+ if ( reIsExternalPath.test(oldKey) ) {
+ assetCacheRegistry[newKey].remoteURL = oldKey;
+ }
+ bin = {};
+ bin['cache/' + newKey] = content;
+ vAPI.cacheStorage.set(bin);
+ }
+ countdown(1);
+ };
+
+ var onEntries = function(bin) {
+ entries = bin && bin.cached_asset_entries;
+ if ( !entries ) { return callback(); }
+ if ( bin && bin.assetCacheRegistry ) {
+ assetCacheRegistry = bin.assetCacheRegistry;
+ }
+ var aliases = api.listKeyAliases;
+ for ( var oldKey in entries ) {
+ var newKey = aliases[oldKey];
+ if ( !newKey && /^https?:\/\//.test(oldKey) ) {
+ newKey = oldKey;
+ }
+ if ( newKey ) {
+ vAPI.cacheStorage.get(
+ 'cached_asset_content://' + oldKey,
+ onContentRead.bind(null, oldKey, newKey)
+ );
+ moveCount += 1;
+ }
+ toRemove.push('cached_asset_content://' + oldKey);
+ }
+ toRemove.push('cached_asset_entries', 'extensionLastVersion');
+ countdown();
+ };
+
+ vAPI.cacheStorage.get(
+ [ 'cached_asset_entries', 'assetCacheRegistry' ],
+ onEntries
+ );
+};
+
+/*******************************************************************************
+
+ The purpose of the asset source registry is to keep key detail information
+ about an asset:
+ - Where to load it from: this may consist of one or more URLs, either local
+ or remote.
+ - After how many days an asset should be deemed obsolete -- i.e. in need of
+ an update.
+ - The origin and type of an asset.
+ - The last time an asset was registered.
+
+**/
+
+var assetSourceRegistryStatus,
+ assetSourceRegistry = Object.create(null);
+
+var registerAssetSource = function(assetKey, dict) {
+ var entry = assetSourceRegistry[assetKey] || {};
+ for ( var prop in dict ) {
+ if ( dict.hasOwnProperty(prop) === false ) { continue; }
+ if ( dict[prop] === undefined ) {
+ delete entry[prop];
+ } else {
+ entry[prop] = dict[prop];
+ }
+ }
+ var contentURL = dict.contentURL;
+ if ( contentURL !== undefined ) {
+ if ( typeof contentURL === 'string' ) {
+ contentURL = entry.contentURL = [ contentURL ];
+ } else if ( Array.isArray(contentURL) === false ) {
+ contentURL = entry.contentURL = [];
+ }
+ var remoteURLCount = 0;
+ for ( var i = 0; i < contentURL.length; i++ ) {
+ if ( reIsExternalPath.test(contentURL[i]) ) {
+ remoteURLCount += 1;
+ }
+ }
+ entry.hasLocalURL = remoteURLCount !== contentURL.length;
+ entry.hasRemoteURL = remoteURLCount !== 0;
+ } else if ( entry.contentURL === undefined ) {
+ entry.contentURL = [];
+ }
+ if ( typeof entry.updateAfter !== 'number' ) {
+ entry.updateAfter = 13;
+ }
+ if ( entry.submitter ) {
+ entry.submitTime = Date.now(); // To detect stale entries
+ }
+ assetSourceRegistry[assetKey] = entry;
+};
+
+var unregisterAssetSource = function(assetKey) {
+ assetCacheRemove(assetKey);
+ delete assetSourceRegistry[assetKey];
+};
+
+var saveAssetSourceRegistry = (function() {
+ var timer;
+ var save = function() {
+ timer = undefined;
+ vAPI.cacheStorage.set({ assetSourceRegistry: assetSourceRegistry });
+ };
+ return function(lazily) {
+ if ( timer !== undefined ) {
+ clearTimeout(timer);
+ }
+ if ( lazily ) {
+ timer = vAPI.setTimeout(save, 500);
+ } else {
+ save();
+ }
+ };
+})();
+
+var updateAssetSourceRegistry = function(json, silent) {
+ var newDict;
+ try {
+ newDict = JSON.parse(json);
+ } catch (ex) {
+ }
+ if ( newDict instanceof Object === false ) { return; }
+
+ var oldDict = assetSourceRegistry,
+ assetKey;
+
+ // Remove obsolete entries (only those which were built-in).
+ for ( assetKey in oldDict ) {
+ if (
+ newDict[assetKey] === undefined &&
+ oldDict[assetKey].submitter === undefined
+ ) {
+ unregisterAssetSource(assetKey);
+ }
+ }
+ // Add/update existing entries. Notify of new asset sources.
+ for ( assetKey in newDict ) {
+ if ( oldDict[assetKey] === undefined && !silent ) {
+ fireNotification(
+ 'builtin-asset-source-added',
+ { assetKey: assetKey, entry: newDict[assetKey] }
+ );
+ }
+ registerAssetSource(assetKey, newDict[assetKey]);
+ }
+ saveAssetSourceRegistry();
+};
+
+var getAssetSourceRegistry = function(callback) {
+ // Already loaded.
+ if ( assetSourceRegistryStatus === 'ready' ) {
+ callback(assetSourceRegistry);
+ return;
+ }
+
+ // Being loaded.
+ if ( Array.isArray(assetSourceRegistryStatus) ) {
+ assetSourceRegistryStatus.push(callback);
+ return;
+ }
+
+ // Not loaded: load it.
+ assetSourceRegistryStatus = [ callback ];
+
+ var registryReady = function() {
+ var callers = assetSourceRegistryStatus;
+ assetSourceRegistryStatus = 'ready';
+ var fn;
+ while ( (fn = callers.shift()) ) {
+ fn(assetSourceRegistry);
+ }
+ };
+
+ // First-install case.
+ var createRegistry = function() {
+ api.fetchText(
+ µMatrix.assetsBootstrapLocation || 'assets/assets.json',
+ function(details) {
+ updateAssetSourceRegistry(details.content, true);
+ registryReady();
+ }
+ );
+ };
+
+ vAPI.cacheStorage.get('assetSourceRegistry', function(bin) {
+ if ( !bin || !bin.assetSourceRegistry ) {
+ createRegistry();
+ return;
+ }
+ assetSourceRegistry = bin.assetSourceRegistry;
+ registryReady();
+ });
+};
+
+api.registerAssetSource = function(assetKey, details) {
+ getAssetSourceRegistry(function() {
+ registerAssetSource(assetKey, details);
+ saveAssetSourceRegistry(true);
+ });
+};
+
+api.unregisterAssetSource = function(assetKey) {
+ getAssetSourceRegistry(function() {
+ unregisterAssetSource(assetKey);
+ saveAssetSourceRegistry(true);
+ });
+};
+
+/*******************************************************************************
+
+ The purpose of the asset cache registry is to keep track of all assets
+ which have been persisted into the local cache.
+
+**/
+
+var assetCacheRegistryStatus,
+ assetCacheRegistryStartTime = Date.now(),
+ assetCacheRegistry = {};
+
+var getAssetCacheRegistry = function(callback) {
+ // Already loaded.
+ if ( assetCacheRegistryStatus === 'ready' ) {
+ callback(assetCacheRegistry);
+ return;
+ }
+
+ // Being loaded.
+ if ( Array.isArray(assetCacheRegistryStatus) ) {
+ assetCacheRegistryStatus.push(callback);
+ return;
+ }
+
+ // Not loaded: load it.
+ assetCacheRegistryStatus = [ callback ];
+
+ var registryReady = function() {
+ var callers = assetCacheRegistryStatus;
+ assetCacheRegistryStatus = 'ready';
+ var fn;
+ while ( (fn = callers.shift()) ) {
+ fn(assetCacheRegistry);
+ }
+ };
+
+ var migrationDone = function() {
+ vAPI.cacheStorage.get('assetCacheRegistry', function(bin) {
+ if ( bin && bin.assetCacheRegistry ) {
+ assetCacheRegistry = bin.assetCacheRegistry;
+ }
+ registryReady();
+ });
+ };
+
+ migrate(migrationDone);
+};
+
+var saveAssetCacheRegistry = (function() {
+ var timer;
+ var save = function() {
+ timer = undefined;
+ vAPI.cacheStorage.set({ assetCacheRegistry: assetCacheRegistry });
+ };
+ return function(lazily) {
+ if ( timer !== undefined ) { clearTimeout(timer); }
+ if ( lazily ) {
+ timer = vAPI.setTimeout(save, 500);
+ } else {
+ save();
+ }
+ };
+})();
+
+var assetCacheRead = function(assetKey, callback) {
+ var internalKey = 'cache/' + assetKey;
+
+ var reportBack = function(content, err) {
+ var details = { assetKey: assetKey, content: content };
+ if ( err ) { details.error = err; }
+ callback(details);
+ };
+
+ var onAssetRead = function(bin) {
+ if ( !bin || !bin[internalKey] ) {
+ return reportBack('', 'E_NOTFOUND');
+ }
+ var entry = assetCacheRegistry[assetKey];
+ if ( entry === undefined ) {
+ return reportBack('', 'E_NOTFOUND');
+ }
+ entry.readTime = Date.now();
+ saveAssetCacheRegistry(true);
+ reportBack(bin[internalKey]);
+ };
+
+ var onReady = function() {
+ vAPI.cacheStorage.get(internalKey, onAssetRead);
+ };
+
+ getAssetCacheRegistry(onReady);
+};
+
+var assetCacheWrite = function(assetKey, details, callback) {
+ var internalKey = 'cache/' + assetKey;
+ var content = '';
+ if ( typeof details === 'string' ) {
+ content = details;
+ } else if ( details instanceof Object ) {
+ content = details.content || '';
+ }
+
+ if ( content === '' ) {
+ return assetCacheRemove(assetKey, callback);
+ }
+
+ var reportBack = function(content) {
+ var details = { assetKey: assetKey, content: content };
+ if ( typeof callback === 'function' ) {
+ callback(details);
+ }
+ fireNotification('after-asset-updated', details);
+ };
+
+ var onReady = function() {
+ var entry = assetCacheRegistry[assetKey];
+ if ( entry === undefined ) {
+ entry = assetCacheRegistry[assetKey] = {};
+ }
+ entry.writeTime = entry.readTime = Date.now();
+ if ( details instanceof Object && typeof details.url === 'string' ) {
+ entry.remoteURL = details.url;
+ }
+ var bin = { assetCacheRegistry: assetCacheRegistry };
+ bin[internalKey] = content;
+ vAPI.cacheStorage.set(bin);
+ reportBack(content);
+ };
+ getAssetCacheRegistry(onReady);
+};
+
+var assetCacheRemove = function(pattern, callback) {
+ var onReady = function() {
+ var cacheDict = assetCacheRegistry,
+ removedEntries = [],
+ removedContent = [];
+ for ( var assetKey in cacheDict ) {
+ if ( pattern instanceof RegExp && !pattern.test(assetKey) ) {
+ continue;
+ }
+ if ( typeof pattern === 'string' && assetKey !== pattern ) {
+ continue;
+ }
+ removedEntries.push(assetKey);
+ removedContent.push('cache/' + assetKey);
+ delete cacheDict[assetKey];
+ }
+ if ( removedContent.length !== 0 ) {
+ vAPI.cacheStorage.remove(removedContent);
+ var bin = { assetCacheRegistry: assetCacheRegistry };
+ vAPI.cacheStorage.set(bin);
+ }
+ if ( typeof callback === 'function' ) {
+ callback();
+ }
+ for ( var i = 0; i < removedEntries.length; i++ ) {
+ fireNotification('after-asset-updated', { assetKey: removedEntries[i] });
+ }
+ };
+
+ getAssetCacheRegistry(onReady);
+};
+
+var assetCacheMarkAsDirty = function(pattern, exclude, callback) {
+ var onReady = function() {
+ var cacheDict = assetCacheRegistry,
+ cacheEntry,
+ mustSave = false;
+ for ( var assetKey in cacheDict ) {
+ if ( pattern instanceof RegExp ) {
+ if ( pattern.test(assetKey) === false ) { continue; }
+ } else if ( typeof pattern === 'string' ) {
+ if ( assetKey !== pattern ) { continue; }
+ } else if ( Array.isArray(pattern) ) {
+ if ( pattern.indexOf(assetKey) === -1 ) { continue; }
+ }
+ if ( exclude instanceof RegExp ) {
+ if ( exclude.test(assetKey) ) { continue; }
+ } else if ( typeof exclude === 'string' ) {
+ if ( assetKey === exclude ) { continue; }
+ } else if ( Array.isArray(exclude) ) {
+ if ( exclude.indexOf(assetKey) !== -1 ) { continue; }
+ }
+ cacheEntry = cacheDict[assetKey];
+ if ( !cacheEntry.writeTime ) { continue; }
+ cacheDict[assetKey].writeTime = 0;
+ mustSave = true;
+ }
+ if ( mustSave ) {
+ var bin = { assetCacheRegistry: assetCacheRegistry };
+ vAPI.cacheStorage.set(bin);
+ }
+ if ( typeof callback === 'function' ) {
+ callback();
+ }
+ };
+ if ( typeof exclude === 'function' ) {
+ callback = exclude;
+ exclude = undefined;
+ }
+ getAssetCacheRegistry(onReady);
+};
+
+/******************************************************************************/
+
+var stringIsNotEmpty = function(s) {
+ return typeof s === 'string' && s !== '';
+};
+
+/******************************************************************************/
+
+api.get = function(assetKey, options, callback) {
+ if ( typeof options === 'function' ) {
+ callback = options;
+ options = {};
+ } else if ( typeof callback !== 'function' ) {
+ callback = noopfunc;
+ }
+
+ var assetDetails = {},
+ contentURLs,
+ contentURL;
+
+ var reportBack = function(content, err) {
+ var details = { assetKey: assetKey, content: content };
+ if ( err ) {
+ details.error = assetDetails.lastError = err;
+ } else {
+ assetDetails.lastError = undefined;
+ }
+ callback(details);
+ };
+
+ var onContentNotLoaded = function() {
+ var isExternal;
+ while ( (contentURL = contentURLs.shift()) ) {
+ isExternal = reIsExternalPath.test(contentURL);
+ if ( isExternal === false || assetDetails.hasLocalURL !== true ) {
+ break;
+ }
+ }
+ if ( !contentURL ) {
+ return reportBack('', 'E_NOTFOUND');
+ }
+ api.fetchText(contentURL, onContentLoaded, onContentNotLoaded);
+ };
+
+ var onContentLoaded = function(details) {
+ if ( stringIsNotEmpty(details.content) === false ) {
+ onContentNotLoaded();
+ return;
+ }
+ if ( reIsExternalPath.test(contentURL) && options.dontCache !== true ) {
+ assetCacheWrite(assetKey, {
+ content: details.content,
+ url: contentURL
+ });
+ }
+ reportBack(details.content);
+ };
+
+ var onCachedContentLoaded = function(details) {
+ if ( details.content !== '' ) {
+ return reportBack(details.content);
+ }
+ getAssetSourceRegistry(function(registry) {
+ assetDetails = registry[assetKey] || {};
+ if ( typeof assetDetails.contentURL === 'string' ) {
+ contentURLs = [ assetDetails.contentURL ];
+ } else if ( Array.isArray(assetDetails.contentURL) ) {
+ contentURLs = assetDetails.contentURL.slice(0);
+ } else {
+ contentURLs = [];
+ }
+ onContentNotLoaded();
+ });
+ };
+
+ assetCacheRead(assetKey, onCachedContentLoaded);
+};
+
+/******************************************************************************/
+
+var getRemote = function(assetKey, callback) {
+ var assetDetails = {},
+ contentURLs,
+ contentURL;
+
+ var reportBack = function(content, err) {
+ var details = { assetKey: assetKey, content: content };
+ if ( err ) {
+ details.error = assetDetails.lastError = err;
+ } else {
+ assetDetails.lastError = undefined;
+ }
+ callback(details);
+ };
+
+ var onRemoteContentLoaded = function(details) {
+ if ( stringIsNotEmpty(details.content) === false ) {
+ registerAssetSource(assetKey, { error: { time: Date.now(), error: 'No content' } });
+ tryLoading();
+ return;
+ }
+ assetCacheWrite(assetKey, {
+ content: details.content,
+ url: contentURL
+ });
+ registerAssetSource(assetKey, { error: undefined });
+ reportBack(details.content);
+ };
+
+ var onRemoteContentError = function(details) {
+ var text = details.statusText;
+ if ( details.statusCode === 0 ) {
+ text = 'network error';
+ }
+ registerAssetSource(assetKey, { error: { time: Date.now(), error: text } });
+ tryLoading();
+ };
+
+ var tryLoading = function() {
+ while ( (contentURL = contentURLs.shift()) ) {
+ if ( reIsExternalPath.test(contentURL) ) { break; }
+ }
+ if ( !contentURL ) {
+ return reportBack('', 'E_NOTFOUND');
+ }
+ api.fetchText(contentURL, onRemoteContentLoaded, onRemoteContentError);
+ };
+
+ getAssetSourceRegistry(function(registry) {
+ assetDetails = registry[assetKey] || {};
+ if ( typeof assetDetails.contentURL === 'string' ) {
+ contentURLs = [ assetDetails.contentURL ];
+ } else if ( Array.isArray(assetDetails.contentURL) ) {
+ contentURLs = assetDetails.contentURL.slice(0);
+ } else {
+ contentURLs = [];
+ }
+ tryLoading();
+ });
+};
+
+/******************************************************************************/
+
+api.put = function(assetKey, content, callback) {
+ assetCacheWrite(assetKey, content, callback);
+};
+
+/******************************************************************************/
+
+api.metadata = function(callback) {
+ var assetRegistryReady = false,
+ cacheRegistryReady = false;
+
+ var onReady = function() {
+ var assetDict = JSON.parse(JSON.stringify(assetSourceRegistry)),
+ cacheDict = assetCacheRegistry,
+ assetEntry, cacheEntry,
+ now = Date.now(), obsoleteAfter;
+ for ( var assetKey in assetDict ) {
+ assetEntry = assetDict[assetKey];
+ cacheEntry = cacheDict[assetKey];
+ if ( cacheEntry ) {
+ assetEntry.cached = true;
+ assetEntry.writeTime = cacheEntry.writeTime;
+ obsoleteAfter = cacheEntry.writeTime + assetEntry.updateAfter * 86400000;
+ assetEntry.obsolete = obsoleteAfter < now;
+ assetEntry.remoteURL = cacheEntry.remoteURL;
+ } else {
+ assetEntry.writeTime = 0;
+ obsoleteAfter = 0;
+ assetEntry.obsolete = true;
+ }
+ }
+ callback(assetDict);
+ };
+
+ getAssetSourceRegistry(function() {
+ assetRegistryReady = true;
+ if ( cacheRegistryReady ) { onReady(); }
+ });
+
+ getAssetCacheRegistry(function() {
+ cacheRegistryReady = true;
+ if ( assetRegistryReady ) { onReady(); }
+ });
+};
+
+/******************************************************************************/
+
+api.purge = assetCacheMarkAsDirty;
+
+api.remove = function(pattern, callback) {
+ assetCacheRemove(pattern, callback);
+};
+
+api.rmrf = function() {
+ assetCacheRemove(/./);
+};
+
+/******************************************************************************/
+
+// Asset updater area.
+var updaterStatus,
+ updaterTimer,
+ updaterAssetDelayDefault = 120000,
+ updaterAssetDelay = updaterAssetDelayDefault,
+ updaterUpdated = [],
+ updaterFetched = new Set();
+
+var updateFirst = function() {
+ updaterStatus = 'updating';
+ updaterFetched.clear();
+ updaterUpdated = [];
+ fireNotification('before-assets-updated');
+ updateNext();
+};
+
+var updateNext = function() {
+ var assetDict, cacheDict;
+
+ // This will remove a cached asset when it's no longer in use.
+ var garbageCollectOne = function(assetKey) {
+ var cacheEntry = cacheDict[assetKey];
+ if ( cacheEntry && cacheEntry.readTime < assetCacheRegistryStartTime ) {
+ assetCacheRemove(assetKey);
+ }
+ };
+
+ var findOne = function() {
+ var now = Date.now(),
+ assetEntry, cacheEntry;
+ for ( var assetKey in assetDict ) {
+ assetEntry = assetDict[assetKey];
+ if ( assetEntry.hasRemoteURL !== true ) { continue; }
+ if ( updaterFetched.has(assetKey) ) { continue; }
+ cacheEntry = cacheDict[assetKey];
+ if ( cacheEntry && (cacheEntry.writeTime + assetEntry.updateAfter * 86400000) > now ) {
+ continue;
+ }
+ if ( fireNotification('before-asset-updated', { assetKey: assetKey }) !== false ) {
+ return assetKey;
+ }
+ garbageCollectOne(assetKey);
+ }
+ };
+
+ var updatedOne = function(details) {
+ if ( details.content !== '' ) {
+ updaterUpdated.push(details.assetKey);
+ if ( details.assetKey === 'assets.json' ) {
+ updateAssetSourceRegistry(details.content);
+ }
+ } else {
+ fireNotification('asset-update-failed', { assetKey: details.assetKey });
+ }
+ if ( findOne() !== undefined ) {
+ vAPI.setTimeout(updateNext, updaterAssetDelay);
+ } else {
+ updateDone();
+ }
+ };
+
+ var updateOne = function() {
+ var assetKey = findOne();
+ if ( assetKey === undefined ) {
+ return updateDone();
+ }
+ updaterFetched.add(assetKey);
+ getRemote(assetKey, updatedOne);
+ };
+
+ getAssetSourceRegistry(function(dict) {
+ assetDict = dict;
+ if ( !cacheDict ) { return; }
+ updateOne();
+ });
+
+ getAssetCacheRegistry(function(dict) {
+ cacheDict = dict;
+ if ( !assetDict ) { return; }
+ updateOne();
+ });
+};
+
+var updateDone = function() {
+ var assetKeys = updaterUpdated.slice(0);
+ updaterFetched.clear();
+ updaterUpdated = [];
+ updaterStatus = undefined;
+ updaterAssetDelay = updaterAssetDelayDefault;
+ fireNotification('after-assets-updated', { assetKeys: assetKeys });
+};
+
+api.updateStart = function(details) {
+ var oldUpdateDelay = updaterAssetDelay,
+ newUpdateDelay = details.delay || updaterAssetDelayDefault;
+ updaterAssetDelay = Math.min(oldUpdateDelay, newUpdateDelay);
+ if ( updaterStatus !== undefined ) {
+ if ( newUpdateDelay < oldUpdateDelay ) {
+ clearTimeout(updaterTimer);
+ updaterTimer = vAPI.setTimeout(updateNext, updaterAssetDelay);
+ }
+ return;
+ }
+ updateFirst();
+};
+
+api.updateStop = function() {
+ if ( updaterTimer ) {
+ clearTimeout(updaterTimer);
+ updaterTimer = undefined;
+ }
+ if ( updaterStatus !== undefined ) {
+ updateDone();
+ }
+};
+
+/******************************************************************************/
+
+return api;
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
diff --git a/js/background.js b/js/background.js
new file mode 100644
index 0000000..1a46616
--- /dev/null
+++ b/js/background.js
@@ -0,0 +1,246 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+/******************************************************************************/
+
+var µMatrix = (function() { // jshint ignore:line
+
+/******************************************************************************/
+
+var oneSecond = 1000;
+var oneMinute = 60 * oneSecond;
+var oneHour = 60 * oneMinute;
+var oneDay = 24 * oneHour;
+
+/******************************************************************************/
+/******************************************************************************/
+
+var _RequestStats = function() {
+ this.reset();
+};
+
+_RequestStats.prototype.reset = function() {
+ this.all =
+ this.doc =
+ this.frame =
+ this.script =
+ this.css =
+ this.image =
+ this.media =
+ this.xhr =
+ this.other =
+ this.cookie = 0;
+};
+
+/******************************************************************************/
+
+var RequestStats = function() {
+ this.allowed = new _RequestStats();
+ this.blocked = new _RequestStats();
+};
+
+RequestStats.prototype.reset = function() {
+ this.blocked.reset();
+ this.allowed.reset();
+};
+
+RequestStats.prototype.record = function(type, blocked) {
+ // Remember: always test against **false**
+ if ( blocked !== false ) {
+ this.blocked[type] += 1;
+ this.blocked.all += 1;
+ } else {
+ this.allowed[type] += 1;
+ this.allowed.all += 1;
+ }
+};
+
+var requestStatsFactory = function() {
+ return new RequestStats();
+};
+
+/*******************************************************************************
+
+ SVG-based icons below were extracted from
+ fontawesome-webfont.svg v4.7. Excerpt of copyright notice at
+ the top of the file:
+
+ > Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016
+ > By ,,,
+ > Copyright Dave Gandy 2016. All rights reserved.
+
+ Excerpt of the license information in the fontawesome CSS
+ file bundled with the package:
+
+ > Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
+ > License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+
+ Font icons:
+ - glyph-name: "external_link"
+
+*/
+
+var rawSettingsDefault = {
+ disableCSPReportInjection: false,
+ placeholderBackground:
+ [
+ 'url("data:image/png;base64,',
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAK',
+ 'CAAAAACoWZBhAAAABGdBTUEAALGPC/xh',
+ 'BQAAAAJiS0dEAP+Hj8y/AAAAB3RJTUUH',
+ '3wwIAAgyL/YaPAAAACJJREFUCFtjfMbO',
+ 'AAQ/gZiFnQPEBAEmGIMIJgtIL8QEgtoA',
+ 'In4D/96X1KAAAAAldEVYdGRhdGU6Y3Jl',
+ 'YXRlADIwMTUtMTItMDhUMDA6MDg6NTAr',
+ 'MDM6MDAasuuJAAAAJXRFWHRkYXRlOm1v',
+ 'ZGlmeQAyMDE1LTEyLTA4VDAwOjA4OjUw',
+ 'KzAzOjAwa+9TNQAAAABJRU5ErkJggg==',
+ '") ',
+ 'repeat scroll #fff'
+ ].join(''),
+ placeholderBorder: '1px solid rgba(0, 0, 0, 0.1)',
+ imagePlaceholder: true,
+ imagePlaceholderBackground: 'default',
+ imagePlaceholderBorder: 'default',
+ framePlaceholder: true,
+ framePlaceholderDocument:
+ [
+ '<html><head>',
+ '<meta charset="utf-8">',
+ '<style>',
+ 'body { ',
+ 'background: {{bg}};',
+ 'color: gray;',
+ 'font: 12px sans-serif;',
+ 'margin: 0;',
+ 'overflow: hidden;',
+ 'padding: 2px;',
+ 'white-space: nowrap;',
+ '}',
+ 'a { ',
+ 'color: inherit;',
+ 'padding: 0 3px;',
+ 'text-decoration: none;',
+ '}',
+ 'svg {',
+ 'display: inline-block;',
+ 'fill: gray;',
+ 'height: 12px;',
+ 'vertical-align: bottom;',
+ 'width: 12px;',
+ '}',
+ '</style></head><body>',
+ '<span><a href="{{url}}" title="{{url}}" target="_blank">',
+ '<svg viewBox="0 0 1792 1792"><path transform="scale(1,-1) translate(0,-1536)" d="M1408 608v-320q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v320q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1792 1472v-512q0 -26 -19 -45t-45 -19t-45 19l-176 176l-652 -652q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l652 652l-176 176q-19 19 -19 45t19 45t45 19h512q26 0 45 -19t19 -45z" /></svg>',
+ '</a>{{url}}</span>',
+ '</body></html>'
+ ].join(''),
+ framePlaceholderBackground: 'default',
+};
+
+/******************************************************************************/
+
+return {
+ onBeforeStartQueue: [],
+
+ userSettings: {
+ alwaysDetachLogger: false,
+ autoUpdate: false,
+ clearBrowserCache: true,
+ clearBrowserCacheAfter: 60,
+ cloudStorageEnabled: false,
+ collapseBlacklisted: true,
+ collapseBlocked: false,
+ colorBlindFriendly: false,
+ deleteCookies: false,
+ deleteUnusedSessionCookies: false,
+ deleteUnusedSessionCookiesAfter: 60,
+ deleteLocalStorage: false,
+ displayTextSize: '14px',
+ externalHostsFiles: '',
+ iconBadgeEnabled: false,
+ maxLoggedRequests: 1000,
+ popupCollapseAllDomains: false,
+ popupCollapseBlacklistedDomains: false,
+ popupScopeLevel: 'domain',
+ processHyperlinkAuditing: true,
+ processReferer: false
+ },
+
+ rawSettingsDefault: rawSettingsDefault,
+ rawSettings: Object.assign({}, rawSettingsDefault),
+ rawSettingsWriteTime: 0,
+
+ clearBrowserCacheCycle: 0,
+ cspNoInlineScript: "script-src 'unsafe-eval' blob: *",
+ cspNoInlineStyle: "style-src blob: *",
+ cspNoWorker: undefined,
+ updateAssetsEvery: 11 * oneDay + 1 * oneHour + 1 * oneMinute + 1 * oneSecond,
+ firstUpdateAfter: 11 * oneMinute,
+ nextUpdateAfter: 11 * oneHour,
+ assetsBootstrapLocation: 'assets/assets.json',
+ pslAssetKey: 'public_suffix_list.dat',
+
+ // list of live hosts files
+ liveHostsFiles: {
+ },
+
+ // urls stats are kept on the back burner while waiting to be reactivated
+ // in a tab or another.
+ pageStores: {},
+ pageStoresToken: 0,
+ pageStoreCemetery: {},
+
+ // page url => permission scope
+ tMatrix: null,
+ pMatrix: null,
+
+ ubiquitousBlacklist: null,
+
+ // various stats
+ requestStatsFactory: requestStatsFactory,
+ requestStats: requestStatsFactory(),
+ cookieRemovedCounter: 0,
+ localStorageRemovedCounter: 0,
+ cookieHeaderFoiledCounter: 0,
+ refererHeaderFoiledCounter: 0,
+ hyperlinkAuditingFoiledCounter: 0,
+ browserCacheClearedCounter: 0,
+ storageUsed: 0,
+
+ // record what the browser is doing behind the scene
+ behindTheSceneScope: 'behind-the-scene',
+
+ noopFunc: function(){},
+
+ // so that I don't have to care for last comma
+ dummy: 0
+};
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
+
diff --git a/js/browsercache.js b/js/browsercache.js
new file mode 100644
index 0000000..3316a63
--- /dev/null
+++ b/js/browsercache.js
@@ -0,0 +1,65 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2015-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global µMatrix */
+
+/******************************************************************************/
+
+(function() {
+
+'use strict';
+
+/******************************************************************************/
+
+// Browser data jobs
+
+var clearCache = function() {
+ vAPI.setTimeout(clearCache, 15 * 60 * 1000);
+
+ var µm = µMatrix;
+ if ( !µm.userSettings.clearBrowserCache ) {
+ return;
+ }
+
+ µm.clearBrowserCacheCycle -= 15;
+ if ( µm.clearBrowserCacheCycle > 0 ) {
+ return;
+ }
+
+ vAPI.browserData.clearCache();
+
+ µm.clearBrowserCacheCycle = µm.userSettings.clearBrowserCacheAfter;
+ µm.browserCacheClearedCounter++;
+
+ // TODO: i18n
+ µm.logger.writeOne('', 'info', vAPI.i18n('loggerEntryBrowserCacheCleared'));
+
+ //console.debug('clearBrowserCacheCallback()> vAPI.browserData.clearCache() called');
+};
+
+vAPI.setTimeout(clearCache, 15 * 60 * 1000);
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
diff --git a/js/cloud-ui.js b/js/cloud-ui.js
new file mode 100644
index 0000000..a017ae9
--- /dev/null
+++ b/js/cloud-ui.js
@@ -0,0 +1,214 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2015-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uBlock
+*/
+
+/* global uDom */
+
+'use strict';
+
+/******************************************************************************/
+
+(function() {
+
+/******************************************************************************/
+
+self.cloud = {
+ options: {},
+ datakey: '',
+ data: undefined,
+ onPush: null,
+ onPull: null
+};
+
+/******************************************************************************/
+
+var widget = uDom.nodeFromId('cloudWidget');
+if ( widget === null ) {
+ return;
+}
+
+self.cloud.datakey = widget.getAttribute('data-cloud-entry') || '';
+if ( self.cloud.datakey === '' ) {
+ return;
+}
+
+/******************************************************************************/
+
+var onCloudDataReceived = function(entry) {
+ if ( typeof entry !== 'object' || entry === null ) {
+ return;
+ }
+
+ self.cloud.data = entry.data;
+
+ uDom.nodeFromId('cloudPull').removeAttribute('disabled');
+ uDom.nodeFromId('cloudPullAndMerge').removeAttribute('disabled');
+
+ var timeOptions = {
+ weekday: 'short',
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ second: 'numeric',
+ timeZoneName: 'short'
+ };
+
+ var time = new Date(entry.tstamp);
+ widget.querySelector('span').textContent =
+ entry.source + '\n' +
+ time.toLocaleString('fullwide', timeOptions);
+};
+
+/******************************************************************************/
+
+var fetchCloudData = function() {
+ vAPI.messaging.send(
+ 'cloud-ui.js',
+ {
+ what: 'cloudPull',
+ datakey: self.cloud.datakey
+ },
+ onCloudDataReceived
+ );
+};
+
+/******************************************************************************/
+
+var pushData = function() {
+ if ( typeof self.cloud.onPush !== 'function' ) {
+ return;
+ }
+ vAPI.messaging.send(
+ 'cloud-ui.js',
+ {
+ what: 'cloudPush',
+ datakey: self.cloud.datakey,
+ data: self.cloud.onPush()
+ },
+ fetchCloudData
+ );
+};
+
+/******************************************************************************/
+
+var pullData = function(ev) {
+ if ( typeof self.cloud.onPull === 'function' ) {
+ self.cloud.onPull(self.cloud.data, ev.shiftKey);
+ }
+};
+
+/******************************************************************************/
+
+var pullAndMergeData = function() {
+ if ( typeof self.cloud.onPull === 'function' ) {
+ self.cloud.onPull(self.cloud.data, true);
+ }
+};
+
+/******************************************************************************/
+
+var openOptions = function() {
+ var input = uDom.nodeFromId('cloudDeviceName');
+ input.value = self.cloud.options.deviceName;
+ input.setAttribute('placeholder', self.cloud.options.defaultDeviceName);
+ uDom.nodeFromId('cloudOptions').classList.add('show');
+};
+
+/******************************************************************************/
+
+var closeOptions = function(ev) {
+ var root = uDom.nodeFromId('cloudOptions');
+ if ( ev.target !== root ) {
+ return;
+ }
+ root.classList.remove('show');
+};
+
+/******************************************************************************/
+
+var submitOptions = function() {
+ var onOptions = function(options) {
+ if ( typeof options !== 'object' || options === null ) {
+ return;
+ }
+ self.cloud.options = options;
+ };
+
+ vAPI.messaging.send('cloud-ui.js', {
+ what: 'cloudSetOptions',
+ options: {
+ deviceName: uDom.nodeFromId('cloudDeviceName').value
+ }
+ }, onOptions);
+ uDom.nodeFromId('cloudOptions').classList.remove('show');
+};
+
+/******************************************************************************/
+
+var onInitialize = function(options) {
+ if ( typeof options !== 'object' || options === null ) {
+ return;
+ }
+
+ if ( !options.enabled ) {
+ return;
+ }
+ self.cloud.options = options;
+
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'cloud-ui.html', true);
+ xhr.overrideMimeType('text/html;charset=utf-8');
+ xhr.responseType = 'text';
+ xhr.onload = function() {
+ this.onload = null;
+ var parser = new DOMParser(),
+ parsed = parser.parseFromString(this.responseText, 'text/html'),
+ fromParent = parsed.body;
+ while ( fromParent.firstElementChild !== null ) {
+ widget.appendChild(
+ document.adoptNode(fromParent.firstElementChild)
+ );
+ }
+
+ vAPI.i18n.render(widget);
+ widget.classList.remove('hide');
+
+ uDom('#cloudPush').on('click', pushData);
+ uDom('#cloudPull').on('click', pullData);
+ uDom('#cloudPullAndMerge').on('click', pullAndMergeData);
+ uDom('#cloudCog').on('click', openOptions);
+ uDom('#cloudOptions').on('click', closeOptions);
+ uDom('#cloudOptionsSubmit').on('click', submitOptions);
+
+ fetchCloudData();
+ };
+ xhr.send();
+};
+
+vAPI.messaging.send('cloud-ui.js', { what: 'cloudGetOptions' }, onInitialize);
+
+/******************************************************************************/
+
+// https://www.youtube.com/watch?v=aQFp67VoiDA
+
+})();
diff --git a/js/contentscript-start.js b/js/contentscript-start.js
new file mode 100644
index 0000000..c449c55
--- /dev/null
+++ b/js/contentscript-start.js
@@ -0,0 +1,97 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2017-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+/******************************************************************************/
+/******************************************************************************/
+
+// Injected into content pages
+
+(function() {
+
+ if ( typeof vAPI !== 'object' ) { return; }
+
+ vAPI.selfWorkerSrcReported = vAPI.selfWorkerSrcReported || false;
+
+ var reGoodWorkerSrc = /(?:child|worker)-src[^;,]+?'none'/;
+
+ var handler = function(ev) {
+ if (
+ ev.isTrusted !== true ||
+ ev.originalPolicy.includes('report-uri about:blank') === false
+ ) {
+ return false;
+ }
+
+ // Firefox and Chromium differs in how they fill the
+ // 'effectiveDirective' property.
+ if (
+ ev.effectiveDirective.startsWith('worker-src') === false &&
+ ev.effectiveDirective.startsWith('child-src') === false
+ ) {
+ return false;
+ }
+
+ // Further validate that the policy violation is relevant to uMatrix:
+ // the event still could have been fired as a result of a CSP header
+ // not injected by uMatrix.
+ if ( reGoodWorkerSrc.test(ev.originalPolicy) === false ) {
+ return false;
+ }
+
+ // We do not want to report internal resources more than once.
+ // However, we do want to report external resources each time.
+ // TODO: this could eventually lead to duplicated reports for external
+ // resources if another extension uses the same approach as
+ // uMatrix. Think about what could be done to avoid duplicate
+ // reports.
+ if ( ev.blockedURI.includes('://') === false ) {
+ if ( vAPI.selfWorkerSrcReported ) { return true; }
+ vAPI.selfWorkerSrcReported = true;
+ }
+
+ vAPI.messaging.send(
+ 'contentscript.js',
+ {
+ what: 'securityPolicyViolation',
+ directive: 'worker-src',
+ blockedURI: ev.blockedURI,
+ documentURI: ev.documentURI,
+ blocked: ev.disposition === 'enforce'
+ }
+ );
+
+ return true;
+ };
+
+ document.addEventListener(
+ 'securitypolicyviolation',
+ function(ev) {
+ if ( !handler(ev) ) { return; }
+ ev.stopPropagation();
+ ev.preventDefault();
+ },
+ true
+ );
+
+})();
diff --git a/js/contentscript.js b/js/contentscript.js
new file mode 100644
index 0000000..dcdd473
--- /dev/null
+++ b/js/contentscript.js
@@ -0,0 +1,541 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global HTMLDocument, XMLDocument */
+
+'use strict';
+
+/******************************************************************************/
+/******************************************************************************/
+
+// Injected into content pages
+
+(function() {
+
+/******************************************************************************/
+
+// https://github.com/chrisaljoudi/uBlock/issues/464
+// https://github.com/gorhill/uMatrix/issues/621
+if (
+ document instanceof HTMLDocument === false &&
+ document instanceof XMLDocument === false
+) {
+ return;
+}
+
+// This can also happen (for example if script injected into a `data:` URI doc)
+if ( !window.location ) {
+ return;
+}
+
+// This can happen
+if ( typeof vAPI !== 'object' ) {
+ //console.debug('contentscript.js > vAPI not found');
+ return;
+}
+
+// https://github.com/chrisaljoudi/uBlock/issues/456
+// Already injected?
+if ( vAPI.contentscriptEndInjected ) {
+ //console.debug('contentscript.js > content script already injected');
+ return;
+}
+vAPI.contentscriptEndInjected = true;
+
+/******************************************************************************/
+/******************************************************************************/
+
+// Executed only once.
+
+(function() {
+ var localStorageHandler = function(mustRemove) {
+ if ( mustRemove ) {
+ window.localStorage.clear();
+ window.sessionStorage.clear();
+ }
+ };
+
+ // Check with extension whether local storage must be emptied
+ // rhill 2014-03-28: we need an exception handler in case 3rd-party access
+ // to site data is disabled.
+ // https://github.com/gorhill/httpswitchboard/issues/215
+ try {
+ var hasLocalStorage =
+ window.localStorage && window.localStorage.length !== 0;
+ var hasSessionStorage =
+ window.sessionStorage && window.sessionStorage.length !== 0;
+ if ( hasLocalStorage || hasSessionStorage ) {
+ vAPI.messaging.send('contentscript.js', {
+ what: 'contentScriptHasLocalStorage',
+ originURL: window.location.origin
+ }, localStorageHandler);
+ }
+
+ // TODO: indexedDB
+ //if ( window.indexedDB && !!window.indexedDB.webkitGetDatabaseNames ) {
+ // var db = window.indexedDB.webkitGetDatabaseNames().onsuccess = function(sender) {
+ // console.debug('webkitGetDatabaseNames(): result=%o', sender.target.result);
+ // };
+ //}
+
+ // TODO: Web SQL
+ // if ( window.openDatabase ) {
+ // Sad:
+ // "There is no way to enumerate or delete the databases available for an origin from this API."
+ // Ref.: http://www.w3.org/TR/webdatabase/#databases
+ // }
+ }
+ catch (e) {
+ }
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+// https://github.com/gorhill/uMatrix/issues/45
+
+var collapser = (function() {
+ var resquestIdGenerator = 1,
+ processTimer,
+ toProcess = [],
+ toFilter = [],
+ toCollapse = new Map(),
+ cachedBlockedMap,
+ cachedBlockedMapHash,
+ cachedBlockedMapTimer,
+ reURLPlaceholder = /\{\{url\}\}/g;
+ var src1stProps = {
+ 'embed': 'src',
+ 'iframe': 'src',
+ 'img': 'src',
+ 'object': 'data'
+ };
+ var src2ndProps = {
+ 'img': 'srcset'
+ };
+ var tagToTypeMap = {
+ embed: 'media',
+ iframe: 'frame',
+ img: 'image',
+ object: 'media'
+ };
+ var cachedBlockedSetClear = function() {
+ cachedBlockedMap =
+ cachedBlockedMapHash =
+ cachedBlockedMapTimer = undefined;
+ };
+
+ // https://github.com/chrisaljoudi/uBlock/issues/174
+ // Do not remove fragment from src URL
+ var onProcessed = function(response) {
+ if ( !response ) { // This happens if uBO is disabled or restarted.
+ toCollapse.clear();
+ return;
+ }
+
+ var targets = toCollapse.get(response.id);
+ if ( targets === undefined ) { return; }
+ toCollapse.delete(response.id);
+ if ( cachedBlockedMapHash !== response.hash ) {
+ cachedBlockedMap = new Map(response.blockedResources);
+ cachedBlockedMapHash = response.hash;
+ if ( cachedBlockedMapTimer !== undefined ) {
+ clearTimeout(cachedBlockedMapTimer);
+ }
+ cachedBlockedMapTimer = vAPI.setTimeout(cachedBlockedSetClear, 30000);
+ }
+ if ( cachedBlockedMap === undefined || cachedBlockedMap.size === 0 ) {
+ return;
+ }
+
+ var placeholders = response.placeholders,
+ tag, prop, src, collapsed, docurl, replaced;
+
+ for ( var target of targets ) {
+ tag = target.localName;
+ prop = src1stProps[tag];
+ if ( prop === undefined ) { continue; }
+ 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; }
+ }
+ collapsed = cachedBlockedMap.get(tagToTypeMap[tag] + ' ' + src);
+ if ( collapsed === undefined ) { continue; }
+ if ( collapsed ) {
+ target.style.setProperty('display', 'none', 'important');
+ target.hidden = true;
+ continue;
+ }
+ switch ( tag ) {
+ case 'iframe':
+ if ( placeholders.frame !== true ) { break; }
+ docurl =
+ 'data:text/html,' +
+ encodeURIComponent(
+ placeholders.frameDocument.replace(
+ reURLPlaceholder,
+ src
+ )
+ );
+ replaced = false;
+ // Using contentWindow.location prevent tainting browser
+ // history -- i.e. breaking back button (seen on Chromium).
+ if ( target.contentWindow ) {
+ try {
+ target.contentWindow.location.replace(docurl);
+ replaced = true;
+ } catch(ex) {
+ }
+ }
+ if ( !replaced ) {
+ target.setAttribute('src', docurl);
+ }
+ break;
+ case 'img':
+ if ( placeholders.image !== true ) { break; }
+ target.style.setProperty('display', 'inline-block');
+ target.style.setProperty('min-width', '20px', 'important');
+ target.style.setProperty('min-height', '20px', 'important');
+ target.style.setProperty(
+ 'border',
+ placeholders.imageBorder,
+ 'important'
+ );
+ target.style.setProperty(
+ 'background',
+ placeholders.imageBackground,
+ 'important'
+ );
+ break;
+ }
+ }
+ };
+
+ var send = function() {
+ processTimer = undefined;
+ toCollapse.set(resquestIdGenerator, toProcess);
+ var msg = {
+ what: 'lookupBlockedCollapsibles',
+ id: resquestIdGenerator,
+ toFilter: toFilter,
+ hash: cachedBlockedMapHash
+ };
+ vAPI.messaging.send('contentscript.js', msg, onProcessed);
+ toProcess = [];
+ toFilter = [];
+ resquestIdGenerator += 1;
+ };
+
+ var 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 || 47);
+ }
+ };
+
+ var add = function(target) {
+ toProcess.push(target);
+ };
+
+ var addMany = function(targets) {
+ var i = targets.length;
+ while ( i-- ) {
+ toProcess.push(targets[i]);
+ }
+ };
+
+ var iframeSourceModified = function(mutations) {
+ var i = mutations.length;
+ while ( i-- ) {
+ addIFrame(mutations[i].target, true);
+ }
+ process();
+ };
+ var iframeSourceObserver;
+ var iframeSourceObserverOptions = {
+ attributes: true,
+ attributeFilter: [ 'src' ]
+ };
+
+ var addIFrame = function(iframe, dontObserve) {
+ // https://github.com/gorhill/uBlock/issues/162
+ // Be prepared to deal with possible change of src attribute.
+ if ( dontObserve !== true ) {
+ if ( iframeSourceObserver === undefined ) {
+ iframeSourceObserver = new MutationObserver(iframeSourceModified);
+ }
+ iframeSourceObserver.observe(iframe, iframeSourceObserverOptions);
+ }
+ var src = iframe.src;
+ if ( src === '' || typeof src !== 'string' ) { return; }
+ if ( src.startsWith('http') === false ) { return; }
+ toFilter.push({ type: 'frame', url: iframe.src });
+ add(iframe);
+ };
+
+ var addIFrames = function(iframes) {
+ var i = iframes.length;
+ while ( i-- ) {
+ addIFrame(iframes[i]);
+ }
+ };
+
+ var addNodeList = function(nodeList) {
+ var node,
+ i = nodeList.length;
+ while ( i-- ) {
+ node = nodeList[i];
+ if ( node.nodeType !== 1 ) { continue; }
+ if ( node.localName === 'iframe' ) {
+ addIFrame(node);
+ }
+ if ( node.childElementCount !== 0 ) {
+ addIFrames(node.querySelectorAll('iframe'));
+ }
+ }
+ };
+
+ var onResourceFailed = function(ev) {
+ if ( tagToTypeMap[ev.target.localName] !== undefined ) {
+ add(ev.target);
+ process();
+ }
+ };
+ document.addEventListener('error', onResourceFailed, true);
+
+ vAPI.shutdown.add(function() {
+ document.removeEventListener('error', onResourceFailed, true);
+ if ( iframeSourceObserver !== undefined ) {
+ iframeSourceObserver.disconnect();
+ iframeSourceObserver = undefined;
+ }
+ if ( processTimer !== undefined ) {
+ clearTimeout(processTimer);
+ processTimer = undefined;
+ }
+ });
+
+ return {
+ addMany: addMany,
+ addIFrames: addIFrames,
+ addNodeList: addNodeList,
+ process: process
+ };
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+// Observe changes in the DOM
+
+// Added node lists will be cumulated here before being processed
+
+(function() {
+ // This fixes http://acid3.acidtests.org/
+ if ( !document.body ) { return; }
+
+ var addedNodeLists = [];
+ var addedNodeListsTimer;
+
+ var treeMutationObservedHandler = function() {
+ addedNodeListsTimer = undefined;
+ var i = addedNodeLists.length;
+ while ( i-- ) {
+ collapser.addNodeList(addedNodeLists[i]);
+ }
+ collapser.process();
+ addedNodeLists = [];
+ };
+
+ // https://github.com/gorhill/uBlock/issues/205
+ // Do not handle added node directly from within mutation observer.
+ var treeMutationObservedHandlerAsync = function(mutations) {
+ var iMutation = mutations.length,
+ nodeList;
+ while ( iMutation-- ) {
+ nodeList = mutations[iMutation].addedNodes;
+ if ( nodeList.length !== 0 ) {
+ addedNodeLists.push(nodeList);
+ }
+ }
+ if ( addedNodeListsTimer === undefined ) {
+ addedNodeListsTimer = vAPI.setTimeout(treeMutationObservedHandler, 47);
+ }
+ };
+
+ // https://github.com/gorhill/httpswitchboard/issues/176
+ var treeObserver = new MutationObserver(treeMutationObservedHandlerAsync);
+ treeObserver.observe(document.body, {
+ childList: true,
+ subtree: true
+ });
+
+ vAPI.shutdown.add(function() {
+ if ( addedNodeListsTimer !== undefined ) {
+ clearTimeout(addedNodeListsTimer);
+ addedNodeListsTimer = undefined;
+ }
+ if ( treeObserver !== null ) {
+ treeObserver.disconnect();
+ treeObserver = undefined;
+ }
+ addedNodeLists = [];
+ });
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+// Executed only once.
+//
+// https://github.com/gorhill/httpswitchboard/issues/25
+//
+// https://github.com/gorhill/httpswitchboard/issues/131
+// Looks for inline javascript also in at least one a[href] element.
+//
+// https://github.com/gorhill/uMatrix/issues/485
+// Mind "on..." attributes.
+//
+// https://github.com/gorhill/uMatrix/issues/924
+// Report inline styles.
+
+(function() {
+ if (
+ document.querySelector('script:not([src])') !== null ||
+ document.querySelector('a[href^="javascript:"]') !== null ||
+ document.querySelector('[onabort],[onblur],[oncancel],[oncanplay],[oncanplaythrough],[onchange],[onclick],[onclose],[oncontextmenu],[oncuechange],[ondblclick],[ondrag],[ondragend],[ondragenter],[ondragexit],[ondragleave],[ondragover],[ondragstart],[ondrop],[ondurationchange],[onemptied],[onended],[onerror],[onfocus],[oninput],[oninvalid],[onkeydown],[onkeypress],[onkeyup],[onload],[onloadeddata],[onloadedmetadata],[onloadstart],[onmousedown],[onmouseenter],[onmouseleave],[onmousemove],[onmouseout],[onmouseover],[onmouseup],[onwheel],[onpause],[onplay],[onplaying],[onprogress],[onratechange],[onreset],[onresize],[onscroll],[onseeked],[onseeking],[onselect],[onshow],[onstalled],[onsubmit],[onsuspend],[ontimeupdate],[ontoggle],[onvolumechange],[onwaiting],[onafterprint],[onbeforeprint],[onbeforeunload],[onhashchange],[onlanguagechange],[onmessage],[onoffline],[ononline],[onpagehide],[onpageshow],[onrejectionhandled],[onpopstate],[onstorage],[onunhandledrejection],[onunload],[oncopy],[oncut],[onpaste]') !== null
+ ) {
+ vAPI.messaging.send('contentscript.js', {
+ what: 'securityPolicyViolation',
+ directive: 'script-src',
+ documentURI: window.location.href
+ });
+ }
+
+ if ( document.querySelector('style,[style]') !== null ) {
+ vAPI.messaging.send('contentscript.js', {
+ what: 'securityPolicyViolation',
+ directive: 'style-src',
+ documentURI: window.location.href
+ });
+ }
+
+ collapser.addMany(document.querySelectorAll('img'));
+ collapser.addIFrames(document.querySelectorAll('iframe'));
+ collapser.process();
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+// Executed only once.
+
+// https://github.com/gorhill/uMatrix/issues/232
+// Force `display` property, Firefox is still affected by the issue.
+
+(function() {
+ var noscripts = document.querySelectorAll('noscript');
+ if ( noscripts.length === 0 ) { return; }
+
+ var redirectTimer,
+ reMetaContent = /^\s*(\d+)\s*;\s*url=(['"]?)([^'"]+)\2/i,
+ reSafeURL = /^https?:\/\//;
+
+ var autoRefresh = function(root) {
+ var meta = root.querySelector('meta[http-equiv="refresh"][content]');
+ if ( meta === null ) { return; }
+ var match = reMetaContent.exec(meta.getAttribute('content'));
+ if ( match === null || match[3].trim() === '' ) { return; }
+ var url = new URL(match[3], document.baseURI);
+ if ( reSafeURL.test(url.href) === false ) { return; }
+ redirectTimer = setTimeout(
+ function() {
+ location.assign(url.href);
+ },
+ parseInt(match[1], 10) * 1000 + 1
+ );
+ meta.parentNode.removeChild(meta);
+ };
+
+ var morphNoscript = function(from) {
+ if ( /^application\/(?:xhtml\+)?xml/.test(document.contentType) ) {
+ var to = document.createElement('span');
+ while ( from.firstChild !== null ) {
+ to.appendChild(from.firstChild);
+ }
+ return to;
+ }
+ var parser = new DOMParser();
+ var doc = parser.parseFromString(
+ '<span>' + from.textContent + '</span>',
+ 'text/html'
+ );
+ return document.adoptNode(doc.querySelector('span'));
+ };
+
+ var renderNoscriptTags = function(response) {
+ if ( response !== true ) { return; }
+ var parent, span;
+ for ( var noscript of noscripts ) {
+ parent = noscript.parentNode;
+ if ( parent === null ) { continue; }
+ span = morphNoscript(noscript);
+ span.style.setProperty('display', 'inline', 'important');
+ if ( redirectTimer === undefined ) {
+ autoRefresh(span);
+ }
+ parent.replaceChild(span, noscript);
+ }
+ };
+
+ vAPI.messaging.send(
+ 'contentscript.js',
+ { what: 'mustRenderNoscriptTags?' },
+ renderNoscriptTags
+ );
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+vAPI.messaging.send(
+ 'contentscript.js',
+ { what: 'shutdown?' },
+ function(response) {
+ if ( response === true ) {
+ vAPI.shutdown.exec();
+ }
+ }
+);
+
+/******************************************************************************/
+/******************************************************************************/
+
+})();
diff --git a/js/cookies.js b/js/cookies.js
new file mode 100644
index 0000000..7626ad0
--- /dev/null
+++ b/js/cookies.js
@@ -0,0 +1,552 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2013-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+// rhill 2013-12-14: the whole cookie management has been rewritten so as
+// to avoid having to call chrome API whenever a single cookie changes, and
+// to record cookie for a web page *only* when its value changes.
+// https://github.com/gorhill/httpswitchboard/issues/79
+
+"use strict";
+
+/******************************************************************************/
+
+// Isolate from global namespace
+
+// Use cached-context approach rather than object-based approach, as details
+// of the implementation do not need to be visible
+
+µMatrix.cookieHunter = (function() {
+
+/******************************************************************************/
+
+var µm = µMatrix;
+
+var recordPageCookiesQueue = new Map();
+var removePageCookiesQueue = new Map();
+var removeCookieQueue = new Set();
+var cookieDict = new Map();
+var cookieEntryJunkyard = [];
+var processRemoveQueuePeriod = 2 * 60 * 1000;
+var processCleanPeriod = 10 * 60 * 1000;
+var processPageRecordQueueTimer = null;
+var processPageRemoveQueueTimer = null;
+
+/******************************************************************************/
+
+var CookieEntry = function(cookie) {
+ this.usedOn = new Set();
+ this.init(cookie);
+};
+
+CookieEntry.prototype.init = function(cookie) {
+ this.secure = cookie.secure;
+ this.session = cookie.session;
+ this.anySubdomain = cookie.domain.charAt(0) === '.';
+ this.hostname = this.anySubdomain ? cookie.domain.slice(1) : cookie.domain;
+ this.domain = µm.URI.domainFromHostname(this.hostname) || this.hostname;
+ this.path = cookie.path;
+ this.name = cookie.name;
+ this.value = cookie.value;
+ this.tstamp = Date.now();
+ this.usedOn.clear();
+ return this;
+};
+
+// Release anything which may consume too much memory
+
+CookieEntry.prototype.dispose = function() {
+ this.hostname = '';
+ this.domain = '';
+ this.path = '';
+ this.name = '';
+ this.value = '';
+ this.usedOn.clear();
+ return this;
+};
+
+/******************************************************************************/
+
+var addCookieToDict = function(cookie) {
+ var cookieKey = cookieKeyFromCookie(cookie),
+ cookieEntry = cookieDict.get(cookieKey);
+ if ( cookieEntry === undefined ) {
+ cookieEntry = cookieEntryJunkyard.pop();
+ if ( cookieEntry ) {
+ cookieEntry.init(cookie);
+ } else {
+ cookieEntry = new CookieEntry(cookie);
+ }
+ cookieDict.set(cookieKey, cookieEntry);
+ }
+ return cookieEntry;
+};
+
+/******************************************************************************/
+
+var addCookiesToDict = function(cookies) {
+ var i = cookies.length;
+ while ( i-- ) {
+ addCookieToDict(cookies[i]);
+ }
+};
+
+/******************************************************************************/
+
+var removeCookieFromDict = function(cookieKey) {
+ var cookieEntry = cookieDict.get(cookieKey);
+ if ( cookieEntry === undefined ) { return false; }
+ cookieDict.delete(cookieKey);
+ if ( cookieEntryJunkyard.length < 25 ) {
+ cookieEntryJunkyard.push(cookieEntry.dispose());
+ }
+ return true;
+};
+
+/******************************************************************************/
+
+var cookieKeyBuilder = [
+ '', // 0 = scheme
+ '://',
+ '', // 2 = domain
+ '', // 3 = path
+ '{',
+ '', // 5 = persistent or session
+ '-cookie:',
+ '', // 7 = name
+ '}'
+];
+
+var cookieKeyFromCookie = function(cookie) {
+ var cb = cookieKeyBuilder;
+ cb[0] = cookie.secure ? 'https' : 'http';
+ cb[2] = cookie.domain.charAt(0) === '.' ? cookie.domain.slice(1) : cookie.domain;
+ cb[3] = cookie.path;
+ cb[5] = cookie.session ? 'session' : 'persistent';
+ cb[7] = cookie.name;
+ return cb.join('');
+};
+
+var cookieKeyFromCookieURL = function(url, type, name) {
+ var µmuri = µm.URI.set(url);
+ var cb = cookieKeyBuilder;
+ cb[0] = µmuri.scheme;
+ cb[2] = µmuri.hostname;
+ cb[3] = µmuri.path;
+ cb[5] = type;
+ cb[7] = name;
+ return cb.join('');
+};
+
+/******************************************************************************/
+
+var cookieURLFromCookieEntry = function(entry) {
+ if ( !entry ) {
+ return '';
+ }
+ return (entry.secure ? 'https://' : 'http://') + entry.hostname + entry.path;
+};
+
+/******************************************************************************/
+
+var cookieMatchDomains = function(cookieKey, allHostnamesString) {
+ var cookieEntry = cookieDict.get(cookieKey);
+ if ( cookieEntry === undefined ) { return false; }
+ if ( allHostnamesString.indexOf(' ' + cookieEntry.hostname + ' ') < 0 ) {
+ if ( !cookieEntry.anySubdomain ) {
+ return false;
+ }
+ if ( allHostnamesString.indexOf('.' + cookieEntry.hostname + ' ') < 0 ) {
+ return false;
+ }
+ }
+ return true;
+};
+
+/******************************************************************************/
+
+// Look for cookies to record for a specific web page
+
+var recordPageCookiesAsync = function(pageStats) {
+ // Store the page stats objects so that it doesn't go away
+ // before we handle the job.
+ // rhill 2013-10-19: pageStats could be nil, for example, this can
+ // happens if a file:// ... makes an xmlHttpRequest
+ if ( !pageStats ) {
+ return;
+ }
+ recordPageCookiesQueue.set(pageStats.pageUrl, pageStats);
+ if ( processPageRecordQueueTimer === null ) {
+ processPageRecordQueueTimer = vAPI.setTimeout(processPageRecordQueue, 1000);
+ }
+};
+
+/******************************************************************************/
+
+var cookieLogEntryBuilder = [
+ '',
+ '{',
+ '',
+ '-cookie:',
+ '',
+ '}'
+];
+
+var recordPageCookie = function(pageStore, cookieKey) {
+ if ( vAPI.isBehindTheSceneTabId(pageStore.tabId) ) { return; }
+
+ var cookieEntry = cookieDict.get(cookieKey);
+ var pageHostname = pageStore.pageHostname;
+ var block = µm.mustBlock(pageHostname, cookieEntry.hostname, 'cookie');
+
+ cookieLogEntryBuilder[0] = cookieURLFromCookieEntry(cookieEntry);
+ cookieLogEntryBuilder[2] = cookieEntry.session ? 'session' : 'persistent';
+ cookieLogEntryBuilder[4] = encodeURIComponent(cookieEntry.name);
+
+ var cookieURL = cookieLogEntryBuilder.join('');
+
+ // rhill 2013-11-20:
+ // https://github.com/gorhill/httpswitchboard/issues/60
+ // Need to URL-encode cookie name
+ pageStore.recordRequest('cookie', cookieURL, block);
+ µm.logger.writeOne(pageStore.tabId, 'net', pageHostname, cookieURL, 'cookie', block);
+
+ cookieEntry.usedOn.add(pageHostname);
+
+ // rhill 2013-11-21:
+ // https://github.com/gorhill/httpswitchboard/issues/65
+ // Leave alone cookies from behind-the-scene requests if
+ // behind-the-scene processing is disabled.
+ if ( !block ) {
+ return;
+ }
+ if ( !µm.userSettings.deleteCookies ) {
+ return;
+ }
+ removeCookieAsync(cookieKey);
+};
+
+/******************************************************************************/
+
+// Look for cookies to potentially remove for a specific web page
+
+var removePageCookiesAsync = function(pageStats) {
+ // Hold onto pageStats objects so that it doesn't go away
+ // before we handle the job.
+ // rhill 2013-10-19: pageStats could be nil, for example, this can
+ // happens if a file:// ... makes an xmlHttpRequest
+ if ( !pageStats ) {
+ return;
+ }
+ removePageCookiesQueue.set(pageStats.pageUrl, pageStats);
+ if ( processPageRemoveQueueTimer === null ) {
+ processPageRemoveQueueTimer = vAPI.setTimeout(processPageRemoveQueue, 15 * 1000);
+ }
+};
+
+/******************************************************************************/
+
+// Candidate for removal
+
+var removeCookieAsync = function(cookieKey) {
+ removeCookieQueue.add(cookieKey);
+};
+
+/******************************************************************************/
+
+var chromeCookieRemove = function(cookieEntry, name) {
+ var url = cookieURLFromCookieEntry(cookieEntry);
+ if ( url === '' ) {
+ return;
+ }
+ var sessionCookieKey = cookieKeyFromCookieURL(url, 'session', name);
+ var persistCookieKey = cookieKeyFromCookieURL(url, 'persistent', name);
+ var callback = function(details) {
+ var success = !!details;
+ var template = success ? i18nCookieDeleteSuccess : i18nCookieDeleteFailure;
+ if ( removeCookieFromDict(sessionCookieKey) ) {
+ if ( success ) {
+ µm.cookieRemovedCounter += 1;
+ }
+ µm.logger.writeOne('', 'info', 'cookie', template.replace('{{value}}', sessionCookieKey));
+ }
+ if ( removeCookieFromDict(persistCookieKey) ) {
+ if ( success ) {
+ µm.cookieRemovedCounter += 1;
+ }
+ µm.logger.writeOne('', 'info', 'cookie', template.replace('{{value}}', persistCookieKey));
+ }
+ };
+
+ vAPI.cookies.remove({ url: url, name: name }, callback);
+};
+
+var i18nCookieDeleteSuccess = vAPI.i18n('loggerEntryCookieDeleted');
+var i18nCookieDeleteFailure = vAPI.i18n('loggerEntryDeleteCookieError');
+
+/******************************************************************************/
+
+var processPageRecordQueue = function() {
+ processPageRecordQueueTimer = null;
+
+ for ( var pageStore of recordPageCookiesQueue.values() ) {
+ findAndRecordPageCookies(pageStore);
+ }
+ recordPageCookiesQueue.clear();
+};
+
+/******************************************************************************/
+
+var processPageRemoveQueue = function() {
+ processPageRemoveQueueTimer = null;
+
+ for ( var pageStore of removePageCookiesQueue.values() ) {
+ findAndRemovePageCookies(pageStore);
+ }
+ removePageCookiesQueue.clear();
+};
+
+/******************************************************************************/
+
+// Effectively remove cookies.
+
+var processRemoveQueue = function() {
+ var userSettings = µm.userSettings;
+ var deleteCookies = userSettings.deleteCookies;
+
+ // Session cookies which timestamp is *after* tstampObsolete will
+ // be left untouched
+ // https://github.com/gorhill/httpswitchboard/issues/257
+ var tstampObsolete = userSettings.deleteUnusedSessionCookies ?
+ Date.now() - userSettings.deleteUnusedSessionCookiesAfter * 60 * 1000 :
+ 0;
+
+ var srcHostnames;
+ var cookieEntry;
+
+ for ( var cookieKey of removeCookieQueue ) {
+ // rhill 2014-05-12: Apparently this can happen. I have to
+ // investigate how (A session cookie has same name as a
+ // persistent cookie?)
+ cookieEntry = cookieDict.get(cookieKey);
+ if ( cookieEntry === undefined ) { continue; }
+
+ // Delete obsolete session cookies: enabled.
+ if ( tstampObsolete !== 0 && cookieEntry.session ) {
+ if ( cookieEntry.tstamp < tstampObsolete ) {
+ chromeCookieRemove(cookieEntry, cookieEntry.name);
+ continue;
+ }
+ }
+
+ // Delete all blocked cookies: disabled.
+ if ( deleteCookies === false ) {
+ continue;
+ }
+
+ // Query scopes only if we are going to use them
+ if ( srcHostnames === undefined ) {
+ srcHostnames = µm.tMatrix.extractAllSourceHostnames();
+ }
+
+ // Ensure cookie is not allowed on ALL current web pages: It can
+ // happen that a cookie is blacklisted on one web page while
+ // being whitelisted on another (because of per-page permissions).
+ if ( canRemoveCookie(cookieKey, srcHostnames) ) {
+ chromeCookieRemove(cookieEntry, cookieEntry.name);
+ }
+ }
+
+ removeCookieQueue.clear();
+
+ vAPI.setTimeout(processRemoveQueue, processRemoveQueuePeriod);
+};
+
+/******************************************************************************/
+
+// Once in a while, we go ahead and clean everything that might have been
+// left behind.
+
+// Remove only some of the cookies which are candidate for removal: who knows,
+// maybe a user has 1000s of cookies sitting in his browser...
+
+var processClean = function() {
+ var us = µm.userSettings;
+ if ( us.deleteCookies || us.deleteUnusedSessionCookies ) {
+ var cookieKeys = Array.from(cookieDict.keys()),
+ len = cookieKeys.length,
+ step, offset, n;
+ if ( len > 25 ) {
+ step = len / 25;
+ offset = Math.floor(Math.random() * len);
+ n = 25;
+ } else {
+ step = 1;
+ offset = 0;
+ n = len;
+ }
+ var i = offset;
+ while ( n-- ) {
+ removeCookieAsync(cookieKeys[Math.floor(i % len)]);
+ i += step;
+ }
+ }
+
+ vAPI.setTimeout(processClean, processCleanPeriod);
+};
+
+/******************************************************************************/
+
+var findAndRecordPageCookies = function(pageStore) {
+ for ( var cookieKey of cookieDict.keys() ) {
+ if ( cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
+ recordPageCookie(pageStore, cookieKey);
+ }
+ }
+};
+
+/******************************************************************************/
+
+var findAndRemovePageCookies = function(pageStore) {
+ for ( var cookieKey of cookieDict.keys() ) {
+ if ( cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
+ removeCookieAsync(cookieKey);
+ }
+ }
+};
+
+/******************************************************************************/
+
+var canRemoveCookie = function(cookieKey, srcHostnames) {
+ var cookieEntry = cookieDict.get(cookieKey);
+ if ( cookieEntry === undefined ) { return false; }
+
+ var cookieHostname = cookieEntry.hostname;
+ var srcHostname;
+
+ for ( srcHostname of cookieEntry.usedOn ) {
+ if ( µm.mustAllow(srcHostname, cookieHostname, 'cookie') ) {
+ return false;
+ }
+ }
+ // Maybe there is a scope in which the cookie is 1st-party-allowed.
+ // For example, if I am logged in into `github.com`, I do not want to be
+ // logged out just because I did not yet open a `github.com` page after
+ // re-starting the browser.
+ srcHostname = cookieHostname;
+ var pos;
+ for (;;) {
+ if ( srcHostnames.has(srcHostname) ) {
+ if ( µm.mustAllow(srcHostname, cookieHostname, 'cookie') ) {
+ return false;
+ }
+ }
+ if ( srcHostname === cookieEntry.domain ) {
+ break;
+ }
+ pos = srcHostname.indexOf('.');
+ if ( pos === -1 ) {
+ break;
+ }
+ srcHostname = srcHostname.slice(pos + 1);
+ }
+ return true;
+};
+
+/******************************************************************************/
+
+// Listen to any change in cookieland, we will update page stats accordingly.
+
+vAPI.cookies.onChanged = function(cookie) {
+ // rhill 2013-12-11: If cookie value didn't change, no need to record.
+ // https://github.com/gorhill/httpswitchboard/issues/79
+ var cookieKey = cookieKeyFromCookie(cookie);
+ var cookieEntry = cookieDict.get(cookieKey);
+ if ( cookieEntry === undefined ) {
+ cookieEntry = addCookieToDict(cookie);
+ } else {
+ cookieEntry.tstamp = Date.now();
+ if ( cookie.value === cookieEntry.value ) { return; }
+ cookieEntry.value = cookie.value;
+ }
+
+ // Go through all pages and update if needed, as one cookie can be used
+ // by many web pages, so they need to be recorded for all these pages.
+ var pageStores = µm.pageStores;
+ var pageStore;
+ for ( var tabId in pageStores ) {
+ if ( pageStores.hasOwnProperty(tabId) === false ) {
+ continue;
+ }
+ pageStore = pageStores[tabId];
+ if ( !cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
+ continue;
+ }
+ recordPageCookie(pageStore, cookieKey);
+ }
+};
+
+/******************************************************************************/
+
+// Listen to any change in cookieland, we will update page stats accordingly.
+
+vAPI.cookies.onRemoved = function(cookie) {
+ var cookieKey = cookieKeyFromCookie(cookie);
+ if ( removeCookieFromDict(cookieKey) ) {
+ µm.logger.writeOne('', 'info', 'cookie', i18nCookieDeleteSuccess.replace('{{value}}', cookieKey));
+ }
+};
+
+/******************************************************************************/
+
+// Listen to any change in cookieland, we will update page stats accordingly.
+
+vAPI.cookies.onAllRemoved = function() {
+ for ( var cookieKey of cookieDict.keys() ) {
+ if ( removeCookieFromDict(cookieKey) ) {
+ µm.logger.writeOne('', 'info', 'cookie', i18nCookieDeleteSuccess.replace('{{value}}', cookieKey));
+ }
+ }
+};
+
+/******************************************************************************/
+
+vAPI.cookies.getAll(addCookiesToDict);
+vAPI.cookies.start();
+
+vAPI.setTimeout(processRemoveQueue, processRemoveQueuePeriod);
+vAPI.setTimeout(processClean, processCleanPeriod);
+
+/******************************************************************************/
+
+// Expose only what is necessary
+
+return {
+ recordPageCookies: recordPageCookiesAsync,
+ removePageCookies: removePageCookiesAsync
+};
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
+
diff --git a/js/dashboard-common.js b/js/dashboard-common.js
new file mode 100644
index 0000000..eecd668
--- /dev/null
+++ b/js/dashboard-common.js
@@ -0,0 +1,41 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/******************************************************************************/
+
+uDom.onLoad(function() {
+
+/******************************************************************************/
+
+// Open links in the proper window
+uDom('a').attr('target', '_blank');
+uDom('a[href*="dashboard.html"]').attr('target', '_parent');
+uDom('.whatisthis').on('click', function() {
+ uDom(this).parent()
+ .descendants('.whatisthis-expandable')
+ .toggleClass('whatisthis-expanded');
+});
+
+
+/******************************************************************************/
+
+});
diff --git a/js/dashboard.js b/js/dashboard.js
new file mode 100644
index 0000000..5ff6ebc
--- /dev/null
+++ b/js/dashboard.js
@@ -0,0 +1,56 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global uDom */
+
+'use strict';
+
+/******************************************************************************/
+
+(function() {
+
+ var loadDashboardPanel = function(hash) {
+ var button = uDom(hash);
+ var url = button.attr('data-dashboard-panel-url');
+ uDom('iframe').attr('src', url);
+ uDom('.tabButton').forEach(function(button){
+ button.toggleClass(
+ 'selected',
+ button.attr('data-dashboard-panel-url') === url
+ );
+ });
+ };
+
+ var onTabClickHandler = function() {
+ loadDashboardPanel(window.location.hash);
+ };
+
+ uDom.onLoad(function() {
+ window.addEventListener('hashchange', onTabClickHandler);
+ var hash = window.location.hash;
+ if ( hash.length < 2 ) {
+ hash = '#settings';
+ }
+ loadDashboardPanel(hash);
+ });
+
+})();
diff --git a/js/hosts-files.js b/js/hosts-files.js
new file mode 100644
index 0000000..a259240
--- /dev/null
+++ b/js/hosts-files.js
@@ -0,0 +1,391 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global uDom */
+
+'use strict';
+
+/******************************************************************************/
+
+(function() {
+
+/******************************************************************************/
+
+var listDetails = {},
+ lastUpdateTemplateString = vAPI.i18n('hostsFilesLastUpdate'),
+ hostsFilesSettingsHash,
+ reValidExternalList = /[a-z-]+:\/\/\S*\/\S+/;
+
+/******************************************************************************/
+
+vAPI.messaging.addListener(function onMessage(msg) {
+ switch ( msg.what ) {
+ case 'assetUpdated':
+ updateAssetStatus(msg);
+ break;
+ case 'assetsUpdated':
+ document.body.classList.remove('updating');
+ break;
+ case 'loadHostsFilesCompleted':
+ renderHostsFiles();
+ break;
+ default:
+ break;
+ }
+});
+
+/******************************************************************************/
+
+var renderNumber = function(value) {
+ return value.toLocaleString();
+};
+
+/******************************************************************************/
+
+var renderHostsFiles = function(soft) {
+ var listEntryTemplate = uDom('#templates .listEntry'),
+ listStatsTemplate = vAPI.i18n('hostsFilesPerFileStats'),
+ renderElapsedTimeToString = vAPI.i18n.renderElapsedTimeToString,
+ reExternalHostFile = /^https?:/;
+
+ // Assemble a pretty list name if possible
+ var listNameFromListKey = function(listKey) {
+ var list = listDetails.current[listKey] || listDetails.available[listKey];
+ var listTitle = list ? list.title : '';
+ if ( listTitle === '' ) { return listKey; }
+ return listTitle;
+ };
+
+ var liFromListEntry = function(listKey, li) {
+ var entry = listDetails.available[listKey],
+ elem;
+ if ( !li ) {
+ li = listEntryTemplate.clone().nodeAt(0);
+ }
+ if ( li.getAttribute('data-listkey') !== listKey ) {
+ li.setAttribute('data-listkey', listKey);
+ elem = li.querySelector('input[type="checkbox"]');
+ elem.checked = entry.off !== true;
+ elem = li.querySelector('a:nth-of-type(1)');
+ elem.setAttribute('href', 'asset-viewer.html?url=' + encodeURI(listKey));
+ elem.setAttribute('type', 'text/html');
+ elem.textContent = listNameFromListKey(listKey);
+ li.classList.remove('toRemove');
+ if ( entry.supportName ) {
+ li.classList.add('support');
+ elem = li.querySelector('a.support');
+ elem.setAttribute('href', entry.supportURL);
+ elem.setAttribute('title', entry.supportName);
+ } else {
+ li.classList.remove('support');
+ }
+ if ( entry.external ) {
+ li.classList.add('external');
+ } else {
+ li.classList.remove('external');
+ }
+ if ( entry.instructionURL ) {
+ li.classList.add('mustread');
+ elem = li.querySelector('a.mustread');
+ elem.setAttribute('href', entry.instructionURL);
+ } else {
+ li.classList.remove('mustread');
+ }
+ }
+ // https://github.com/gorhill/uBlock/issues/1429
+ if ( !soft ) {
+ elem = li.querySelector('input[type="checkbox"]');
+ elem.checked = entry.off !== true;
+ }
+ elem = li.querySelector('span.counts');
+ var text = '';
+ if ( !isNaN(+entry.entryUsedCount) && !isNaN(+entry.entryCount) ) {
+ text = listStatsTemplate
+ .replace('{{used}}', renderNumber(entry.off ? 0 : entry.entryUsedCount))
+ .replace('{{total}}', renderNumber(entry.entryCount));
+ }
+ elem.textContent = text;
+ // https://github.com/chrisaljoudi/uBlock/issues/104
+ var asset = listDetails.cache[listKey] || {};
+ var remoteURL = asset.remoteURL;
+ li.classList.toggle(
+ 'unsecure',
+ typeof remoteURL === 'string' && remoteURL.lastIndexOf('http:', 0) === 0
+ );
+ li.classList.toggle('failed', asset.error !== undefined);
+ li.classList.toggle('obsolete', asset.obsolete === true);
+ li.classList.toggle('cached', asset.cached === true && asset.writeTime > 0);
+ if ( asset.cached ) {
+ li.querySelector('.status.cache').setAttribute(
+ 'title',
+ lastUpdateTemplateString.replace('{{ago}}', renderElapsedTimeToString(asset.writeTime))
+ );
+ }
+ li.classList.remove('discard');
+ return li;
+ };
+
+ var onListsReceived = function(details) {
+ // Before all, set context vars
+ listDetails = details;
+
+ // Incremental rendering: this will allow us to easily discard unused
+ // DOM list entries.
+ uDom('#lists .listEntry').addClass('discard');
+
+ var availableLists = details.available,
+ listKeys = Object.keys(details.available);
+
+ // Sort works this way:
+ // - Send /^https?:/ items at the end (custom hosts file URL)
+ listKeys.sort(function(a, b) {
+ var ta = availableLists[a].title || a,
+ tb = availableLists[b].title || b;
+ if ( reExternalHostFile.test(ta) === reExternalHostFile.test(tb) ) {
+ return ta.localeCompare(tb);
+ }
+ return reExternalHostFile.test(tb) ? -1 : 1;
+ });
+
+ var ulList = document.querySelector('#lists');
+ for ( var i = 0; i < listKeys.length; i++ ) {
+ var liEntry = liFromListEntry(listKeys[i], ulList.children[i]);
+ if ( liEntry.parentElement === null ) {
+ ulList.appendChild(liEntry);
+ }
+ }
+
+ uDom('#lists .listEntry.discard').remove();
+ uDom('#listsOfBlockedHostsPrompt').text(
+ vAPI.i18n('hostsFilesStats').replace(
+ '{{blockedHostnameCount}}',
+ renderNumber(details.blockedHostnameCount)
+ )
+ );
+ uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true);
+
+ if ( !soft ) {
+ hostsFilesSettingsHash = hashFromCurrentFromSettings();
+ }
+ renderWidgets();
+ };
+
+ vAPI.messaging.send('hosts-files.js', { what: 'getLists' }, onListsReceived);
+};
+
+/******************************************************************************/
+
+var renderWidgets = function() {
+ uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('body:not(.updating) #lists .listEntry.obsolete > input[type="checkbox"]:checked') === null);
+ uDom('#buttonPurgeAll').toggleClass('disabled', document.querySelector('#lists .listEntry.cached') === null);
+ uDom('#buttonApply').toggleClass('disabled', hostsFilesSettingsHash === hashFromCurrentFromSettings());
+};
+
+/******************************************************************************/
+
+var updateAssetStatus = function(details) {
+ var li = document.querySelector('#lists .listEntry[data-listkey="' + details.key + '"]');
+ if ( li === null ) { return; }
+ li.classList.toggle('failed', !!details.failed);
+ li.classList.toggle('obsolete', !details.cached);
+ li.classList.toggle('cached', !!details.cached);
+ if ( details.cached ) {
+ li.querySelector('.status.cache').setAttribute(
+ 'title',
+ lastUpdateTemplateString.replace(
+ '{{ago}}',
+ vAPI.i18n.renderElapsedTimeToString(Date.now())
+ )
+ );
+ }
+ renderWidgets();
+};
+
+/*******************************************************************************
+
+ Compute a hash from all the settings affecting how filter lists are loaded
+ in memory.
+
+**/
+
+var hashFromCurrentFromSettings = function() {
+ var hash = [],
+ listHash = [],
+ listEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'),
+ liEntry,
+ i = listEntries.length;
+ while ( i-- ) {
+ liEntry = listEntries[i];
+ if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) {
+ listHash.push(liEntry.getAttribute('data-listkey'));
+ }
+ }
+ hash.push(
+ listHash.sort().join(),
+ reValidExternalList.test(document.getElementById('externalHostsFiles').value),
+ document.querySelector('#lists .listEntry.toRemove') !== null
+ );
+ return hash.join();
+};
+
+/******************************************************************************/
+
+var onHostsFilesSettingsChanged = function() {
+ renderWidgets();
+};
+
+/******************************************************************************/
+
+var onRemoveExternalHostsFile = function(ev) {
+ var liEntry = uDom(this).ancestors('[data-listkey]'),
+ listKey = liEntry.attr('data-listkey');
+ if ( listKey ) {
+ liEntry.toggleClass('toRemove');
+ renderWidgets();
+ }
+ ev.preventDefault();
+};
+
+/******************************************************************************/
+
+var onPurgeClicked = function() {
+ var button = uDom(this),
+ liEntry = button.ancestors('[data-listkey]'),
+ listKey = liEntry.attr('data-listkey');
+ if ( !listKey ) { return; }
+
+ vAPI.messaging.send('hosts-files.js', { what: 'purgeCache', assetKey: listKey });
+ liEntry.addClass('obsolete');
+ liEntry.removeClass('cached');
+
+ if ( liEntry.descendants('input').first().prop('checked') ) {
+ renderWidgets();
+ }
+};
+
+/******************************************************************************/
+
+var selectHostsFiles = function(callback) {
+ // Hosts files to select
+ var toSelect = [],
+ liEntries = document.querySelectorAll('#lists .listEntry[data-listkey]:not(.toRemove)'),
+ i = liEntries.length,
+ liEntry;
+ while ( i-- ) {
+ liEntry = liEntries[i];
+ if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) {
+ toSelect.push(liEntry.getAttribute('data-listkey'));
+ }
+ }
+
+ // External hosts files to remove
+ var toRemove = [];
+ liEntries = document.querySelectorAll('#lists .listEntry.toRemove[data-listkey]');
+ i = liEntries.length;
+ while ( i-- ) {
+ toRemove.push(liEntries[i].getAttribute('data-listkey'));
+ }
+
+ // External hosts files to import
+ var externalListsElem = document.getElementById('externalHostsFiles'),
+ toImport = externalListsElem.value.trim();
+ externalListsElem.value = '';
+
+ vAPI.messaging.send(
+ 'hosts-files.js',
+ {
+ what: 'selectHostsFiles',
+ toSelect: toSelect,
+ toImport: toImport,
+ toRemove: toRemove
+ },
+ callback
+ );
+
+ hostsFilesSettingsHash = hashFromCurrentFromSettings();
+};
+
+/******************************************************************************/
+
+var buttonApplyHandler = function() {
+ uDom('#buttonApply').removeClass('enabled');
+ selectHostsFiles(function() {
+ vAPI.messaging.send('hosts-files.js', { what: 'reloadHostsFiles' });
+ });
+ renderWidgets();
+};
+
+/******************************************************************************/
+
+var buttonUpdateHandler = function() {
+ uDom('#buttonUpdate').removeClass('enabled');
+ selectHostsFiles(function() {
+ document.body.classList.add('updating');
+ vAPI.messaging.send('hosts-files.js', { what: 'forceUpdateAssets' });
+ renderWidgets();
+ });
+ renderWidgets();
+};
+
+/******************************************************************************/
+
+var buttonPurgeAllHandler = function() {
+ uDom('#buttonPurgeAll').removeClass('enabled');
+ vAPI.messaging.send(
+ 'hosts-files.js',
+ { what: 'purgeAllCaches' },
+ function() {
+ renderHostsFiles(true);
+ }
+ );
+};
+
+/******************************************************************************/
+
+var autoUpdateCheckboxChanged = function() {
+ vAPI.messaging.send(
+ 'hosts-files.js',
+ {
+ what: 'userSettings',
+ name: 'autoUpdate',
+ value: this.checked
+ }
+ );
+};
+
+/******************************************************************************/
+
+uDom('#autoUpdate').on('change', autoUpdateCheckboxChanged);
+uDom('#buttonApply').on('click', buttonApplyHandler);
+uDom('#buttonUpdate').on('click', buttonUpdateHandler);
+uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler);
+uDom('#lists').on('change', '.listEntry > input', onHostsFilesSettingsChanged);
+uDom('#lists').on('click', '.listEntry > a.remove', onRemoveExternalHostsFile);
+uDom('#lists').on('click', 'span.cache', onPurgeClicked);
+uDom('#externalHostsFiles').on('input', onHostsFilesSettingsChanged);
+
+renderHostsFiles();
+
+/******************************************************************************/
+
+})();
+
diff --git a/js/httpsb.js b/js/httpsb.js
new file mode 100644
index 0000000..d76371a
--- /dev/null
+++ b/js/httpsb.js
@@ -0,0 +1,212 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global chrome, µMatrix */
+
+'use strict';
+
+/******************************************************************************/
+
+(function() {
+ var µm = µMatrix;
+ µm.pMatrix = new µm.Matrix();
+ µm.pMatrix.setSwitch('matrix-off', 'about-scheme', 1);
+ µm.pMatrix.setSwitch('matrix-off', 'chrome-extension-scheme', 1);
+ µm.pMatrix.setSwitch('matrix-off', 'chrome-scheme', 1);
+ µm.pMatrix.setSwitch('matrix-off', 'moz-extension-scheme', 1);
+ µm.pMatrix.setSwitch('matrix-off', 'opera-scheme', 1);
+ // https://discourse.mozilla.org/t/support-umatrix/5131/157
+ µm.pMatrix.setSwitch('matrix-off', 'wyciwyg-scheme', 1);
+ µm.pMatrix.setSwitch('matrix-off', 'behind-the-scene', 1);
+ µm.pMatrix.setSwitch('referrer-spoof', 'behind-the-scene', 2);
+ µm.pMatrix.setSwitch('https-strict', 'behind-the-scene', 2);
+ // Global rules
+ µm.pMatrix.setSwitch('referrer-spoof', '*', 1);
+ µm.pMatrix.setSwitch('noscript-spoof', '*', 1);
+ µm.pMatrix.setCell('*', '*', '*', µm.Matrix.Red);
+ µm.pMatrix.setCell('*', '*', 'css', µm.Matrix.Green);
+ µm.pMatrix.setCell('*', '*', 'image', µm.Matrix.Green);
+ µm.pMatrix.setCell('*', '*', 'frame', µm.Matrix.Red);
+ // 1st-party rules
+ µm.pMatrix.setCell('*', '1st-party', '*', µm.Matrix.Green);
+ µm.pMatrix.setCell('*', '1st-party', 'frame', µm.Matrix.Green);
+
+ µm.tMatrix = new µm.Matrix();
+ µm.tMatrix.assign(µm.pMatrix);
+})();
+
+/******************************************************************************/
+
+µMatrix.hostnameFromURL = function(url) {
+ var hn = this.URI.hostnameFromURI(url);
+ return hn === '' ? '*' : hn;
+};
+
+µMatrix.scopeFromURL = µMatrix.hostnameFromURL;
+
+/******************************************************************************/
+
+µMatrix.evaluateURL = function(srcURL, desHostname, type) {
+ var srcHostname = this.URI.hostnameFromURI(srcURL);
+ return this.tMatrix.evaluateCellZXY(srcHostname, desHostname, type);
+};
+
+
+/******************************************************************************/
+
+// Whitelist something
+
+µMatrix.whitelistTemporarily = function(srcHostname, desHostname, type) {
+ this.tMatrix.whitelistCell(srcHostname, desHostname, type);
+};
+
+µMatrix.whitelistPermanently = function(srcHostname, desHostname, type) {
+ if ( this.pMatrix.whitelistCell(srcHostname, desHostname, type) ) {
+ this.saveMatrix();
+ }
+};
+
+/******************************************************************************/
+
+// Auto-whitelisting the `all` cell is a serious action, hence this will be
+// done only from within a scope.
+
+µMatrix.autoWhitelistAllTemporarily = function(pageURL) {
+ var srcHostname = this.URI.hostnameFromURI(pageURL);
+ if ( this.mustBlock(srcHostname, '*', '*') === false ) {
+ return false;
+ }
+ this.tMatrix.whitelistCell(srcHostname, '*', '*');
+ return true;
+};
+
+/******************************************************************************/
+
+// Blacklist something
+
+µMatrix.blacklistTemporarily = function(srcHostname, desHostname, type) {
+ this.tMatrix.blacklistCell(srcHostname, desHostname, type);
+};
+
+µMatrix.blacklistPermanently = function(srcHostname, desHostname, type) {
+ if ( this.pMatrix.blacklist(srcHostname, desHostname, type) ) {
+ this.saveMatrix();
+ }
+};
+
+/******************************************************************************/
+
+// Remove something from both black and white lists.
+
+µMatrix.graylistTemporarily = function(srcHostname, desHostname, type) {
+ this.tMatrix.graylistCell(srcHostname, desHostname, type);
+};
+
+µMatrix.graylistPermanently = function(srcHostname, desHostname, type) {
+ if ( this.pMatrix.graylistCell(srcHostname, desHostname, type) ) {
+ this.saveMatrix();
+ }
+};
+
+/******************************************************************************/
+
+// TODO: Should type be transposed by the caller or in place here? Not an
+// issue at this point but to keep in mind as this function is called
+// more and more from different places.
+
+µMatrix.filterRequest = function(fromURL, type, toURL) {
+ // Block request?
+ var srcHostname = this.hostnameFromURL(fromURL);
+ var desHostname = this.hostnameFromURL(toURL);
+
+ // If no valid hostname, use the hostname of the source.
+ // For example, this case can happen with data URI.
+ if ( desHostname === '' ) {
+ desHostname = srcHostname;
+ }
+
+ // Blocked by matrix filtering?
+ return this.mustBlock(srcHostname, desHostname, type);
+};
+
+/******************************************************************************/
+
+µMatrix.mustBlock = function(srcHostname, desHostname, type) {
+ return this.tMatrix.mustBlock(srcHostname, desHostname, type);
+};
+
+µMatrix.mustAllow = function(srcHostname, desHostname, type) {
+ return this.mustBlock(srcHostname, desHostname, type) === false;
+};
+
+/******************************************************************************/
+
+// Commit temporary permissions.
+
+µMatrix.commitPermissions = function(persist) {
+ this.pMatrix.assign(this.tMatrix);
+ if ( persist ) {
+ this.saveMatrix();
+ }
+};
+
+/******************************************************************************/
+
+// Reset all rules to their default state.
+
+µMatrix.revertAllRules = function() {
+ this.tMatrix.assign(this.pMatrix);
+};
+
+/******************************************************************************/
+
+µMatrix.turnOff = function() {
+ vAPI.app.start();
+};
+
+µMatrix.turnOn = function() {
+ vAPI.app.stop();
+};
+
+/******************************************************************************/
+
+µMatrix.formatCount = function(count) {
+ if ( typeof count !== 'number' ) {
+ return '';
+ }
+ var s = count.toFixed(0);
+ if ( count >= 1000 ) {
+ if ( count < 10000 ) {
+ s = '>' + s.slice(0,1) + 'K';
+ } else if ( count < 100000 ) {
+ s = s.slice(0,2) + 'K';
+ } else if ( count < 1000000 ) {
+ s = s.slice(0,3) + 'K';
+ } else if ( count < 10000000 ) {
+ s = s.slice(0,1) + 'M';
+ } else {
+ s = s.slice(0,-6) + 'M';
+ }
+ }
+ return s;
+};
+
diff --git a/js/i18n.js b/js/i18n.js
new file mode 100644
index 0000000..5bb854c
--- /dev/null
+++ b/js/i18n.js
@@ -0,0 +1,209 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global vAPI, uDom */
+
+/******************************************************************************/
+
+// This file should always be included at the end of the `body` tag, so as
+// to ensure all i18n targets are already loaded.
+
+(function() {
+
+'use strict';
+
+/******************************************************************************/
+
+// https://github.com/gorhill/uBlock/issues/2084
+// Anything else than <a>, <b>, <code>, <em>, <i>, <input>, and <span> will
+// be rendered as plain text.
+// For <input>, only the type attribute is allowed.
+// For <a>, only href attribute must be present, and it MUST starts with
+// `https://`, and includes no single- or double-quotes.
+// No HTML entities are allowed, there is code to handle existing HTML
+// entities already present in translation files until they are all gone.
+
+var reSafeTags = /^([\s\S]*?)<(b|blockquote|code|em|i|kbd|span|sup)>(.+?)<\/\2>([\s\S]*)$/,
+ reSafeInput = /^([\s\S]*?)<(input type="[^"]+")>(.*?)([\s\S]*)$/,
+ reInput = /^input type=(['"])([a-z]+)\1$/,
+ reSafeLink = /^([\s\S]*?)<(a href=['"]https?:\/\/[^'" <>]+['"])>(.+?)<\/a>([\s\S]*)$/,
+ reLink = /^a href=(['"])(https?:\/\/[^'"]+)\1$/;
+
+var safeTextToTagNode = function(text) {
+ var matches, node;
+ if ( text.lastIndexOf('a ', 0) === 0 ) {
+ matches = reLink.exec(text);
+ if ( matches === null ) { return null; }
+ node = document.createElement('a');
+ node.setAttribute('href', matches[2]);
+ return node;
+ }
+ if ( text.lastIndexOf('input ', 0) === 0 ) {
+ matches = reInput.exec(text);
+ if ( matches === null ) { return null; }
+ node = document.createElement('input');
+ node.setAttribute('type', matches[2]);
+ return node;
+ }
+ // Firefox extension validator warns if using a variable as argument for
+ // document.createElement().
+ switch ( text ) {
+ case 'b':
+ return document.createElement('b');
+ case 'blockquote':
+ return document.createElement('blockquote');
+ case 'code':
+ return document.createElement('code');
+ case 'em':
+ return document.createElement('em');
+ case 'i':
+ return document.createElement('i');
+ case 'kbd':
+ return document.createElement('kbd');
+ case 'span':
+ return document.createElement('span');
+ case 'sup':
+ return document.createElement('sup');
+ default:
+ break;
+ }
+};
+
+var safeTextToTextNode = function(text) {
+ // TODO: remove once no more HTML entities in translation files.
+ if ( text.indexOf('&') !== -1 ) {
+ text = text.replace(/&ldquo;/g, '“')
+ .replace(/&rdquo;/g, '”')
+ .replace(/&lsquo;/g, '‘')
+ .replace(/&rsquo;/g, '’');
+ }
+ return document.createTextNode(text);
+};
+
+var safeTextToDOM = function(text, parent) {
+ if ( text === '' ) { return; }
+ // Fast path (most common).
+ if ( text.indexOf('<') === -1 ) {
+ return parent.appendChild(safeTextToTextNode(text));
+ }
+ // Slow path.
+ // `<p>` no longer allowed. Code below can be remove once all <p>'s are
+ // gone from translation files.
+ text = text.replace(/^<p>|<\/p>/g, '')
+ .replace(/<p>/g, '\n\n');
+ // Parse allowed HTML tags.
+ var matches,
+ matches1 = reSafeTags.exec(text),
+ matches2 = reSafeLink.exec(text);
+ if ( matches1 !== null && matches2 !== null ) {
+ matches = matches1.index < matches2.index ? matches1 : matches2;
+ } else if ( matches1 !== null ) {
+ matches = matches1;
+ } else if ( matches2 !== null ) {
+ matches = matches2;
+ } else {
+ matches = reSafeInput.exec(text);
+ }
+ if ( matches === null ) {
+ parent.appendChild(safeTextToTextNode(text));
+ return;
+ }
+ safeTextToDOM(matches[1], parent);
+ var node = safeTextToTagNode(matches[2]) || parent;
+ safeTextToDOM(matches[3], node);
+ parent.appendChild(node);
+ safeTextToDOM(matches[4], parent);
+};
+
+/******************************************************************************/
+
+// Helper to deal with the i18n'ing of HTML files.
+vAPI.i18n.render = function(context) {
+ var docu = document,
+ root = context || docu,
+ elems, n, i, elem, text;
+
+ elems = root.querySelectorAll('[data-i18n]');
+ n = elems.length;
+ for ( i = 0; i < n; i++ ) {
+ elem = elems[i];
+ text = vAPI.i18n(elem.getAttribute('data-i18n'));
+ if ( !text ) { continue; }
+ // TODO: remove once it's all replaced with <input type="...">
+ if ( text.indexOf('{') !== -1 ) {
+ text = text.replace(/\{\{input:([^}]+)\}\}/g, '<input type="$1">');
+ }
+ safeTextToDOM(text, elem);
+ }
+
+ uDom('[title]', context).forEach(function(elem) {
+ var title = vAPI.i18n(elem.attr('title'));
+ if ( title ) {
+ elem.attr('title', title);
+ }
+ });
+
+ uDom('[placeholder]', context).forEach(function(elem) {
+ elem.attr('placeholder', vAPI.i18n(elem.attr('placeholder')));
+ });
+
+ uDom('[data-i18n-tip]', context).forEach(function(elem) {
+ elem.attr(
+ 'data-tip',
+ vAPI.i18n(elem.attr('data-i18n-tip'))
+ .replace(/<br>/g, '\n')
+ .replace(/\n{3,}/g, '\n\n')
+ );
+ });
+};
+
+vAPI.i18n.render();
+
+/******************************************************************************/
+
+vAPI.i18n.renderElapsedTimeToString = function(tstamp) {
+ var value = (Date.now() - tstamp) / 60000;
+ if ( value < 2 ) {
+ return vAPI.i18n('elapsedOneMinuteAgo');
+ }
+ if ( value < 60 ) {
+ return vAPI.i18n('elapsedManyMinutesAgo').replace('{{value}}', Math.floor(value).toLocaleString());
+ }
+ value /= 60;
+ if ( value < 2 ) {
+ return vAPI.i18n('elapsedOneHourAgo');
+ }
+ if ( value < 24 ) {
+ return vAPI.i18n('elapsedManyHoursAgo').replace('{{value}}', Math.floor(value).toLocaleString());
+ }
+ value /= 24;
+ if ( value < 2 ) {
+ return vAPI.i18n('elapsedOneDayAgo');
+ }
+ return vAPI.i18n('elapsedManyDaysAgo').replace('{{value}}', Math.floor(value).toLocaleString());
+};
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
diff --git a/js/liquid-dict.js b/js/liquid-dict.js
new file mode 100644
index 0000000..92ca58c
--- /dev/null
+++ b/js/liquid-dict.js
@@ -0,0 +1,203 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/******************************************************************************/
+
+µMatrix.LiquidDict = (function() {
+
+/******************************************************************************/
+
+var LiquidDict = function() {
+ this.dict = {};
+ this.count = 0;
+ this.duplicateCount = 0;
+ this.bucketCount = 0;
+ this.frozenBucketCount = 0;
+
+ // Somewhat arbitrary: I need to come up with hard data to know at which
+ // point binary search is better than indexOf.
+ this.cutoff = 500;
+};
+
+/******************************************************************************/
+
+var meltBucket = function(ldict, len, bucket) {
+ ldict.frozenBucketCount -= 1;
+ var map = {};
+ if ( bucket.charAt(0) === ' ' ) {
+ bucket.trim().split(' ').map(function(k) {
+ map[k] = true;
+ });
+ } else {
+ var offset = 0;
+ while ( offset < bucket.length ) {
+ map[bucket.substring(offset, len)] = true;
+ offset += len;
+ }
+ }
+ return map;
+};
+
+/******************************************************************************/
+
+var melt = function(ldict) {
+ var buckets = ldict.dict;
+ var bucket;
+ for ( var key in buckets ) {
+ bucket = buckets[key];
+ if ( typeof bucket === 'string' ) {
+ buckets[key] = meltBucket(ldict, key.charCodeAt(0) & 0xFF, bucket);
+ }
+ }
+};
+
+/******************************************************************************/
+
+var freezeBucket = function(ldict, bucket) {
+ ldict.frozenBucketCount += 1;
+ var words = Object.keys(bucket);
+ var wordLen = words[0].length;
+ if ( wordLen * words.length < ldict.cutoff ) {
+ return ' ' + words.join(' ') + ' ';
+ }
+ return words.sort().join('');
+};
+
+/******************************************************************************/
+
+// How the key is derived dictates the number and size of buckets.
+//
+// http://jsperf.com/makekey-concat-vs-join/3
+//
+// Question: Why is using a prototyped function better than a standalone
+// helper function?
+
+LiquidDict.prototype.makeKey = function(word) {
+ var len = word.length;
+ if ( len > 255 ) {
+ len = 255;
+ }
+ var i = len >> 2;
+ return String.fromCharCode(
+ (word.charCodeAt( 0) & 0x03) << 14 |
+ (word.charCodeAt( i) & 0x03) << 12 |
+ (word.charCodeAt( i+i) & 0x03) << 10 |
+ (word.charCodeAt(i+i+i) & 0x03) << 8 |
+ len
+ );
+};
+
+/******************************************************************************/
+
+LiquidDict.prototype.test = function(word) {
+ var key = this.makeKey(word);
+ var bucket = this.dict[key];
+ if ( bucket === undefined ) {
+ return false;
+ }
+ if ( typeof bucket === 'object' ) {
+ return bucket[word] !== undefined;
+ }
+ if ( bucket.charAt(0) === ' ' ) {
+ return bucket.indexOf(' ' + word + ' ') >= 0;
+ }
+ // binary search
+ var len = word.length;
+ var left = 0;
+ // http://jsperf.com/or-vs-floor/3
+ var right = ~~(bucket.length / len + 0.5);
+ var i, needle;
+ while ( left < right ) {
+ i = left + right >> 1;
+ needle = bucket.substr( len * i, len );
+ if ( word < needle ) {
+ right = i;
+ } else if ( word > needle ) {
+ left = i + 1;
+ } else {
+ return true;
+ }
+ }
+ return false;
+};
+
+/******************************************************************************/
+
+LiquidDict.prototype.add = function(word) {
+ var key = this.makeKey(word);
+ if ( key === undefined ) {
+ return false;
+ }
+ var bucket = this.dict[key];
+ if ( bucket === undefined ) {
+ this.dict[key] = bucket = {};
+ this.bucketCount += 1;
+ bucket[word] = true;
+ this.count += 1;
+ return true;
+ } else if ( typeof bucket === 'string' ) {
+ this.dict[key] = bucket = meltBucket(this, word.len, bucket);
+ }
+ if ( bucket[word] === undefined ) {
+ bucket[word] = true;
+ this.count += 1;
+ return true;
+ }
+ this.duplicateCount += 1;
+ return false;
+};
+
+/******************************************************************************/
+
+LiquidDict.prototype.freeze = function() {
+ var buckets = this.dict;
+ var bucket;
+ for ( var key in buckets ) {
+ bucket = buckets[key];
+ if ( typeof bucket === 'object' ) {
+ buckets[key] = freezeBucket(this, bucket);
+ }
+ }
+};
+
+/******************************************************************************/
+
+LiquidDict.prototype.reset = function() {
+ this.dict = {};
+ this.count = 0;
+ this.duplicateCount = 0;
+ this.bucketCount = 0;
+ this.frozenBucketCount = 0;
+};
+
+/******************************************************************************/
+
+return LiquidDict;
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
+
+µMatrix.ubiquitousBlacklist = new µMatrix.LiquidDict();
+µMatrix.ubiquitousWhitelist = new µMatrix.LiquidDict();
diff --git a/js/logger-ui.js b/js/logger-ui.js
new file mode 100644
index 0000000..a3e9d9b
--- /dev/null
+++ b/js/logger-ui.js
@@ -0,0 +1,908 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2015-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/sessbench
+*/
+
+/* global uDom */
+
+'use strict';
+
+/******************************************************************************/
+
+(function() {
+
+/******************************************************************************/
+
+var tbody = document.querySelector('#content tbody');
+var trJunkyard = [];
+var tdJunkyard = [];
+var firstVarDataCol = 2; // currently, column 2 (0-based index)
+var lastVarDataIndex = 3; // currently, d0-d3
+var maxEntries = 0;
+var noTabId = '';
+var allTabIds = {};
+var allTabIdsToken;
+var ownerId = Date.now();
+
+var emphasizeTemplate = document.querySelector('#emphasizeTemplate > span');
+var hiddenTemplate = document.querySelector('#hiddenTemplate > span');
+
+var prettyRequestTypes = {
+ 'main_frame': 'doc',
+ 'stylesheet': 'css',
+ 'sub_frame': 'frame',
+ 'xmlhttprequest': 'xhr'
+};
+
+var dontEmphasizeSet = new Set([
+ 'COOKIE',
+ 'CSP',
+ 'REFERER'
+]);
+
+/******************************************************************************/
+
+// Adjust top padding of content table, to match that of toolbar height.
+
+document.getElementById('content').style.setProperty(
+ 'margin-top',
+ document.getElementById('toolbar').clientHeight + 'px'
+);
+
+/******************************************************************************/
+
+var classNameFromTabId = function(tabId) {
+ if ( tabId === noTabId ) {
+ return 'tab_bts';
+ }
+ if ( tabId !== '' ) {
+ return 'tab_' + tabId;
+ }
+ return '';
+};
+
+/******************************************************************************/
+
+// Emphasize hostname and cookie name.
+
+var emphasizeCookie = function(s) {
+ var pnode = emphasizeHostname(s);
+ if ( pnode.childNodes.length !== 3 ) {
+ return pnode;
+ }
+ var prefix = '-cookie:';
+ var text = pnode.childNodes[2].textContent;
+ var beg = text.indexOf(prefix);
+ if ( beg === -1 ) {
+ return pnode;
+ }
+ beg += prefix.length;
+ var end = text.indexOf('}', beg);
+ if ( end === -1 ) {
+ return pnode;
+ }
+ var cnode = emphasizeTemplate.cloneNode(true);
+ cnode.childNodes[0].textContent = text.slice(0, beg);
+ cnode.childNodes[1].textContent = text.slice(beg, end);
+ cnode.childNodes[2].textContent = text.slice(end);
+ pnode.replaceChild(cnode.childNodes[0], pnode.childNodes[2]);
+ pnode.appendChild(cnode.childNodes[0]);
+ pnode.appendChild(cnode.childNodes[0]);
+ return pnode;
+};
+
+/******************************************************************************/
+
+// Emphasize hostname in URL.
+
+var emphasizeHostname = function(url) {
+ var hnbeg = url.indexOf('://');
+ if ( hnbeg === -1 ) {
+ return document.createTextNode(url);
+ }
+ hnbeg += 3;
+
+ var hnend = url.indexOf('/', hnbeg);
+ if ( hnend === -1 ) {
+ hnend = url.slice(hnbeg).search(/\?#/);
+ if ( hnend !== -1 ) {
+ hnend += hnbeg;
+ } else {
+ hnend = url.length;
+ }
+ }
+
+ var node = emphasizeTemplate.cloneNode(true);
+ node.childNodes[0].textContent = url.slice(0, hnbeg);
+ node.childNodes[1].textContent = url.slice(hnbeg, hnend);
+ node.childNodes[2].textContent = url.slice(hnend);
+ return node;
+};
+
+/******************************************************************************/
+
+var createCellAt = function(tr, index) {
+ var td = tr.cells[index];
+ var mustAppend = !td;
+ if ( mustAppend ) {
+ td = tdJunkyard.pop();
+ }
+ if ( td ) {
+ td.removeAttribute('colspan');
+ td.textContent = '';
+ } else {
+ td = document.createElement('td');
+ }
+ if ( mustAppend ) {
+ tr.appendChild(td);
+ }
+ return td;
+};
+
+/******************************************************************************/
+
+var createRow = function(layout) {
+ var tr = trJunkyard.pop();
+ if ( tr ) {
+ tr.className = '';
+ } else {
+ tr = document.createElement('tr');
+ }
+ for ( var index = 0; index < firstVarDataCol; index++ ) {
+ createCellAt(tr, index);
+ }
+ var i = 1, span = 1, td;
+ for (;;) {
+ td = createCellAt(tr, index);
+ if ( i === lastVarDataIndex ) {
+ break;
+ }
+ if ( layout.charAt(i) !== '1' ) {
+ span += 1;
+ } else {
+ if ( span !== 1 ) {
+ td.setAttribute('colspan', span);
+ }
+ index += 1;
+ span = 1;
+ }
+ i += 1;
+ }
+ if ( span !== 1 ) {
+ td.setAttribute('colspan', span);
+ }
+ index += 1;
+ while ( (td = tr.cells[index]) ) {
+ tdJunkyard.push(tr.removeChild(td));
+ }
+ return tr;
+};
+
+/******************************************************************************/
+
+var createHiddenTextNode = function(text) {
+ var node = hiddenTemplate.cloneNode(true);
+ node.textContent = text;
+ return node;
+};
+
+/******************************************************************************/
+
+var padTo2 = function(v) {
+ return v < 10 ? '0' + v : v;
+};
+
+/******************************************************************************/
+
+var createGap = function(tabId, url) {
+ var tr = createRow('1');
+ tr.classList.add('doc');
+ tr.classList.add('tab');
+ tr.classList.add('canMtx');
+ tr.classList.add('tab_' + tabId);
+ tr.cells[firstVarDataCol].textContent = url;
+ tbody.insertBefore(tr, tbody.firstChild);
+};
+
+/******************************************************************************/
+
+var renderLogEntry = function(entry) {
+ var tr;
+ var fvdc = firstVarDataCol;
+
+ switch ( entry.cat ) {
+ case 'error':
+ case 'info':
+ tr = createRow('1');
+ if ( entry.d0 === 'cookie' ) {
+ tr.cells[fvdc].appendChild(emphasizeCookie(entry.d1));
+ } else {
+ tr.cells[fvdc].textContent = entry.d0;
+ }
+ break;
+
+ case 'net':
+ tr = createRow('111');
+ tr.classList.add('canMtx');
+ // If the request is that of a root frame, insert a gap in the table
+ // in order to visually separate entries for different documents.
+ if ( entry.d2 === 'doc' && entry.tab !== noTabId ) {
+ createGap(entry.tab, entry.d1);
+ }
+ if ( entry.d3 ) {
+ tr.classList.add('blocked');
+ tr.cells[fvdc].textContent = '--';
+ } else {
+ tr.cells[fvdc].textContent = '';
+ }
+ tr.cells[fvdc+1].textContent = (prettyRequestTypes[entry.d2] || entry.d2);
+ if ( dontEmphasizeSet.has(entry.d2) ) {
+ tr.cells[fvdc+2].textContent = entry.d1;
+ } else if ( entry.d2 === 'cookie' ) {
+ tr.cells[fvdc+2].appendChild(emphasizeCookie(entry.d1));
+ } else {
+ tr.cells[fvdc+2].appendChild(emphasizeHostname(entry.d1));
+ }
+ break;
+
+ default:
+ tr = createRow('1');
+ tr.cells[fvdc].textContent = entry.d0;
+ break;
+ }
+
+ // Fields common to all rows.
+ var time = logDate;
+ time.setTime(entry.tstamp - logDateTimezoneOffset);
+ tr.cells[0].textContent = padTo2(time.getUTCHours()) + ':' +
+ padTo2(time.getUTCMinutes()) + ':' +
+ padTo2(time.getSeconds());
+
+ if ( entry.tab ) {
+ tr.classList.add('tab');
+ tr.classList.add(classNameFromTabId(entry.tab));
+ if ( entry.tab === noTabId ) {
+ tr.cells[1].appendChild(createHiddenTextNode('bts'));
+ }
+ }
+ if ( entry.cat !== '' ) {
+ tr.classList.add('cat_' + entry.cat);
+ }
+
+ rowFilterer.filterOne(tr, true);
+
+ tbody.insertBefore(tr, tbody.firstChild);
+};
+
+// Reuse date objects.
+var logDate = new Date(),
+ logDateTimezoneOffset = logDate.getTimezoneOffset() * 60000;
+
+/******************************************************************************/
+
+var renderLogEntries = function(response) {
+ var entries = response.entries;
+ if ( entries.length === 0 ) {
+ return;
+ }
+
+ // Preserve scroll position
+ var height = tbody.offsetHeight;
+
+ var tabIds = response.tabIds;
+ var n = entries.length;
+ var entry;
+ for ( var i = 0; i < n; i++ ) {
+ entry = entries[i];
+ // Unlikely, but it may happen
+ if ( entry.tab && tabIds.hasOwnProperty(entry.tab) === false ) {
+ continue;
+ }
+ renderLogEntry(entries[i]);
+ }
+
+ // Prevent logger from growing infinitely and eating all memory. For
+ // instance someone could forget that it is left opened for some
+ // dynamically refreshed pages.
+ truncateLog(maxEntries);
+
+ var yDelta = tbody.offsetHeight - height;
+ if ( yDelta === 0 ) {
+ return;
+ }
+
+ // Chromium:
+ // body.scrollTop = good value
+ // body.parentNode.scrollTop = 0
+ if ( document.body.scrollTop !== 0 ) {
+ document.body.scrollTop += yDelta;
+ return;
+ }
+
+ // Firefox:
+ // body.scrollTop = 0
+ // body.parentNode.scrollTop = good value
+ var parentNode = document.body.parentNode;
+ if ( parentNode && parentNode.scrollTop !== 0 ) {
+ parentNode.scrollTop += yDelta;
+ }
+};
+
+/******************************************************************************/
+
+var synchronizeTabIds = function(newTabIds) {
+ var oldTabIds = allTabIds;
+ var autoDeleteVoidRows = !!vAPI.localStorage.getItem('loggerAutoDeleteVoidRows');
+ var rowVoided = false;
+ var trs;
+ for ( var tabId in oldTabIds ) {
+ if ( oldTabIds.hasOwnProperty(tabId) === false ) {
+ continue;
+ }
+ if ( newTabIds.hasOwnProperty(tabId) ) {
+ continue;
+ }
+ // Mark or remove voided rows
+ trs = uDom('.tab_' + tabId);
+ if ( autoDeleteVoidRows ) {
+ toJunkyard(trs);
+ } else {
+ trs.removeClass('canMtx');
+ rowVoided = true;
+ }
+ // Remove popup if it is currently bound to a removed tab.
+ if ( tabId === popupManager.tabId ) {
+ popupManager.toggleOff();
+ }
+ }
+
+ var select = document.getElementById('pageSelector');
+ var selectValue = select.value;
+ var tabIds = Object.keys(newTabIds).sort(function(a, b) {
+ return newTabIds[a].localeCompare(newTabIds[b]);
+ });
+ var option;
+ for ( var i = 0, j = 2; i < tabIds.length; i++ ) {
+ tabId = tabIds[i];
+ if ( tabId === noTabId ) {
+ continue;
+ }
+ option = select.options[j];
+ j += 1;
+ if ( !option ) {
+ option = document.createElement('option');
+ select.appendChild(option);
+ }
+ option.textContent = newTabIds[tabId];
+ option.value = classNameFromTabId(tabId);
+ if ( option.value === selectValue ) {
+ option.setAttribute('selected', '');
+ } else {
+ option.removeAttribute('selected');
+ }
+ }
+ while ( j < select.options.length ) {
+ select.removeChild(select.options[j]);
+ }
+ if ( select.value !== selectValue ) {
+ select.selectedIndex = 0;
+ select.value = '';
+ select.options[0].setAttribute('selected', '');
+ pageSelectorChanged();
+ }
+
+ allTabIds = newTabIds;
+
+ return rowVoided;
+};
+
+/******************************************************************************/
+
+var truncateLog = function(size) {
+ if ( size === 0 ) {
+ size = 5000;
+ }
+ var tbody = document.querySelector('#content tbody');
+ size = Math.min(size, 10000);
+ var tr;
+ while ( tbody.childElementCount > size ) {
+ tr = tbody.lastElementChild;
+ trJunkyard.push(tbody.removeChild(tr));
+ }
+};
+
+/******************************************************************************/
+
+var onLogBufferRead = function(response) {
+ if ( !response || response.unavailable ) {
+ readLogBufferAsync();
+ return;
+ }
+
+ // This tells us the behind-the-scene tab id
+ noTabId = response.noTabId;
+
+ // This may have changed meanwhile
+ if ( response.maxLoggedRequests !== maxEntries ) {
+ maxEntries = response.maxLoggedRequests;
+ uDom('#maxEntries').val(maxEntries || '');
+ }
+
+ // Neuter rows for which a tab does not exist anymore
+ var rowVoided = false;
+ if ( response.tabIdsToken !== allTabIdsToken ) {
+ rowVoided = synchronizeTabIds(response.tabIds);
+ allTabIdsToken = response.tabIdsToken;
+ }
+
+ renderLogEntries(response);
+
+ if ( rowVoided ) {
+ uDom('#clean').toggleClass(
+ 'disabled',
+ tbody.querySelector('tr.tab:not(.canMtx)') === null
+ );
+ }
+
+ // Synchronize toolbar with content of log
+ uDom('#clear').toggleClass(
+ 'disabled',
+ tbody.querySelector('tr') === null
+ );
+
+ readLogBufferAsync();
+};
+
+/******************************************************************************/
+
+// This can be called only once, at init time. After that, this will be called
+// automatically. If called after init time, this will be messy, and this would
+// require a bit more code to ensure no multi time out events.
+
+var readLogBuffer = function() {
+ if ( ownerId === undefined ) { return; }
+ vAPI.messaging.send(
+ 'logger-ui.js',
+ { what: 'readMany', ownerId: ownerId },
+ onLogBufferRead
+ );
+};
+
+var readLogBufferAsync = function() {
+ if ( ownerId === undefined ) { return; }
+ vAPI.setTimeout(readLogBuffer, 1200);
+};
+
+/******************************************************************************/
+
+var pageSelectorChanged = function() {
+ var style = document.getElementById('tabFilterer');
+ var tabClass = document.getElementById('pageSelector').value;
+ var sheet = style.sheet;
+ while ( sheet.cssRules.length !== 0 ) {
+ sheet.deleteRule(0);
+ }
+ if ( tabClass !== '' ) {
+ sheet.insertRule(
+ '#content table tr:not(.' + tabClass + ') { display: none; }',
+ 0
+ );
+ }
+ uDom('#refresh').toggleClass(
+ 'disabled',
+ tabClass === '' || tabClass === 'tab_bts'
+ );
+};
+
+/******************************************************************************/
+
+var refreshTab = function() {
+ var tabClass = document.getElementById('pageSelector').value;
+ var matches = tabClass.match(/^tab_(.+)$/);
+ if ( matches === null ) {
+ return;
+ }
+ if ( matches[1] === 'bts' ) {
+ return;
+ }
+ vAPI.messaging.send(
+ 'logger-ui.js',
+ { what: 'forceReloadTab', tabId: matches[1] }
+ );
+};
+
+/******************************************************************************/
+
+var onMaxEntriesChanged = function() {
+ var raw = uDom(this).val();
+ try {
+ maxEntries = parseInt(raw, 10);
+ if ( isNaN(maxEntries) ) {
+ maxEntries = 0;
+ }
+ } catch (e) {
+ maxEntries = 0;
+ }
+
+ vAPI.messaging.send('logger-ui.js', {
+ what: 'userSettings',
+ name: 'maxLoggedRequests',
+ value: maxEntries
+ });
+
+ truncateLog(maxEntries);
+};
+
+/******************************************************************************/
+
+var rowFilterer = (function() {
+ var filters = [];
+
+ var parseInput = function() {
+ filters = [];
+
+ var rawPart, hardBeg, hardEnd;
+ var raw = uDom('#filterInput').val().trim();
+ var rawParts = raw.split(/\s+/);
+ var reStr, reStrs = [], not = false;
+ var n = rawParts.length;
+ for ( var i = 0; i < n; i++ ) {
+ rawPart = rawParts[i];
+ if ( rawPart.charAt(0) === '!' ) {
+ if ( reStrs.length === 0 ) {
+ not = true;
+ }
+ rawPart = rawPart.slice(1);
+ }
+ hardBeg = rawPart.charAt(0) === '|';
+ if ( hardBeg ) {
+ rawPart = rawPart.slice(1);
+ }
+ hardEnd = rawPart.slice(-1) === '|';
+ if ( hardEnd ) {
+ rawPart = rawPart.slice(0, -1);
+ }
+ if ( rawPart === '' ) {
+ continue;
+ }
+ // https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
+ reStr = rawPart.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ if ( hardBeg ) {
+ reStr = '(?:^|\\s)' + reStr;
+ }
+ if ( hardEnd ) {
+ reStr += '(?:\\s|$)';
+ }
+ reStrs.push(reStr);
+ if ( i < (n - 1) && rawParts[i + 1] === '||' ) {
+ i += 1;
+ continue;
+ }
+ reStr = reStrs.length === 1 ? reStrs[0] : reStrs.join('|');
+ filters.push({
+ re: new RegExp(reStr, 'i'),
+ r: !not
+ });
+ reStrs = [];
+ not = false;
+ }
+ };
+
+ var filterOne = function(tr, clean) {
+ var ff = filters;
+ var fcount = ff.length;
+ if ( fcount === 0 && clean === true ) {
+ return;
+ }
+ // do not filter out doc boundaries, they help separate important
+ // section of log.
+ var cl = tr.classList;
+ if ( cl.contains('doc') ) {
+ return;
+ }
+ if ( fcount === 0 ) {
+ cl.remove('f');
+ return;
+ }
+ var cc = tr.cells;
+ var ccount = cc.length;
+ var hit, j, f;
+ // each filter expression must hit (implicit and-op)
+ // if...
+ // positive filter expression = there must one hit on any field
+ // negative filter expression = there must be no hit on all fields
+ for ( var i = 0; i < fcount; i++ ) {
+ f = ff[i];
+ hit = !f.r;
+ for ( j = 0; j < ccount; j++ ) {
+ if ( f.re.test(cc[j].textContent) ) {
+ hit = f.r;
+ break;
+ }
+ }
+ if ( !hit ) {
+ cl.add('f');
+ return;
+ }
+ }
+ cl.remove('f');
+ };
+
+ var filterAll = function() {
+ // Special case: no filter
+ if ( filters.length === 0 ) {
+ uDom('#content tr').removeClass('f');
+ return;
+ }
+ var tbody = document.querySelector('#content tbody');
+ var rows = tbody.rows;
+ var i = rows.length;
+ while ( i-- ) {
+ filterOne(rows[i]);
+ }
+ };
+
+ var onFilterChangedAsync = (function() {
+ var timer = null;
+ var commit = function() {
+ timer = null;
+ parseInput();
+ filterAll();
+ };
+ return function() {
+ if ( timer !== null ) {
+ clearTimeout(timer);
+ }
+ timer = vAPI.setTimeout(commit, 750);
+ };
+ })();
+
+ var onFilterButton = function() {
+ var cl = document.body.classList;
+ cl.toggle('f', cl.contains('f') === false);
+ };
+
+ uDom('#filterButton').on('click', onFilterButton);
+ uDom('#filterInput').on('input', onFilterChangedAsync);
+
+ return {
+ filterOne: filterOne,
+ filterAll: filterAll
+ };
+})();
+
+/******************************************************************************/
+
+var toJunkyard = function(trs) {
+ trs.remove();
+ var i = trs.length;
+ while ( i-- ) {
+ trJunkyard.push(trs.nodeAt(i));
+ }
+};
+
+/******************************************************************************/
+
+var clearBuffer = function() {
+ var tbody = document.querySelector('#content tbody');
+ var tr;
+ while ( tbody.firstChild !== null ) {
+ tr = tbody.lastElementChild;
+ trJunkyard.push(tbody.removeChild(tr));
+ }
+ uDom('#clear').addClass('disabled');
+ uDom('#clean').addClass('disabled');
+};
+
+/******************************************************************************/
+
+var cleanBuffer = function() {
+ var rows = uDom('#content tr.tab:not(.canMtx)').remove();
+ var i = rows.length;
+ while ( i-- ) {
+ trJunkyard.push(rows.nodeAt(i));
+ }
+ uDom('#clean').addClass('disabled');
+};
+
+/******************************************************************************/
+
+var toggleCompactView = function() {
+ document.body.classList.toggle('compactView');
+ uDom('#content table .vExpanded').removeClass('vExpanded');
+};
+
+var toggleCompactRow = function(ev) {
+ ev.target.parentElement.classList.toggle('vExpanded');
+};
+
+/******************************************************************************/
+
+var popupManager = (function() {
+ var realTabId = null;
+ var localTabId = null;
+ var container = null;
+ var popup = null;
+ var popupObserver = null;
+ var style = null;
+ var styleTemplate = [
+ 'tr:not(.tab_{{tabId}}) {',
+ 'cursor: not-allowed;',
+ 'opacity: 0.2;',
+ '}'
+ ].join('\n');
+
+ var resizePopup = function() {
+ if ( popup === null ) {
+ return;
+ }
+ var popupBody = popup.contentWindow.document.body;
+ if ( popupBody.clientWidth !== 0 && container.clientWidth !== popupBody.clientWidth ) {
+ container.style.setProperty('width', popupBody.clientWidth + 'px');
+ }
+ popup.style.removeProperty('height');
+ if ( popupBody.clientHeight !== 0 && popup.clientHeight !== popupBody.clientHeight ) {
+ popup.style.setProperty('height', popupBody.clientHeight + 'px');
+ }
+ var ph = document.documentElement.clientHeight;
+ var crect = container.getBoundingClientRect();
+ if ( crect.height > ph ) {
+ popup.style.setProperty('height', 'calc(' + ph + 'px - 1.8em)');
+ }
+ // Adjust width for presence/absence of vertical scroll bar which may
+ // have appeared as a result of last operation.
+ var cw = container.clientWidth;
+ var dw = popup.contentWindow.document.documentElement.clientWidth;
+ if ( cw !== dw ) {
+ container.style.setProperty('width', (2 * cw - dw) + 'px');
+ }
+ };
+
+ var toggleSize = function() {
+ container.classList.toggle('hide');
+ };
+
+ var onResizeRequested = function() {
+ var popupBody = popup.contentWindow.document.body;
+ if ( popupBody.hasAttribute('data-resize-popup') === false ) {
+ return;
+ }
+ popupBody.removeAttribute('data-resize-popup');
+ resizePopup();
+ };
+
+ var onLoad = function() {
+ resizePopup();
+ var popupBody = popup.contentDocument.body;
+ popupBody.removeAttribute('data-resize-popup');
+ popupObserver.observe(popupBody, {
+ attributes: true,
+ attributesFilter: [ 'data-resize-popup' ]
+ });
+ };
+
+ var toggleOn = function(td) {
+ var tr = td.parentNode;
+ var matches = tr.className.match(/(?:^| )tab_([^ ]+)/);
+ if ( matches === null ) {
+ return;
+ }
+ realTabId = localTabId = matches[1];
+ if ( localTabId === 'bts' ) {
+ realTabId = noTabId;
+ }
+
+ container = document.getElementById('popupContainer');
+
+ container.querySelector('div > span:nth-of-type(1)').addEventListener('click', toggleSize);
+ container.querySelector('div > span:nth-of-type(2)').addEventListener('click', toggleOff);
+
+ popup = document.createElement('iframe');
+ popup.addEventListener('load', onLoad);
+ popup.setAttribute('src', 'popup.html?tabId=' + realTabId);
+ popupObserver = new MutationObserver(onResizeRequested);
+ container.appendChild(popup);
+
+ style = document.getElementById('popupFilterer');
+ style.textContent = styleTemplate.replace('{{tabId}}', localTabId);
+
+ document.body.classList.add('popupOn');
+ };
+
+ var toggleOff = function() {
+ document.body.classList.remove('popupOn');
+
+ container.querySelector('div > span:nth-of-type(1)').removeEventListener('click', toggleSize);
+ container.querySelector('div > span:nth-of-type(2)').removeEventListener('click', toggleOff);
+ container.classList.remove('hide');
+
+ popup.removeEventListener('load', onLoad);
+ popupObserver.disconnect();
+ popupObserver = null;
+ popup.setAttribute('src', '');
+ container.removeChild(popup);
+ popup = null;
+
+ style.textContent = '';
+ style = null;
+
+ container = null;
+ realTabId = null;
+ };
+
+ var exports = {
+ toggleOn: function(ev) {
+ if ( realTabId === null ) {
+ toggleOn(ev.target);
+ }
+ },
+ toggleOff: function() {
+ if ( realTabId !== null ) {
+ toggleOff();
+ }
+ }
+ };
+
+ Object.defineProperty(exports, 'tabId', {
+ get: function() { return realTabId || 0; }
+ });
+
+ return exports;
+})();
+
+/******************************************************************************/
+
+var grabView = function() {
+ if ( ownerId === undefined ) {
+ ownerId = Date.now();
+ }
+ readLogBufferAsync();
+};
+
+var releaseView = function() {
+ if ( ownerId === undefined ) { return; }
+ vAPI.messaging.send(
+ 'logger-ui.js',
+ { what: 'releaseView', ownerId: ownerId }
+ );
+ ownerId = undefined;
+};
+
+window.addEventListener('pagehide', releaseView);
+window.addEventListener('pageshow', grabView);
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1398625
+window.addEventListener('beforeunload', releaseView);
+
+/******************************************************************************/
+
+readLogBuffer();
+
+uDom('#pageSelector').on('change', pageSelectorChanged);
+uDom('#refresh').on('click', refreshTab);
+uDom('#compactViewToggler').on('click', toggleCompactView);
+uDom('#clean').on('click', cleanBuffer);
+uDom('#clear').on('click', clearBuffer);
+uDom('#maxEntries').on('change', onMaxEntriesChanged);
+uDom('#content table').on('click', 'tr > td:nth-of-type(1)', toggleCompactRow);
+uDom('#content table').on('click', 'tr.canMtx > td:nth-of-type(2)', popupManager.toggleOn);
+
+/******************************************************************************/
+
+})();
diff --git a/js/logger.js b/js/logger.js
new file mode 100644
index 0000000..896fbdf
--- /dev/null
+++ b/js/logger.js
@@ -0,0 +1,93 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2015-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uBlock
+*/
+
+'use strict';
+
+/******************************************************************************/
+/******************************************************************************/
+
+µMatrix.logger = (function() {
+
+ var LogEntry = function(args) {
+ this.init(args);
+ };
+
+ LogEntry.prototype.init = function(args) {
+ this.tstamp = Date.now();
+ this.tab = args[0] || '';
+ this.cat = args[1] || '';
+ this.d0 = args[2];
+ this.d1 = args[3];
+ this.d2 = args[4];
+ this.d3 = args[5];
+ };
+
+ var buffer = null;
+ var lastReadTime = 0;
+ var writePtr = 0;
+
+ // After 60 seconds without being read, a buffer will be considered
+ // unused, and thus removed from memory.
+ var logBufferObsoleteAfter = 30 * 1000;
+
+ var janitor = function() {
+ if (
+ buffer !== null &&
+ lastReadTime < (Date.now() - logBufferObsoleteAfter)
+ ) {
+ buffer = null;
+ writePtr = 0;
+ api.ownerId = undefined;
+ }
+ if ( buffer !== null ) {
+ vAPI.setTimeout(janitor, logBufferObsoleteAfter);
+ }
+ };
+
+ var api = {
+ ownerId: undefined,
+ writeOne: function() {
+ if ( buffer === null ) { return; }
+ if ( writePtr === buffer.length ) {
+ buffer.push(new LogEntry(arguments));
+ } else {
+ buffer[writePtr].init(arguments);
+ }
+ writePtr += 1;
+ },
+ readAll: function(ownerId) {
+ this.ownerId = ownerId;
+ if ( buffer === null ) {
+ buffer = [];
+ vAPI.setTimeout(janitor, logBufferObsoleteAfter);
+ }
+ var out = buffer.slice(0, writePtr);
+ writePtr = 0;
+ lastReadTime = Date.now();
+ return out;
+ }
+ };
+
+ return api;
+})();
+
+/******************************************************************************/
diff --git a/js/main-blocked.js b/js/main-blocked.js
new file mode 100644
index 0000000..cb11910
--- /dev/null
+++ b/js/main-blocked.js
@@ -0,0 +1,176 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2015-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uBlock
+*/
+
+/* global uDom */
+
+'use strict';
+
+/******************************************************************************/
+
+(function() {
+
+/******************************************************************************/
+
+var details = {};
+
+(function() {
+ var matches = /details=([^&]+)/.exec(window.location.search);
+ if ( matches === null ) { return; }
+ try {
+ details = JSON.parse(atob(matches[1]));
+ } catch(ex) {
+ }
+})();
+
+/******************************************************************************/
+
+uDom('.what').text(details.url);
+// uDom('#why').text(details.why.slice(3));
+
+/******************************************************************************/
+
+// https://github.com/gorhill/uMatrix/issues/502
+// Code below originally imported from:
+// https://github.com/gorhill/uBlock/blob/master/src/js/document-blocked.js
+
+(function() {
+ if ( typeof URL !== 'function' ) { return; }
+
+ var reURL = /^https?:\/\//;
+
+ var liFromParam = function(name, value) {
+ if ( value === '' ) {
+ value = name;
+ name = '';
+ }
+ var li = document.createElement('li');
+ var span = document.createElement('span');
+ span.textContent = name;
+ li.appendChild(span);
+ if ( name !== '' && value !== '' ) {
+ li.appendChild(document.createTextNode(' = '));
+ }
+ span = document.createElement('span');
+ if ( reURL.test(value) ) {
+ var a = document.createElement('a');
+ a.href = a.textContent = value;
+ span.appendChild(a);
+ } else {
+ span.textContent = value;
+ }
+ li.appendChild(span);
+ return li;
+ };
+
+ var safeDecodeURIComponent = function(s) {
+ try {
+ s = decodeURIComponent(s);
+ } catch (ex) {
+ }
+ return s;
+ };
+
+ var renderParams = function(parentNode, rawURL) {
+ var a = document.createElement('a');
+ a.href = rawURL;
+ if ( a.search.length === 0 ) { return false; }
+
+ var pos = rawURL.indexOf('?');
+ var li = liFromParam(
+ vAPI.i18n('docblockedNoParamsPrompt'),
+ rawURL.slice(0, pos)
+ );
+ parentNode.appendChild(li);
+
+ var params = a.search.slice(1).split('&');
+ var param, name, value, ul;
+ for ( var i = 0; i < params.length; i++ ) {
+ param = params[i];
+ pos = param.indexOf('=');
+ if ( pos === -1 ) {
+ pos = param.length;
+ }
+ name = safeDecodeURIComponent(param.slice(0, pos));
+ value = safeDecodeURIComponent(param.slice(pos + 1));
+ li = liFromParam(name, value);
+ if ( reURL.test(value) ) {
+ ul = document.createElement('ul');
+ renderParams(ul, value);
+ li.appendChild(ul);
+ }
+ parentNode.appendChild(li);
+ }
+ return true;
+ };
+
+ if ( renderParams(uDom.nodeFromId('parsed'), details.url) === false ) {
+ return;
+ }
+
+ var toggler = document.createElement('span');
+ toggler.className = 'fa';
+ uDom('#theURL > p').append(toggler);
+
+ uDom(toggler).on('click', function() {
+ var collapsed = uDom.nodeFromId('theURL').classList.toggle('collapsed');
+ vAPI.localStorage.setItem(
+ 'document-blocked-collapse-url',
+ collapsed.toString()
+ );
+ });
+
+ uDom.nodeFromId('theURL').classList.toggle(
+ 'collapsed',
+ vAPI.localStorage.getItem('document-blocked-collapse-url') === 'true'
+ );
+})();
+
+/******************************************************************************/
+
+if ( window.history.length > 1 ) {
+ uDom('#back').on('click', function() { window.history.back(); });
+ uDom('#bye').css('display', 'none');
+} else {
+ uDom('#bye').on('click', function() { window.close(); });
+ uDom('#back').css('display', 'none');
+}
+
+/******************************************************************************/
+
+// See if the target hostname is still blacklisted, and if not, navigate to it.
+
+vAPI.messaging.send('main-blocked.js', {
+ what: 'mustBlock',
+ scope: details.hn,
+ hostname: details.hn,
+ type: 'doc'
+}, function(response) {
+ if ( response === false ) {
+ window.location.replace(details.url);
+ }
+});
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
diff --git a/js/matrix.js b/js/matrix.js
new file mode 100644
index 0000000..59eb84a
--- /dev/null
+++ b/js/matrix.js
@@ -0,0 +1,873 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global punycode */
+/* jshint bitwise: false */
+
+'use strict';
+
+/******************************************************************************/
+
+µMatrix.Matrix = (function() {
+
+/******************************************************************************/
+
+var µm = µMatrix;
+var magicId = 'axyorpwxtmnf';
+var uniqueIdGenerator = 1;
+
+/******************************************************************************/
+
+var Matrix = function() {
+ this.id = uniqueIdGenerator++;
+ this.reset();
+ this.sourceRegister = '';
+ this.decomposedSourceRegister = [''];
+ this.specificityRegister = 0;
+};
+
+/******************************************************************************/
+
+Matrix.Transparent = 0;
+Matrix.Red = 1;
+Matrix.Green = 2;
+Matrix.Gray = 3;
+
+Matrix.Indirect = 0x00;
+Matrix.Direct = 0x80;
+
+Matrix.RedDirect = Matrix.Red | Matrix.Direct;
+Matrix.RedIndirect = Matrix.Red | Matrix.Indirect;
+Matrix.GreenDirect = Matrix.Green | Matrix.Direct;
+Matrix.GreenIndirect = Matrix.Green | Matrix.Indirect;
+Matrix.GrayDirect = Matrix.Gray | Matrix.Direct;
+Matrix.GrayIndirect = Matrix.Gray | Matrix.Indirect;
+
+/******************************************************************************/
+
+var typeBitOffsets = new Map([
+ [ '*', 0 ],
+ [ 'doc', 2 ],
+ [ 'cookie', 4 ],
+ [ 'css', 6 ],
+ [ 'image', 8 ],
+ [ 'media', 10 ],
+ [ 'script', 12 ],
+ [ 'xhr', 14 ],
+ [ 'frame', 16 ],
+ [ 'other', 18 ]
+]);
+
+var stateToNameMap = new Map([
+ [ 1, 'block' ],
+ [ 2, 'allow' ],
+ [ 3, 'inherit' ]
+]);
+
+var nameToStateMap = {
+ 'block': 1,
+ 'allow': 2,
+ 'noop': 2,
+ 'inherit': 3
+};
+
+var switchBitOffsets = new Map([
+ [ 'matrix-off', 0 ],
+ [ 'https-strict', 2 ],
+ /* 4 is now unused, formerly assigned to UA spoofing */
+ [ 'referrer-spoof', 6 ],
+ [ 'noscript-spoof', 8 ],
+ [ 'no-workers', 10 ]
+]);
+
+var switchStateToNameMap = new Map([
+ [ 1, 'true' ],
+ [ 2, 'false' ]
+]);
+
+var nameToSwitchStateMap = {
+ 'true': 1,
+ 'false': 2
+};
+
+/******************************************************************************/
+
+Matrix.columnHeaderIndices = (function() {
+ var out = new Map(),
+ i = 0;
+ for ( var type of typeBitOffsets.keys() ) {
+ out.set(type, i++);
+ }
+ return out;
+})();
+
+
+Matrix.switchNames = new Set(switchBitOffsets.keys());
+
+/******************************************************************************/
+
+// For performance purpose, as simple tests as possible
+var reHostnameVeryCoarse = /[g-z_-]/;
+var reIPv4VeryCoarse = /\.\d+$/;
+
+// http://tools.ietf.org/html/rfc5952
+// 4.3: "MUST be represented in lowercase"
+// Also: http://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_network_resource_identifiers
+
+var isIPAddress = function(hostname) {
+ if ( reHostnameVeryCoarse.test(hostname) ) {
+ return false;
+ }
+ if ( reIPv4VeryCoarse.test(hostname) ) {
+ return true;
+ }
+ return hostname.charAt(0) === '[';
+};
+
+/******************************************************************************/
+
+var toBroaderHostname = function(hostname) {
+ if ( hostname === '*' ) { return ''; }
+ if ( isIPAddress(hostname) ) {
+ return toBroaderIPAddress(hostname);
+ }
+ var pos = hostname.indexOf('.');
+ if ( pos === -1 ) {
+ return '*';
+ }
+ return hostname.slice(pos + 1);
+};
+
+var toBroaderIPAddress = function(ipaddress) {
+ // Can't broaden IPv6 (for now)
+ if ( ipaddress.charAt(0) === '[' ) {
+ return '*';
+ }
+ var pos = ipaddress.lastIndexOf('.');
+ return pos !== -1 ? ipaddress.slice(0, pos) : '*';
+};
+
+Matrix.toBroaderHostname = toBroaderHostname;
+
+/******************************************************************************/
+
+// Find out src-des relationship, using coarse-to-fine grained tests for
+// speed. If desHostname is 1st-party to srcHostname, the domain is returned,
+// otherwise the empty string.
+
+var extractFirstPartyDesDomain = function(srcHostname, desHostname) {
+ if ( srcHostname === '*' || desHostname === '*' || desHostname === '1st-party' ) {
+ return '';
+ }
+ var µmuri = µm.URI;
+ var srcDomain = µmuri.domainFromHostname(srcHostname) || srcHostname;
+ var desDomain = µmuri.domainFromHostname(desHostname) || desHostname;
+ return desDomain === srcDomain ? desDomain : '';
+};
+
+/******************************************************************************/
+
+Matrix.prototype.reset = function() {
+ this.switches = new Map();
+ this.rules = new Map();
+ this.rootValue = Matrix.RedIndirect;
+ this.modifiedTime = 0;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.decomposeSource = function(srcHostname) {
+ if ( srcHostname === this.sourceRegister ) { return; }
+ var hn = srcHostname;
+ this.decomposedSourceRegister[0] = this.sourceRegister = hn;
+ var i = 1;
+ for (;;) {
+ hn = toBroaderHostname(hn);
+ this.decomposedSourceRegister[i++] = hn;
+ if ( hn === '' ) { break; }
+ }
+};
+
+/******************************************************************************/
+
+// Copy another matrix to self. Do this incrementally to minimize impact on
+// a live matrix.
+
+Matrix.prototype.assign = function(other) {
+ var k, entry;
+ // Remove rules not in other
+ for ( k of this.rules.keys() ) {
+ if ( other.rules.has(k) === false ) {
+ this.rules.delete(k);
+ }
+ }
+ // Remove switches not in other
+ for ( k of this.switches.keys() ) {
+ if ( other.switches.has(k) === false ) {
+ this.switches.delete(k);
+ }
+ }
+ // Add/change rules in other
+ for ( entry of other.rules ) {
+ this.rules.set(entry[0], entry[1]);
+ }
+ // Add/change switches in other
+ for ( entry of other.switches ) {
+ this.switches.set(entry[0], entry[1]);
+ }
+ this.modifiedTime = other.modifiedTime;
+ return this;
+};
+
+// https://www.youtube.com/watch?v=e9RS4biqyAc
+
+/******************************************************************************/
+
+// If value is undefined, the switch is removed
+
+Matrix.prototype.setSwitch = function(switchName, srcHostname, newVal) {
+ var bitOffset = switchBitOffsets.get(switchName);
+ if ( bitOffset === undefined ) {
+ return false;
+ }
+ if ( newVal === this.evaluateSwitch(switchName, srcHostname) ) {
+ return false;
+ }
+ var bits = this.switches.get(srcHostname) || 0;
+ bits &= ~(3 << bitOffset);
+ bits |= newVal << bitOffset;
+ if ( bits === 0 ) {
+ this.switches.delete(srcHostname);
+ } else {
+ this.switches.set(srcHostname, bits);
+ }
+ this.modifiedTime = Date.now();
+ return true;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.setCell = function(srcHostname, desHostname, type, state) {
+ var bitOffset = typeBitOffsets.get(type),
+ k = srcHostname + ' ' + desHostname,
+ oldBitmap = this.rules.get(k);
+ if ( oldBitmap === undefined ) {
+ oldBitmap = 0;
+ }
+ var newBitmap = oldBitmap & ~(3 << bitOffset) | (state << bitOffset);
+ if ( newBitmap === oldBitmap ) {
+ return false;
+ }
+ if ( newBitmap === 0 ) {
+ this.rules.delete(k);
+ } else {
+ this.rules.set(k, newBitmap);
+ }
+ this.modifiedTime = Date.now();
+ return true;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.blacklistCell = function(srcHostname, desHostname, type) {
+ var r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 1 ) {
+ return false;
+ }
+ this.setCell(srcHostname, desHostname, type, 0);
+ r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 1 ) {
+ return true;
+ }
+ this.setCell(srcHostname, desHostname, type, 1);
+ return true;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.whitelistCell = function(srcHostname, desHostname, type) {
+ var r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 2 ) {
+ return false;
+ }
+ this.setCell(srcHostname, desHostname, type, 0);
+ r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 2 ) {
+ return true;
+ }
+ this.setCell(srcHostname, desHostname, type, 2);
+ return true;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.graylistCell = function(srcHostname, desHostname, type) {
+ var r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 0 || r === 3 ) {
+ return false;
+ }
+ this.setCell(srcHostname, desHostname, type, 0);
+ r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 0 || r === 3 ) {
+ return true;
+ }
+ this.setCell(srcHostname, desHostname, type, 3);
+ return true;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.evaluateCell = function(srcHostname, desHostname, type) {
+ var key = srcHostname + ' ' + desHostname;
+ var bitmap = this.rules.get(key);
+ if ( bitmap === undefined ) {
+ return 0;
+ }
+ return bitmap >> typeBitOffsets.get(type) & 3;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.evaluateCellZ = function(srcHostname, desHostname, type) {
+ this.decomposeSource(srcHostname);
+
+ var bitOffset = typeBitOffsets.get(type),
+ s, v, i = 0;
+ for (;;) {
+ s = this.decomposedSourceRegister[i++];
+ if ( s === '' ) { break; }
+ v = this.rules.get(s + ' ' + desHostname);
+ if ( v !== undefined ) {
+ v = v >> bitOffset & 3;
+ if ( v !== 0 ) {
+ return v;
+ }
+ }
+ }
+ // srcHostname is '*' at this point
+
+ // Preset blacklisted hostnames are blacklisted in global scope
+ if ( type === '*' && µm.ubiquitousBlacklist.test(desHostname) ) {
+ return 1;
+ }
+
+ // https://github.com/gorhill/uMatrix/issues/65
+ // Hardcoded global `doc` rule
+ if ( type === 'doc' && desHostname === '*' ) {
+ return 2;
+ }
+
+ return 0;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.evaluateCellZXY = function(srcHostname, desHostname, type) {
+ // Matrix filtering switch
+ this.specificityRegister = 0;
+ if ( this.evaluateSwitchZ('matrix-off', srcHostname) ) {
+ return Matrix.GreenIndirect;
+ }
+
+ // TODO: There are cells evaluated twice when the type is '*'. Unsure
+ // whether it's worth trying to avoid that, as this could introduce
+ // overhead which may not be gained back by skipping the redundant tests.
+ // And this happens *only* when building the matrix UI, not when
+ // evaluating net requests.
+
+ // Specific-hostname specific-type cell
+ this.specificityRegister = 1;
+ var r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( r === 1 ) { return Matrix.RedDirect; }
+ if ( r === 2 ) { return Matrix.GreenDirect; }
+
+ // Specific-hostname any-type cell
+ this.specificityRegister = 2;
+ var rl = this.evaluateCellZ(srcHostname, desHostname, '*');
+ if ( rl === 1 ) { return Matrix.RedIndirect; }
+
+ var d = desHostname;
+ var firstPartyDesDomain = extractFirstPartyDesDomain(srcHostname, desHostname);
+
+ // Ancestor cells, up to 1st-party destination domain
+ if ( firstPartyDesDomain !== '' ) {
+ this.specificityRegister = 3;
+ for (;;) {
+ if ( d === firstPartyDesDomain ) { break; }
+ d = d.slice(d.indexOf('.') + 1);
+
+ // specific-hostname specific-type cell
+ r = this.evaluateCellZ(srcHostname, d, type);
+ if ( r === 1 ) { return Matrix.RedIndirect; }
+ if ( r === 2 ) { return Matrix.GreenIndirect; }
+ // Do not override a narrower rule
+ if ( rl !== 2 ) {
+ rl = this.evaluateCellZ(srcHostname, d, '*');
+ if ( rl === 1 ) { return Matrix.RedIndirect; }
+ }
+ }
+
+ // 1st-party specific-type cell: it's a special row, looked up only
+ // when destination is 1st-party to source.
+ r = this.evaluateCellZ(srcHostname, '1st-party', type);
+ if ( r === 1 ) { return Matrix.RedIndirect; }
+ if ( r === 2 ) { return Matrix.GreenIndirect; }
+ // Do not override narrower rule
+ if ( rl !== 2 ) {
+ rl = this.evaluateCellZ(srcHostname, '1st-party', '*');
+ if ( rl === 1 ) { return Matrix.RedIndirect; }
+ }
+ }
+
+ // Keep going, up to root
+ this.specificityRegister = 4;
+ for (;;) {
+ d = toBroaderHostname(d);
+ if ( d === '*' ) { break; }
+
+ // specific-hostname specific-type cell
+ r = this.evaluateCellZ(srcHostname, d, type);
+ if ( r === 1 ) { return Matrix.RedIndirect; }
+ if ( r === 2 ) { return Matrix.GreenIndirect; }
+ // Do not override narrower rule
+ if ( rl !== 2 ) {
+ rl = this.evaluateCellZ(srcHostname, d, '*');
+ if ( rl === 1 ) { return Matrix.RedIndirect; }
+ }
+ }
+
+ // Any-hostname specific-type cells
+ this.specificityRegister = 5;
+ r = this.evaluateCellZ(srcHostname, '*', type);
+ // Line below is strict-blocking
+ if ( r === 1 ) { return Matrix.RedIndirect; }
+ // Narrower rule wins
+ if ( rl === 2 ) { return Matrix.GreenIndirect; }
+ if ( r === 2 ) { return Matrix.GreenIndirect; }
+
+ // Any-hostname any-type cell
+ this.specificityRegister = 6;
+ r = this.evaluateCellZ(srcHostname, '*', '*');
+ if ( r === 1 ) { return Matrix.RedIndirect; }
+ if ( r === 2 ) { return Matrix.GreenIndirect; }
+ return this.rootValue;
+};
+
+// https://www.youtube.com/watch?v=4C5ZkwrnVfM
+
+/******************************************************************************/
+
+Matrix.prototype.evaluateRowZXY = function(srcHostname, desHostname) {
+ var out = [];
+ for ( var type of typeBitOffsets.keys() ) {
+ out.push(this.evaluateCellZXY(srcHostname, desHostname, type));
+ }
+ return out;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.mustBlock = function(srcHostname, desHostname, type) {
+ return (this.evaluateCellZXY(srcHostname, desHostname, type) & 3) === Matrix.Red;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.srcHostnameFromRule = function(rule) {
+ return rule.slice(0, rule.indexOf(' '));
+};
+
+/******************************************************************************/
+
+Matrix.prototype.desHostnameFromRule = function(rule) {
+ return rule.slice(rule.indexOf(' ') + 1);
+};
+
+/******************************************************************************/
+
+Matrix.prototype.setSwitchZ = function(switchName, srcHostname, newState) {
+ var bitOffset = switchBitOffsets.get(switchName);
+ if ( bitOffset === undefined ) {
+ return false;
+ }
+ var state = this.evaluateSwitchZ(switchName, srcHostname);
+ if ( newState === state ) {
+ return false;
+ }
+ if ( newState === undefined ) {
+ newState = !state;
+ }
+ var bits = this.switches.get(srcHostname) || 0;
+ bits &= ~(3 << bitOffset);
+ if ( bits === 0 ) {
+ this.switches.delete(srcHostname);
+ } else {
+ this.switches.set(srcHostname, bits);
+ }
+ this.modifiedTime = Date.now();
+ state = this.evaluateSwitchZ(switchName, srcHostname);
+ if ( state === newState ) {
+ return true;
+ }
+ this.switches.set(srcHostname, bits | ((newState ? 1 : 2) << bitOffset));
+ return true;
+};
+
+/******************************************************************************/
+
+// 0 = inherit from broader scope, up to default state
+// 1 = non-default state
+// 2 = forced default state (to override a broader non-default state)
+
+Matrix.prototype.evaluateSwitch = function(switchName, srcHostname) {
+ var bits = this.switches.get(srcHostname) || 0;
+ if ( bits === 0 ) {
+ return 0;
+ }
+ var bitOffset = switchBitOffsets.get(switchName);
+ if ( bitOffset === undefined ) {
+ return 0;
+ }
+ return (bits >> bitOffset) & 3;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.evaluateSwitchZ = function(switchName, srcHostname) {
+ var bitOffset = switchBitOffsets.get(switchName);
+ if ( bitOffset === undefined ) { return false; }
+
+ this.decomposeSource(srcHostname);
+
+ var s, bits, i = 0;
+ for (;;) {
+ s = this.decomposedSourceRegister[i++];
+ if ( s === '' ) { break; }
+ bits = this.switches.get(s) || 0;
+ if ( bits !== 0 ) {
+ bits = bits >> bitOffset & 3;
+ if ( bits !== 0 ) {
+ return bits === 1;
+ }
+ }
+ }
+ return false;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.extractAllSourceHostnames = (function() {
+ var cachedResult = new Set();
+ var matrixId = 0;
+ var readTime = 0;
+
+ return function() {
+ if ( matrixId !== this.id || readTime !== this.modifiedTime ) {
+ cachedResult.clear();
+ for ( var rule of this.rules.keys() ) {
+ cachedResult.add(rule.slice(0, rule.indexOf(' ')));
+ }
+ matrixId = this.id;
+ readTime = this.modifiedTime;
+ }
+ return cachedResult;
+ };
+})();
+
+/******************************************************************************/
+
+Matrix.prototype.toString = function() {
+ var out = [];
+ var rule, type, switchName, val;
+ var srcHostname, desHostname;
+ for ( rule of this.rules.keys() ) {
+ srcHostname = this.srcHostnameFromRule(rule);
+ desHostname = this.desHostnameFromRule(rule);
+ for ( type of typeBitOffsets.keys() ) {
+ val = this.evaluateCell(srcHostname, desHostname, type);
+ if ( val === 0 ) { continue; }
+ out.push(
+ punycode.toUnicode(srcHostname) + ' ' +
+ punycode.toUnicode(desHostname) + ' ' +
+ type + ' ' +
+ stateToNameMap.get(val)
+ );
+ }
+ }
+ for ( srcHostname of this.switches.keys() ) {
+ for ( switchName of switchBitOffsets.keys() ) {
+ val = this.evaluateSwitch(switchName, srcHostname);
+ if ( val === 0 ) { continue; }
+ out.push(switchName + ': ' + srcHostname + ' ' + switchStateToNameMap.get(val));
+ }
+ }
+ return out.sort().join('\n');
+};
+
+/******************************************************************************/
+
+Matrix.prototype.fromString = function(text, append) {
+ var matrix = append ? this : new Matrix();
+ var textEnd = text.length;
+ var lineBeg = 0, lineEnd;
+ var line, pos;
+ var fields, fieldVal;
+ var switchName;
+ var srcHostname = '';
+ var desHostname = '';
+ var type, state;
+
+ while ( lineBeg < textEnd ) {
+ lineEnd = text.indexOf('\n', lineBeg);
+ if ( lineEnd < 0 ) {
+ lineEnd = text.indexOf('\r', lineBeg);
+ if ( lineEnd < 0 ) {
+ lineEnd = textEnd;
+ }
+ }
+ line = text.slice(lineBeg, lineEnd).trim();
+ lineBeg = lineEnd + 1;
+
+ pos = line.indexOf('# ');
+ if ( pos !== -1 ) {
+ line = line.slice(0, pos).trim();
+ }
+ if ( line === '' ) {
+ continue;
+ }
+
+ fields = line.split(/\s+/);
+
+ // Less than 2 fields makes no sense
+ if ( fields.length < 2 ) {
+ continue;
+ }
+
+ fieldVal = fields[0];
+
+ // Special directives:
+
+ // title
+ pos = fieldVal.indexOf('title:');
+ if ( pos !== -1 ) {
+ // TODO
+ continue;
+ }
+
+ // Name
+ pos = fieldVal.indexOf('name:');
+ if ( pos !== -1 ) {
+ // TODO
+ continue;
+ }
+
+ // Switch on/off
+
+ // `switch:` srcHostname state
+ // state = [`true`, `false`]
+ switchName = '';
+ if ( fieldVal === 'switch:' || fieldVal === 'matrix:' ) {
+ fieldVal = 'matrix-off:';
+ }
+ pos = fieldVal.indexOf(':');
+ if ( pos !== -1 ) {
+ switchName = fieldVal.slice(0, pos);
+ }
+ if ( switchBitOffsets.has(switchName) ) {
+ srcHostname = punycode.toASCII(fields[1]);
+
+ // No state field: reject
+ fieldVal = fields[2];
+ if ( fieldVal === null ) {
+ continue;
+ }
+ // Unknown state: reject
+ if ( nameToSwitchStateMap.hasOwnProperty(fieldVal) === false ) {
+ continue;
+ }
+
+ // Backward compatibility:
+ // `chromium-behind-the-scene` is now `behind-the-scene`
+ if ( srcHostname === 'chromium-behind-the-scene' ) {
+ srcHostname = 'behind-the-scene';
+ }
+
+ matrix.setSwitch(switchName, srcHostname, nameToSwitchStateMap[fieldVal]);
+ continue;
+ }
+
+ // Unknown directive
+ if ( fieldVal.endsWith(':') ) {
+ continue;
+ }
+
+ // Valid rule syntax:
+
+ // srcHostname desHostname [type [state]]
+ // type = a valid request type
+ // state = [`block`, `allow`, `inherit`]
+
+ // srcHostname desHostname type
+ // type = a valid request type
+ // state = `allow`
+
+ // srcHostname desHostname
+ // type = `*`
+ // state = `allow`
+
+ // Lines with invalid syntax silently ignored
+
+ srcHostname = punycode.toASCII(fields[0]);
+ desHostname = punycode.toASCII(fields[1]);
+
+ fieldVal = fields[2];
+
+ if ( fieldVal !== undefined ) {
+ type = fieldVal;
+ // https://github.com/gorhill/uMatrix/issues/759
+ // Backward compatibility.
+ if ( type === 'plugin' ) {
+ type = 'media';
+ }
+ // Unknown type: reject
+ if ( typeBitOffsets.has(type) === false ) {
+ continue;
+ }
+ } else {
+ type = '*';
+ }
+
+ fieldVal = fields[3];
+
+ if ( fieldVal !== undefined ) {
+ // Unknown state: reject
+ if ( nameToStateMap.hasOwnProperty(fieldVal) === false ) {
+ continue;
+ }
+ state = nameToStateMap[fieldVal];
+ } else {
+ state = 2;
+ }
+
+ matrix.setCell(srcHostname, desHostname, type, state);
+ }
+
+ if ( !append ) {
+ this.assign(matrix);
+ }
+
+ this.modifiedTime = Date.now();
+};
+
+/******************************************************************************/
+
+Matrix.prototype.toSelfie = function() {
+ return {
+ magicId: magicId,
+ switches: Array.from(this.switches),
+ rules: Array.from(this.rules)
+ };
+};
+
+/******************************************************************************/
+
+Matrix.prototype.fromSelfie = function(selfie) {
+ if ( selfie.magicId !== magicId ) { return false; }
+ this.switches = new Map(selfie.switches);
+ this.rules = new Map(selfie.rules);
+ this.modifiedTime = Date.now();
+ return true;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.diff = function(other, srcHostname, desHostnames) {
+ var out = [];
+ var desHostname, type;
+ var switchName, i, thisVal, otherVal;
+ for (;;) {
+ for ( switchName of switchBitOffsets.keys() ) {
+ thisVal = this.evaluateSwitch(switchName, srcHostname);
+ otherVal = other.evaluateSwitch(switchName, srcHostname);
+ if ( thisVal !== otherVal ) {
+ out.push({
+ 'what': switchName,
+ 'src': srcHostname
+ });
+ }
+ }
+ i = desHostnames.length;
+ while ( i-- ) {
+ desHostname = desHostnames[i];
+ for ( type of typeBitOffsets.keys() ) {
+ thisVal = this.evaluateCell(srcHostname, desHostname, type);
+ otherVal = other.evaluateCell(srcHostname, desHostname, type);
+ if ( thisVal === otherVal ) { continue; }
+ out.push({
+ 'what': 'rule',
+ 'src': srcHostname,
+ 'des': desHostname,
+ 'type': type
+ });
+ }
+ }
+ srcHostname = toBroaderHostname(srcHostname);
+ if ( srcHostname === '' ) {
+ break;
+ }
+ }
+ return out;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.applyDiff = function(diff, from) {
+ var changed = false;
+ var i = diff.length;
+ var action, val;
+ while ( i-- ) {
+ action = diff[i];
+ if ( action.what === 'rule' ) {
+ val = from.evaluateCell(action.src, action.des, action.type);
+ changed = this.setCell(action.src, action.des, action.type, val) || changed;
+ continue;
+ }
+ if ( switchBitOffsets.has(action.what) ) {
+ val = from.evaluateSwitch(action.what, action.src);
+ changed = this.setSwitch(action.what, action.src, val) || changed;
+ continue;
+ }
+ }
+ return changed;
+};
+
+/******************************************************************************/
+
+return Matrix;
+
+/******************************************************************************/
+
+// https://www.youtube.com/watch?v=wlNrQGmj6oQ
+
+})();
+
+/******************************************************************************/
diff --git a/js/messaging.js b/js/messaging.js
new file mode 100644
index 0000000..d5c472f
--- /dev/null
+++ b/js/messaging.js
@@ -0,0 +1,963 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+/******************************************************************************/
+/******************************************************************************/
+
+// Default handler
+
+(function() {
+
+var µm = µMatrix;
+
+/******************************************************************************/
+
+// Default is for commonly used message.
+
+function onMessage(request, sender, callback) {
+ // Async
+ switch ( request.what ) {
+ case 'getAssetContent':
+ µm.assets.get(request.url, { dontCache: true }, callback);
+ return;
+
+ case 'selectHostsFiles':
+ µm.selectHostsFiles(request, callback);
+ return;
+
+ default:
+ break;
+ }
+
+ // Sync
+ var response;
+
+ switch ( request.what ) {
+ case 'forceReloadTab':
+ µm.forceReload(request.tabId, request.bypassCache);
+ break;
+
+ case 'forceUpdateAssets':
+ µm.scheduleAssetUpdater(0);
+ µm.assets.updateStart({ delay: 2000 });
+ break;
+
+ case 'getUserSettings':
+ response = {
+ userSettings: µm.userSettings,
+ matrixSwitches: {
+ 'https-strict': µm.pMatrix.evaluateSwitch('https-strict', '*') === 1,
+ 'referrer-spoof': µm.pMatrix.evaluateSwitch('referrer-spoof', '*') === 1,
+ 'noscript-spoof': µm.pMatrix.evaluateSwitch('noscript-spoof', '*') === 1
+ }
+ };
+ break;
+
+ case 'gotoExtensionURL':
+ µm.gotoExtensionURL(request);
+ break;
+
+ case 'gotoURL':
+ µm.gotoURL(request);
+ break;
+
+ case 'mustBlock':
+ response = µm.mustBlock(
+ request.scope,
+ request.hostname,
+ request.type
+ );
+ break;
+
+ case 'readRawSettings':
+ response = µm.stringFromRawSettings();
+ break;
+
+ case 'reloadHostsFiles':
+ µm.reloadHostsFiles();
+ break;
+
+ case 'setMatrixSwitch':
+ µm.tMatrix.setSwitch(request.switchName, '*', request.state);
+ if ( µm.pMatrix.setSwitch(request.switchName, '*', request.state) ) {
+ µm.saveMatrix();
+ }
+ break;
+
+ case 'userSettings':
+ if ( request.hasOwnProperty('value') === false ) {
+ request.value = undefined;
+ }
+ response = µm.changeUserSettings(request.name, request.value);
+ break;
+
+ case 'writeRawSettings':
+ µm.rawSettingsFromString(request.content);
+ break;
+
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
+
+ callback(response);
+}
+
+/******************************************************************************/
+
+vAPI.messaging.setup(onMessage);
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+(function() {
+
+// popup.js
+
+var µm = µMatrix;
+
+/******************************************************************************/
+
+// Constructor is faster than object literal
+
+var RowSnapshot = function(srcHostname, desHostname, desDomain) {
+ this.domain = desDomain;
+ this.temporary = µm.tMatrix.evaluateRowZXY(srcHostname, desHostname);
+ this.permanent = µm.pMatrix.evaluateRowZXY(srcHostname, desHostname);
+ this.counts = RowSnapshot.counts.slice();
+ this.totals = RowSnapshot.counts.slice();
+};
+
+RowSnapshot.counts = (function() {
+ var aa = [];
+ for ( var i = 0, n = µm.Matrix.columnHeaderIndices.size; i < n; i++ ) {
+ aa[i] = 0;
+ }
+ return aa;
+})();
+
+/******************************************************************************/
+
+var matrixSnapshot = function(pageStore, details) {
+ var µmuser = µm.userSettings;
+ var headerIndices = µm.Matrix.columnHeaderIndices;
+
+ var r = {
+ appVersion: vAPI.app.version,
+ blockedCount: pageStore.requestStats.blocked.all,
+ collapseAllDomains: µmuser.popupCollapseAllDomains,
+ collapseBlacklistedDomains: µmuser.popupCollapseBlacklistedDomains,
+ diff: [],
+ domain: pageStore.pageDomain,
+ has3pReferrer: pageStore.has3pReferrer,
+ hasMixedContent: pageStore.hasMixedContent,
+ hasNoscriptTags: pageStore.hasNoscriptTags,
+ hasWebWorkers: pageStore.hasWebWorkers,
+ headerIndices: Array.from(headerIndices),
+ hostname: pageStore.pageHostname,
+ mtxContentModified: pageStore.mtxContentModifiedTime !== details.mtxContentModifiedTime,
+ mtxCountModified: pageStore.mtxCountModifiedTime !== details.mtxCountModifiedTime,
+ mtxContentModifiedTime: pageStore.mtxContentModifiedTime,
+ mtxCountModifiedTime: pageStore.mtxCountModifiedTime,
+ pMatrixModified: µm.pMatrix.modifiedTime !== details.pMatrixModifiedTime,
+ pMatrixModifiedTime: µm.pMatrix.modifiedTime,
+ pSwitches: {},
+ rows: {},
+ rowCount: 0,
+ scope: '*',
+ tabId: pageStore.tabId,
+ tMatrixModified: µm.tMatrix.modifiedTime !== details.tMatrixModifiedTime,
+ tMatrixModifiedTime: µm.tMatrix.modifiedTime,
+ tSwitches: {},
+ url: pageStore.pageUrl,
+ userSettings: {
+ colorBlindFriendly: µmuser.colorBlindFriendly,
+ displayTextSize: µmuser.displayTextSize,
+ popupScopeLevel: µmuser.popupScopeLevel
+ }
+ };
+
+ if ( typeof details.scope === 'string' ) {
+ r.scope = details.scope;
+ } else if ( µmuser.popupScopeLevel === 'site' ) {
+ r.scope = r.hostname;
+ } else if ( µmuser.popupScopeLevel === 'domain' ) {
+ r.scope = r.domain;
+ }
+
+ for ( var switchName of µm.Matrix.switchNames ) {
+ r.tSwitches[switchName] = µm.tMatrix.evaluateSwitchZ(switchName, r.scope);
+ r.pSwitches[switchName] = µm.pMatrix.evaluateSwitchZ(switchName, r.scope);
+ }
+
+ // These rows always exist
+ r.rows['*'] = new RowSnapshot(r.scope, '*', '*');
+ r.rows['1st-party'] = new RowSnapshot(r.scope, '1st-party', '1st-party');
+ r.rowCount += 1;
+
+ var µmuri = µm.URI;
+ var reqType, reqHostname, reqDomain;
+ var desHostname;
+ var row, typeIndex;
+ var anyIndex = headerIndices.get('*');
+ var pos, count;
+
+ for ( var entry of pageStore.hostnameTypeCells ) {
+ pos = entry[0].indexOf(' ');
+ reqHostname = entry[0].slice(0, pos);
+ reqType = entry[0].slice(pos + 1);
+ // rhill 2013-10-23: hostname can be empty if the request is a data url
+ // https://github.com/gorhill/httpswitchboard/issues/26
+ if ( reqHostname === '' ) {
+ reqHostname = pageStore.pageHostname;
+ }
+ reqDomain = µmuri.domainFromHostname(reqHostname) || reqHostname;
+
+ // We want rows of self and ancestors
+ desHostname = reqHostname;
+ for (;;) {
+ // If row exists, ancestors exist
+ if ( r.rows.hasOwnProperty(desHostname) !== false ) { break; }
+ r.rows[desHostname] = new RowSnapshot(r.scope, desHostname, reqDomain);
+ r.rowCount += 1;
+ if ( desHostname === reqDomain ) { break; }
+ pos = desHostname.indexOf('.');
+ if ( pos === -1 ) { break; }
+ desHostname = desHostname.slice(pos + 1);
+ }
+
+ count = entry[1].size;
+ typeIndex = headerIndices.get(reqType);
+ row = r.rows[reqHostname];
+ row.counts[typeIndex] += count;
+ row.counts[anyIndex] += count;
+ row = r.rows[reqDomain];
+ row.totals[typeIndex] += count;
+ row.totals[anyIndex] += count;
+ row = r.rows['*'];
+ row.totals[typeIndex] += count;
+ row.totals[anyIndex] += count;
+ }
+
+ r.diff = µm.tMatrix.diff(µm.pMatrix, r.hostname, Object.keys(r.rows));
+
+ return r;
+};
+
+/******************************************************************************/
+
+var matrixSnapshotFromTabId = function(details, callback) {
+ var matrixSnapshotIf = function(tabId, details) {
+ var pageStore = µm.pageStoreFromTabId(tabId);
+ if ( pageStore === null ) {
+ callback('ENOTFOUND');
+ return;
+ }
+
+ // First verify whether we must return data or not.
+ if (
+ µm.tMatrix.modifiedTime === details.tMatrixModifiedTime &&
+ µm.pMatrix.modifiedTime === details.pMatrixModifiedTime &&
+ pageStore.mtxContentModifiedTime === details.mtxContentModifiedTime &&
+ pageStore.mtxCountModifiedTime === details.mtxCountModifiedTime
+ ) {
+ callback('ENOCHANGE');
+ return ;
+ }
+
+ callback(matrixSnapshot(pageStore, details));
+ };
+
+ // Specific tab id requested?
+ if ( details.tabId ) {
+ matrixSnapshotIf(details.tabId, details);
+ return;
+ }
+
+ // Fall back to currently active tab
+ var onTabReady = function(tab) {
+ if ( tab instanceof Object === false ) {
+ callback('ENOTFOUND');
+ return;
+ }
+
+ // Allow examination of behind-the-scene requests
+ var tabId = tab.url.lastIndexOf(vAPI.getURL('dashboard.html'), 0) !== 0 ?
+ tab.id :
+ vAPI.noTabId;
+ matrixSnapshotIf(tabId, details);
+ };
+
+ vAPI.tabs.get(null, onTabReady);
+};
+
+/******************************************************************************/
+
+var onMessage = function(request, sender, callback) {
+ // Async
+ switch ( request.what ) {
+ case 'matrixSnapshot':
+ matrixSnapshotFromTabId(request, callback);
+ return;
+
+ default:
+ break;
+ }
+
+ // Sync
+ var response;
+
+ switch ( request.what ) {
+ case 'toggleMatrixSwitch':
+ µm.tMatrix.setSwitchZ(
+ request.switchName,
+ request.srcHostname,
+ µm.tMatrix.evaluateSwitchZ(request.switchName, request.srcHostname) === false
+ );
+ break;
+
+ case 'blacklistMatrixCell':
+ µm.tMatrix.blacklistCell(
+ request.srcHostname,
+ request.desHostname,
+ request.type
+ );
+ break;
+
+ case 'whitelistMatrixCell':
+ µm.tMatrix.whitelistCell(
+ request.srcHostname,
+ request.desHostname,
+ request.type
+ );
+ break;
+
+ case 'graylistMatrixCell':
+ µm.tMatrix.graylistCell(
+ request.srcHostname,
+ request.desHostname,
+ request.type
+ );
+ break;
+
+ case 'applyDiffToPermanentMatrix': // aka "persist"
+ if ( µm.pMatrix.applyDiff(request.diff, µm.tMatrix) ) {
+ µm.saveMatrix();
+ }
+ break;
+
+ case 'applyDiffToTemporaryMatrix': // aka "revert"
+ µm.tMatrix.applyDiff(request.diff, µm.pMatrix);
+ break;
+
+ case 'revertTemporaryMatrix':
+ µm.tMatrix.assign(µm.pMatrix);
+ break;
+
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
+
+ callback(response);
+};
+
+vAPI.messaging.listen('popup.js', onMessage);
+
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+// content scripts
+
+(function() {
+
+var µm = µMatrix;
+
+/******************************************************************************/
+
+var foundInlineCode = function(tabId, pageStore, details, type) {
+ if ( pageStore === null ) { return; }
+
+ var pageHostname = pageStore.pageHostname,
+ µmuri = µm.URI.set(details.documentURI),
+ frameURL = µmuri.normalizedURI();
+
+ var blocked = details.blocked;
+ if ( blocked === undefined ) {
+ blocked = µm.mustBlock(pageHostname, µmuri.hostname, type);
+ }
+
+ var mapTo = {
+ css: 'style',
+ script: 'script'
+ };
+
+ // https://github.com/gorhill/httpswitchboard/issues/333
+ // Look-up here whether inline scripting is blocked for the frame.
+ var url = frameURL + '{inline_' + mapTo[type] + '}';
+ pageStore.recordRequest(type, url, blocked);
+ µm.logger.writeOne(tabId, 'net', pageHostname, url, type, blocked);
+};
+
+/******************************************************************************/
+
+var contentScriptLocalStorageHandler = function(tabId, originURL) {
+ var tabContext = µm.tabContextManager.lookup(tabId);
+ if ( tabContext === null ) { return; }
+
+ var blocked = µm.mustBlock(
+ tabContext.rootHostname,
+ µm.URI.hostnameFromURI(originURL),
+ 'cookie'
+ );
+
+ var pageStore = µm.pageStoreFromTabId(tabId);
+ if ( pageStore !== null ) {
+ var requestURL = originURL + '/{localStorage}';
+ pageStore.recordRequest('cookie', requestURL, blocked);
+ µm.logger.writeOne(tabId, 'net', tabContext.rootHostname, requestURL, 'cookie', blocked);
+ }
+
+ var removeStorage = blocked && µm.userSettings.deleteLocalStorage;
+ if ( removeStorage ) {
+ µm.localStorageRemovedCounter++;
+ }
+
+ return removeStorage;
+};
+
+/******************************************************************************/
+
+// Evaluate many URLs against the matrix.
+
+var lookupBlockedCollapsibles = function(tabId, requests) {
+ if ( placeholdersReadTime < µm.rawSettingsWriteTime ) {
+ placeholders = undefined;
+ }
+
+ if ( placeholders === undefined ) {
+ placeholders = {
+ frame: µm.rawSettings.framePlaceholder,
+ image: µm.rawSettings.imagePlaceholder
+ };
+ if ( placeholders.frame ) {
+ placeholders.frameDocument =
+ µm.rawSettings.framePlaceholderDocument.replace(
+ '{{bg}}',
+ µm.rawSettings.framePlaceholderBackground !== 'default' ?
+ µm.rawSettings.framePlaceholderBackground :
+ µm.rawSettings.placeholderBackground
+ );
+ }
+ if ( placeholders.image ) {
+ placeholders.imageBorder =
+ µm.rawSettings.imagePlaceholderBorder !== 'default' ?
+ µm.rawSettings.imagePlaceholderBorder :
+ µm.rawSettings.placeholderBorder;
+ placeholders.imageBackground =
+ µm.rawSettings.imagePlaceholderBackground !== 'default' ?
+ µm.rawSettings.imagePlaceholderBackground :
+ µm.rawSettings.placeholderBackground;
+ }
+ placeholdersReadTime = Date.now();
+ }
+
+ var response = {
+ blockedResources: [],
+ hash: requests.hash,
+ id: requests.id,
+ placeholders: placeholders
+ };
+
+ var tabContext = µm.tabContextManager.lookup(tabId);
+ if ( tabContext === null ) {
+ return response;
+ }
+
+ var pageStore = µm.pageStoreFromTabId(tabId);
+ if ( pageStore !== null ) {
+ pageStore.lookupBlockedCollapsibles(requests, response);
+ }
+
+ return response;
+};
+
+var placeholders,
+ placeholdersReadTime = 0;
+
+/******************************************************************************/
+
+var onMessage = function(request, sender, callback) {
+ // Async
+ switch ( request.what ) {
+ default:
+ break;
+ }
+
+ var tabId = sender && sender.tab ? sender.tab.id || 0 : 0,
+ tabContext = µm.tabContextManager.lookup(tabId),
+ rootHostname = tabContext && tabContext.rootHostname,
+ pageStore = µm.pageStoreFromTabId(tabId);
+
+ // Sync
+ var response;
+
+ switch ( request.what ) {
+ case 'contentScriptHasLocalStorage':
+ response = contentScriptLocalStorageHandler(tabId, request.originURL);
+ break;
+
+ case 'lookupBlockedCollapsibles':
+ response = lookupBlockedCollapsibles(tabId, request);
+ break;
+
+ case 'mustRenderNoscriptTags?':
+ if ( tabContext === null ) { break; }
+ response =
+ µm.tMatrix.mustBlock(rootHostname, rootHostname, 'script') &&
+ µm.tMatrix.evaluateSwitchZ('noscript-spoof', rootHostname);
+ if ( pageStore !== null ) {
+ pageStore.hasNoscriptTags = true;
+ }
+ // https://github.com/gorhill/uMatrix/issues/225
+ // A good place to force an update of the page title, as at
+ // this point the DOM has been loaded.
+ µm.updateTitle(tabId);
+ break;
+
+ case 'securityPolicyViolation':
+ if ( request.directive === 'worker-src' ) {
+ var url = µm.URI.hostnameFromURI(request.blockedURI) !== '' ?
+ request.blockedURI :
+ request.documentURI;
+ if ( pageStore !== null ) {
+ pageStore.hasWebWorkers = true;
+ pageStore.recordRequest('script', url, true);
+ }
+ if ( tabContext !== null ) {
+ µm.logger.writeOne(tabId, 'net', rootHostname, url, 'worker', request.blocked);
+ }
+ } else if ( request.directive === 'script-src' ) {
+ foundInlineCode(tabId, pageStore, request, 'script');
+ } else if ( request.directive === 'style-src' ) {
+ foundInlineCode(tabId, pageStore, request, 'css');
+ }
+ break;
+
+ case 'shutdown?':
+ if ( tabContext !== null ) {
+ response = µm.tMatrix.evaluateSwitchZ('matrix-off', rootHostname);
+ }
+ break;
+
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
+
+ callback(response);
+};
+
+vAPI.messaging.listen('contentscript.js', onMessage);
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+// cloud-ui.js
+
+(function() {
+
+/******************************************************************************/
+
+var µm = µMatrix;
+
+/******************************************************************************/
+
+var onMessage = function(request, sender, callback) {
+ // Async
+ switch ( request.what ) {
+ case 'cloudGetOptions':
+ vAPI.cloud.getOptions(function(options) {
+ options.enabled = µm.userSettings.cloudStorageEnabled === true;
+ callback(options);
+ });
+ return;
+
+ case 'cloudSetOptions':
+ vAPI.cloud.setOptions(request.options, callback);
+ return;
+
+ case 'cloudPull':
+ return vAPI.cloud.pull(request.datakey, callback);
+
+ case 'cloudPush':
+ return vAPI.cloud.push(request.datakey, request.data, callback);
+
+ default:
+ break;
+ }
+
+ // Sync
+ var response;
+
+ switch ( request.what ) {
+ // For when cloud storage is disabled.
+ case 'cloudPull':
+ // fallthrough
+ case 'cloudPush':
+ break;
+
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
+
+ callback(response);
+};
+
+/******************************************************************************/
+
+vAPI.messaging.listen('cloud-ui.js', onMessage);
+
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+// user-rules.js
+
+(function() {
+
+var µm = µMatrix;
+
+/******************************************************************************/
+
+var onMessage = function(request, sender, callback) {
+
+ // Async
+ switch ( request.what ) {
+ default:
+ break;
+ }
+
+ // Sync
+ var response;
+
+ switch ( request.what ) {
+ case 'getUserRules':
+ response = {
+ temporaryRules: µm.tMatrix.toString(),
+ permanentRules: µm.pMatrix.toString()
+ };
+ break;
+
+ case 'setUserRules':
+ if ( typeof request.temporaryRules === 'string' ) {
+ µm.tMatrix.fromString(request.temporaryRules);
+ }
+ if ( typeof request.permanentRules === 'string' ) {
+ µm.pMatrix.fromString(request.permanentRules);
+ µm.saveMatrix();
+ }
+ response = {
+ temporaryRules: µm.tMatrix.toString(),
+ permanentRules: µm.pMatrix.toString()
+ };
+ break;
+
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
+
+ callback(response);
+};
+
+vAPI.messaging.listen('user-rules.js', onMessage);
+
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+// hosts-files.js
+
+(function() {
+
+var µm = µMatrix;
+
+/******************************************************************************/
+
+var prepEntries = function(entries) {
+ var µmuri = µm.URI;
+ var entry;
+ for ( var k in entries ) {
+ if ( entries.hasOwnProperty(k) === false ) {
+ continue;
+ }
+ entry = entries[k];
+ if ( typeof entry.homeURL === 'string' ) {
+ entry.homeHostname = µmuri.hostnameFromURI(entry.homeURL);
+ entry.homeDomain = µmuri.domainFromHostname(entry.homeHostname);
+ }
+ }
+};
+
+/******************************************************************************/
+
+var getLists = function(callback) {
+ var r = {
+ autoUpdate: µm.userSettings.autoUpdate,
+ available: null,
+ cache: null,
+ current: µm.liveHostsFiles,
+ blockedHostnameCount: µm.ubiquitousBlacklist.count
+ };
+ var onMetadataReady = function(entries) {
+ r.cache = entries;
+ prepEntries(r.cache);
+ callback(r);
+ };
+ var onAvailableHostsFilesReady = function(lists) {
+ r.available = lists;
+ prepEntries(r.available);
+ µm.assets.metadata(onMetadataReady);
+ };
+ µm.getAvailableHostsFiles(onAvailableHostsFilesReady);
+};
+
+/******************************************************************************/
+
+var onMessage = function(request, sender, callback) {
+ var µm = µMatrix;
+
+ // Async
+ switch ( request.what ) {
+ case 'getLists':
+ return getLists(callback);
+
+ default:
+ break;
+ }
+
+ // Sync
+ var response;
+
+ switch ( request.what ) {
+ case 'purgeCache':
+ µm.assets.purge(request.assetKey);
+ µm.assets.remove('compiled/' + request.assetKey);
+ break;
+
+ case 'purgeAllCaches':
+ if ( request.hard ) {
+ µm.assets.remove(/./);
+ } else {
+ µm.assets.purge(/./, 'public_suffix_list.dat');
+ }
+ break;
+
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
+
+ callback(response);
+};
+
+vAPI.messaging.listen('hosts-files.js', onMessage);
+
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+// about.js
+
+(function() {
+
+var µm = µMatrix;
+
+/******************************************************************************/
+
+var restoreUserData = function(userData) {
+ var countdown = 4;
+ var onCountdown = function() {
+ countdown -= 1;
+ if ( countdown === 0 ) {
+ vAPI.app.restart();
+ }
+ };
+
+ var onAllRemoved = function() {
+ vAPI.storage.set(userData.settings, onCountdown);
+ vAPI.storage.set({ userMatrix: userData.rules }, onCountdown);
+ vAPI.storage.set({ liveHostsFiles: userData.hostsFiles }, onCountdown);
+ if ( userData.rawSettings instanceof Object ) {
+ µMatrix.saveRawSettings(userData.rawSettings, onCountdown);
+ }
+ };
+
+ // If we are going to restore all, might as well wipe out clean local
+ // storage
+ µm.XAL.keyvalRemoveAll(onAllRemoved);
+};
+
+/******************************************************************************/
+
+var resetUserData = function() {
+ var onAllRemoved = function() {
+ vAPI.app.restart();
+ };
+ µm.XAL.keyvalRemoveAll(onAllRemoved);
+};
+
+/******************************************************************************/
+
+var onMessage = function(request, sender, callback) {
+ // Async
+ switch ( request.what ) {
+ default:
+ break;
+ }
+
+ // Sync
+ var response;
+
+ switch ( request.what ) {
+ case 'getAllUserData':
+ response = {
+ app: vAPI.app.name,
+ version: vAPI.app.version,
+ when: Date.now(),
+ settings: µm.userSettings,
+ rules: µm.pMatrix.toString(),
+ hostsFiles: µm.liveHostsFiles,
+ rawSettings: µm.rawSettings
+ };
+ break;
+
+ case 'getSomeStats':
+ response = {
+ version: vAPI.app.version,
+ storageUsed: µm.storageUsed
+ };
+ break;
+
+ case 'restoreAllUserData':
+ restoreUserData(request.userData);
+ break;
+
+ case 'resetAllUserData':
+ resetUserData();
+ break;
+
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
+
+ callback(response);
+};
+
+vAPI.messaging.listen('about.js', onMessage);
+
+/******************************************************************************/
+/******************************************************************************/
+
+// logger-ui.js
+
+(function() {
+
+/******************************************************************************/
+
+var µm = µMatrix,
+ loggerURL = vAPI.getURL('logger-ui.html');
+
+/******************************************************************************/
+
+var onMessage = function(request, sender, callback) {
+ // Async
+ switch ( request.what ) {
+ default:
+ break;
+ }
+
+ // Sync
+ var response;
+
+ switch ( request.what ) {
+ case 'readMany':
+ if (
+ µm.logger.ownerId !== undefined &&
+ request.ownerId !== µm.logger.ownerId
+ ) {
+ response = { unavailable: true };
+ break;
+ }
+ var tabIds = {};
+ for ( var tabId in µm.pageStores ) {
+ var pageStore = µm.pageStoreFromTabId(tabId);
+ if ( pageStore === null ) { continue; }
+ if ( pageStore.rawUrl.startsWith(loggerURL) ) { continue; }
+ tabIds[tabId] = pageStore.title || pageStore.rawUrl;
+ }
+ response = {
+ colorBlind: false,
+ entries: µm.logger.readAll(request.ownerId),
+ maxLoggedRequests: µm.userSettings.maxLoggedRequests,
+ noTabId: vAPI.noTabId,
+ tabIds: tabIds,
+ tabIdsToken: µm.pageStoresToken
+ };
+ break;
+
+ case 'releaseView':
+ if ( request.ownerId === µm.logger.ownerId ) {
+ µm.logger.ownerId = undefined;
+ }
+ break;
+
+ default:
+ return vAPI.messaging.UNHANDLED;
+ }
+
+ callback(response);
+};
+
+vAPI.messaging.listen('logger-ui.js', onMessage);
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
diff --git a/js/pagestats.js b/js/pagestats.js
new file mode 100644
index 0000000..37e94c7
--- /dev/null
+++ b/js/pagestats.js
@@ -0,0 +1,274 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2013-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+/******************************************************************************/
+
+µMatrix.pageStoreFactory = (function() {
+
+/******************************************************************************/
+
+var µm = µMatrix;
+
+/******************************************************************************/
+
+var BlockedCollapsibles = function() {
+ this.boundPruneAsyncCallback = this.pruneAsyncCallback.bind(this);
+ this.blocked = new Map();
+ this.hash = 0;
+ this.timer = null;
+};
+
+BlockedCollapsibles.prototype = {
+
+ shelfLife: 10 * 1000,
+
+ add: function(type, url, isSpecific) {
+ if ( this.blocked.size === 0 ) { this.pruneAsync(); }
+ var now = Date.now() / 1000 | 0;
+ // The following "trick" is to encode the specifity into the lsb of the
+ // time stamp so as to avoid to have to allocate a memory structure to
+ // store both time stamp and specificity.
+ if ( isSpecific ) {
+ now |= 0x00000001;
+ } else {
+ now &= 0xFFFFFFFE;
+ }
+ this.blocked.set(type + ' ' + url, now);
+ this.hash = now;
+ },
+
+ reset: function() {
+ this.blocked.clear();
+ this.hash = 0;
+ if ( this.timer !== null ) {
+ clearTimeout(this.timer);
+ this.timer = null;
+ }
+ },
+
+ pruneAsync: function() {
+ if ( this.timer === null ) {
+ this.timer = vAPI.setTimeout(
+ this.boundPruneAsyncCallback,
+ this.shelfLife * 2
+ );
+ }
+ },
+
+ pruneAsyncCallback: function() {
+ this.timer = null;
+ var obsolete = Date.now() - this.shelfLife;
+ for ( var entry of this.blocked ) {
+ if ( entry[1] <= obsolete ) {
+ this.blocked.delete(entry[0]);
+ }
+ }
+ if ( this.blocked.size !== 0 ) { this.pruneAsync(); }
+ }
+};
+
+/******************************************************************************/
+
+// Ref: Given a URL, returns a (somewhat) unique 32-bit value
+// Based on: FNV32a
+// http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-reference-source
+// The rest is custom, suited for uMatrix.
+
+var PageStore = function(tabContext) {
+ this.hostnameTypeCells = new Map();
+ this.domains = new Set();
+ this.blockedCollapsibles = new BlockedCollapsibles();
+ this.requestStats = µm.requestStatsFactory();
+ this.off = false;
+ this.init(tabContext);
+};
+
+PageStore.prototype = {
+
+ collapsibleTypes: new Set([ 'image' ]),
+ pageStoreJunkyard: [],
+
+ init: function(tabContext) {
+ this.tabId = tabContext.tabId;
+ this.rawUrl = tabContext.rawURL;
+ this.pageUrl = tabContext.normalURL;
+ this.pageHostname = tabContext.rootHostname;
+ this.pageDomain = tabContext.rootDomain;
+ this.title = '';
+ this.hostnameTypeCells.clear();
+ this.domains.clear();
+ this.allHostnamesString = ' ';
+ this.blockedCollapsibles.reset();
+ this.requestStats.reset();
+ this.distinctRequestCount = 0;
+ this.perLoadAllowedRequestCount = 0;
+ this.perLoadBlockedRequestCount = 0;
+ this.has3pReferrer = false;
+ this.hasMixedContent = false;
+ this.hasNoscriptTags = false;
+ this.hasWebWorkers = false;
+ this.incinerationTimer = null;
+ this.mtxContentModifiedTime = 0;
+ this.mtxCountModifiedTime = 0;
+ return this;
+ },
+
+ dispose: function() {
+ this.rawUrl = '';
+ this.pageUrl = '';
+ this.pageHostname = '';
+ this.pageDomain = '';
+ this.title = '';
+ this.hostnameTypeCells.clear();
+ this.domains.clear();
+ this.allHostnamesString = ' ';
+ this.blockedCollapsibles.reset();
+ if ( this.incinerationTimer !== null ) {
+ clearTimeout(this.incinerationTimer);
+ this.incinerationTimer = null;
+ }
+ if ( this.pageStoreJunkyard.length < 8 ) {
+ this.pageStoreJunkyard.push(this);
+ }
+ },
+
+ cacheBlockedCollapsible: function(type, url, specificity) {
+ if ( this.collapsibleTypes.has(type) ) {
+ this.blockedCollapsibles.add(
+ type,
+ url,
+ specificity !== 0 && specificity < 5
+ );
+ }
+ },
+
+ lookupBlockedCollapsibles: function(request, response) {
+ var tabContext = µm.tabContextManager.lookup(this.tabId);
+ if ( tabContext === null ) { return; }
+
+ var collapseBlacklisted = µm.userSettings.collapseBlacklisted,
+ collapseBlocked = µm.userSettings.collapseBlocked,
+ entry;
+
+ var blockedResources = response.blockedResources;
+
+ if (
+ Array.isArray(request.toFilter) &&
+ request.toFilter.length !== 0
+ ) {
+ var roothn = tabContext.rootHostname,
+ hnFromURI = µm.URI.hostnameFromURI,
+ tMatrix = µm.tMatrix;
+ for ( entry of request.toFilter ) {
+ if ( tMatrix.mustBlock(roothn, hnFromURI(entry.url), entry.type) === false ) {
+ continue;
+ }
+ blockedResources.push([
+ entry.type + ' ' + entry.url,
+ collapseBlocked ||
+ collapseBlacklisted && tMatrix.specificityRegister !== 0 &&
+ tMatrix.specificityRegister < 5
+ ]);
+ }
+ }
+
+ if ( this.blockedCollapsibles.hash === response.hash ) { return; }
+ response.hash = this.blockedCollapsibles.hash;
+
+ for ( entry of this.blockedCollapsibles.blocked ) {
+ blockedResources.push([
+ entry[0],
+ collapseBlocked || collapseBlacklisted && (entry[1] & 1) !== 0
+ ]);
+ }
+ },
+
+ recordRequest: function(type, url, block) {
+ // Store distinct network requests. This is used to:
+ // - remember which hostname/type were seen
+ // - count the number of distinct URLs for any given
+ // hostname-type pair
+ var hostname = µm.URI.hostnameFromURI(url),
+ key = hostname + ' ' + type,
+ uids = this.hostnameTypeCells.get(key);
+ if ( uids === undefined ) {
+ this.hostnameTypeCells.set(key, (uids = new Set()));
+ } else if ( uids.size > 99 ) {
+ return;
+ }
+ var uid = this.uidFromURL(url);
+ if ( uids.has(uid) ) { return; }
+ uids.add(uid);
+
+ // Count blocked/allowed requests
+ this.requestStats.record(type, block);
+
+ // https://github.com/gorhill/httpswitchboard/issues/306
+ // If it is recorded locally, record globally
+ µm.requestStats.record(type, block);
+ µm.updateBadgeAsync(this.tabId);
+
+ if ( block !== false ) {
+ this.perLoadBlockedRequestCount++;
+ } else {
+ this.perLoadAllowedRequestCount++;
+ }
+
+ this.distinctRequestCount++;
+ this.mtxCountModifiedTime = Date.now();
+
+ if ( this.domains.has(hostname) === false ) {
+ this.domains.add(hostname);
+ this.allHostnamesString += hostname + ' ';
+ this.mtxContentModifiedTime = Date.now();
+ }
+ },
+
+ uidFromURL: function(uri) {
+ var hint = 0x811c9dc5,
+ i = uri.length;
+ while ( i-- ) {
+ hint ^= uri.charCodeAt(i) | 0;
+ hint += (hint<<1) + (hint<<4) + (hint<<7) + (hint<<8) + (hint<<24) | 0;
+ hint >>>= 0;
+ }
+ return hint;
+ }
+};
+
+/******************************************************************************/
+
+return function pageStoreFactory(tabContext) {
+ var entry = PageStore.prototype.pageStoreJunkyard.pop();
+ if ( entry ) {
+ return entry.init(tabContext);
+ }
+ return new PageStore(tabContext);
+};
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
diff --git a/js/polyfill.js b/js/polyfill.js
new file mode 100644
index 0000000..dea7e39
--- /dev/null
+++ b/js/polyfill.js
@@ -0,0 +1,96 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2017-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+
+ This file has been originally imported from:
+ https://github.com/gorhill/uBlock/tree/master/platform/chromium
+
+*/
+
+// For background page or non-background pages
+
+/* exported objectAssign */
+
+'use strict';
+
+/******************************************************************************/
+/******************************************************************************/
+
+// As per MDN, Object.assign appeared first in Firefox 34.
+// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Browser_compatibility
+
+var objectAssign = Object.assign || function(target, source) {
+ var keys = Object.keys(source);
+ for ( var i = 0, n = keys.length, key; i < n; i++ ) {
+ key = keys[i];
+ target[key] = source[key];
+ }
+ return target;
+};
+
+/******************************************************************************/
+
+// Patching for Pale Moon which does not implement ES6 Set/Map.
+// Test for non-ES6 Set/Map: check if property `iterator` is present.
+// The code is strictly to satisfy uBO's core, not to be an accurate
+// implementation of ES6.
+
+if ( self.Set.prototype.iterator instanceof Function ) {
+ //console.log('Patching non-ES6 Set() to be more ES6-like.');
+ self.Set.prototype._values = self.Set.prototype.values;
+ self.Set.prototype.values = function() {
+ this._valueIter = this._values();
+ this.value = undefined;
+ this.done = false;
+ return this;
+ };
+ self.Set.prototype.next = function() {
+ try {
+ this.value = this._valueIter.next();
+ } catch (ex) {
+ this._valueIter = undefined;
+ this.value = undefined;
+ this.done = true;
+ }
+ return this;
+ };
+}
+
+if ( self.Map.prototype.iterator instanceof Function ) {
+ //console.log('Patching non-ES6 Map() to be more ES6-like.');
+ self.Map.prototype._entries = self.Map.prototype.entries;
+ self.Map.prototype.entries = function() {
+ this._entryIter = this._entries();
+ this.value = undefined;
+ this.done = false;
+ return this;
+ };
+ self.Map.prototype.next = function() {
+ try {
+ this.value = this._entryIter.next();
+ } catch (ex) {
+ this._entryIter = undefined;
+ this.value = undefined;
+ this.done = true;
+ }
+ return this;
+ };
+}
+
diff --git a/js/popup.js b/js/popup.js
new file mode 100644
index 0000000..98781b4
--- /dev/null
+++ b/js/popup.js
@@ -0,0 +1,1538 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global punycode, uDom */
+/* jshint esnext: true, bitwise: false */
+
+'use strict';
+
+/******************************************************************************/
+/******************************************************************************/
+
+(function() {
+
+/******************************************************************************/
+/******************************************************************************/
+
+// Stuff which is good to do very early so as to avoid visual glitches.
+
+(function() {
+ var paneContentPaddingTop = vAPI.localStorage.getItem('paneContentPaddingTop'),
+ touchDevice = vAPI.localStorage.getItem('touchDevice');
+
+ if ( typeof paneContentPaddingTop === 'string' ) {
+ document.querySelector('.paneContent').style.setProperty(
+ 'padding-top',
+ paneContentPaddingTop
+ );
+ }
+ if ( touchDevice === 'true' ) {
+ document.body.setAttribute('data-touch', 'true');
+ } else {
+ document.addEventListener('touchstart', function onTouched(ev) {
+ document.removeEventListener(ev.type, onTouched);
+ document.body.setAttribute('data-touch', 'true');
+ vAPI.localStorage.setItem('touchDevice', 'true');
+ resizePopup();
+ });
+ }
+})();
+
+var popupWasResized = function() {
+ document.body.setAttribute('data-resize-popup', '');
+};
+
+var resizePopup = (function() {
+ var timer;
+ var fix = function() {
+ timer = undefined;
+ var doc = document;
+ // Manually adjust the position of the main matrix according to the
+ // height of the toolbar/matrix header.
+ var paddingTop = (doc.querySelector('.paneHead').clientHeight + 2) + 'px',
+ paneContent = doc.querySelector('.paneContent');
+ if ( paddingTop !== paneContent.style.paddingTop ) {
+ paneContent.style.setProperty('padding-top', paddingTop);
+ vAPI.localStorage.setItem('paneContentPaddingTop', paddingTop);
+ }
+ document.body.classList.toggle(
+ 'hConstrained',
+ window.innerWidth < document.body.clientWidth
+ );
+ popupWasResized();
+ };
+ return function() {
+ if ( timer !== undefined ) {
+ clearTimeout(timer);
+ }
+ timer = vAPI.setTimeout(fix, 97);
+ };
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+// Must be consistent with definitions in matrix.js
+var Dark = 0x80;
+var Red = 1;
+var Green = 2;
+var DarkRed = Dark | Red;
+var DarkGreen = Dark | Green;
+
+var matrixSnapshot = {};
+var groupsSnapshot = [];
+var allHostnamesSnapshot = 'do not leave this initial string empty';
+
+var matrixCellHotspots = null;
+
+var matrixHeaderPrettyNames = {
+ 'all': '',
+ 'cookie': '',
+ 'css': '',
+ 'image': '',
+ 'media': '',
+ 'script': '',
+ 'xhr': '',
+ 'frame': '',
+ 'other': ''
+};
+
+var firstPartyLabel = '';
+var blacklistedHostnamesLabel = '';
+
+var expandosIdGenerator = 1;
+var nodeToExpandosMap = (function() {
+ if ( typeof window.Map === 'function' ) {
+ return new window.Map();
+ }
+})();
+
+var expandosFromNode = function(node) {
+ if (
+ node instanceof HTMLElement === false &&
+ typeof node.nodeAt === 'function'
+ ) {
+ node = node.nodeAt(0);
+ }
+ if ( nodeToExpandosMap ) {
+ var expandosId = node.getAttribute('data-expandos');
+ if ( !expandosId ) {
+ expandosId = '' + (expandosIdGenerator++);
+ node.setAttribute('data-expandos', expandosId);
+ }
+ var expandos = nodeToExpandosMap.get(expandosId);
+ if ( expandos === undefined ) {
+ nodeToExpandosMap.set(expandosId, (expandos = Object.create(null)));
+ }
+ return expandos;
+ }
+ return node;
+};
+
+/******************************************************************************/
+/******************************************************************************/
+
+function getUserSetting(setting) {
+ return matrixSnapshot.userSettings[setting];
+ }
+
+function setUserSetting(setting, value) {
+ matrixSnapshot.userSettings[setting] = value;
+ vAPI.messaging.send('popup.js', {
+ what: 'userSettings',
+ name: setting,
+ value: value
+ });
+}
+
+/******************************************************************************/
+
+function getUISetting(setting) {
+ var r = vAPI.localStorage.getItem(setting);
+ if ( typeof r !== 'string' ) {
+ return undefined;
+ }
+ return JSON.parse(r);
+}
+
+function setUISetting(setting, value) {
+ vAPI.localStorage.setItem(
+ setting,
+ JSON.stringify(value)
+ );
+}
+
+/******************************************************************************/
+
+function updateMatrixSnapshot() {
+ matrixSnapshotPoller.pollNow();
+}
+
+/******************************************************************************/
+
+// For display purpose, create four distinct groups of rows:
+// 0th: literal "1st-party" row
+// 1st: page domain's related
+// 2nd: whitelisted
+// 3rd: graylisted
+// 4th: blacklisted
+
+function getGroupStats() {
+
+ // Try to not reshuffle groups around while popup is opened if
+ // no new hostname added.
+ var latestDomainListSnapshot = Object.keys(matrixSnapshot.rows).sort().join();
+ if ( latestDomainListSnapshot === allHostnamesSnapshot ) {
+ return groupsSnapshot;
+ }
+ allHostnamesSnapshot = latestDomainListSnapshot;
+
+ // First, group according to whether at least one node in the domain
+ // hierarchy is white or blacklisted
+ var pageDomain = matrixSnapshot.domain;
+ var rows = matrixSnapshot.rows;
+ var anyTypeOffset = matrixSnapshot.headerIndices.get('*');
+ var hostname, domain;
+ var row, color, count, groupIndex;
+ var domainToGroupMap = {};
+
+ // These have hard-coded position which cannot be overriden
+ domainToGroupMap['1st-party'] = 0;
+ domainToGroupMap[pageDomain] = 1;
+
+ // 1st pass: domain wins if it has an explicit rule or a count
+ for ( hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ if ( hostname === '*' || hostname === '1st-party' ) {
+ continue;
+ }
+ domain = rows[hostname].domain;
+ if ( domain === pageDomain || hostname !== domain ) {
+ continue;
+ }
+ row = rows[domain];
+ color = row.temporary[anyTypeOffset];
+ if ( color === DarkGreen ) {
+ domainToGroupMap[domain] = 2;
+ continue;
+ }
+ if ( color === DarkRed ) {
+ domainToGroupMap[domain] = 4;
+ continue;
+ }
+ count = row.counts[anyTypeOffset];
+ if ( count !== 0 ) {
+ domainToGroupMap[domain] = 3;
+ continue;
+ }
+ }
+ // 2nd pass: green wins
+ for ( hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ row = rows[hostname];
+ domain = row.domain;
+ if ( domainToGroupMap.hasOwnProperty(domain) ) {
+ continue;
+ }
+ color = row.temporary[anyTypeOffset];
+ if ( color === DarkGreen ) {
+ domainToGroupMap[domain] = 2;
+ }
+ }
+ // 3rd pass: gray with count wins
+ for ( hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ row = rows[hostname];
+ domain = row.domain;
+ if ( domainToGroupMap.hasOwnProperty(domain) ) {
+ continue;
+ }
+ color = row.temporary[anyTypeOffset];
+ count = row.counts[anyTypeOffset];
+ if ( color !== DarkRed && count !== 0 ) {
+ domainToGroupMap[domain] = 3;
+ }
+ }
+ // 4th pass: red wins whatever is left
+ for ( hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ row = rows[hostname];
+ domain = row.domain;
+ if ( domainToGroupMap.hasOwnProperty(domain) ) {
+ continue;
+ }
+ color = row.temporary[anyTypeOffset];
+ if ( color === DarkRed ) {
+ domainToGroupMap[domain] = 4;
+ }
+ }
+ // 5th pass: gray wins whatever is left
+ for ( hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ domain = rows[hostname].domain;
+ if ( domainToGroupMap.hasOwnProperty(domain) ) {
+ continue;
+ }
+ domainToGroupMap[domain] = 3;
+ }
+
+ // Last pass: put each domain in a group
+ var groups = [ {}, {}, {}, {}, {} ];
+ var group;
+ for ( hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ if ( hostname === '*' ) {
+ continue;
+ }
+ domain = rows[hostname].domain;
+ groupIndex = domainToGroupMap[domain];
+ group = groups[groupIndex];
+ if ( group.hasOwnProperty(domain) === false ) {
+ group[domain] = {};
+ }
+ group[domain][hostname] = true;
+ }
+
+ groupsSnapshot = groups;
+
+ return groups;
+}
+
+/******************************************************************************/
+
+// helpers
+
+function getTemporaryColor(hostname, type) {
+ return matrixSnapshot.rows[hostname].temporary[matrixSnapshot.headerIndices.get(type)];
+}
+
+function getPermanentColor(hostname, type) {
+ return matrixSnapshot.rows[hostname].permanent[matrixSnapshot.headerIndices.get(type)];
+}
+
+function addCellClass(cell, hostname, type) {
+ var cl = cell.classList;
+ cl.add('matCell');
+ cl.add('t' + getTemporaryColor(hostname, type).toString(16));
+ cl.add('p' + getPermanentColor(hostname, type).toString(16));
+}
+
+/******************************************************************************/
+
+// This is required for when we update the matrix while it is open:
+// the user might have collapsed/expanded one or more domains, and we don't
+// want to lose all his hardwork.
+
+function getCollapseState(domain) {
+ var states = getUISetting('popupCollapseSpecificDomains');
+ if ( typeof states === 'object' && states[domain] !== undefined ) {
+ return states[domain];
+ }
+ return matrixSnapshot.collapseAllDomains === true;
+}
+
+function toggleCollapseState(elem) {
+ if ( elem.ancestors('#matHead.collapsible').length > 0 ) {
+ toggleMainCollapseState(elem);
+ } else {
+ toggleSpecificCollapseState(elem);
+ }
+ popupWasResized();
+}
+
+function toggleMainCollapseState(uelem) {
+ var matHead = uelem.ancestors('#matHead.collapsible').toggleClass('collapsed');
+ var collapsed = matrixSnapshot.collapseAllDomains = matHead.hasClass('collapsed');
+ uDom('#matList .matSection.collapsible').toggleClass('collapsed', collapsed);
+ setUserSetting('popupCollapseAllDomains', collapsed);
+
+ var specificCollapseStates = getUISetting('popupCollapseSpecificDomains') || {};
+ var domains = Object.keys(specificCollapseStates);
+ var i = domains.length;
+ var domain;
+ while ( i-- ) {
+ domain = domains[i];
+ if ( specificCollapseStates[domain] === collapsed ) {
+ delete specificCollapseStates[domain];
+ }
+ }
+ setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
+}
+
+function toggleSpecificCollapseState(uelem) {
+ // Remember collapse state forever, but only if it is different
+ // from main collapse switch.
+ var section = uelem.ancestors('.matSection.collapsible').toggleClass('collapsed'),
+ domain = expandosFromNode(section).domain,
+ collapsed = section.hasClass('collapsed'),
+ mainCollapseState = matrixSnapshot.collapseAllDomains === true,
+ specificCollapseStates = getUISetting('popupCollapseSpecificDomains') || {};
+ if ( collapsed !== mainCollapseState ) {
+ specificCollapseStates[domain] = collapsed;
+ setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
+ } else if ( specificCollapseStates[domain] !== undefined ) {
+ delete specificCollapseStates[domain];
+ setUISetting('popupCollapseSpecificDomains', specificCollapseStates);
+ }
+}
+
+/******************************************************************************/
+
+// Update count value of matrix cells(s)
+
+function updateMatrixCounts() {
+ var matCells = uDom('.matrix .matRow.rw > .matCell'),
+ i = matCells.length,
+ matRow, matCell, count, counts,
+ headerIndices = matrixSnapshot.headerIndices,
+ rows = matrixSnapshot.rows,
+ expandos;
+ while ( i-- ) {
+ matCell = matCells.nodeAt(i);
+ expandos = expandosFromNode(matCell);
+ if ( expandos.hostname === '*' || expandos.reqType === '*' ) {
+ continue;
+ }
+ matRow = matCell.parentNode;
+ counts = matRow.classList.contains('meta') ? 'totals' : 'counts';
+ count = rows[expandos.hostname][counts][headerIndices.get(expandos.reqType)];
+ if ( count === expandos.count ) { continue; }
+ expandos.count = count;
+ matCell.textContent = cellTextFromCount(count);
+ }
+}
+
+function cellTextFromCount(count) {
+ if ( count === 0 ) { return '\u00A0'; }
+ if ( count < 100 ) { return count; }
+ return '99+';
+}
+
+/******************************************************************************/
+
+// Update color of matrix cells(s)
+// Color changes when rules change
+
+function updateMatrixColors() {
+ var cells = uDom('.matrix .matRow.rw > .matCell').removeClass(),
+ i = cells.length,
+ cell, expandos;
+ while ( i-- ) {
+ cell = cells.nodeAt(i);
+ expandos = expandosFromNode(cell);
+ addCellClass(cell, expandos.hostname, expandos.reqType);
+ }
+ popupWasResized();
+}
+
+/******************************************************************************/
+
+// Update behavior of matrix:
+// - Whether a section is collapsible or not. It is collapsible if:
+// - It has at least one subdomain AND
+// - There is no explicit rule anywhere in the subdomain cells AND
+// - It is not part of group 3 (blacklisted hostnames)
+
+function updateMatrixBehavior() {
+ matrixList = matrixList || uDom('#matList');
+ var sections = matrixList.descendants('.matSection');
+ var i = sections.length;
+ var section, subdomainRows, j, subdomainRow;
+ while ( i-- ) {
+ section = sections.at(i);
+ subdomainRows = section.descendants('.l2:not(.g4)');
+ j = subdomainRows.length;
+ while ( j-- ) {
+ subdomainRow = subdomainRows.at(j);
+ subdomainRow.toggleClass('collapsible', subdomainRow.descendants('.t81,.t82').length === 0);
+ }
+ section.toggleClass('collapsible', subdomainRows.filter('.collapsible').length > 0);
+ }
+}
+
+/******************************************************************************/
+
+// handle user interaction with filters
+
+function getCellAction(hostname, type, leaning) {
+ var temporaryColor = getTemporaryColor(hostname, type);
+ var hue = temporaryColor & 0x03;
+ // Special case: root toggle only between two states
+ if ( type === '*' && hostname === '*' ) {
+ return hue === Green ? 'blacklistMatrixCell' : 'whitelistMatrixCell';
+ }
+ // When explicitly blocked/allowed, can only graylist
+ var saturation = temporaryColor & 0x80;
+ if ( saturation === Dark ) {
+ return 'graylistMatrixCell';
+ }
+ return leaning === 'whitelisting' ? 'whitelistMatrixCell' : 'blacklistMatrixCell';
+}
+
+function handleFilter(button, leaning) {
+ // our parent cell knows who we are
+ var cell = button.ancestors('div.matCell'),
+ expandos = expandosFromNode(cell),
+ type = expandos.reqType,
+ desHostname = expandos.hostname;
+ // https://github.com/gorhill/uMatrix/issues/24
+ // No hostname can happen -- like with blacklist meta row
+ if ( desHostname === '' ) {
+ return;
+ }
+ var request = {
+ what: getCellAction(desHostname, type, leaning),
+ srcHostname: matrixSnapshot.scope,
+ desHostname: desHostname,
+ type: type
+ };
+ vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
+}
+
+function handleWhitelistFilter(button) {
+ handleFilter(button, 'whitelisting');
+}
+
+function handleBlacklistFilter(button) {
+ handleFilter(button, 'blacklisting');
+}
+
+/******************************************************************************/
+
+var matrixRowPool = [];
+var matrixSectionPool = [];
+var matrixGroupPool = [];
+var matrixRowTemplate = null;
+var matrixList = null;
+
+var startMatrixUpdate = function() {
+ matrixList = matrixList || uDom('#matList');
+ matrixList.detach();
+ var rows = matrixList.descendants('.matRow');
+ rows.detach();
+ matrixRowPool = matrixRowPool.concat(rows.toArray());
+ var sections = matrixList.descendants('.matSection');
+ sections.detach();
+ matrixSectionPool = matrixSectionPool.concat(sections.toArray());
+ var groups = matrixList.descendants('.matGroup');
+ groups.detach();
+ matrixGroupPool = matrixGroupPool.concat(groups.toArray());
+};
+
+var endMatrixUpdate = function() {
+ // https://github.com/gorhill/httpswitchboard/issues/246
+ // If the matrix has no rows, we need to insert a dummy one, invisible,
+ // to ensure the extension pop-up is properly sized. This is needed because
+ // the header pane's `position` property is `fixed`, which means it doesn't
+ // affect layout size, hence the matrix header row will be truncated.
+ if ( matrixSnapshot.rowCount <= 1 ) {
+ matrixList.append(createMatrixRow().css('visibility', 'hidden'));
+ }
+ updateMatrixBehavior();
+ matrixList.css('display', '');
+ matrixList.appendTo('.paneContent');
+};
+
+var createMatrixGroup = function() {
+ var group = matrixGroupPool.pop();
+ if ( group ) {
+ return uDom(group).removeClass().addClass('matGroup');
+ }
+ return uDom(document.createElement('div')).addClass('matGroup');
+};
+
+var createMatrixSection = function() {
+ var section = matrixSectionPool.pop();
+ if ( section ) {
+ return uDom(section).removeClass().addClass('matSection');
+ }
+ return uDom(document.createElement('div')).addClass('matSection');
+};
+
+var createMatrixRow = function() {
+ var row = matrixRowPool.pop();
+ if ( row ) {
+ row.style.visibility = '';
+ row = uDom(row);
+ row.descendants('.matCell').removeClass().addClass('matCell');
+ row.removeClass().addClass('matRow');
+ return row;
+ }
+ if ( matrixRowTemplate === null ) {
+ matrixRowTemplate = uDom('#templates .matRow');
+ }
+ return matrixRowTemplate.clone();
+};
+
+/******************************************************************************/
+
+function renderMatrixHeaderRow() {
+ var matHead = uDom('#matHead.collapsible');
+ matHead.toggleClass('collapsed', matrixSnapshot.collapseAllDomains === true);
+ var cells = matHead.descendants('.matCell'), cell, expandos;
+ cell = cells.nodeAt(0);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = '*';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', '*');
+ cell = cells.nodeAt(1);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'cookie';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'cookie');
+ cell = cells.nodeAt(2);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'css';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'css');
+ cell = cells.nodeAt(3);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'image';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'image');
+ cell = cells.nodeAt(4);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'media';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'media');
+ cell = cells.nodeAt(5);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'script';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'script');
+ cell = cells.nodeAt(6);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'xhr';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'xhr');
+ cell = cells.nodeAt(7);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'frame';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'frame');
+ cell = cells.nodeAt(8);
+ expandos = expandosFromNode(cell);
+ expandos.reqType = 'other';
+ expandos.hostname = '*';
+ addCellClass(cell, '*', 'other');
+ uDom('#matHead .matRow').css('display', '');
+}
+
+/******************************************************************************/
+
+function renderMatrixCellDomain(cell, domain) {
+ var expandos = expandosFromNode(cell);
+ expandos.hostname = domain;
+ expandos.reqType = '*';
+ addCellClass(cell.nodeAt(0), domain, '*');
+ var contents = cell.contents();
+ contents.nodeAt(0).textContent = domain === '1st-party' ?
+ firstPartyLabel :
+ punycode.toUnicode(domain);
+ contents.nodeAt(1).textContent = ' ';
+}
+
+function renderMatrixCellSubdomain(cell, domain, subomain) {
+ var expandos = expandosFromNode(cell);
+ expandos.hostname = subomain;
+ expandos.reqType = '*';
+ addCellClass(cell.nodeAt(0), subomain, '*');
+ var contents = cell.contents();
+ contents.nodeAt(0).textContent = punycode.toUnicode(subomain.slice(0, subomain.lastIndexOf(domain)-1)) + '.';
+ contents.nodeAt(1).textContent = punycode.toUnicode(domain);
+}
+
+function renderMatrixMetaCellDomain(cell, domain) {
+ var expandos = expandosFromNode(cell);
+ expandos.hostname = domain;
+ expandos.reqType = '*';
+ addCellClass(cell.nodeAt(0), domain, '*');
+ var contents = cell.contents();
+ contents.nodeAt(0).textContent = '\u2217.' + punycode.toUnicode(domain);
+ contents.nodeAt(1).textContent = ' ';
+}
+
+function renderMatrixCellType(cell, hostname, type, count) {
+ var node = cell.nodeAt(0),
+ expandos = expandosFromNode(node);
+ expandos.hostname = hostname;
+ expandos.reqType = type;
+ expandos.count = count;
+ addCellClass(node, hostname, type);
+ node.textContent = cellTextFromCount(count);
+}
+
+function renderMatrixCellTypes(cells, hostname, countName) {
+ var counts = matrixSnapshot.rows[hostname][countName];
+ var headerIndices = matrixSnapshot.headerIndices;
+ renderMatrixCellType(cells.at(1), hostname, 'cookie', counts[headerIndices.get('cookie')]);
+ renderMatrixCellType(cells.at(2), hostname, 'css', counts[headerIndices.get('css')]);
+ renderMatrixCellType(cells.at(3), hostname, 'image', counts[headerIndices.get('image')]);
+ renderMatrixCellType(cells.at(4), hostname, 'media', counts[headerIndices.get('media')]);
+ renderMatrixCellType(cells.at(5), hostname, 'script', counts[headerIndices.get('script')]);
+ renderMatrixCellType(cells.at(6), hostname, 'xhr', counts[headerIndices.get('xhr')]);
+ renderMatrixCellType(cells.at(7), hostname, 'frame', counts[headerIndices.get('frame')]);
+ renderMatrixCellType(cells.at(8), hostname, 'other', counts[headerIndices.get('other')]);
+}
+
+/******************************************************************************/
+
+function makeMatrixRowDomain(domain) {
+ var matrixRow = createMatrixRow().addClass('rw');
+ var cells = matrixRow.descendants('.matCell');
+ renderMatrixCellDomain(cells.at(0), domain);
+ renderMatrixCellTypes(cells, domain, 'counts');
+ return matrixRow;
+}
+
+function makeMatrixRowSubdomain(domain, subdomain) {
+ var matrixRow = createMatrixRow().addClass('rw');
+ var cells = matrixRow.descendants('.matCell');
+ renderMatrixCellSubdomain(cells.at(0), domain, subdomain);
+ renderMatrixCellTypes(cells, subdomain, 'counts');
+ return matrixRow;
+}
+
+function makeMatrixMetaRowDomain(domain) {
+ var matrixRow = createMatrixRow().addClass('rw');
+ var cells = matrixRow.descendants('.matCell');
+ renderMatrixMetaCellDomain(cells.at(0), domain);
+ renderMatrixCellTypes(cells, domain, 'totals');
+ return matrixRow;
+}
+
+/******************************************************************************/
+
+function renderMatrixMetaCellType(cell, count) {
+ // https://github.com/gorhill/uMatrix/issues/24
+ // Don't forget to reset cell properties
+ var node = cell.nodeAt(0),
+ expandos = expandosFromNode(node);
+ expandos.hostname = '';
+ expandos.reqType = '';
+ expandos.count = count;
+ cell.addClass('t1');
+ node.textContent = cellTextFromCount(count);
+}
+
+function makeMatrixMetaRow(totals) {
+ var headerIndices = matrixSnapshot.headerIndices,
+ matrixRow = createMatrixRow().at(0).addClass('ro'),
+ cells = matrixRow.descendants('.matCell'),
+ contents = cells.at(0).addClass('t81').contents(),
+ expandos = expandosFromNode(cells.nodeAt(0));
+ expandos.hostname = '';
+ expandos.reqType = '*';
+ contents.nodeAt(0).textContent = ' ';
+ contents.nodeAt(1).textContent = blacklistedHostnamesLabel.replace(
+ '{{count}}',
+ totals[headerIndices.get('*')].toLocaleString()
+ );
+ renderMatrixMetaCellType(cells.at(1), totals[headerIndices.get('cookie')]);
+ renderMatrixMetaCellType(cells.at(2), totals[headerIndices.get('css')]);
+ renderMatrixMetaCellType(cells.at(3), totals[headerIndices.get('image')]);
+ renderMatrixMetaCellType(cells.at(4), totals[headerIndices.get('media')]);
+ renderMatrixMetaCellType(cells.at(5), totals[headerIndices.get('script')]);
+ renderMatrixMetaCellType(cells.at(6), totals[headerIndices.get('xhr')]);
+ renderMatrixMetaCellType(cells.at(7), totals[headerIndices.get('frame')]);
+ renderMatrixMetaCellType(cells.at(8), totals[headerIndices.get('other')]);
+ return matrixRow;
+}
+
+/******************************************************************************/
+
+function computeMatrixGroupMetaStats(group) {
+ var headerIndices = matrixSnapshot.headerIndices,
+ anyTypeIndex = headerIndices.get('*'),
+ n = headerIndices.size,
+ totals = new Array(n),
+ i = n;
+ while ( i-- ) {
+ totals[i] = 0;
+ }
+ var rows = matrixSnapshot.rows, row;
+ for ( var hostname in rows ) {
+ if ( rows.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ row = rows[hostname];
+ if ( group.hasOwnProperty(row.domain) === false ) {
+ continue;
+ }
+ if ( row.counts[anyTypeIndex] === 0 ) {
+ continue;
+ }
+ totals[0] += 1;
+ for ( i = 1; i < n; i++ ) {
+ totals[i] += row.counts[i];
+ }
+ }
+ return totals;
+}
+
+/******************************************************************************/
+
+// Compare hostname helper, to order hostname in a logical manner:
+// top-most < bottom-most, take into account whether IP address or
+// named hostname
+
+function hostnameCompare(a,b) {
+ // Normalize: most significant parts first
+ if ( !a.match(/^\d+(\.\d+){1,3}$/) ) {
+ var aa = a.split('.');
+ a = aa.slice(-2).concat(aa.slice(0,-2).reverse()).join('.');
+ }
+ if ( !b.match(/^\d+(\.\d+){1,3}$/) ) {
+ var bb = b.split('.');
+ b = bb.slice(-2).concat(bb.slice(0,-2).reverse()).join('.');
+ }
+ return a.localeCompare(b);
+}
+
+/******************************************************************************/
+
+function makeMatrixGroup0SectionDomain() {
+ return makeMatrixRowDomain('1st-party').addClass('g0 l1');
+}
+
+function makeMatrixGroup0Section() {
+ var domainDiv = createMatrixSection();
+ expandosFromNode(domainDiv).domain = '1st-party';
+ makeMatrixGroup0SectionDomain().appendTo(domainDiv);
+ return domainDiv;
+}
+
+function makeMatrixGroup0() {
+ // Show literal "1st-party" row only if there is
+ // at least one 1st-party hostname
+ if ( Object.keys(groupsSnapshot[1]).length === 0 ) {
+ return;
+ }
+ var groupDiv = createMatrixGroup().addClass('g0');
+ makeMatrixGroup0Section().appendTo(groupDiv);
+ groupDiv.appendTo(matrixList);
+}
+
+/******************************************************************************/
+
+function makeMatrixGroup1SectionDomain(domain) {
+ return makeMatrixRowDomain(domain)
+ .addClass('g1 l1');
+}
+
+function makeMatrixGroup1SectionSubomain(domain, subdomain) {
+ return makeMatrixRowSubdomain(domain, subdomain)
+ .addClass('g1 l2');
+}
+
+function makeMatrixGroup1SectionMetaDomain(domain) {
+ return makeMatrixMetaRowDomain(domain).addClass('g1 l1 meta');
+}
+
+function makeMatrixGroup1Section(hostnames) {
+ var domain = hostnames[0];
+ var domainDiv = createMatrixSection()
+ .toggleClass('collapsed', getCollapseState(domain));
+ expandosFromNode(domainDiv).domain = domain;
+ if ( hostnames.length > 1 ) {
+ makeMatrixGroup1SectionMetaDomain(domain)
+ .appendTo(domainDiv);
+ }
+ makeMatrixGroup1SectionDomain(domain)
+ .appendTo(domainDiv);
+ for ( var i = 1; i < hostnames.length; i++ ) {
+ makeMatrixGroup1SectionSubomain(domain, hostnames[i])
+ .appendTo(domainDiv);
+ }
+ return domainDiv;
+}
+
+function makeMatrixGroup1(group) {
+ var domains = Object.keys(group).sort(hostnameCompare);
+ if ( domains.length ) {
+ var groupDiv = createMatrixGroup().addClass('g1');
+ makeMatrixGroup1Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
+ .appendTo(groupDiv);
+ for ( var i = 1; i < domains.length; i++ ) {
+ makeMatrixGroup1Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ .appendTo(groupDiv);
+ }
+ groupDiv.appendTo(matrixList);
+ }
+}
+
+/******************************************************************************/
+
+function makeMatrixGroup2SectionDomain(domain) {
+ return makeMatrixRowDomain(domain)
+ .addClass('g2 l1');
+}
+
+function makeMatrixGroup2SectionSubomain(domain, subdomain) {
+ return makeMatrixRowSubdomain(domain, subdomain)
+ .addClass('g2 l2');
+}
+
+function makeMatrixGroup2SectionMetaDomain(domain) {
+ return makeMatrixMetaRowDomain(domain).addClass('g2 l1 meta');
+}
+
+function makeMatrixGroup2Section(hostnames) {
+ var domain = hostnames[0];
+ var domainDiv = createMatrixSection()
+ .toggleClass('collapsed', getCollapseState(domain));
+ expandosFromNode(domainDiv).domain = domain;
+ if ( hostnames.length > 1 ) {
+ makeMatrixGroup2SectionMetaDomain(domain).appendTo(domainDiv);
+ }
+ makeMatrixGroup2SectionDomain(domain)
+ .appendTo(domainDiv);
+ for ( var i = 1; i < hostnames.length; i++ ) {
+ makeMatrixGroup2SectionSubomain(domain, hostnames[i])
+ .appendTo(domainDiv);
+ }
+ return domainDiv;
+}
+
+function makeMatrixGroup2(group) {
+ var domains = Object.keys(group).sort(hostnameCompare);
+ if ( domains.length) {
+ var groupDiv = createMatrixGroup()
+ .addClass('g2');
+ makeMatrixGroup2Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
+ .appendTo(groupDiv);
+ for ( var i = 1; i < domains.length; i++ ) {
+ makeMatrixGroup2Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ .appendTo(groupDiv);
+ }
+ groupDiv.appendTo(matrixList);
+ }
+}
+
+/******************************************************************************/
+
+function makeMatrixGroup3SectionDomain(domain) {
+ return makeMatrixRowDomain(domain)
+ .addClass('g3 l1');
+}
+
+function makeMatrixGroup3SectionSubomain(domain, subdomain) {
+ return makeMatrixRowSubdomain(domain, subdomain)
+ .addClass('g3 l2');
+}
+
+function makeMatrixGroup3SectionMetaDomain(domain) {
+ return makeMatrixMetaRowDomain(domain).addClass('g3 l1 meta');
+}
+
+function makeMatrixGroup3Section(hostnames) {
+ var domain = hostnames[0];
+ var domainDiv = createMatrixSection()
+ .toggleClass('collapsed', getCollapseState(domain));
+ expandosFromNode(domainDiv).domain = domain;
+ if ( hostnames.length > 1 ) {
+ makeMatrixGroup3SectionMetaDomain(domain).appendTo(domainDiv);
+ }
+ makeMatrixGroup3SectionDomain(domain)
+ .appendTo(domainDiv);
+ for ( var i = 1; i < hostnames.length; i++ ) {
+ makeMatrixGroup3SectionSubomain(domain, hostnames[i])
+ .appendTo(domainDiv);
+ }
+ return domainDiv;
+}
+
+function makeMatrixGroup3(group) {
+ var domains = Object.keys(group).sort(hostnameCompare);
+ if ( domains.length) {
+ var groupDiv = createMatrixGroup()
+ .addClass('g3');
+ makeMatrixGroup3Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
+ .appendTo(groupDiv);
+ for ( var i = 1; i < domains.length; i++ ) {
+ makeMatrixGroup3Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ .appendTo(groupDiv);
+ }
+ groupDiv.appendTo(matrixList);
+ }
+}
+
+/******************************************************************************/
+
+function makeMatrixGroup4SectionDomain(domain) {
+ return makeMatrixRowDomain(domain)
+ .addClass('g4 l1');
+}
+
+function makeMatrixGroup4SectionSubomain(domain, subdomain) {
+ return makeMatrixRowSubdomain(domain, subdomain)
+ .addClass('g4 l2');
+}
+
+function makeMatrixGroup4Section(hostnames) {
+ var domain = hostnames[0];
+ var domainDiv = createMatrixSection();
+ expandosFromNode(domainDiv).domain = domain;
+ makeMatrixGroup4SectionDomain(domain)
+ .appendTo(domainDiv);
+ for ( var i = 1; i < hostnames.length; i++ ) {
+ makeMatrixGroup4SectionSubomain(domain, hostnames[i])
+ .appendTo(domainDiv);
+ }
+ return domainDiv;
+}
+
+function makeMatrixGroup4(group) {
+ var domains = Object.keys(group).sort(hostnameCompare);
+ if ( domains.length === 0 ) {
+ return;
+ }
+ var groupDiv = createMatrixGroup().addClass('g4');
+ createMatrixSection()
+ .addClass('g4Meta')
+ .toggleClass('g4Collapsed', !!matrixSnapshot.collapseBlacklistedDomains)
+ .appendTo(groupDiv);
+ makeMatrixMetaRow(computeMatrixGroupMetaStats(group), 'g4')
+ .appendTo(groupDiv);
+ makeMatrixGroup4Section(Object.keys(group[domains[0]]).sort(hostnameCompare))
+ .appendTo(groupDiv);
+ for ( var i = 1; i < domains.length; i++ ) {
+ makeMatrixGroup4Section(Object.keys(group[domains[i]]).sort(hostnameCompare))
+ .appendTo(groupDiv);
+ }
+ groupDiv.appendTo(matrixList);
+}
+
+/******************************************************************************/
+
+var makeMenu = function() {
+ var groupStats = getGroupStats();
+
+ if ( Object.keys(groupStats).length === 0 ) { return; }
+
+ // https://github.com/gorhill/httpswitchboard/issues/31
+ if ( matrixCellHotspots ) {
+ matrixCellHotspots.detach();
+ }
+
+ renderMatrixHeaderRow();
+
+ startMatrixUpdate();
+ makeMatrixGroup0(groupStats[0]);
+ makeMatrixGroup1(groupStats[1]);
+ makeMatrixGroup2(groupStats[2]);
+ makeMatrixGroup3(groupStats[3]);
+ makeMatrixGroup4(groupStats[4]);
+ endMatrixUpdate();
+
+ initScopeCell();
+ updateMatrixButtons();
+ resizePopup();
+};
+
+/******************************************************************************/
+
+// Do all the stuff that needs to be done before building menu et al.
+
+function initMenuEnvironment() {
+ document.body.style.setProperty(
+ 'font-size',
+ getUserSetting('displayTextSize')
+ );
+ document.body.classList.toggle(
+ 'colorblind',
+ getUserSetting('colorBlindFriendly')
+ );
+ uDom.nodeFromId('version').textContent = matrixSnapshot.appVersion || '';
+
+ var prettyNames = matrixHeaderPrettyNames;
+ var keys = Object.keys(prettyNames);
+ var i = keys.length;
+ var cell, key, text;
+ while ( i-- ) {
+ key = keys[i];
+ cell = uDom('#matHead .matCell[data-req-type="'+ key +'"]');
+ text = vAPI.i18n(key + 'PrettyName');
+ cell.text(text);
+ prettyNames[key] = text;
+ }
+
+ firstPartyLabel = uDom('[data-i18n="matrix1stPartyLabel"]').text();
+ blacklistedHostnamesLabel = uDom('[data-i18n="matrixBlacklistedHostnames"]').text();
+}
+
+/******************************************************************************/
+
+// Create page scopes for the web page
+
+function selectGlobalScope() {
+ if ( matrixSnapshot.scope === '*' ) { return; }
+ matrixSnapshot.scope = '*';
+ document.body.classList.add('globalScope');
+ matrixSnapshot.tMatrixModifiedTime = undefined;
+ updateMatrixSnapshot();
+ dropDownMenuHide();
+}
+
+function selectSpecificScope(ev) {
+ var newScope = ev.target.getAttribute('data-scope');
+ if ( !newScope || matrixSnapshot.scope === newScope ) { return; }
+ document.body.classList.remove('globalScope');
+ matrixSnapshot.scope = newScope;
+ matrixSnapshot.tMatrixModifiedTime = undefined;
+ updateMatrixSnapshot();
+ dropDownMenuHide();
+}
+
+function initScopeCell() {
+ // It's possible there is no page URL at this point: some pages cannot
+ // be filtered by uMatrix.
+ if ( matrixSnapshot.url === '' ) { return; }
+ var specificScope = uDom.nodeFromId('specificScope');
+
+ while ( specificScope.firstChild !== null ) {
+ specificScope.removeChild(specificScope.firstChild);
+ }
+
+ // Fill in the scope menu entries
+ var pos = matrixSnapshot.domain.indexOf('.');
+ var tld, labels;
+ if ( pos === -1 ) {
+ tld = '';
+ labels = matrixSnapshot.hostname;
+ } else {
+ tld = matrixSnapshot.domain.slice(pos + 1);
+ labels = matrixSnapshot.hostname.slice(0, -tld.length);
+ }
+ var beg = 0, span, label;
+ while ( beg < labels.length ) {
+ pos = labels.indexOf('.', beg);
+ if ( pos === -1 ) {
+ pos = labels.length;
+ } else {
+ pos += 1;
+ }
+ label = document.createElement('span');
+ label.appendChild(
+ document.createTextNode(punycode.toUnicode(labels.slice(beg, pos)))
+ );
+ span = document.createElement('span');
+ span.setAttribute('data-scope', labels.slice(beg) + tld);
+ span.appendChild(label);
+ specificScope.appendChild(span);
+ beg = pos;
+ }
+ if ( tld !== '' ) {
+ label = document.createElement('span');
+ label.appendChild(document.createTextNode(punycode.toUnicode(tld)));
+ span = document.createElement('span');
+ span.setAttribute('data-scope', tld);
+ span.appendChild(label);
+ specificScope.appendChild(span);
+ }
+ updateScopeCell();
+}
+
+function updateScopeCell() {
+ var specificScope = uDom.nodeFromId('specificScope'),
+ isGlobal = matrixSnapshot.scope === '*';
+ document.body.classList.toggle('globalScope', isGlobal);
+ specificScope.classList.toggle('on', !isGlobal);
+ uDom.nodeFromId('globalScope').classList.toggle('on', isGlobal);
+ for ( var node of specificScope.children ) {
+ node.classList.toggle(
+ 'on',
+ !isGlobal &&
+ matrixSnapshot.scope.endsWith(node.getAttribute('data-scope'))
+ );
+ }
+}
+
+/******************************************************************************/
+
+function updateMatrixSwitches() {
+ var count = 0,
+ enabled,
+ switches = matrixSnapshot.tSwitches;
+ for ( var switchName in switches ) {
+ if ( switches.hasOwnProperty(switchName) === false ) { continue; }
+ enabled = switches[switchName];
+ if ( enabled && switchName !== 'matrix-off' ) {
+ count += 1;
+ }
+ uDom('#mtxSwitch_' + switchName).toggleClass('switchTrue', enabled);
+ }
+ uDom.nodeFromId('mtxSwitch_https-strict').classList.toggle(
+ 'relevant',
+ matrixSnapshot.hasMixedContent
+ );
+ uDom.nodeFromId('mtxSwitch_no-workers').classList.toggle(
+ 'relevant',
+ matrixSnapshot.hasWebWorkers
+ );
+ uDom.nodeFromId('mtxSwitch_referrer-spoof').classList.toggle(
+ 'relevant',
+ matrixSnapshot.has3pReferrer
+ );
+ uDom.nodeFromId('mtxSwitch_noscript-spoof').classList.toggle(
+ 'relevant',
+ matrixSnapshot.hasNoscriptTags
+ );
+ uDom.nodeFromSelector('#buttonMtxSwitches span.badge').textContent =
+ count.toLocaleString();
+ uDom.nodeFromSelector('#mtxSwitch_matrix-off span.badge').textContent =
+ matrixSnapshot.blockedCount.toLocaleString();
+ document.body.classList.toggle('powerOff', switches['matrix-off']);
+}
+
+function toggleMatrixSwitch(ev) {
+ if ( ev.target.localName === 'a' ) { return; }
+ var elem = ev.currentTarget;
+ var pos = elem.id.indexOf('_');
+ if ( pos === -1 ) { return; }
+ var switchName = elem.id.slice(pos + 1);
+ var request = {
+ what: 'toggleMatrixSwitch',
+ switchName: switchName,
+ srcHostname: matrixSnapshot.scope
+ };
+ vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
+}
+
+/******************************************************************************/
+
+function updatePersistButton() {
+ var diffCount = matrixSnapshot.diff.length;
+ var button = uDom('#buttonPersist');
+ button.contents()
+ .filter(function(){return this.nodeType===3;})
+ .first()
+ .text(diffCount > 0 ? '\uf13e' : '\uf023');
+ button.descendants('span.badge').text(diffCount > 0 ? diffCount : '');
+ var disabled = diffCount === 0;
+ button.toggleClass('disabled', disabled);
+ uDom('#buttonRevertScope').toggleClass('disabled', disabled);
+}
+
+/******************************************************************************/
+
+function persistMatrix() {
+ var request = {
+ what: 'applyDiffToPermanentMatrix',
+ diff: matrixSnapshot.diff
+ };
+ vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
+}
+
+/******************************************************************************/
+
+// rhill 2014-03-12: revert completely ALL changes related to the
+// current page, including scopes.
+
+function revertMatrix() {
+ var request = {
+ what: 'applyDiffToTemporaryMatrix',
+ diff: matrixSnapshot.diff
+ };
+ vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
+}
+
+/******************************************************************************/
+
+// Buttons which are affected by any changes in the matrix
+
+function updateMatrixButtons() {
+ updateScopeCell();
+ updateMatrixSwitches();
+ updatePersistButton();
+}
+
+/******************************************************************************/
+
+function revertAll() {
+ var request = {
+ what: 'revertTemporaryMatrix'
+ };
+ vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
+ dropDownMenuHide();
+}
+
+/******************************************************************************/
+
+function buttonReloadHandler(ev) {
+ vAPI.messaging.send('popup.js', {
+ what: 'forceReloadTab',
+ tabId: matrixSnapshot.tabId,
+ bypassCache: ev.ctrlKey || ev.metaKey || ev.shiftKey
+ });
+}
+
+/******************************************************************************/
+
+function mouseenterMatrixCellHandler(ev) {
+ matrixCellHotspots.appendTo(ev.target);
+}
+
+function mouseleaveMatrixCellHandler() {
+ matrixCellHotspots.detach();
+}
+
+/******************************************************************************/
+
+function gotoExtensionURL(ev) {
+ var url = uDom(ev.currentTarget).attr('data-extension-url');
+ if ( url ) {
+ vAPI.messaging.send('popup.js', {
+ what: 'gotoExtensionURL',
+ url: url,
+ shiftKey: ev.shiftKey
+ });
+ }
+ dropDownMenuHide();
+ vAPI.closePopup();
+}
+
+/******************************************************************************/
+
+function dropDownMenuShow(ev) {
+ var button = ev.target;
+ var menuOverlay = document.getElementById(button.getAttribute('data-dropdown-menu'));
+ var butnRect = button.getBoundingClientRect();
+ var viewRect = document.body.getBoundingClientRect();
+ var butnNormalLeft = butnRect.left / (viewRect.width - butnRect.width);
+ menuOverlay.classList.add('show');
+ var menu = menuOverlay.querySelector('.dropdown-menu');
+ var menuRect = menu.getBoundingClientRect();
+ var menuLeft = butnNormalLeft * (viewRect.width - menuRect.width);
+ menu.style.left = menuLeft.toFixed(0) + 'px';
+ menu.style.top = butnRect.bottom + 'px';
+}
+
+function dropDownMenuHide() {
+ uDom('.dropdown-menu-capture').removeClass('show');
+}
+
+/******************************************************************************/
+
+var onMatrixSnapshotReady = function(response) {
+ if ( response === 'ENOTFOUND' ) {
+ uDom.nodeFromId('noTabFound').textContent =
+ vAPI.i18n('matrixNoTabFound');
+ document.body.classList.add('noTabFound');
+ return;
+ }
+
+ // Now that tabId and pageURL are set, we can build our menu
+ initMenuEnvironment();
+ makeMenu();
+
+ // After popup menu is built, check whether there is a non-empty matrix
+ if ( matrixSnapshot.url === '' ) {
+ uDom('#matHead').remove();
+ uDom('#toolbarContainer').remove();
+
+ // https://github.com/gorhill/httpswitchboard/issues/191
+ uDom('#noNetTrafficPrompt').text(vAPI.i18n('matrixNoNetTrafficPrompt'));
+ uDom('#noNetTrafficPrompt').css('display', '');
+ }
+
+ // Create a hash to find out whether the reload button needs to be
+ // highlighted.
+ // TODO:
+};
+
+/******************************************************************************/
+
+var matrixSnapshotPoller = (function() {
+ var timer = null;
+
+ var preprocessMatrixSnapshot = function(snapshot) {
+ if ( Array.isArray(snapshot.headerIndices) ) {
+ snapshot.headerIndices = new Map(snapshot.headerIndices);
+ }
+ return snapshot;
+ };
+
+ var processPollResult = function(response) {
+ if ( typeof response !== 'object' ) {
+ return;
+ }
+ if (
+ response.mtxContentModified === false &&
+ response.mtxCountModified === false &&
+ response.pMatrixModified === false &&
+ response.tMatrixModified === false
+ ) {
+ return;
+ }
+ matrixSnapshot = preprocessMatrixSnapshot(response);
+
+ if ( response.mtxContentModified ) {
+ makeMenu();
+ return;
+ }
+ if ( response.mtxCountModified ) {
+ updateMatrixCounts();
+ }
+ if (
+ response.pMatrixModified ||
+ response.tMatrixModified ||
+ response.scopeModified
+ ) {
+ updateMatrixColors();
+ updateMatrixBehavior();
+ updateMatrixButtons();
+ }
+ };
+
+ var onPolled = function(response) {
+ processPollResult(response);
+ pollAsync();
+ };
+
+ var pollNow = function() {
+ unpollAsync();
+ vAPI.messaging.send('popup.js', {
+ what: 'matrixSnapshot',
+ tabId: matrixSnapshot.tabId,
+ scope: matrixSnapshot.scope,
+ mtxContentModifiedTime: matrixSnapshot.mtxContentModifiedTime,
+ mtxCountModifiedTime: matrixSnapshot.mtxCountModifiedTime,
+ mtxDiffCount: matrixSnapshot.diff.length,
+ pMatrixModifiedTime: matrixSnapshot.pMatrixModifiedTime,
+ tMatrixModifiedTime: matrixSnapshot.tMatrixModifiedTime,
+ }, onPolled);
+ };
+
+ var poll = function() {
+ timer = null;
+ pollNow();
+ };
+
+ var pollAsync = function() {
+ if ( timer !== null ) {
+ return;
+ }
+ if ( document.defaultView === null ) {
+ return;
+ }
+ timer = vAPI.setTimeout(poll, 1414);
+ };
+
+ var unpollAsync = function() {
+ if ( timer !== null ) {
+ clearTimeout(timer);
+ timer = null;
+ }
+ };
+
+ (function() {
+ var tabId = matrixSnapshot.tabId;
+
+ // If no tab id yet, see if there is one specified in our URL
+ if ( tabId === undefined ) {
+ var matches = window.location.search.match(/(?:\?|&)tabId=([^&]+)/);
+ if ( matches !== null ) {
+ tabId = matches[1];
+ // No need for logger button when embedded in logger
+ uDom('[data-extension-url="logger-ui.html"]').remove();
+ }
+ }
+
+ var snapshotFetched = function(response) {
+ if ( typeof response === 'object' ) {
+ matrixSnapshot = preprocessMatrixSnapshot(response);
+ }
+ onMatrixSnapshotReady(response);
+ pollAsync();
+ };
+
+ vAPI.messaging.send('popup.js', {
+ what: 'matrixSnapshot',
+ tabId: tabId
+ }, snapshotFetched);
+ })();
+
+ return {
+ pollNow: pollNow
+ };
+})();
+
+/******************************************************************************/
+
+// Below is UI stuff which is not key to make the menu, so this can
+// be done without having to wait for a tab to be bound to the menu.
+
+// We reuse for all cells the one and only cell hotspots.
+uDom('#whitelist').on('click', function() {
+ handleWhitelistFilter(uDom(this));
+ return false;
+ });
+uDom('#blacklist').on('click', function() {
+ handleBlacklistFilter(uDom(this));
+ return false;
+ });
+uDom('#domainOnly').on('click', function() {
+ toggleCollapseState(uDom(this));
+ return false;
+ });
+matrixCellHotspots = uDom('#cellHotspots').detach();
+uDom('body')
+ .on('mouseenter', '.matCell', mouseenterMatrixCellHandler)
+ .on('mouseleave', '.matCell', mouseleaveMatrixCellHandler);
+uDom('#specificScope').on('click', selectSpecificScope);
+uDom('#globalScope').on('click', selectGlobalScope);
+uDom('[id^="mtxSwitch_"]').on('click', toggleMatrixSwitch);
+uDom('#buttonPersist').on('click', persistMatrix);
+uDom('#buttonRevertScope').on('click', revertMatrix);
+
+uDom('#buttonRevertAll').on('click', revertAll);
+uDom('#buttonReload').on('click', buttonReloadHandler);
+uDom('.extensionURL').on('click', gotoExtensionURL);
+
+uDom('body').on('click', '[data-dropdown-menu]', dropDownMenuShow);
+uDom('body').on('click', '.dropdown-menu-capture', dropDownMenuHide);
+
+uDom('#matList').on('click', '.g4Meta', function(ev) {
+ matrixSnapshot.collapseBlacklistedDomains =
+ ev.target.classList.toggle('g4Collapsed');
+ setUserSetting(
+ 'popupCollapseBlacklistedDomains',
+ matrixSnapshot.collapseBlacklistedDomains
+ );
+});
+
+/******************************************************************************/
+
+})();
diff --git a/js/profiler.js b/js/profiler.js
new file mode 100644
index 0000000..732403c
--- /dev/null
+++ b/js/profiler.js
@@ -0,0 +1,63 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/******************************************************************************/
+
+var quickProfiler = (function() {
+ var timer = performance;
+ var time = 0;
+ var count = 0;
+ var tstart = 0;
+ var lastlog = timer.now();
+ var prompt = '';
+ var reset = function() {
+ time = 0;
+ count = 0;
+ tstart = 0;
+ };
+ var avg = function() {
+ return count > 0 ? time / count : 0;
+ };
+ var start = function(s) {
+ prompt = s || '';
+ tstart = timer.now();
+ };
+ var stop = function(period) {
+ if ( period === undefined ) {
+ period = 10000;
+ }
+ var now = timer.now();
+ count += 1;
+ time += (now - tstart);
+ if ( (now - lastlog) >= period ) {
+ console.log('µMatrix> %s: %s ms (%d samples)', prompt, avg().toFixed(3), count);
+ lastlog = now;
+ }
+ };
+ return {
+ reset: reset,
+ start: start,
+ stop: stop
+ };
+})();
+
+/******************************************************************************/
diff --git a/js/raw-settings.js b/js/raw-settings.js
new file mode 100644
index 0000000..4abcd97
--- /dev/null
+++ b/js/raw-settings.js
@@ -0,0 +1,116 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2018-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uBlock
+*/
+
+/* global uDom */
+
+'use strict';
+
+/******************************************************************************/
+
+(function() {
+
+/******************************************************************************/
+
+var messaging = vAPI.messaging;
+var cachedData = '';
+var rawSettingsInput = uDom.nodeFromId('rawSettings');
+
+/******************************************************************************/
+
+var hashFromRawSettings = function(raw) {
+ return raw.trim().replace(/\s+/g, '|');
+};
+
+/******************************************************************************/
+
+// This is to give a visual hint that the content of user blacklist has changed.
+
+var rawSettingsChanged = (function () {
+ var timer = null;
+
+ var handler = function() {
+ timer = null;
+ var changed =
+ hashFromRawSettings(rawSettingsInput.value) !== cachedData;
+ uDom.nodeFromId('rawSettingsApply').disabled = !changed;
+ };
+
+ return function() {
+ if ( timer !== null ) {
+ clearTimeout(timer);
+ }
+ timer = vAPI.setTimeout(handler, 100);
+ };
+})();
+
+/******************************************************************************/
+
+function renderRawSettings() {
+ var onRead = function(raw) {
+ cachedData = hashFromRawSettings(raw);
+ var pretty = [],
+ whitespaces = ' ',
+ lines = raw.split('\n'),
+ max = 0,
+ pos,
+ i, n = lines.length;
+ for ( i = 0; i < n; i++ ) {
+ pos = lines[i].indexOf(' ');
+ if ( pos > max ) {
+ max = pos;
+ }
+ }
+ for ( i = 0; i < n; i++ ) {
+ pos = lines[i].indexOf(' ');
+ pretty.push(whitespaces.slice(0, max - pos) + lines[i]);
+ }
+ rawSettingsInput.value = pretty.join('\n') + '\n';
+ rawSettingsChanged();
+ rawSettingsInput.focus();
+ };
+ messaging.send('dashboard', { what: 'readRawSettings' }, onRead);
+}
+
+/******************************************************************************/
+
+var applyChanges = function() {
+ messaging.send(
+ 'dashboard',
+ {
+ what: 'writeRawSettings',
+ content: rawSettingsInput.value
+ },
+ renderRawSettings
+ );
+};
+
+/******************************************************************************/
+
+// Handle user interaction
+uDom('#rawSettings').on('input', rawSettingsChanged);
+uDom('#rawSettingsApply').on('click', applyChanges);
+
+renderRawSettings();
+
+/******************************************************************************/
+
+})();
diff --git a/js/settings.js b/js/settings.js
new file mode 100644
index 0000000..bc11c7a
--- /dev/null
+++ b/js/settings.js
@@ -0,0 +1,195 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global uDom */
+
+'use strict';
+
+/******************************************************************************/
+
+(function() {
+
+/******************************************************************************/
+
+var cachedSettings = {};
+
+/******************************************************************************/
+
+function changeUserSettings(name, value) {
+ vAPI.messaging.send('settings.js', {
+ what: 'userSettings',
+ name: name,
+ value: value
+ });
+}
+
+/******************************************************************************/
+
+function changeMatrixSwitch(name, state) {
+ vAPI.messaging.send('settings.js', {
+ what: 'setMatrixSwitch',
+ switchName: name,
+ state: state
+ });
+}
+
+/******************************************************************************/
+
+function onChangeValueHandler(elem, setting, min, max) {
+ var oldVal = cachedSettings.userSettings[setting];
+ var newVal = Math.round(parseFloat(elem.value));
+ if ( typeof newVal !== 'number' ) {
+ newVal = oldVal;
+ } else {
+ newVal = Math.max(newVal, min);
+ newVal = Math.min(newVal, max);
+ }
+ elem.value = newVal;
+ if ( newVal !== oldVal ) {
+ changeUserSettings(setting, newVal);
+ }
+}
+
+/******************************************************************************/
+
+function prepareToDie() {
+ onChangeValueHandler(
+ uDom.nodeFromId('deleteUnusedSessionCookiesAfter'),
+ 'deleteUnusedSessionCookiesAfter',
+ 15, 1440
+ );
+ onChangeValueHandler(
+ uDom.nodeFromId('clearBrowserCacheAfter'),
+ 'clearBrowserCacheAfter',
+ 15, 1440
+ );
+}
+
+/******************************************************************************/
+
+function onInputChanged(ev) {
+ var target = ev.target;
+
+ switch ( target.id ) {
+ case 'displayTextSize':
+ changeUserSettings('displayTextSize', target.value + 'px');
+ break;
+ case 'clearBrowserCache':
+ case 'cloudStorageEnabled':
+ case 'collapseBlacklisted':
+ case 'collapseBlocked':
+ case 'colorBlindFriendly':
+ case 'deleteCookies':
+ case 'deleteLocalStorage':
+ case 'deleteUnusedSessionCookies':
+ case 'iconBadgeEnabled':
+ case 'processHyperlinkAuditing':
+ changeUserSettings(target.id, target.checked);
+ break;
+ case 'noMixedContent':
+ case 'noscriptTagsSpoofed':
+ case 'processReferer':
+ changeMatrixSwitch(
+ target.getAttribute('data-matrix-switch'),
+ target.checked
+ );
+ break;
+ case 'deleteUnusedSessionCookiesAfter':
+ onChangeValueHandler(target, 'deleteUnusedSessionCookiesAfter', 15, 1440);
+ break;
+ case 'clearBrowserCacheAfter':
+ onChangeValueHandler(target, 'clearBrowserCacheAfter', 15, 1440);
+ break;
+ case 'popupScopeLevel':
+ changeUserSettings('popupScopeLevel', target.value);
+ break;
+ default:
+ break;
+ }
+
+ switch ( target.id ) {
+ case 'collapseBlocked':
+ synchronizeWidgets();
+ break;
+ default:
+ break;
+ }
+}
+
+/******************************************************************************/
+
+function synchronizeWidgets() {
+ var e1, e2;
+
+ e1 = uDom.nodeFromId('collapseBlocked');
+ e2 = uDom.nodeFromId('collapseBlacklisted');
+ if ( e1.checked ) {
+ e2.setAttribute('disabled', '');
+ } else {
+ e2.removeAttribute('disabled');
+ }
+}
+
+/******************************************************************************/
+
+vAPI.messaging.send(
+ 'settings.js',
+ { what: 'getUserSettings' },
+ function onSettingsReceived(settings) {
+ // Cache copy
+ cachedSettings = settings;
+
+ var userSettings = settings.userSettings;
+ var matrixSwitches = settings.matrixSwitches;
+
+ uDom('[data-setting-bool]').forEach(function(elem){
+ elem.prop('checked', userSettings[elem.prop('id')] === true);
+ });
+
+ uDom('[data-matrix-switch]').forEach(function(elem){
+ var switchName = elem.attr('data-matrix-switch');
+ if ( typeof switchName === 'string' && switchName !== '' ) {
+ elem.prop('checked', matrixSwitches[switchName] === true);
+ }
+ });
+
+ uDom.nodeFromId('displayTextSize').value =
+ parseInt(userSettings.displayTextSize, 10) || 14;
+
+ uDom.nodeFromId('popupScopeLevel').value = userSettings.popupScopeLevel;
+ uDom.nodeFromId('deleteUnusedSessionCookiesAfter').value =
+ userSettings.deleteUnusedSessionCookiesAfter;
+ uDom.nodeFromId('clearBrowserCacheAfter').value =
+ userSettings.clearBrowserCacheAfter;
+
+ synchronizeWidgets();
+
+ document.addEventListener('change', onInputChanged);
+
+ // https://github.com/gorhill/httpswitchboard/issues/197
+ uDom(window).on('beforeunload', prepareToDie);
+ }
+);
+
+/******************************************************************************/
+
+})();
diff --git a/js/start.js b/js/start.js
new file mode 100644
index 0000000..051e58f
--- /dev/null
+++ b/js/start.js
@@ -0,0 +1,108 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+// ORDER IS IMPORTANT
+
+/******************************************************************************/
+
+// Load everything
+
+(function() {
+
+'use strict';
+
+/******************************************************************************/
+
+var µm = µMatrix;
+
+/******************************************************************************/
+
+var processCallbackQueue = function(queue, callback) {
+ var processOne = function() {
+ var fn = queue.pop();
+ if ( fn ) {
+ fn(processOne);
+ } else if ( typeof callback === 'function' ) {
+ callback();
+ }
+ };
+ processOne();
+};
+
+/******************************************************************************/
+
+var onAllDone = function() {
+ µm.webRequest.start();
+
+ µm.assets.addObserver(µm.assetObserver.bind(µm));
+ µm.scheduleAssetUpdater(µm.userSettings.autoUpdate ? 7 * 60 * 1000 : 0);
+
+ vAPI.cloud.start([ 'myRulesPane' ]);
+};
+
+/******************************************************************************/
+
+var onTabsReady = function(tabs) {
+ var tab;
+ var i = tabs.length;
+ // console.debug('start.js > binding %d tabs', i);
+ while ( i-- ) {
+ tab = tabs[i];
+ µm.tabContextManager.push(tab.id, tab.url, 'newURL');
+ }
+
+ onAllDone();
+};
+
+/******************************************************************************/
+
+var onUserSettingsLoaded = function() {
+ µm.loadHostsFiles();
+};
+
+/******************************************************************************/
+
+var onPSLReady = function() {
+ µm.loadUserSettings(onUserSettingsLoaded);
+ µm.loadRawSettings();
+ µm.loadMatrix();
+
+ // rhill 2013-11-24: bind behind-the-scene virtual tab/url manually, since the
+ // normal way forbid binding behind the scene tab.
+ // https://github.com/gorhill/httpswitchboard/issues/67
+ µm.pageStores[vAPI.noTabId] = µm.pageStoreFactory(µm.tabContextManager.mustLookup(vAPI.noTabId));
+ µm.pageStores[vAPI.noTabId].title = vAPI.i18n('statsPageDetailedBehindTheScenePage');
+
+ vAPI.tabs.getAll(onTabsReady);
+};
+
+/******************************************************************************/
+
+processCallbackQueue(µm.onBeforeStartQueue, function() {
+ µm.loadPublicSuffixList(onPSLReady);
+});
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
diff --git a/js/storage.js b/js/storage.js
new file mode 100644
index 0000000..c2ece8f
--- /dev/null
+++ b/js/storage.js
@@ -0,0 +1,615 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global objectAssign, punycode, publicSuffixList */
+
+'use strict';
+
+/******************************************************************************/
+
+µMatrix.getBytesInUse = function() {
+ var µm = this;
+ var getBytesInUseHandler = function(bytesInUse) {
+ µm.storageUsed = bytesInUse;
+ };
+ // Not all WebExtension implementations support getBytesInUse().
+ if ( typeof vAPI.storage.getBytesInUse === 'function' ) {
+ vAPI.storage.getBytesInUse(null, getBytesInUseHandler);
+ } else {
+ µm.storageUsed = undefined;
+ }
+};
+
+/******************************************************************************/
+
+µMatrix.saveUserSettings = function() {
+ this.XAL.keyvalSetMany(
+ this.userSettings,
+ this.getBytesInUse.bind(this)
+ );
+};
+
+µMatrix.loadUserSettings = function(callback) {
+ var µm = this;
+
+ if ( typeof callback !== 'function' ) {
+ callback = this.noopFunc;
+ }
+
+ var settingsLoaded = function(store) {
+ // console.log('storage.js > loaded user settings');
+
+ µm.userSettings = store;
+
+ callback(µm.userSettings);
+ };
+
+ vAPI.storage.get(this.userSettings, settingsLoaded);
+};
+
+/******************************************************************************/
+
+µMatrix.loadRawSettings = function() {
+ var µm = this;
+
+ var onLoaded = function(bin) {
+ if ( !bin || bin.rawSettings instanceof Object === false ) { return; }
+ for ( var key of Object.keys(bin.rawSettings) ) {
+ if (
+ µm.rawSettings.hasOwnProperty(key) === false ||
+ typeof bin.rawSettings[key] !== typeof µm.rawSettings[key]
+ ) {
+ continue;
+ }
+ µm.rawSettings[key] = bin.rawSettings[key];
+ }
+ µm.rawSettingsWriteTime = Date.now();
+ };
+
+ vAPI.storage.get('rawSettings', onLoaded);
+};
+
+µMatrix.saveRawSettings = function(rawSettings, callback) {
+ var keys = Object.keys(rawSettings);
+ if ( keys.length === 0 ) {
+ if ( typeof callback === 'function' ) {
+ callback();
+ }
+ return;
+ }
+ for ( var key of keys ) {
+ if (
+ this.rawSettingsDefault.hasOwnProperty(key) &&
+ typeof rawSettings[key] === typeof this.rawSettingsDefault[key]
+ ) {
+ this.rawSettings[key] = rawSettings[key];
+ }
+ }
+ vAPI.storage.set({ rawSettings: this.rawSettings }, callback);
+ this.rawSettingsWriteTime = Date.now();
+};
+
+µMatrix.rawSettingsFromString = function(raw) {
+ var result = {},
+ lineIter = new this.LineIterator(raw),
+ line, matches, name, value;
+ while ( lineIter.eot() === false ) {
+ line = lineIter.next().trim();
+ matches = /^(\S+)(\s+(.+))?$/.exec(line);
+ if ( matches === null ) { continue; }
+ name = matches[1];
+ if ( this.rawSettingsDefault.hasOwnProperty(name) === false ) {
+ continue;
+ }
+ value = (matches[2] || '').trim();
+ switch ( typeof this.rawSettingsDefault[name] ) {
+ case 'boolean':
+ if ( value === 'true' ) {
+ value = true;
+ } else if ( value === 'false' ) {
+ value = false;
+ } else {
+ value = this.rawSettingsDefault[name];
+ }
+ break;
+ case 'string':
+ if ( value === '' ) {
+ value = this.rawSettingsDefault[name];
+ }
+ break;
+ case 'number':
+ value = parseInt(value, 10);
+ if ( isNaN(value) ) {
+ value = this.rawSettingsDefault[name];
+ }
+ break;
+ default:
+ break;
+ }
+ if ( this.rawSettings[name] !== value ) {
+ result[name] = value;
+ }
+ }
+ this.saveRawSettings(result);
+};
+
+µMatrix.stringFromRawSettings = function() {
+ var out = [];
+ for ( var key of Object.keys(this.rawSettings).sort() ) {
+ out.push(key + ' ' + this.rawSettings[key]);
+ }
+ return out.join('\n');
+};
+
+/******************************************************************************/
+
+// save white/blacklist
+µMatrix.saveMatrix = function() {
+ µMatrix.XAL.keyvalSetOne('userMatrix', this.pMatrix.toString());
+};
+
+/******************************************************************************/
+
+µMatrix.loadMatrix = function(callback) {
+ if ( typeof callback !== 'function' ) {
+ callback = this.noopFunc;
+ }
+ var µm = this;
+ var onLoaded = function(bin) {
+ if ( bin.hasOwnProperty('userMatrix') ) {
+ µm.pMatrix.fromString(bin.userMatrix);
+ µm.tMatrix.assign(µm.pMatrix);
+ callback();
+ }
+ };
+ this.XAL.keyvalGetOne('userMatrix', onLoaded);
+};
+
+/******************************************************************************/
+
+µMatrix.listKeysFromCustomHostsFiles = function(raw) {
+ var out = new Set(),
+ reIgnore = /^[!#]/,
+ reValid = /^[a-z-]+:\/\/\S+/,
+ lineIter = new this.LineIterator(raw),
+ location;
+ while ( lineIter.eot() === false ) {
+ location = lineIter.next().trim();
+ if ( reIgnore.test(location) || !reValid.test(location) ) { continue; }
+ out.add(location);
+ }
+ return this.setToArray(out);
+};
+
+/******************************************************************************/
+
+µMatrix.getAvailableHostsFiles = function(callback) {
+ var µm = this,
+ availableHostsFiles = {};
+
+ // Custom filter lists.
+ var importedListKeys = this.listKeysFromCustomHostsFiles(µm.userSettings.externalHostsFiles),
+ i = importedListKeys.length,
+ listKey, entry;
+ while ( i-- ) {
+ listKey = importedListKeys[i];
+ entry = {
+ content: 'filters',
+ contentURL: listKey,
+ external: true,
+ submitter: 'user',
+ title: listKey
+ };
+ availableHostsFiles[listKey] = entry;
+ this.assets.registerAssetSource(listKey, entry);
+ }
+
+ // selected lists
+ var onSelectedHostsFilesLoaded = function(bin) {
+ // Now get user's selection of lists
+ for ( var assetKey in bin.liveHostsFiles ) {
+ var availableEntry = availableHostsFiles[assetKey];
+ if ( availableEntry === undefined ) { continue; }
+ var liveEntry = bin.liveHostsFiles[assetKey];
+ availableEntry.off = liveEntry.off || false;
+ if ( liveEntry.entryCount !== undefined ) {
+ availableEntry.entryCount = liveEntry.entryCount;
+ }
+ if ( liveEntry.entryUsedCount !== undefined ) {
+ availableEntry.entryUsedCount = liveEntry.entryUsedCount;
+ }
+ // This may happen if the list name was pulled from the list content
+ if ( availableEntry.title === '' && liveEntry.title !== undefined ) {
+ availableEntry.title = liveEntry.title;
+ }
+ }
+
+ // Remove unreferenced imported filter lists.
+ var dict = new Set(importedListKeys);
+ for ( assetKey in availableHostsFiles ) {
+ var entry = availableHostsFiles[assetKey];
+ if ( entry.submitter !== 'user' ) { continue; }
+ if ( dict.has(assetKey) ) { continue; }
+ delete availableHostsFiles[assetKey];
+ µm.assets.unregisterAssetSource(assetKey);
+ µm.assets.remove(assetKey);
+ }
+
+ callback(availableHostsFiles);
+ };
+
+ // built-in lists
+ var onBuiltinHostsFilesLoaded = function(entries) {
+ for ( var assetKey in entries ) {
+ if ( entries.hasOwnProperty(assetKey) === false ) { continue; }
+ entry = entries[assetKey];
+ if ( entry.content !== 'filters' ) { continue; }
+ availableHostsFiles[assetKey] = objectAssign({}, entry);
+ }
+
+ // Now get user's selection of lists
+ vAPI.storage.get(
+ { 'liveHostsFiles': availableHostsFiles },
+ onSelectedHostsFilesLoaded
+ );
+ };
+
+ this.assets.metadata(onBuiltinHostsFilesLoaded);
+};
+
+/******************************************************************************/
+
+µMatrix.loadHostsFiles = function(callback) {
+ var µm = µMatrix;
+ var hostsFileLoadCount;
+
+ if ( typeof callback !== 'function' ) {
+ callback = this.noopFunc;
+ }
+
+ var loadHostsFilesEnd = function() {
+ µm.ubiquitousBlacklist.freeze();
+ vAPI.storage.set({ 'liveHostsFiles': µm.liveHostsFiles });
+ vAPI.messaging.broadcast({ what: 'loadHostsFilesCompleted' });
+ µm.getBytesInUse();
+ callback();
+ };
+
+ var mergeHostsFile = function(details) {
+ µm.mergeHostsFile(details);
+ hostsFileLoadCount -= 1;
+ if ( hostsFileLoadCount === 0 ) {
+ loadHostsFilesEnd();
+ }
+ };
+
+ var loadHostsFilesStart = function(hostsFiles) {
+ µm.liveHostsFiles = hostsFiles;
+ µm.ubiquitousBlacklist.reset();
+ var locations = Object.keys(hostsFiles);
+ hostsFileLoadCount = locations.length;
+
+ // Load all hosts file which are not disabled.
+ var location;
+ while ( (location = locations.pop()) ) {
+ if ( hostsFiles[location].off ) {
+ hostsFileLoadCount -= 1;
+ continue;
+ }
+ µm.assets.get(location, mergeHostsFile);
+ }
+
+ // https://github.com/gorhill/uMatrix/issues/2
+ if ( hostsFileLoadCount === 0 ) {
+ loadHostsFilesEnd();
+ return;
+ }
+ };
+
+ this.getAvailableHostsFiles(loadHostsFilesStart);
+};
+
+/******************************************************************************/
+
+µMatrix.mergeHostsFile = function(details) {
+ var usedCount = this.ubiquitousBlacklist.count;
+ var duplicateCount = this.ubiquitousBlacklist.duplicateCount;
+
+ this.mergeHostsFileContent(details.content);
+
+ usedCount = this.ubiquitousBlacklist.count - usedCount;
+ duplicateCount = this.ubiquitousBlacklist.duplicateCount - duplicateCount;
+
+ var hostsFilesMeta = this.liveHostsFiles[details.assetKey];
+ hostsFilesMeta.entryCount = usedCount + duplicateCount;
+ hostsFilesMeta.entryUsedCount = usedCount;
+};
+
+/******************************************************************************/
+
+µMatrix.mergeHostsFileContent = function(rawText) {
+ var rawEnd = rawText.length;
+ var ubiquitousBlacklist = this.ubiquitousBlacklist;
+ var reLocalhost = /(^|\s)(localhost\.localdomain|localhost|local|broadcasthost|0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)(?=\s|$)/g;
+ var reAsciiSegment = /^[\x21-\x7e]+$/;
+ var matches;
+ var lineBeg = 0, lineEnd;
+ var line;
+
+ while ( lineBeg < rawEnd ) {
+ lineEnd = rawText.indexOf('\n', lineBeg);
+ if ( lineEnd < 0 ) {
+ lineEnd = rawText.indexOf('\r', lineBeg);
+ if ( lineEnd < 0 ) {
+ lineEnd = rawEnd;
+ }
+ }
+
+ // rhill 2014-04-18: The trim is important here, as without it there
+ // could be a lingering `\r` which would cause problems in the
+ // following parsing code.
+ line = rawText.slice(lineBeg, lineEnd).trim();
+ lineBeg = lineEnd + 1;
+
+ // https://github.com/gorhill/httpswitchboard/issues/15
+ // Ensure localhost et al. don't end up in the ubiquitous blacklist.
+ line = line
+ .replace(/#.*$/, '')
+ .toLowerCase()
+ .replace(reLocalhost, '')
+ .trim();
+
+ // The filter is whatever sequence of printable ascii character without
+ // whitespaces
+ matches = reAsciiSegment.exec(line);
+ if ( !matches || matches.length === 0 ) {
+ continue;
+ }
+
+ // Bypass anomalies
+ // For example, when a filter contains whitespace characters, or
+ // whatever else outside the range of printable ascii characters.
+ if ( matches[0] !== line ) {
+ continue;
+ }
+
+ line = matches[0];
+ if ( line === '' ) {
+ continue;
+ }
+
+ ubiquitousBlacklist.add(line);
+ }
+};
+
+/******************************************************************************/
+
+// `switches` contains the filter lists for which the switch must be revisited.
+
+µMatrix.selectHostsFiles = function(details, callback) {
+ var µm = this,
+ externalHostsFiles = this.userSettings.externalHostsFiles,
+ i, n, assetKey;
+
+ // Hosts file to select
+ if ( Array.isArray(details.toSelect) ) {
+ for ( assetKey in this.liveHostsFiles ) {
+ if ( this.liveHostsFiles.hasOwnProperty(assetKey) === false ) {
+ continue;
+ }
+ if ( details.toSelect.indexOf(assetKey) !== -1 ) {
+ this.liveHostsFiles[assetKey].off = false;
+ } else if ( details.merge !== true ) {
+ this.liveHostsFiles[assetKey].off = true;
+ }
+ }
+ }
+
+ // Imported hosts files to remove
+ if ( Array.isArray(details.toRemove) ) {
+ var removeURLFromHaystack = function(haystack, needle) {
+ return haystack.replace(
+ new RegExp(
+ '(^|\\n)' +
+ needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
+ '(\\n|$)', 'g'),
+ '\n'
+ ).trim();
+ };
+ for ( i = 0, n = details.toRemove.length; i < n; i++ ) {
+ assetKey = details.toRemove[i];
+ delete this.liveHostsFiles[assetKey];
+ externalHostsFiles = removeURLFromHaystack(externalHostsFiles, assetKey);
+ this.assets.remove(assetKey);
+ }
+ }
+
+ // Hosts file to import
+ if ( typeof details.toImport === 'string' ) {
+ // https://github.com/gorhill/uBlock/issues/1181
+ // Try mapping the URL of an imported filter list to the assetKey of an
+ // existing stock list.
+ var assetKeyFromURL = function(url) {
+ var needle = url.replace(/^https?:/, '');
+ var assets = µm.liveHostsFiles, asset;
+ for ( var assetKey in assets ) {
+ asset = assets[assetKey];
+ if ( asset.content !== 'filters' ) { continue; }
+ if ( typeof asset.contentURL === 'string' ) {
+ if ( asset.contentURL.endsWith(needle) ) { return assetKey; }
+ continue;
+ }
+ if ( Array.isArray(asset.contentURL) === false ) { continue; }
+ for ( i = 0, n = asset.contentURL.length; i < n; i++ ) {
+ if ( asset.contentURL[i].endsWith(needle) ) {
+ return assetKey;
+ }
+ }
+ }
+ return url;
+ };
+ var importedSet = new Set(this.listKeysFromCustomHostsFiles(externalHostsFiles)),
+ toImportSet = new Set(this.listKeysFromCustomHostsFiles(details.toImport)),
+ iter = toImportSet.values();
+ for (;;) {
+ var entry = iter.next();
+ if ( entry.done ) { break; }
+ if ( importedSet.has(entry.value) ) { continue; }
+ assetKey = assetKeyFromURL(entry.value);
+ if ( assetKey === entry.value ) {
+ importedSet.add(entry.value);
+ }
+ this.liveHostsFiles[assetKey] = {
+ content: 'filters',
+ contentURL: [ assetKey ],
+ title: assetKey
+ };
+ }
+ externalHostsFiles = this.setToArray(importedSet).sort().join('\n');
+ }
+
+ if ( externalHostsFiles !== this.userSettings.externalHostsFiles ) {
+ this.userSettings.externalHostsFiles = externalHostsFiles;
+ vAPI.storage.set({ externalHostsFiles: externalHostsFiles });
+ }
+ vAPI.storage.set({ 'liveHostsFiles': this.liveHostsFiles });
+
+ if ( typeof callback === 'function' ) {
+ callback();
+ }
+};
+
+/******************************************************************************/
+
+// `switches` contains the preset blacklists for which the switch must be
+// revisited.
+
+µMatrix.reloadHostsFiles = function() {
+ this.loadHostsFiles();
+};
+
+/******************************************************************************/
+
+µMatrix.loadPublicSuffixList = function(callback) {
+ if ( typeof callback !== 'function' ) {
+ callback = this.noopFunc;
+ }
+
+ var applyPublicSuffixList = function(details) {
+ if ( !details.error ) {
+ publicSuffixList.parse(details.content, punycode.toASCII);
+ }
+ callback();
+ };
+
+ this.assets.get(this.pslAssetKey, applyPublicSuffixList);
+};
+
+/******************************************************************************/
+
+µMatrix.scheduleAssetUpdater = (function() {
+ var timer, next = 0;
+ return function(updateDelay) {
+ if ( timer ) {
+ clearTimeout(timer);
+ timer = undefined;
+ }
+ if ( updateDelay === 0 ) {
+ next = 0;
+ return;
+ }
+ var now = Date.now();
+ // Use the new schedule if and only if it is earlier than the previous
+ // one.
+ if ( next !== 0 ) {
+ updateDelay = Math.min(updateDelay, Math.max(next - now, 0));
+ }
+ next = now + updateDelay;
+ timer = vAPI.setTimeout(function() {
+ timer = undefined;
+ next = 0;
+ µMatrix.assets.updateStart({ delay: 120000 });
+ }, updateDelay);
+ };
+})();
+
+/******************************************************************************/
+
+µMatrix.assetObserver = function(topic, details) {
+ // Do not update filter list if not in use.
+ if ( topic === 'before-asset-updated' ) {
+ if (
+ this.liveHostsFiles.hasOwnProperty(details.assetKey) === false ||
+ this.liveHostsFiles[details.assetKey].off === true
+ ) {
+ return false;
+ }
+ return;
+ }
+
+ if ( topic === 'after-asset-updated' ) {
+ vAPI.messaging.broadcast({
+ what: 'assetUpdated',
+ key: details.assetKey,
+ cached: true
+ });
+ return;
+ }
+
+ // Update failed.
+ if ( topic === 'asset-update-failed' ) {
+ vAPI.messaging.broadcast({
+ what: 'assetUpdated',
+ key: details.assetKey,
+ failed: true
+ });
+ return;
+ }
+
+ // Reload all filter lists if needed.
+ if ( topic === 'after-assets-updated' ) {
+ if ( details.assetKeys.length !== 0 ) {
+ this.loadHostsFiles();
+ }
+ if ( this.userSettings.autoUpdate ) {
+ this.scheduleAssetUpdater(25200000);
+ } else {
+ this.scheduleAssetUpdater(0);
+ }
+ vAPI.messaging.broadcast({
+ what: 'assetsUpdated',
+ assetKeys: details.assetKeys
+ });
+ return;
+ }
+
+ // New asset source became available, if it's a filter list, should we
+ // auto-select it?
+ if ( topic === 'builtin-asset-source-added' ) {
+ if ( details.entry.content === 'filters' ) {
+ if ( details.entry.off !== true ) {
+ this.saveSelectedFilterLists([ details.assetKey ], true);
+ }
+ }
+ return;
+ }
+};
diff --git a/js/tab.js b/js/tab.js
new file mode 100644
index 0000000..b0dd1ab
--- /dev/null
+++ b/js/tab.js
@@ -0,0 +1,710 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/******************************************************************************/
+/******************************************************************************/
+
+(function() {
+
+'use strict';
+
+/******************************************************************************/
+
+var µm = µMatrix;
+
+// https://github.com/gorhill/httpswitchboard/issues/303
+// Some kind of trick going on here:
+// Any scheme other than 'http' and 'https' is remapped into a fake
+// URL which trick the rest of µMatrix into being able to process an
+// otherwise unmanageable scheme. µMatrix needs web page to have a proper
+// hostname to work properly, so just like the 'behind-the-scene'
+// fake domain name, we map unknown schemes into a fake '{scheme}-scheme'
+// hostname. This way, for a specific scheme you can create scope with
+// rules which will apply only to that scheme.
+
+/******************************************************************************/
+/******************************************************************************/
+
+µm.normalizePageURL = function(tabId, pageURL) {
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return 'http://' + this.behindTheSceneScope + '/';
+ }
+
+ // If the URL is that of our "blocked page" document, return the URL of
+ // the blocked page.
+ if ( pageURL.lastIndexOf(vAPI.getURL('main-blocked.html'), 0) === 0 ) {
+ var matches = /main-blocked\.html\?details=([^&]+)/.exec(pageURL);
+ if ( matches && matches.length === 2 ) {
+ try {
+ var details = JSON.parse(atob(matches[1]));
+ pageURL = details.url;
+ } catch (e) {
+ }
+ }
+ }
+
+ var uri = this.URI.set(pageURL);
+ var scheme = uri.scheme;
+ if ( scheme === 'https' || scheme === 'http' ) {
+ return uri.normalizedURI();
+ }
+
+ var fakeHostname = scheme + '-scheme';
+
+ if ( uri.hostname !== '' ) {
+ fakeHostname = uri.hostname + '.' + fakeHostname;
+ } else if ( scheme === 'about' ) {
+ fakeHostname = uri.path + '.' + fakeHostname;
+ }
+
+ return 'http://' + fakeHostname + '/';
+};
+
+/******************************************************************************/
+/******************************************************************************
+
+To keep track from which context *exactly* network requests are made. This is
+often tricky for various reasons, and the challenge is not specific to one
+browser.
+
+The time at which a URL is assigned to a tab and the time when a network
+request for a root document is made must be assumed to be unrelated: it's all
+asynchronous. There is no guaranteed order in which the two events are fired.
+
+Also, other "anomalies" can occur:
+
+- a network request for a root document is fired without the corresponding
+tab being really assigned a new URL
+<https://github.com/chrisaljoudi/uBlock/issues/516>
+
+- a network request for a secondary resource is labeled with a tab id for
+which no root document was pulled for that tab.
+<https://github.com/chrisaljoudi/uBlock/issues/1001>
+
+- a network request for a secondary resource is made without the root
+document to which it belongs being formally bound yet to the proper tab id,
+causing a bad scope to be used for filtering purpose.
+<https://github.com/chrisaljoudi/uBlock/issues/1205>
+<https://github.com/chrisaljoudi/uBlock/issues/1140>
+
+So the solution here is to keep a lightweight data structure which only
+purpose is to keep track as accurately as possible of which root document
+belongs to which tab. That's the only purpose, and because of this, there are
+no restrictions for when the URL of a root document can be associated to a tab.
+
+Before, the PageStore object was trying to deal with this, but it had to
+enforce some restrictions so as to not descend into one of the above issues, or
+other issues. The PageStore object can only be associated with a tab for which
+a definitive navigation event occurred, because it collects information about
+what occurred in the tab (for example, the number of requests blocked for a
+page).
+
+The TabContext objects do not suffer this restriction, and as a result they
+offer the most reliable picture of which root document URL is really associated
+to which tab. Moreover, the TabObject can undo an association from a root
+document, and automatically re-associate with the next most recent. This takes
+care of <https://github.com/chrisaljoudi/uBlock/issues/516>.
+
+The PageStore object no longer cache the various information about which
+root document it is currently bound. When it needs to find out, it will always
+defer to the TabContext object, which will provide the real answer. This takes
+case of <https://github.com/chrisaljoudi/uBlock/issues/1205>. In effect, the
+master switch and dynamic filtering rules can be evaluated now properly even
+in the absence of a PageStore object, this was not the case before.
+
+Also, the TabContext object will try its best to find a good candidate root
+document URL for when none exists. This takes care of
+<https://github.com/chrisaljoudi/uBlock/issues/1001>.
+
+The TabContext manager is self-contained, and it takes care to properly
+housekeep itself.
+
+*/
+
+µm.tabContextManager = (function() {
+ var tabContexts = Object.create(null);
+
+ // https://github.com/chrisaljoudi/uBlock/issues/1001
+ // This is to be used as last-resort fallback in case a tab is found to not
+ // be bound while network requests are fired for the tab.
+ var mostRecentRootDocURL = '';
+ var mostRecentRootDocURLTimestamp = 0;
+
+ var gcPeriod = 31 * 60 * 1000; // every 31 minutes
+
+ // A pushed entry is removed from the stack unless it is committed with
+ // a set time.
+ var StackEntry = function(url, commit) {
+ this.url = url;
+ this.committed = commit;
+ this.tstamp = Date.now();
+ };
+
+ var TabContext = function(tabId) {
+ this.tabId = tabId;
+ this.stack = [];
+ this.rawURL =
+ this.normalURL =
+ this.scheme =
+ this.rootHostname =
+ this.rootDomain = '';
+ this.secure = false;
+ this.commitTimer = null;
+ this.gcTimer = null;
+
+ tabContexts[tabId] = this;
+ };
+
+ TabContext.prototype.destroy = function() {
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ return;
+ }
+ if ( this.gcTimer !== null ) {
+ clearTimeout(this.gcTimer);
+ this.gcTimer = null;
+ }
+ delete tabContexts[this.tabId];
+ };
+
+ TabContext.prototype.onTab = function(tab) {
+ if ( tab ) {
+ this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
+ } else {
+ this.destroy();
+ }
+ };
+
+ TabContext.prototype.onGC = function() {
+ this.gcTimer = null;
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ return;
+ }
+ vAPI.tabs.get(this.tabId, this.onTab.bind(this));
+ };
+
+ // https://github.com/gorhill/uBlock/issues/248
+ // Stack entries have to be committed to stick. Non-committed stack
+ // entries are removed after a set delay.
+ TabContext.prototype.onCommit = function() {
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ return;
+ }
+ this.commitTimer = null;
+ // Remove uncommitted entries at the top of the stack.
+ var i = this.stack.length;
+ while ( i-- ) {
+ if ( this.stack[i].committed ) {
+ break;
+ }
+ }
+ // https://github.com/gorhill/uBlock/issues/300
+ // If no committed entry was found, fall back on the bottom-most one
+ // as being the committed one by default.
+ if ( i === -1 && this.stack.length !== 0 ) {
+ this.stack[0].committed = true;
+ i = 0;
+ }
+ i += 1;
+ if ( i < this.stack.length ) {
+ this.stack.length = i;
+ this.update();
+ µm.bindTabToPageStats(this.tabId, 'newURL');
+ }
+ };
+
+ // This takes care of orphanized tab contexts. Can't be started for all
+ // contexts, as the behind-the-scene context is permanent -- so we do not
+ // want to flush it.
+ TabContext.prototype.autodestroy = function() {
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
+ return;
+ }
+ this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
+ };
+
+ // Update just force all properties to be updated to match the most recent
+ // root URL.
+ TabContext.prototype.update = function() {
+ if ( this.stack.length === 0 ) {
+ this.rawURL = this.normalURL = this.scheme =
+ this.rootHostname = this.rootDomain = '';
+ this.secure = false;
+ return;
+ }
+ this.rawURL = this.stack[this.stack.length - 1].url;
+ this.normalURL = µm.normalizePageURL(this.tabId, this.rawURL);
+ this.scheme = µm.URI.schemeFromURI(this.rawURL);
+ this.rootHostname = µm.URI.hostnameFromURI(this.normalURL);
+ this.rootDomain = µm.URI.domainFromHostname(this.rootHostname) || this.rootHostname;
+ this.secure = µm.URI.isSecureScheme(this.scheme);
+ };
+
+ // Called whenever a candidate root URL is spotted for the tab.
+ TabContext.prototype.push = function(url, context) {
+ if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; }
+ var committed = context !== undefined;
+ var count = this.stack.length;
+ var topEntry = this.stack[count - 1];
+ if ( topEntry && topEntry.url === url ) {
+ if ( committed ) {
+ topEntry.committed = true;
+ }
+ return;
+ }
+ if ( this.commitTimer !== null ) {
+ clearTimeout(this.commitTimer);
+ }
+ if ( committed ) {
+ this.stack = [new StackEntry(url, true)];
+ } else {
+ this.stack.push(new StackEntry(url));
+ this.commitTimer = vAPI.setTimeout(this.onCommit.bind(this), 1000);
+ }
+ this.update();
+ µm.bindTabToPageStats(this.tabId, context);
+ };
+
+ // These are to be used for the API of the tab context manager.
+
+ var push = function(tabId, url, context) {
+ var entry = tabContexts[tabId];
+ if ( entry === undefined ) {
+ entry = new TabContext(tabId);
+ entry.autodestroy();
+ }
+ entry.push(url, context);
+ mostRecentRootDocURL = url;
+ mostRecentRootDocURLTimestamp = Date.now();
+ return entry;
+ };
+
+ // Find a tab context for a specific tab. If none is found, attempt to
+ // fix this. When all fail, the behind-the-scene context is returned.
+ var mustLookup = function(tabId, url) {
+ var entry;
+ if ( url !== undefined ) {
+ entry = push(tabId, url);
+ } else {
+ entry = tabContexts[tabId];
+ }
+ if ( entry !== undefined ) {
+ return entry;
+ }
+ // https://github.com/chrisaljoudi/uBlock/issues/1025
+ // Google Hangout popup opens without a root frame. So for now we will
+ // just discard that best-guess root frame if it is too far in the
+ // future, at which point it ceases to be a "best guess".
+ if ( mostRecentRootDocURL !== '' && mostRecentRootDocURLTimestamp + 500 < Date.now() ) {
+ mostRecentRootDocURL = '';
+ }
+ // https://github.com/chrisaljoudi/uBlock/issues/1001
+ // Not a behind-the-scene request, yet no page store found for the
+ // tab id: we will thus bind the last-seen root document to the
+ // unbound tab. It's a guess, but better than ending up filtering
+ // nothing at all.
+ if ( mostRecentRootDocURL !== '' ) {
+ return push(tabId, mostRecentRootDocURL);
+ }
+ // If all else fail at finding a page store, re-categorize the
+ // request as behind-the-scene. At least this ensures that ultimately
+ // the user can still inspect/filter those net requests which were
+ // about to fall through the cracks.
+ // Example: Chromium + case #12 at
+ // http://raymondhill.net/ublock/popup.html
+ return tabContexts[vAPI.noTabId];
+ };
+
+ var lookup = function(tabId) {
+ return tabContexts[tabId] || null;
+ };
+
+ // Behind-the-scene tab context
+ (function() {
+ var entry = new TabContext(vAPI.noTabId);
+ entry.stack.push(new StackEntry('', true));
+ entry.rawURL = '';
+ entry.normalURL = µm.normalizePageURL(entry.tabId);
+ entry.rootHostname = µm.URI.hostnameFromURI(entry.normalURL);
+ entry.rootDomain = µm.URI.domainFromHostname(entry.rootHostname) || entry.rootHostname;
+ })();
+
+ // https://github.com/gorhill/uMatrix/issues/513
+ // Force a badge update here, it could happen that all the subsequent
+ // network requests are already in the page store, which would cause
+ // the badge to no be updated for these network requests.
+
+ vAPI.tabs.onNavigation = function(details) {
+ var tabId = details.tabId;
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
+ push(tabId, details.url, 'newURL');
+ µm.updateBadgeAsync(tabId);
+ };
+
+ // https://github.com/gorhill/uMatrix/issues/872
+ // `changeInfo.url` may not always be available (Firefox).
+
+ vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) {
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
+ if ( typeof tab.url !== 'string' || tab.url === '' ) { return; }
+ var url = changeInfo.url || tab.url;
+ if ( url ) {
+ push(tabId, url, 'updateURL');
+ }
+ };
+
+ vAPI.tabs.onClosed = function(tabId) {
+ µm.unbindTabFromPageStats(tabId);
+ var entry = tabContexts[tabId];
+ if ( entry instanceof TabContext ) {
+ entry.destroy();
+ }
+ };
+
+ return {
+ push: push,
+ lookup: lookup,
+ mustLookup: mustLookup
+ };
+})();
+
+vAPI.tabs.registerListeners();
+
+/******************************************************************************/
+/******************************************************************************/
+
+// Create an entry for the tab if it doesn't exist
+
+µm.bindTabToPageStats = function(tabId, context) {
+ this.updateBadgeAsync(tabId);
+
+ // Do not create a page store for URLs which are of no interests
+ // Example: dev console
+ var tabContext = this.tabContextManager.lookup(tabId);
+ if ( tabContext === null ) {
+ throw new Error('Unmanaged tab id: ' + tabId);
+ }
+
+ // rhill 2013-11-24: Never ever rebind behind-the-scene
+ // virtual tab.
+ // https://github.com/gorhill/httpswitchboard/issues/67
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return this.pageStores[tabId];
+ }
+
+ var normalURL = tabContext.normalURL;
+ var pageStore = this.pageStores[tabId] || null;
+
+ // The previous page URL, if any, associated with the tab
+ if ( pageStore !== null ) {
+ // No change, do not rebind
+ if ( pageStore.pageUrl === normalURL ) {
+ return pageStore;
+ }
+
+ // https://github.com/gorhill/uMatrix/issues/37
+ // Just rebind whenever possible: the URL changed, but the document
+ // maybe is the same.
+ // Example: Google Maps, Github
+ // https://github.com/gorhill/uMatrix/issues/72
+ // Need to double-check that the new scope is same as old scope
+ if ( context === 'updateURL' && pageStore.pageHostname === tabContext.rootHostname ) {
+ pageStore.rawURL = tabContext.rawURL;
+ pageStore.normalURL = normalURL;
+ this.updateTitle(tabId);
+ this.pageStoresToken = Date.now();
+ return pageStore;
+ }
+
+ // We won't be reusing this page store.
+ this.unbindTabFromPageStats(tabId);
+ }
+
+ // Try to resurrect first.
+ pageStore = this.resurrectPageStore(tabId, normalURL);
+ if ( pageStore === null ) {
+ pageStore = this.pageStoreFactory(tabContext);
+ }
+ this.pageStores[tabId] = pageStore;
+ this.updateTitle(tabId);
+ this.pageStoresToken = Date.now();
+
+ // console.debug('tab.js > bindTabToPageStats(): dispatching traffic in tab id %d to page store "%s"', tabId, pageUrl);
+
+ return pageStore;
+};
+
+/******************************************************************************/
+
+µm.unbindTabFromPageStats = function(tabId) {
+ // Never unbind behind-the-scene page store.
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return;
+ }
+
+ var pageStore = this.pageStores[tabId] || null;
+ if ( pageStore === null ) {
+ return;
+ }
+
+ delete this.pageStores[tabId];
+ this.pageStoresToken = Date.now();
+
+ if ( pageStore.incinerationTimer ) {
+ clearTimeout(pageStore.incinerationTimer);
+ pageStore.incinerationTimer = null;
+ }
+
+ if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
+ this.pageStoreCemetery[tabId] = {};
+ }
+ var pageStoreCrypt = this.pageStoreCemetery[tabId];
+
+ var pageURL = pageStore.pageUrl;
+ pageStoreCrypt[pageURL] = pageStore;
+
+ pageStore.incinerationTimer = vAPI.setTimeout(
+ this.incineratePageStore.bind(this, tabId, pageURL),
+ 4 * 60 * 1000
+ );
+};
+
+/******************************************************************************/
+
+µm.resurrectPageStore = function(tabId, pageURL) {
+ if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
+ return null;
+ }
+ var pageStoreCrypt = this.pageStoreCemetery[tabId];
+
+ if ( pageStoreCrypt.hasOwnProperty(pageURL) === false ) {
+ return null;
+ }
+
+ var pageStore = pageStoreCrypt[pageURL];
+
+ if ( pageStore.incinerationTimer !== null ) {
+ clearTimeout(pageStore.incinerationTimer);
+ pageStore.incinerationTimer = null;
+ }
+
+ delete pageStoreCrypt[pageURL];
+ if ( Object.keys(pageStoreCrypt).length === 0 ) {
+ delete this.pageStoreCemetery[tabId];
+ }
+
+ return pageStore;
+};
+
+/******************************************************************************/
+
+µm.incineratePageStore = function(tabId, pageURL) {
+ if ( this.pageStoreCemetery.hasOwnProperty(tabId) === false ) {
+ return;
+ }
+ var pageStoreCrypt = this.pageStoreCemetery[tabId];
+
+ if ( pageStoreCrypt.hasOwnProperty(pageURL) === false ) {
+ return;
+ }
+
+ var pageStore = pageStoreCrypt[pageURL];
+ if ( pageStore.incinerationTimer !== null ) {
+ clearTimeout(pageStore.incinerationTimer);
+ pageStore.incinerationTimer = null;
+ }
+
+ delete pageStoreCrypt[pageURL];
+ if ( Object.keys(pageStoreCrypt).length === 0 ) {
+ delete this.pageStoreCemetery[tabId];
+ }
+
+ pageStore.dispose();
+};
+
+/******************************************************************************/
+
+µm.pageStoreFromTabId = function(tabId) {
+ return this.pageStores[tabId] || null;
+};
+
+// Never return null
+µm.mustPageStoreFromTabId = function(tabId) {
+ return this.pageStores[tabId] || this.pageStores[vAPI.noTabId];
+};
+
+/******************************************************************************/
+
+µm.forceReload = function(tabId, bypassCache) {
+ vAPI.tabs.reload(tabId, bypassCache);
+};
+
+/******************************************************************************/
+
+// Update badge
+
+// rhill 2013-11-09: well this sucks, I can't update icon/badge
+// incrementally, as chromium overwrite the icon at some point without
+// notifying me, and this causes internal cached state to be out of sync.
+
+µm.updateBadgeAsync = (function() {
+ var tabIdToTimer = Object.create(null);
+
+ var updateBadge = function(tabId) {
+ delete tabIdToTimer[tabId];
+
+ var iconId = null;
+ var badgeStr = '';
+
+ var pageStore = this.pageStoreFromTabId(tabId);
+ if ( pageStore !== null ) {
+ var total = pageStore.perLoadAllowedRequestCount +
+ pageStore.perLoadBlockedRequestCount;
+ if ( total ) {
+ var squareSize = 19;
+ var greenSize = squareSize * Math.sqrt(pageStore.perLoadAllowedRequestCount / total);
+ iconId = greenSize < squareSize/2 ? Math.ceil(greenSize) : Math.floor(greenSize);
+ }
+ if ( this.userSettings.iconBadgeEnabled && pageStore.distinctRequestCount !== 0) {
+ badgeStr = this.formatCount(pageStore.distinctRequestCount);
+ }
+ }
+
+ vAPI.setIcon(tabId, iconId, badgeStr);
+ };
+
+ return function(tabId) {
+ if ( tabIdToTimer[tabId] ) {
+ return;
+ }
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return;
+ }
+ tabIdToTimer[tabId] = vAPI.setTimeout(updateBadge.bind(this, tabId), 750);
+ };
+})();
+
+/******************************************************************************/
+
+µm.updateTitle = (function() {
+ var tabIdToTimer = Object.create(null);
+ var tabIdToTryCount = Object.create(null);
+ var delay = 499;
+
+ var tryNoMore = function(tabId) {
+ delete tabIdToTryCount[tabId];
+ };
+
+ var tryAgain = function(tabId) {
+ var count = tabIdToTryCount[tabId];
+ if ( count === undefined ) {
+ return false;
+ }
+ if ( count === 1 ) {
+ delete tabIdToTryCount[tabId];
+ return false;
+ }
+ tabIdToTryCount[tabId] = count - 1;
+ tabIdToTimer[tabId] = vAPI.setTimeout(updateTitle.bind(µm, tabId), delay);
+ return true;
+ };
+
+ var onTabReady = function(tabId, tab) {
+ if ( !tab ) {
+ return tryNoMore(tabId);
+ }
+ var pageStore = this.pageStoreFromTabId(tabId);
+ if ( pageStore === null ) {
+ return tryNoMore(tabId);
+ }
+ if ( !tab.title && tryAgain(tabId) ) {
+ return;
+ }
+ // https://github.com/gorhill/uMatrix/issues/225
+ // Sometimes title changes while page is loading.
+ var settled = tab.title && tab.title === pageStore.title;
+ pageStore.title = tab.title || tab.url || '';
+ this.pageStoresToken = Date.now();
+ if ( settled || !tryAgain(tabId) ) {
+ tryNoMore(tabId);
+ }
+ };
+
+ var updateTitle = function(tabId) {
+ delete tabIdToTimer[tabId];
+ vAPI.tabs.get(tabId, onTabReady.bind(this, tabId));
+ };
+
+ return function(tabId) {
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return;
+ }
+ if ( tabIdToTimer[tabId] ) {
+ clearTimeout(tabIdToTimer[tabId]);
+ }
+ tabIdToTimer[tabId] = vAPI.setTimeout(updateTitle.bind(this, tabId), delay);
+ tabIdToTryCount[tabId] = 5;
+ };
+})();
+
+/******************************************************************************/
+
+// Stale page store entries janitor
+// https://github.com/chrisaljoudi/uBlock/issues/455
+
+(function() {
+ var cleanupPeriod = 7 * 60 * 1000;
+ var cleanupSampleAt = 0;
+ var cleanupSampleSize = 11;
+
+ var cleanup = function() {
+ var vapiTabs = vAPI.tabs;
+ var tabIds = Object.keys(µm.pageStores).sort();
+ var checkTab = function(tabId) {
+ vapiTabs.get(tabId, function(tab) {
+ if ( !tab ) {
+ µm.unbindTabFromPageStats(tabId);
+ }
+ });
+ };
+ if ( cleanupSampleAt >= tabIds.length ) {
+ cleanupSampleAt = 0;
+ }
+ var tabId;
+ var n = Math.min(cleanupSampleAt + cleanupSampleSize, tabIds.length);
+ for ( var i = cleanupSampleAt; i < n; i++ ) {
+ tabId = tabIds[i];
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ continue;
+ }
+ checkTab(tabId);
+ }
+ cleanupSampleAt = n;
+
+ vAPI.setTimeout(cleanup, cleanupPeriod);
+ };
+
+ vAPI.setTimeout(cleanup, cleanupPeriod);
+})();
+
+/******************************************************************************/
+
+})();
diff --git a/js/traffic.js b/js/traffic.js
new file mode 100644
index 0000000..3e6a6ac
--- /dev/null
+++ b/js/traffic.js
@@ -0,0 +1,444 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+/******************************************************************************/
+
+// Start isolation from global scope
+
+µMatrix.webRequest = (function() {
+
+/******************************************************************************/
+
+// Intercept and filter web requests according to white and black lists.
+
+var onBeforeRootFrameRequestHandler = function(details) {
+ var µm = µMatrix;
+ var requestURL = details.url;
+ var requestHostname = µm.URI.hostnameFromURI(requestURL);
+ var tabId = details.tabId;
+
+ µm.tabContextManager.push(tabId, requestURL);
+
+ var tabContext = µm.tabContextManager.mustLookup(tabId);
+ var rootHostname = tabContext.rootHostname;
+
+ // Disallow request as per matrix?
+ var block = µm.mustBlock(rootHostname, requestHostname, 'doc');
+
+ var pageStore = µm.pageStoreFromTabId(tabId);
+ pageStore.recordRequest('doc', requestURL, block);
+ µm.logger.writeOne(tabId, 'net', rootHostname, requestURL, 'doc', block);
+
+ // Not blocked
+ if ( !block ) {
+ // rhill 2013-11-07: Senseless to do this for behind-the-scene requests.
+ µm.cookieHunter.recordPageCookies(pageStore);
+ return;
+ }
+
+ // Blocked
+ var query = btoa(JSON.stringify({
+ url: requestURL,
+ hn: requestHostname,
+ why: '?'
+ }));
+
+ vAPI.tabs.replace(tabId, vAPI.getURL('main-blocked.html?details=') + query);
+
+ return { cancel: true };
+};
+
+/******************************************************************************/
+
+// Intercept and filter web requests according to white and black lists.
+
+var onBeforeRequestHandler = function(details) {
+ var µm = µMatrix,
+ µmuri = µm.URI,
+ requestURL = details.url,
+ requestScheme = µmuri.schemeFromURI(requestURL);
+
+ if ( µmuri.isNetworkScheme(requestScheme) === false ) { return; }
+
+ var requestType = requestTypeNormalizer[details.type] || 'other';
+
+ // https://github.com/gorhill/httpswitchboard/issues/303
+ // Wherever the main doc comes from, create a receiver page URL: synthetize
+ // one if needed.
+ if ( requestType === 'doc' && details.parentFrameId === -1 ) {
+ return onBeforeRootFrameRequestHandler(details);
+ }
+
+ // Re-classify orphan HTTP requests as behind-the-scene requests. There is
+ // not much else which can be done, because there are URLs
+ // which cannot be handled by µMatrix, i.e. `opera://startpage`,
+ // as this would lead to complications with no obvious solution, like how
+ // to scope on unknown scheme? Etc.
+ // https://github.com/gorhill/httpswitchboard/issues/191
+ // https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275
+ var tabContext = µm.tabContextManager.mustLookup(details.tabId),
+ tabId = tabContext.tabId,
+ rootHostname = tabContext.rootHostname,
+ specificity = 0;
+
+ // Filter through matrix
+ var block = µm.tMatrix.mustBlock(
+ rootHostname,
+ µmuri.hostnameFromURI(requestURL),
+ requestType
+ );
+ if ( block ) {
+ specificity = µm.tMatrix.specificityRegister;
+ }
+
+ // Record request.
+ // https://github.com/gorhill/httpswitchboard/issues/342
+ // The way requests are handled now, it may happen at this point some
+ // processing has already been performed, and that a synthetic URL has
+ // been constructed for logging purpose. Use this synthetic URL if
+ // it is available.
+ var pageStore = µm.mustPageStoreFromTabId(tabId);
+
+ // Enforce strict secure connection?
+ if ( tabContext.secure && µmuri.isSecureScheme(requestScheme) === false ) {
+ pageStore.hasMixedContent = true;
+ if ( block === false ) {
+ block = µm.tMatrix.evaluateSwitchZ('https-strict', rootHostname);
+ }
+ }
+
+ pageStore.recordRequest(requestType, requestURL, block);
+ µm.logger.writeOne(tabId, 'net', rootHostname, requestURL, details.type, block);
+
+ if ( block ) {
+ pageStore.cacheBlockedCollapsible(requestType, requestURL, specificity);
+ return { 'cancel': true };
+ }
+};
+
+/******************************************************************************/
+
+// Sanitize outgoing headers as per user settings.
+
+var onBeforeSendHeadersHandler = function(details) {
+ var µm = µMatrix,
+ µmuri = µm.URI,
+ requestURL = details.url,
+ requestScheme = µmuri.schemeFromURI(requestURL);
+
+ // Ignore non-network schemes
+ if ( µmuri.isNetworkScheme(requestScheme) === false ) { return; }
+
+ // Re-classify orphan HTTP requests as behind-the-scene requests. There is
+ // not much else which can be done, because there are URLs
+ // which cannot be handled by HTTP Switchboard, i.e. `opera://startpage`,
+ // as this would lead to complications with no obvious solution, like how
+ // to scope on unknown scheme? Etc.
+ // https://github.com/gorhill/httpswitchboard/issues/191
+ // https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275
+ var tabId = details.tabId,
+ pageStore = µm.mustPageStoreFromTabId(tabId),
+ requestType = requestTypeNormalizer[details.type] || 'other',
+ requestHeaders = details.requestHeaders,
+ headerIndex, headerValue;
+
+ // https://github.com/gorhill/httpswitchboard/issues/342
+ // Is this hyperlink auditing?
+ // If yes, create a synthetic URL for reporting hyperlink auditing
+ // in request log. This way the user is better informed of what went
+ // on.
+
+ // https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing
+ //
+ // Target URL = the href of the link
+ // Doc URL = URL of the document containing the target URL
+ // Ping URLs = servers which will be told that user clicked target URL
+ //
+ // `Content-Type` = `text/ping` (always present)
+ // `Ping-To` = target URL (always present)
+ // `Ping-From` = doc URL
+ // `Referer` = doc URL
+ // request URL = URL which will receive the information
+ //
+ // With hyperlink-auditing, removing header(s) is pointless, the whole
+ // request must be cancelled.
+
+ headerIndex = headerIndexFromName('ping-to', requestHeaders);
+ if ( headerIndex !== -1 ) {
+ headerValue = requestHeaders[headerIndex].value;
+ if ( headerValue !== '' ) {
+ var block = µm.userSettings.processHyperlinkAuditing;
+ pageStore.recordRequest('other', requestURL + '{Ping-To:' + headerValue + '}', block);
+ µm.logger.writeOne(tabId, 'net', '', requestURL, 'ping', block);
+ if ( block ) {
+ µm.hyperlinkAuditingFoiledCounter += 1;
+ return { 'cancel': true };
+ }
+ }
+ }
+
+ // If we reach this point, request is not blocked, so what is left to do
+ // is to sanitize headers.
+
+ var rootHostname = pageStore.pageHostname,
+ requestHostname = µmuri.hostnameFromURI(requestURL),
+ modified = false;
+
+ // Process `Cookie` header.
+
+ headerIndex = headerIndexFromName('cookie', requestHeaders);
+ if (
+ headerIndex !== -1 &&
+ µm.mustBlock(rootHostname, requestHostname, 'cookie')
+ ) {
+ modified = true;
+ headerValue = requestHeaders[headerIndex].value;
+ requestHeaders.splice(headerIndex, 1);
+ µm.cookieHeaderFoiledCounter++;
+ if ( requestType === 'doc' ) {
+ µm.logger.writeOne(tabId, 'net', '', headerValue, 'COOKIE', true);
+ }
+ }
+
+ // Process `Referer` header.
+
+ // https://github.com/gorhill/httpswitchboard/issues/222#issuecomment-44828402
+
+ // https://github.com/gorhill/uMatrix/issues/320
+ // http://tools.ietf.org/html/rfc6454#section-7.3
+ // "The user agent MAY include an Origin header field in any HTTP
+ // "request.
+ // "The user agent MUST NOT include more than one Origin header field in
+ // "any HTTP request.
+ // "Whenever a user agent issues an HTTP request from a "privacy-
+ // "sensitive" context, the user agent MUST send the value "null" in the
+ // "Origin header field."
+
+ // https://github.com/gorhill/uMatrix/issues/358
+ // Do not spoof `Origin` header for the time being.
+
+ // https://github.com/gorhill/uMatrix/issues/773
+ // For non-GET requests, remove `Referer` header instead of spoofing it.
+
+ headerIndex = headerIndexFromName('referer', requestHeaders);
+ if ( headerIndex !== -1 ) {
+ headerValue = requestHeaders[headerIndex].value;
+ if ( headerValue !== '' ) {
+ var toDomain = µmuri.domainFromHostname(requestHostname);
+ if ( toDomain !== '' && toDomain !== µmuri.domainFromURI(headerValue) ) {
+ pageStore.has3pReferrer = true;
+ if ( µm.tMatrix.evaluateSwitchZ('referrer-spoof', rootHostname) ) {
+ modified = true;
+ var newValue;
+ if ( details.method === 'GET' ) {
+ newValue = requestHeaders[headerIndex].value =
+ requestScheme + '://' + requestHostname + '/';
+ } else {
+ requestHeaders.splice(headerIndex, 1);
+ }
+ µm.refererHeaderFoiledCounter++;
+ if ( requestType === 'doc' ) {
+ µm.logger.writeOne(tabId, 'net', '', headerValue, 'REFERER', true);
+ if ( newValue !== undefined ) {
+ µm.logger.writeOne(tabId, 'net', '', newValue, 'REFERER', false);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( modified ) {
+ return { requestHeaders: requestHeaders };
+ }
+};
+
+/******************************************************************************/
+
+// To prevent inline javascript from being executed.
+
+// Prevent inline scripting using `Content-Security-Policy`:
+// https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html
+
+// This fixes:
+// https://github.com/gorhill/httpswitchboard/issues/35
+
+var onHeadersReceived = function(details) {
+ // Ignore schemes other than 'http...'
+ var µm = µMatrix,
+ tabId = details.tabId,
+ requestURL = details.url,
+ requestType = requestTypeNormalizer[details.type] || 'other';
+
+ // https://github.com/gorhill/uMatrix/issues/145
+ // Check if the main_frame is a download
+ if ( requestType === 'doc' ) {
+ µm.tabContextManager.push(tabId, requestURL);
+ }
+
+ var tabContext = µm.tabContextManager.lookup(tabId);
+ if ( tabContext === null ) { return; }
+
+ var csp = [],
+ cspReport = [],
+ rootHostname = tabContext.rootHostname,
+ requestHostname = µm.URI.hostnameFromURI(requestURL);
+
+ // Inline script tags.
+ if ( µm.mustAllow(rootHostname, requestHostname, 'script' ) !== true ) {
+ csp.push(µm.cspNoInlineScript);
+ }
+
+ // Inline style tags.
+ if ( µm.mustAllow(rootHostname, requestHostname, 'css' ) !== true ) {
+ csp.push(µm.cspNoInlineStyle);
+ }
+
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1302667
+ var cspNoWorker = µm.cspNoWorker;
+ if ( cspNoWorker === undefined ) {
+ cspNoWorker = cspNoWorkerInit();
+ }
+
+ if ( µm.tMatrix.evaluateSwitchZ('no-workers', rootHostname) ) {
+ csp.push(cspNoWorker);
+ } else if ( µm.rawSettings.disableCSPReportInjection === false ) {
+ cspReport.push(cspNoWorker);
+ }
+
+ var headers = details.responseHeaders,
+ cspDirectives, i;
+
+ if ( csp.length !== 0 ) {
+ cspDirectives = csp.join(',');
+ i = headerIndexFromName('content-security-policy', headers);
+ if ( i !== -1 ) {
+ headers[i].value += ',' + cspDirectives;
+ } else {
+ headers.push({
+ name: 'Content-Security-Policy',
+ value: cspDirectives
+ });
+ }
+ if ( requestType === 'doc' ) {
+ µm.logger.writeOne(tabId, 'net', '', cspDirectives, 'CSP', false);
+ }
+ }
+
+ if ( cspReport.length !== 0 ) {
+ cspDirectives = cspReport.join(',');
+ i = headerIndexFromName('content-security-policy-report-only', headers);
+ if ( i !== -1 ) {
+ headers[i].value += ',' + cspDirectives;
+ } else {
+ headers.push({
+ name: 'Content-Security-Policy-Report-Only',
+ value: cspDirectives
+ });
+ }
+ }
+
+ return { responseHeaders: headers };
+};
+
+/******************************************************************************/
+
+var cspNoWorkerInit = function() {
+ if ( vAPI.webextFlavor === undefined ) {
+ return "child-src 'none'; frame-src data: blob: *; report-uri about:blank";
+ }
+ µMatrix.cspNoWorker = /^Mozilla-Firefox-5[67]/.test(vAPI.webextFlavor) ?
+ "child-src 'none'; frame-src data: blob: *; report-uri about:blank" :
+ "worker-src 'none'; report-uri about:blank" ;
+ return µMatrix.cspNoWorker;
+};
+
+/******************************************************************************/
+
+// Caller must ensure headerName is normalized to lower case.
+
+var headerIndexFromName = function(headerName, headers) {
+ var i = headers.length;
+ while ( i-- ) {
+ if ( headers[i].name.toLowerCase() === headerName ) {
+ return i;
+ }
+ }
+ return -1;
+};
+
+/******************************************************************************/
+
+var requestTypeNormalizer = {
+ 'font' : 'css',
+ 'image' : 'image',
+ 'imageset' : 'image',
+ 'main_frame' : 'doc',
+ 'media' : 'media',
+ 'object' : 'media',
+ 'other' : 'other',
+ 'script' : 'script',
+ 'stylesheet' : 'css',
+ 'sub_frame' : 'frame',
+ 'websocket' : 'xhr',
+ 'xmlhttprequest': 'xhr'
+};
+
+/******************************************************************************/
+
+vAPI.net.onBeforeRequest = {
+ extra: [ 'blocking' ],
+ callback: onBeforeRequestHandler
+};
+
+vAPI.net.onBeforeSendHeaders = {
+ extra: [ 'blocking', 'requestHeaders' ],
+ callback: onBeforeSendHeadersHandler
+};
+
+vAPI.net.onHeadersReceived = {
+ urls: [ 'http://*/*', 'https://*/*' ],
+ types: [ 'main_frame', 'sub_frame' ],
+ extra: [ 'blocking', 'responseHeaders' ],
+ callback: onHeadersReceived
+};
+
+/******************************************************************************/
+
+var start = function() {
+ vAPI.net.registerListeners();
+};
+
+/******************************************************************************/
+
+return {
+ start: start
+};
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
+
diff --git a/js/udom.js b/js/udom.js
new file mode 100644
index 0000000..19848aa
--- /dev/null
+++ b/js/udom.js
@@ -0,0 +1,729 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uBlock
+*/
+
+/* global DOMTokenList */
+/* exported uDom */
+
+'use strict';
+
+/******************************************************************************/
+
+// It's just a silly, minimalist DOM framework: this allows me to not rely
+// on jQuery. jQuery contains way too much stuff than I need, and as per
+// Opera rules, I am not allowed to use a cut-down version of jQuery. So
+// the code here does *only* what I need, and nothing more, and with a lot
+// of assumption on passed parameters, etc. I grow it on a per-need-basis only.
+
+var uDom = (function() {
+
+/******************************************************************************/
+
+var DOMList = function() {
+ this.nodes = [];
+};
+
+/******************************************************************************/
+
+Object.defineProperty(
+ DOMList.prototype,
+ 'length',
+ {
+ get: function() {
+ return this.nodes.length;
+ }
+ }
+);
+
+/******************************************************************************/
+
+var DOMListFactory = function(selector, context) {
+ var r = new DOMList();
+ if ( typeof selector === 'string' ) {
+ selector = selector.trim();
+ if ( selector !== '' ) {
+ return addSelectorToList(r, selector, context);
+ }
+ }
+ if ( selector instanceof Node ) {
+ return addNodeToList(r, selector);
+ }
+ if ( selector instanceof NodeList ) {
+ return addNodeListToList(r, selector);
+ }
+ if ( selector instanceof DOMList ) {
+ return addListToList(r, selector);
+ }
+ return r;
+};
+
+/******************************************************************************/
+
+DOMListFactory.onLoad = function(callback) {
+ window.addEventListener('load', callback);
+};
+
+/******************************************************************************/
+
+DOMListFactory.nodeFromId = function(id) {
+ return document.getElementById(id);
+};
+
+DOMListFactory.nodeFromSelector = function(selector) {
+ return document.querySelector(selector);
+};
+
+/******************************************************************************/
+
+var addNodeToList = function(list, node) {
+ if ( node ) {
+ list.nodes.push(node);
+ }
+ return list;
+};
+
+/******************************************************************************/
+
+var addNodeListToList = function(list, nodelist) {
+ if ( nodelist ) {
+ var n = nodelist.length;
+ for ( var i = 0; i < n; i++ ) {
+ list.nodes.push(nodelist[i]);
+ }
+ }
+ return list;
+};
+
+/******************************************************************************/
+
+var addListToList = function(list, other) {
+ list.nodes = list.nodes.concat(other.nodes);
+ return list;
+};
+
+/******************************************************************************/
+
+var addSelectorToList = function(list, selector, context) {
+ var p = context || document;
+ var r = p.querySelectorAll(selector);
+ var n = r.length;
+ for ( var i = 0; i < n; i++ ) {
+ list.nodes.push(r[i]);
+ }
+ return list;
+};
+
+/******************************************************************************/
+
+var nodeInNodeList = function(node, nodeList) {
+ var i = nodeList.length;
+ while ( i-- ) {
+ if ( nodeList[i] === node ) {
+ return true;
+ }
+ }
+ return false;
+};
+
+/******************************************************************************/
+
+var doesMatchSelector = function(node, selector) {
+ if ( !node ) {
+ return false;
+ }
+ if ( node.nodeType !== 1 ) {
+ return false;
+ }
+ if ( selector === undefined ) {
+ return true;
+ }
+ var parentNode = node.parentNode;
+ if ( !parentNode || !parentNode.setAttribute ) {
+ return false;
+ }
+ var doesMatch = false;
+ parentNode.setAttribute('uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO', '');
+ var grandpaNode = parentNode.parentNode || document;
+ var nl = grandpaNode.querySelectorAll('[uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO] > ' + selector);
+ var i = nl.length;
+ while ( doesMatch === false && i-- ) {
+ doesMatch = nl[i] === node;
+ }
+ parentNode.removeAttribute('uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO');
+ return doesMatch;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.nodeAt = function(i) {
+ return this.nodes[i] || null;
+};
+
+DOMList.prototype.at = function(i) {
+ return addNodeToList(new DOMList(), this.nodes[i]);
+};
+
+/******************************************************************************/
+
+DOMList.prototype.toArray = function() {
+ return this.nodes.slice();
+};
+
+/******************************************************************************/
+
+DOMList.prototype.pop = function() {
+ return addNodeToList(new DOMList(), this.nodes.pop());
+};
+
+/******************************************************************************/
+
+DOMList.prototype.forEach = function(fn) {
+ var n = this.nodes.length;
+ for ( var i = 0; i < n; i++ ) {
+ fn(this.at(i), i);
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.subset = function(i, l) {
+ var r = new DOMList();
+ var n = l !== undefined ? l : this.nodes.length;
+ var j = Math.min(i + n, this.nodes.length);
+ if ( i < j ) {
+ r.nodes = this.nodes.slice(i, j);
+ }
+ return r;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.first = function() {
+ return this.subset(0, 1);
+};
+
+/******************************************************************************/
+
+DOMList.prototype.next = function(selector) {
+ var r = new DOMList();
+ var n = this.nodes.length;
+ var node;
+ for ( var i = 0; i < n; i++ ) {
+ node = this.nodes[i];
+ while ( node.nextSibling !== null ) {
+ node = node.nextSibling;
+ if ( node.nodeType !== 1 ) {
+ continue;
+ }
+ if ( doesMatchSelector(node, selector) === false ) {
+ continue;
+ }
+ addNodeToList(r, node);
+ break;
+ }
+ }
+ return r;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.parent = function() {
+ var r = new DOMList();
+ if ( this.nodes.length ) {
+ addNodeToList(r, this.nodes[0].parentNode);
+ }
+ return r;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.filter = function(filter) {
+ var r = new DOMList();
+ var filterFunc;
+ if ( typeof filter === 'string' ) {
+ filterFunc = function() {
+ return doesMatchSelector(this, filter);
+ };
+ } else if ( typeof filter === 'function' ) {
+ filterFunc = filter;
+ } else {
+ filterFunc = function(){
+ return true;
+ };
+ }
+ var n = this.nodes.length;
+ var node;
+ for ( var i = 0; i < n; i++ ) {
+ node = this.nodes[i];
+ if ( filterFunc.apply(node) ) {
+ addNodeToList(r, node);
+ }
+ }
+ return r;
+};
+
+/******************************************************************************/
+
+// TODO: Avoid possible duplicates
+
+DOMList.prototype.ancestors = function(selector) {
+ var r = new DOMList();
+ var n = this.nodes.length;
+ var node;
+ for ( var i = 0; i < n; i++ ) {
+ node = this.nodes[i].parentNode;
+ while ( node ) {
+ if ( doesMatchSelector(node, selector) ) {
+ addNodeToList(r, node);
+ }
+ node = node.parentNode;
+ }
+ }
+ return r;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.descendants = function(selector) {
+ var r = new DOMList();
+ var n = this.nodes.length;
+ var nl;
+ for ( var i = 0; i < n; i++ ) {
+ nl = this.nodes[i].querySelectorAll(selector);
+ addNodeListToList(r, nl);
+ }
+ return r;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.contents = function() {
+ var r = new DOMList();
+ var cnodes, cn, ci;
+ var n = this.nodes.length;
+ for ( var i = 0; i < n; i++ ) {
+ cnodes = this.nodes[i].childNodes;
+ cn = cnodes.length;
+ for ( ci = 0; ci < cn; ci++ ) {
+ addNodeToList(r, cnodes.item(ci));
+ }
+ }
+ return r;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.remove = function() {
+ var cn, p;
+ var i = this.nodes.length;
+ while ( i-- ) {
+ cn = this.nodes[i];
+ if ( (p = cn.parentNode) ) {
+ p.removeChild(cn);
+ }
+ }
+ return this;
+};
+
+DOMList.prototype.detach = DOMList.prototype.remove;
+
+/******************************************************************************/
+
+DOMList.prototype.empty = function() {
+ var node;
+ var i = this.nodes.length;
+ while ( i-- ) {
+ node = this.nodes[i];
+ while ( node.firstChild ) {
+ node.removeChild(node.firstChild);
+ }
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.append = function(selector, context) {
+ var p = this.nodes[0];
+ if ( p ) {
+ var c = DOMListFactory(selector, context);
+ var n = c.nodes.length;
+ for ( var i = 0; i < n; i++ ) {
+ p.appendChild(c.nodes[i]);
+ }
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.prepend = function(selector, context) {
+ var p = this.nodes[0];
+ if ( p ) {
+ var c = DOMListFactory(selector, context);
+ var i = c.nodes.length;
+ while ( i-- ) {
+ p.insertBefore(c.nodes[i], p.firstChild);
+ }
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.appendTo = function(selector, context) {
+ var p = selector instanceof DOMListFactory ? selector : DOMListFactory(selector, context);
+ var n = p.length;
+ for ( var i = 0; i < n; i++ ) {
+ p.nodes[0].appendChild(this.nodes[i]);
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.insertAfter = function(selector, context) {
+ if ( this.nodes.length === 0 ) {
+ return this;
+ }
+ var p = this.nodes[0].parentNode;
+ if ( !p ) {
+ return this;
+ }
+ var c = DOMListFactory(selector, context);
+ var n = c.nodes.length;
+ for ( var i = 0; i < n; i++ ) {
+ p.appendChild(c.nodes[i]);
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.insertBefore = function(selector, context) {
+ if ( this.nodes.length === 0 ) {
+ return this;
+ }
+ var referenceNodes = DOMListFactory(selector, context);
+ if ( referenceNodes.nodes.length === 0 ) {
+ return this;
+ }
+ var referenceNode = referenceNodes.nodes[0];
+ var parentNode = referenceNode.parentNode;
+ if ( !parentNode ) {
+ return this;
+ }
+ var n = this.nodes.length;
+ for ( var i = 0; i < n; i++ ) {
+ parentNode.insertBefore(this.nodes[i], referenceNode);
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.clone = function(notDeep) {
+ var r = new DOMList();
+ var n = this.nodes.length;
+ for ( var i = 0; i < n; i++ ) {
+ addNodeToList(r, this.nodes[i].cloneNode(!notDeep));
+ }
+ return r;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.nthOfType = function() {
+ if ( this.nodes.length === 0 ) {
+ return 0;
+ }
+ var node = this.nodes[0];
+ var tagName = node.tagName;
+ var i = 1;
+ while ( node.previousElementSibling !== null ) {
+ node = node.previousElementSibling;
+ if ( typeof node.tagName !== 'string' ) {
+ continue;
+ }
+ if ( node.tagName !== tagName ) {
+ continue;
+ }
+ i++;
+ }
+ return i;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.attr = function(attr, value) {
+ var i = this.nodes.length;
+ if ( value === undefined && typeof attr !== 'object' ) {
+ return i ? this.nodes[0].getAttribute(attr) : undefined;
+ }
+ if ( typeof attr === 'object' ) {
+ var attrNames = Object.keys(attr);
+ var node, j, attrName;
+ while ( i-- ) {
+ node = this.nodes[i];
+ j = attrNames.length;
+ while ( j-- ) {
+ attrName = attrNames[j];
+ node.setAttribute(attrName, attr[attrName]);
+ }
+ }
+ } else {
+ while ( i-- ) {
+ this.nodes[i].setAttribute(attr, value);
+ }
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.prop = function(prop, value) {
+ var i = this.nodes.length;
+ if ( value === undefined ) {
+ return i !== 0 ? this.nodes[0][prop] : undefined;
+ }
+ while ( i-- ) {
+ this.nodes[i][prop] = value;
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.css = function(prop, value) {
+ var i = this.nodes.length;
+ if ( value === undefined ) {
+ return i ? this.nodes[0].style[prop] : undefined;
+ }
+ if ( value !== '' ) {
+ while ( i-- ) {
+ this.nodes[i].style.setProperty(prop, value);
+ }
+ return this;
+ }
+ while ( i-- ) {
+ this.nodes[i].style.removeProperty(prop);
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.val = function(value) {
+ return this.prop('value', value);
+};
+
+/******************************************************************************/
+
+DOMList.prototype.html = function(html) {
+ var i = this.nodes.length;
+ if ( html === undefined ) {
+ return i ? this.nodes[0].innerHTML : '';
+ }
+ while ( i-- ) {
+ vAPI.insertHTML(this.nodes[i], html);
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.text = function(text) {
+ var i = this.nodes.length;
+ if ( text === undefined ) {
+ return i ? this.nodes[0].textContent : '';
+ }
+ while ( i-- ) {
+ this.nodes[i].textContent = text;
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+var toggleClass = function(node, className, targetState) {
+ var tokenList = node.classList;
+ if ( tokenList instanceof DOMTokenList === false ) {
+ return;
+ }
+ var currentState = tokenList.contains(className);
+ var newState = targetState;
+ if ( newState === undefined ) {
+ newState = !currentState;
+ }
+ if ( newState === currentState ) {
+ return;
+ }
+ tokenList.toggle(className, newState);
+};
+
+/******************************************************************************/
+
+DOMList.prototype.hasClass = function(className) {
+ if ( !this.nodes.length ) {
+ return false;
+ }
+ var tokenList = this.nodes[0].classList;
+ return tokenList instanceof DOMTokenList &&
+ tokenList.contains(className);
+};
+DOMList.prototype.hasClassName = DOMList.prototype.hasClass;
+
+DOMList.prototype.addClass = function(className) {
+ return this.toggleClass(className, true);
+};
+
+DOMList.prototype.removeClass = function(className) {
+ if ( className !== undefined ) {
+ return this.toggleClass(className, false);
+ }
+ var i = this.nodes.length;
+ while ( i-- ) {
+ this.nodes[i].className = '';
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.toggleClass = function(className, targetState) {
+ if ( className.indexOf(' ') !== -1 ) {
+ return this.toggleClasses(className, targetState);
+ }
+ var i = this.nodes.length;
+ while ( i-- ) {
+ toggleClass(this.nodes[i], className, targetState);
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.toggleClasses = function(classNames, targetState) {
+ var tokens = classNames.split(/\s+/);
+ var i = this.nodes.length;
+ var node, j;
+ while ( i-- ) {
+ node = this.nodes[i];
+ j = tokens.length;
+ while ( j-- ) {
+ toggleClass(node, tokens[j], targetState);
+ }
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+var listenerEntries = [];
+
+var ListenerEntry = function(target, type, capture, callback) {
+ this.target = target;
+ this.type = type;
+ this.capture = capture;
+ this.callback = callback;
+ target.addEventListener(type, callback, capture);
+};
+
+ListenerEntry.prototype.dispose = function() {
+ this.target.removeEventListener(this.type, this.callback, this.capture);
+ this.target = null;
+ this.callback = null;
+};
+
+/******************************************************************************/
+
+var makeEventHandler = function(selector, callback) {
+ return function(event) {
+ var dispatcher = event.currentTarget;
+ if ( !dispatcher || typeof dispatcher.querySelectorAll !== 'function' ) {
+ return;
+ }
+ var receiver = event.target;
+ if ( nodeInNodeList(receiver, dispatcher.querySelectorAll(selector)) ) {
+ callback.call(receiver, event);
+ }
+ };
+};
+
+DOMList.prototype.on = function(etype, selector, callback) {
+ if ( typeof selector === 'function' ) {
+ callback = selector;
+ selector = undefined;
+ } else {
+ callback = makeEventHandler(selector, callback);
+ }
+
+ var i = this.nodes.length;
+ while ( i-- ) {
+ listenerEntries.push(new ListenerEntry(this.nodes[i], etype, selector !== undefined, callback));
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+// TODO: Won't work for delegated handlers. Need to figure
+// what needs to be done.
+
+DOMList.prototype.off = function(evtype, callback) {
+ var i = this.nodes.length;
+ while ( i-- ) {
+ this.nodes[i].removeEventListener(evtype, callback);
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+DOMList.prototype.trigger = function(etype) {
+ var ev = new CustomEvent(etype);
+ var i = this.nodes.length;
+ while ( i-- ) {
+ this.nodes[i].dispatchEvent(ev);
+ }
+ return this;
+};
+
+/******************************************************************************/
+
+// Cleanup
+
+var onBeforeUnload = function() {
+ var entry;
+ while ( (entry = listenerEntries.pop()) ) {
+ entry.dispose();
+ }
+ window.removeEventListener('beforeunload', onBeforeUnload);
+};
+
+window.addEventListener('beforeunload', onBeforeUnload);
+
+/******************************************************************************/
+
+return DOMListFactory;
+
+})();
diff --git a/js/uritools.js b/js/uritools.js
new file mode 100644
index 0000000..2e50fd2
--- /dev/null
+++ b/js/uritools.js
@@ -0,0 +1,537 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global publicSuffixList, punycode */
+
+'use strict';
+
+/*******************************************************************************
+
+RFC 3986 as reference: http://tools.ietf.org/html/rfc3986#appendix-A
+
+Naming convention from https://en.wikipedia.org/wiki/URI_scheme#Examples
+
+*/
+
+/******************************************************************************/
+
+µMatrix.URI = (function() {
+
+/******************************************************************************/
+
+// Favorite regex tool: http://regex101.com/
+
+// Ref: <http://tools.ietf.org/html/rfc3986#page-50>
+// I removed redundant capture groups: capture less = peform faster. See
+// <http://jsperf.com/old-uritools-vs-new-uritools>
+// Performance improvements welcomed.
+// jsperf: <http://jsperf.com/old-uritools-vs-new-uritools>
+var reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/;
+
+// Derived
+var reSchemeFromURI = /^[^:\/?#]+:/;
+var reAuthorityFromURI = /^(?:[^:\/?#]+:)?(\/\/[^\/?#]+)/;
+var reOriginFromURI = /^(?:[^:\/?#]+:)?(?:\/\/[^\/?#]+)/;
+var reCommonHostnameFromURL = /^https?:\/\/([0-9a-z_][0-9a-z._-]*[0-9a-z])\//;
+var rePathFromURI = /^(?:[^:\/?#]+:)?(?:\/\/[^\/?#]*)?([^?#]*)/;
+var reMustNormalizeHostname = /[^0-9a-z._-]/;
+
+// These are to parse authority field, not parsed by above official regex
+// IPv6 is seen as an exception: a non-compatible IPv6 is first tried, and
+// if it fails, the IPv6 compatible regex istr used. This helps
+// peformance by avoiding the use of a too complicated regex first.
+
+// https://github.com/gorhill/httpswitchboard/issues/211
+// "While a hostname may not contain other characters, such as the
+// "underscore character (_), other DNS names may contain the underscore"
+var reHostPortFromAuthority = /^(?:[^@]*@)?([^:]*)(:\d*)?$/;
+var reIPv6PortFromAuthority = /^(?:[^@]*@)?(\[[0-9a-f:]*\])(:\d*)?$/i;
+
+var reHostFromNakedAuthority = /^[0-9a-z._-]+[0-9a-z]$/i;
+var reHostFromAuthority = /^(?:[^@]*@)?([^:]+)(?::\d*)?$/;
+var reIPv6FromAuthority = /^(?:[^@]*@)?(\[[0-9a-f:]+\])(?::\d*)?$/i;
+
+// Coarse (but fast) tests
+var reValidHostname = /^([a-z\d]+(-*[a-z\d]+)*)(\.[a-z\d]+(-*[a-z\d])*)*$/;
+var reIPAddressNaive = /^\d+\.\d+\.\d+\.\d+$|^\[[\da-zA-Z:]+\]$/;
+
+// Accurate tests
+// Source.: http://stackoverflow.com/questions/5284147/validating-ipv4-addresses-with-regexp/5284410#5284410
+//var reIPv4 = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)(\.|$)){4}/;
+
+// Source: http://forums.intermapper.com/viewtopic.php?p=1096#1096
+//var reIPv6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
+
+/******************************************************************************/
+
+var reset = function(o) {
+ o.scheme = '';
+ o.hostname = '';
+ o._ipv4 = undefined;
+ o._ipv6 = undefined;
+ o.port = '';
+ o.path = '';
+ o.query = '';
+ o.fragment = '';
+ return o;
+};
+
+var resetAuthority = function(o) {
+ o.hostname = '';
+ o._ipv4 = undefined;
+ o._ipv6 = undefined;
+ o.port = '';
+ return o;
+};
+
+/******************************************************************************/
+
+// This will be exported
+
+var URI = {
+ scheme: '',
+ authority: '',
+ hostname: '',
+ _ipv4: undefined,
+ _ipv6: undefined,
+ port: '',
+ domain: undefined,
+ path: '',
+ query: '',
+ fragment: '',
+ schemeBit: (1 << 0),
+ userBit: (1 << 1),
+ passwordBit: (1 << 2),
+ hostnameBit: (1 << 3),
+ portBit: (1 << 4),
+ pathBit: (1 << 5),
+ queryBit: (1 << 6),
+ fragmentBit: (1 << 7),
+ allBits: (0xFFFF)
+};
+
+URI.authorityBit = (URI.userBit | URI.passwordBit | URI.hostnameBit | URI.portBit);
+URI.normalizeBits = (URI.schemeBit | URI.hostnameBit | URI.pathBit | URI.queryBit);
+
+/******************************************************************************/
+
+// See: https://en.wikipedia.org/wiki/URI_scheme#Examples
+// URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+//
+// foo://example.com:8042/over/there?name=ferret#nose
+// \_/ \______________/\_________/ \_________/ \__/
+// | | | | |
+// scheme authority path query fragment
+// | _____________________|__
+// / \ / \
+// urn:example:animal:ferret:nose
+
+URI.set = function(uri) {
+ if ( uri === undefined ) {
+ return reset(URI);
+ }
+ var matches = reRFC3986.exec(uri);
+ if ( !matches ) {
+ return reset(URI);
+ }
+ this.scheme = matches[1] !== undefined ? matches[1].slice(0, -1) : '';
+ this.authority = matches[2] !== undefined ? matches[2].slice(2).toLowerCase() : '';
+ this.path = matches[3] !== undefined ? matches[3] : '';
+
+ // <http://tools.ietf.org/html/rfc3986#section-6.2.3>
+ // "In general, a URI that uses the generic syntax for authority
+ // "with an empty path should be normalized to a path of '/'."
+ if ( this.authority !== '' && this.path === '' ) {
+ this.path = '/';
+ }
+ this.query = matches[4] !== undefined ? matches[4].slice(1) : '';
+ this.fragment = matches[5] !== undefined ? matches[5].slice(1) : '';
+
+ // Assume very simple authority, i.e. just a hostname (highest likelihood
+ // case for µMatrix)
+ if ( reHostFromNakedAuthority.test(this.authority) ) {
+ this.hostname = this.authority;
+ this.port = '';
+ return this;
+ }
+ // Authority contains more than just a hostname
+ matches = reHostPortFromAuthority.exec(this.authority);
+ if ( !matches ) {
+ matches = reIPv6PortFromAuthority.exec(this.authority);
+ if ( !matches ) {
+ return resetAuthority(URI);
+ }
+ }
+ this.hostname = matches[1] !== undefined ? matches[1] : '';
+ // http://en.wikipedia.org/wiki/FQDN
+ if ( this.hostname.slice(-1) === '.' ) {
+ this.hostname = this.hostname.slice(0, -1);
+ }
+ this.port = matches[2] !== undefined ? matches[2].slice(1) : '';
+ return this;
+};
+
+/******************************************************************************/
+
+// URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+//
+// foo://example.com:8042/over/there?name=ferret#nose
+// \_/ \______________/\_________/ \_________/ \__/
+// | | | | |
+// scheme authority path query fragment
+// | _____________________|__
+// / \ / \
+// urn:example:animal:ferret:nose
+
+URI.assemble = function(bits) {
+ if ( bits === undefined ) {
+ bits = this.allBits;
+ }
+ var s = [];
+ if ( this.scheme && (bits & this.schemeBit) ) {
+ s.push(this.scheme, ':');
+ }
+ if ( this.hostname && (bits & this.hostnameBit) ) {
+ s.push('//', this.hostname);
+ }
+ if ( this.port && (bits & this.portBit) ) {
+ s.push(':', this.port);
+ }
+ if ( this.path && (bits & this.pathBit) ) {
+ s.push(this.path);
+ }
+ if ( this.query && (bits & this.queryBit) ) {
+ s.push('?', this.query);
+ }
+ if ( this.fragment && (bits & this.fragmentBit) ) {
+ s.push('#', this.fragment);
+ }
+ return s.join('');
+};
+
+/******************************************************************************/
+
+URI.originFromURI = function(uri) {
+ var matches = reOriginFromURI.exec(uri);
+ return matches !== null ? matches[0].toLowerCase() : '';
+};
+
+/******************************************************************************/
+
+URI.schemeFromURI = function(uri) {
+ var matches = reSchemeFromURI.exec(uri);
+ if ( matches === null ) {
+ return '';
+ }
+ return matches[0].slice(0, -1).toLowerCase();
+};
+
+/******************************************************************************/
+
+URI.isNetworkScheme = function(scheme) {
+ return this.reNetworkScheme.test(scheme);
+};
+
+URI.reNetworkScheme = /^(?:https?|wss?|ftps?)\b/;
+
+/******************************************************************************/
+
+URI.isSecureScheme = function(scheme) {
+ return this.reSecureScheme.test(scheme);
+};
+
+URI.reSecureScheme = /^(?:https|wss|ftps)\b/;
+
+/******************************************************************************/
+
+URI.authorityFromURI = function(uri) {
+ var matches = reAuthorityFromURI.exec(uri);
+ if ( !matches ) {
+ return '';
+ }
+ return matches[1].slice(2).toLowerCase();
+};
+
+/******************************************************************************/
+
+// The most used function, so it better be fast.
+
+// https://github.com/gorhill/uBlock/issues/1559
+// See http://en.wikipedia.org/wiki/FQDN
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1360285
+// Revisit punycode dependency when above issue is fixed in Firefox.
+
+URI.hostnameFromURI = function(uri) {
+ var matches = reCommonHostnameFromURL.exec(uri);
+ if ( matches !== null ) { return matches[1]; }
+ matches = reAuthorityFromURI.exec(uri);
+ if ( matches === null ) { return ''; }
+ var authority = matches[1].slice(2);
+ // Assume very simple authority (most common case for µBlock)
+ if ( reHostFromNakedAuthority.test(authority) ) {
+ return authority.toLowerCase();
+ }
+ matches = reHostFromAuthority.exec(authority);
+ if ( matches === null ) {
+ matches = reIPv6FromAuthority.exec(authority);
+ if ( matches === null ) { return ''; }
+ }
+ var hostname = matches[1];
+ while ( hostname.endsWith('.') ) {
+ hostname = hostname.slice(0, -1);
+ }
+ if ( reMustNormalizeHostname.test(hostname) ) {
+ hostname = punycode.toASCII(hostname.toLowerCase());
+ }
+ return hostname;
+};
+
+/******************************************************************************/
+
+URI.domainFromHostname = function(hostname) {
+ // Try to skip looking up the PSL database
+ var entry = domainCache.get(hostname);
+ if ( entry !== undefined ) {
+ entry.tstamp = Date.now();
+ return entry.domain;
+ }
+ // Meh.. will have to search it
+ if ( reIPAddressNaive.test(hostname) === false ) {
+ return domainCacheAdd(hostname, psl.getDomain(hostname));
+ }
+ return domainCacheAdd(hostname, hostname);
+};
+
+URI.domain = function() {
+ return this.domainFromHostname(this.hostname);
+};
+
+// It is expected that there is higher-scoped `publicSuffixList` lingering
+// somewhere. Cache it. See <https://github.com/gorhill/publicsuffixlist.js>.
+var psl = publicSuffixList;
+
+/******************************************************************************/
+
+URI.pathFromURI = function(uri) {
+ var matches = rePathFromURI.exec(uri);
+ return matches !== null ? matches[1] : '';
+};
+
+/******************************************************************************/
+
+ // Trying to alleviate the worries of looking up too often the domain name from
+// a hostname. With a cache, uBlock benefits given that it deals with a
+// specific set of hostnames within a narrow time span -- in other words, I
+// believe probability of cache hit are high in uBlock.
+
+var domainCache = new Map();
+var domainCacheCountLowWaterMark = 75;
+var domainCacheCountHighWaterMark = 100;
+var domainCacheEntryJunkyard = [];
+var domainCacheEntryJunkyardMax = domainCacheCountHighWaterMark - domainCacheCountLowWaterMark;
+
+var DomainCacheEntry = function(domain) {
+ this.init(domain);
+};
+
+DomainCacheEntry.prototype.init = function(domain) {
+ this.domain = domain;
+ this.tstamp = Date.now();
+ return this;
+};
+
+DomainCacheEntry.prototype.dispose = function() {
+ this.domain = '';
+ if ( domainCacheEntryJunkyard.length < domainCacheEntryJunkyardMax ) {
+ domainCacheEntryJunkyard.push(this);
+ }
+};
+
+var domainCacheEntryFactory = function(domain) {
+ var entry = domainCacheEntryJunkyard.pop();
+ if ( entry ) {
+ return entry.init(domain);
+ }
+ return new DomainCacheEntry(domain);
+};
+
+var domainCacheAdd = function(hostname, domain) {
+ var entry = domainCache.get(hostname);
+ if ( entry !== undefined ) {
+ entry.tstamp = Date.now();
+ } else {
+ domainCache.set(hostname, domainCacheEntryFactory(domain));
+ if ( domainCache.size === domainCacheCountHighWaterMark ) {
+ domainCachePrune();
+ }
+ }
+ return domain;
+};
+
+var domainCacheEntrySort = function(a, b) {
+ return domainCache.get(b).tstamp - domainCache.get(a).tstamp;
+};
+
+var domainCachePrune = function() {
+ var hostnames = Array.from(domainCache.keys())
+ .sort(domainCacheEntrySort)
+ .slice(domainCacheCountLowWaterMark);
+ var i = hostnames.length;
+ var hostname;
+ while ( i-- ) {
+ hostname = hostnames[i];
+ domainCache.get(hostname).dispose();
+ domainCache.delete(hostname);
+ }
+};
+
+var domainCacheReset = function() {
+ domainCache.clear();
+};
+
+psl.onChanged.addListener(domainCacheReset);
+
+/******************************************************************************/
+
+URI.domainFromURI = function(uri) {
+ if ( !uri ) {
+ return '';
+ }
+ return this.domainFromHostname(this.hostnameFromURI(uri));
+};
+
+/******************************************************************************/
+
+// Normalize the way µMatrix expects it
+
+URI.normalizedURI = function() {
+ // Will be removed:
+ // - port
+ // - user id/password
+ // - fragment
+ return this.assemble(this.normalizeBits);
+};
+
+/******************************************************************************/
+
+URI.rootURL = function() {
+ if ( !this.hostname ) {
+ return '';
+ }
+ return this.assemble(this.schemeBit | this.hostnameBit);
+};
+
+/******************************************************************************/
+
+URI.isValidHostname = function(hostname) {
+ var r;
+ try {
+ r = reValidHostname.test(hostname);
+ }
+ catch (e) {
+ return false;
+ }
+ return r;
+};
+
+/******************************************************************************/
+
+// Return the parent domain. For IP address, there is no parent domain.
+
+URI.parentHostnameFromHostname = function(hostname) {
+ // `locahost` => ``
+ // `example.org` => `example.org`
+ // `www.example.org` => `example.org`
+ // `tomato.www.example.org` => `example.org`
+ var domain = this.domainFromHostname(hostname);
+
+ // `locahost` === `` => bye
+ // `example.org` === `example.org` => bye
+ // `www.example.org` !== `example.org` => stay
+ // `tomato.www.example.org` !== `example.org` => stay
+ if ( domain === '' || domain === hostname ) {
+ return undefined;
+ }
+
+ // Parent is hostname minus first label
+ return hostname.slice(hostname.indexOf('.') + 1);
+};
+
+/******************************************************************************/
+
+// Return all possible parent hostnames which can be derived from `hostname`,
+// ordered from direct parent up to domain inclusively.
+
+URI.parentHostnamesFromHostname = function(hostname) {
+ // TODO: I should create an object which is optimized to receive
+ // the list of hostnames by making it reusable (junkyard etc.) and which
+ // has its own element counter property in order to avoid memory
+ // alloc/dealloc.
+ var domain = this.domainFromHostname(hostname);
+ if ( domain === '' || domain === hostname ) {
+ return [];
+ }
+ var nodes = [];
+ var pos;
+ for (;;) {
+ pos = hostname.indexOf('.');
+ if ( pos < 0 ) {
+ break;
+ }
+ hostname = hostname.slice(pos + 1);
+ nodes.push(hostname);
+ if ( hostname === domain ) {
+ break;
+ }
+ }
+ return nodes;
+};
+
+/******************************************************************************/
+
+// Return all possible hostnames which can be derived from `hostname`,
+// ordered from self up to domain inclusively.
+
+URI.allHostnamesFromHostname = function(hostname) {
+ var nodes = this.parentHostnamesFromHostname(hostname);
+ nodes.unshift(hostname);
+ return nodes;
+};
+
+/******************************************************************************/
+
+URI.toString = function() {
+ return this.assemble();
+};
+
+/******************************************************************************/
+
+// Export
+
+return URI;
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
+
diff --git a/js/user-rules.js b/js/user-rules.js
new file mode 100644
index 0000000..c9c48be
--- /dev/null
+++ b/js/user-rules.js
@@ -0,0 +1,341 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global uDom */
+
+'use strict';
+
+/******************************************************************************/
+
+(function() {
+
+/******************************************************************************/
+
+// Switches before, rules after
+
+var directiveSort = function(a, b) {
+ var aIsSwitch = a.indexOf(':') !== -1;
+ var bIsSwitch = b.indexOf(':') !== -1;
+ if ( aIsSwitch === bIsSwitch ) {
+ return a.localeCompare(b);
+ }
+ return aIsSwitch ? -1 : 1;
+};
+
+/******************************************************************************/
+
+var processUserRules = function(response) {
+ var rules, rule, i;
+ var allRules = {};
+ var permanentRules = {};
+ var temporaryRules = {};
+ var onLeft, onRight;
+
+ rules = response.permanentRules.split(/\n+/);
+ i = rules.length;
+ while ( i-- ) {
+ rule = rules[i].trim();
+ if ( rule.length !== 0 ) {
+ permanentRules[rule] = allRules[rule] = true;
+ }
+ }
+ rules = response.temporaryRules.split(/\n+/);
+ i = rules.length;
+ while ( i-- ) {
+ rule = rules[i].trim();
+ if ( rule.length !== 0 ) {
+ temporaryRules[rule] = allRules[rule] = true;
+ }
+ }
+
+ var permanentList = document.createDocumentFragment(),
+ temporaryList = document.createDocumentFragment(),
+ li;
+
+ rules = Object.keys(allRules).sort(directiveSort);
+ for ( i = 0; i < rules.length; i++ ) {
+ rule = rules[i];
+ onLeft = permanentRules.hasOwnProperty(rule);
+ onRight = temporaryRules.hasOwnProperty(rule);
+ if ( onLeft && onRight ) {
+ li = document.createElement('li');
+ li.textContent = rule;
+ permanentList.appendChild(li);
+ li = document.createElement('li');
+ li.textContent = rule;
+ temporaryList.appendChild(li);
+ } else if ( onLeft ) {
+ li = document.createElement('li');
+ li.textContent = rule;
+ permanentList.appendChild(li);
+ li = document.createElement('li');
+ li.textContent = rule;
+ li.className = 'notRight toRemove';
+ temporaryList.appendChild(li);
+ } else if ( onRight ) {
+ li = document.createElement('li');
+ li.textContent = '\xA0';
+ permanentList.appendChild(li);
+ li = document.createElement('li');
+ li.textContent = rule;
+ li.className = 'notLeft';
+ temporaryList.appendChild(li);
+ }
+ }
+
+ // TODO: build incrementally.
+
+ uDom('#diff > .left > ul > li').remove();
+ document.querySelector('#diff > .left > ul').appendChild(permanentList);
+ uDom('#diff > .right > ul > li').remove();
+ document.querySelector('#diff > .right > ul').appendChild(temporaryList);
+ uDom('#diff').toggleClass('dirty', response.temporaryRules !== response.permanentRules);
+};
+
+/******************************************************************************/
+
+// https://github.com/chrisaljoudi/uBlock/issues/757
+// Support RequestPolicy rule syntax
+
+var fromRequestPolicy = function(content) {
+ var matches = /\[origins-to-destinations\]([^\[]+)/.exec(content);
+ if ( matches === null || matches.length !== 2 ) {
+ return;
+ }
+ return matches[1].trim()
+ .replace(/\|/g, ' ')
+ .replace(/\n/g, ' * allow\n');
+};
+
+/******************************************************************************/
+
+// https://github.com/gorhill/uMatrix/issues/270
+
+var fromNoScript = function(content) {
+ var noscript = null;
+ try {
+ noscript = JSON.parse(content);
+ } catch (e) {
+ }
+ if (
+ noscript === null ||
+ typeof noscript !== 'object' ||
+ typeof noscript.prefs !== 'object' ||
+ typeof noscript.prefs.clearClick === 'undefined' ||
+ typeof noscript.whitelist !== 'string' ||
+ typeof noscript.V !== 'string'
+ ) {
+ return;
+ }
+ var out = new Set();
+ var reBad = /[a-z]+:\w*$/;
+ var reURL = /[a-z]+:\/\/([0-9a-z.-]+)/;
+ var directives = noscript.whitelist.split(/\s+/);
+ var i = directives.length;
+ var directive, matches;
+ while ( i-- ) {
+ directive = directives[i].trim();
+ if ( directive === '' ) {
+ continue;
+ }
+ if ( reBad.test(directive) ) {
+ continue;
+ }
+ matches = reURL.exec(directive);
+ if ( matches !== null ) {
+ directive = matches[1];
+ }
+ out.add('* ' + directive + ' * allow');
+ out.add('* ' + directive + ' script allow');
+ out.add('* ' + directive + ' frame allow');
+ }
+ return Array.from(out).join('\n');
+};
+
+/******************************************************************************/
+
+var handleImportFilePicker = function() {
+ var fileReaderOnLoadHandler = function() {
+ if ( typeof this.result !== 'string' || this.result === '' ) {
+ return;
+ }
+ var result = fromRequestPolicy(this.result);
+ if ( result === undefined ) {
+ result = fromNoScript(this.result);
+ if ( result === undefined ) {
+ result = this.result;
+ }
+ }
+ if ( this.result === '' ) { return; }
+ var request = {
+ 'what': 'setUserRules',
+ 'temporaryRules': rulesFromHTML('#diff .right li') + '\n' + result
+ };
+ vAPI.messaging.send('user-rules.js', request, processUserRules);
+ };
+ var file = this.files[0];
+ if ( file === undefined || file.name === '' ) {
+ return;
+ }
+ if ( file.type.indexOf('text') !== 0 && file.type !== 'application/json') {
+ return;
+ }
+ var fr = new FileReader();
+ fr.onload = fileReaderOnLoadHandler;
+ fr.readAsText(file);
+};
+
+/******************************************************************************/
+
+var startImportFilePicker = function() {
+ var input = document.getElementById('importFilePicker');
+ // Reset to empty string, this will ensure an change event is properly
+ // triggered if the user pick a file, even if it is the same as the last
+ // one picked.
+ input.value = '';
+ input.click();
+};
+
+/******************************************************************************/
+
+function exportUserRulesToFile() {
+ vAPI.download({
+ 'url': 'data:text/plain,' + encodeURIComponent(rulesFromHTML('#diff .left li') + '\n'),
+ 'filename': uDom('[data-i18n="userRulesDefaultFileName"]').text()
+ });
+}
+
+/******************************************************************************/
+
+var rulesFromHTML = function(selector) {
+ var rules = [];
+ var lis = uDom(selector);
+ var li;
+ for ( var i = 0; i < lis.length; i++ ) {
+ li = lis.at(i);
+ if ( li.hasClassName('toRemove') ) {
+ rules.push('');
+ } else {
+ rules.push(li.text());
+ }
+ }
+ return rules.join('\n');
+};
+
+/******************************************************************************/
+
+var revertHandler = function() {
+ var request = {
+ 'what': 'setUserRules',
+ 'temporaryRules': rulesFromHTML('#diff .left li')
+ };
+ vAPI.messaging.send('user-rules.js', request, processUserRules);
+};
+
+/******************************************************************************/
+
+var commitHandler = function() {
+ var request = {
+ 'what': 'setUserRules',
+ 'permanentRules': rulesFromHTML('#diff .right li')
+ };
+ vAPI.messaging.send('user-rules.js', request, processUserRules);
+};
+
+/******************************************************************************/
+
+var editStartHandler = function() {
+ uDom('#diff .right textarea').val(rulesFromHTML('#diff .right li'));
+ var parent = uDom(this).ancestors('#diff');
+ parent.toggleClass('edit', true);
+};
+
+/******************************************************************************/
+
+var editStopHandler = function() {
+ var parent = uDom(this).ancestors('#diff');
+ parent.toggleClass('edit', false);
+ var request = {
+ 'what': 'setUserRules',
+ 'temporaryRules': uDom('#diff .right textarea').val()
+ };
+ vAPI.messaging.send('user-rules.js', request, processUserRules);
+};
+
+/******************************************************************************/
+
+var editCancelHandler = function() {
+ var parent = uDom(this).ancestors('#diff');
+ parent.toggleClass('edit', false);
+};
+
+/******************************************************************************/
+
+var temporaryRulesToggler = function() {
+ var li = uDom(this);
+ li.toggleClass('toRemove');
+ var request = {
+ 'what': 'setUserRules',
+ 'temporaryRules': rulesFromHTML('#diff .right li')
+ };
+ vAPI.messaging.send('user-rules.js', request, processUserRules);
+};
+
+/******************************************************************************/
+
+self.cloud.onPush = function() {
+ return rulesFromHTML('#diff .left li');
+};
+
+self.cloud.onPull = function(data, append) {
+ if ( typeof data !== 'string' ) { return; }
+ if ( append ) {
+ data = rulesFromHTML('#diff .right li') + '\n' + data;
+ }
+ var request = {
+ 'what': 'setUserRules',
+ 'temporaryRules': data
+ };
+ vAPI.messaging.send('user-rules.js', request, processUserRules);
+};
+
+/******************************************************************************/
+
+uDom.onLoad(function() {
+ // Handle user interaction
+ uDom('#importButton').on('click', startImportFilePicker);
+ uDom('#importFilePicker').on('change', handleImportFilePicker);
+ uDom('#exportButton').on('click', exportUserRulesToFile);
+ uDom('#revertButton').on('click', revertHandler);
+ uDom('#commitButton').on('click', commitHandler);
+ uDom('#editEnterButton').on('click', editStartHandler);
+ uDom('#editStopButton').on('click', editStopHandler);
+ uDom('#editCancelButton').on('click', editCancelHandler);
+ uDom('#diff > .right > ul').on('click', 'li', temporaryRulesToggler);
+
+ vAPI.messaging.send('user-rules.js', { what: 'getUserRules' }, processUserRules);
+});
+
+/******************************************************************************/
+
+})();
+
diff --git a/js/usersettings.js b/js/usersettings.js
new file mode 100644
index 0000000..b5c5b51
--- /dev/null
+++ b/js/usersettings.js
@@ -0,0 +1,59 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+/******************************************************************************/
+
+µMatrix.changeUserSettings = function(name, value) {
+ if ( typeof name !== 'string' || name === '' ) {
+ return;
+ }
+
+ // Do not allow an unknown user setting to be created
+ if ( this.userSettings[name] === undefined ) {
+ return;
+ }
+
+ if ( value === undefined ) {
+ return this.userSettings[name];
+ }
+
+ // Pre-change
+ switch ( name ) {
+
+ default:
+ break;
+ }
+
+ // Change
+ this.userSettings[name] = value;
+
+ // Post-change
+ switch ( name ) {
+
+ default:
+ break;
+ }
+
+ this.saveUserSettings();
+};
diff --git a/js/utils.js b/js/utils.js
new file mode 100644
index 0000000..3b1d170
--- /dev/null
+++ b/js/utils.js
@@ -0,0 +1,106 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+/******************************************************************************/
+
+µMatrix.gotoURL = function(details) {
+ vAPI.tabs.open(details);
+};
+
+/******************************************************************************/
+
+µMatrix.gotoExtensionURL = function(details) {
+ if ( details.url.startsWith('logger-ui.html') ) {
+ if ( details.shiftKey ) {
+ this.changeUserSettings(
+ 'alwaysDetachLogger',
+ !this.userSettings.alwaysDetachLogger
+ );
+ }
+ details.popup = this.userSettings.alwaysDetachLogger;
+ }
+ details.select = true;
+ vAPI.tabs.open(details);
+};
+
+/******************************************************************************/
+
+µMatrix.LineIterator = function(text, offset) {
+ this.text = text;
+ this.textLen = this.text.length;
+ this.offset = offset || 0;
+};
+
+µMatrix.LineIterator.prototype = {
+ next: function() {
+ var lineEnd = this.text.indexOf('\n', this.offset);
+ if ( lineEnd === -1 ) {
+ lineEnd = this.text.indexOf('\r', this.offset);
+ if ( lineEnd === -1 ) {
+ lineEnd = this.textLen;
+ }
+ }
+ var line = this.text.slice(this.offset, lineEnd);
+ this.offset = lineEnd + 1;
+ return line;
+ },
+ rewind: function() {
+ if ( this.offset <= 1 ) {
+ this.offset = 0;
+ return;
+ }
+ var lineEnd = this.text.lastIndexOf('\n', this.offset - 2);
+ if ( lineEnd !== -1 ) {
+ this.offset = lineEnd + 1;
+ } else {
+ lineEnd = this.text.lastIndexOf('\r', this.offset - 2);
+ this.offset = lineEnd !== -1 ? lineEnd + 1 : 0;
+ }
+ },
+ eot: function() {
+ return this.offset >= this.textLen;
+ }
+};
+
+/******************************************************************************/
+
+µMatrix.setToArray = typeof Array.from === 'function' ?
+ Array.from :
+ function(dict) {
+ var out = [],
+ entries = dict.values(),
+ entry;
+ for (;;) {
+ entry = entries.next();
+ if ( entry.done ) { break; }
+ out.push(entry.value);
+ }
+ return out;
+ };
+
+µMatrix.setFromArray = function(arr) {
+ return new Set(arr);
+};
+
+/******************************************************************************/
diff --git a/js/vapi-background.js b/js/vapi-background.js
new file mode 100644
index 0000000..b4f1eac
--- /dev/null
+++ b/js/vapi-background.js
@@ -0,0 +1,3466 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* jshint bitwise: false, esnext: true */
+/* global self, Components, punycode */
+
+// For background page
+
+'use strict';
+
+/******************************************************************************/
+
+(function() {
+
+/******************************************************************************/
+
+// Useful links
+//
+// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface
+// https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Services.jsm
+
+/******************************************************************************/
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+const {Services} = Cu.import('resource://gre/modules/Services.jsm', null);
+
+/******************************************************************************/
+
+var vAPI = self.vAPI = self.vAPI || {};
+vAPI.firefox = true;
+vAPI.modernFirefox = Services.appinfo.ID === '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}' &&
+ Services.vc.compare(Services.appinfo.platformVersion, '44') > 0;
+
+/******************************************************************************/
+
+var deferUntil = function(testFn, mainFn, details) {
+ if ( typeof details !== 'object' ) {
+ details = {};
+ }
+
+ var now = 0;
+ var next = details.next || 200;
+ var until = details.until || 2000;
+
+ var check = function() {
+ if ( testFn() === true || now >= until ) {
+ mainFn();
+ return;
+ }
+ now += next;
+ vAPI.setTimeout(check, next);
+ };
+
+ if ( 'sync' in details && details.sync === true ) {
+ check();
+ } else {
+ vAPI.setTimeout(check, 1);
+ }
+};
+
+/******************************************************************************/
+
+vAPI.app = {
+ name: 'uMatrix',
+ version: location.hash.slice(1)
+};
+
+/******************************************************************************/
+
+vAPI.app.start = function() {
+};
+
+/******************************************************************************/
+
+vAPI.app.stop = function() {
+};
+
+/******************************************************************************/
+
+vAPI.app.restart = function() {
+ // Listening in bootstrap.js
+ Cc['@mozilla.org/childprocessmessagemanager;1']
+ .getService(Ci.nsIMessageSender)
+ .sendAsyncMessage(location.host + '-restart');
+};
+
+/******************************************************************************/
+
+// List of things that needs to be destroyed when disabling the extension
+// Only functions should be added to it
+
+var cleanupTasks = [];
+
+// This must be updated manually, every time a new task is added/removed
+
+// Fixed by github.com/AlexVallat:
+// https://github.com/AlexVallat/uBlock/commit/7b781248f00cbe3d61b1cc367c440db80fa06049
+// 7 instances of cleanupTasks.push, but one is unique to fennec, and one to desktop.
+var expectedNumberOfCleanups = 7;
+
+window.addEventListener('unload', function() {
+ if ( typeof vAPI.app.onShutdown === 'function' ) {
+ vAPI.app.onShutdown();
+ }
+
+ // IMPORTANT: cleanup tasks must be executed using LIFO order.
+ var i = cleanupTasks.length;
+ while ( i-- ) {
+ cleanupTasks[i]();
+ }
+
+ if ( cleanupTasks.length < expectedNumberOfCleanups ) {
+ console.error(
+ 'uMatrix> Cleanup tasks performed: %s (out of %s)',
+ cleanupTasks.length,
+ expectedNumberOfCleanups
+ );
+ }
+
+ // frameModule needs to be cleared too
+ var frameModuleURL = vAPI.getURL('frameModule.js');
+ var frameModule = {};
+ Cu.import(frameModuleURL, frameModule);
+ frameModule.contentObserver.unregister();
+ Cu.unload(frameModuleURL);
+});
+
+/******************************************************************************/
+
+// For now, only booleans.
+
+vAPI.browserSettings = {
+ originalValues: {},
+
+ rememberOriginalValue: function(path, setting) {
+ var key = path + '.' + setting;
+ if ( this.originalValues.hasOwnProperty(key) ) {
+ return;
+ }
+ var hasUserValue;
+ var branch = Services.prefs.getBranch(path + '.');
+ try {
+ hasUserValue = branch.prefHasUserValue(setting);
+ } catch (ex) {
+ }
+ if ( hasUserValue !== undefined ) {
+ this.originalValues[key] = hasUserValue ? this.getValue(path, setting) : undefined;
+ }
+ },
+
+ clear: function(path, setting) {
+ var key = path + '.' + setting;
+
+ // Value was not overriden -- nothing to restore
+ if ( this.originalValues.hasOwnProperty(key) === false ) {
+ return;
+ }
+
+ var value = this.originalValues[key];
+ // https://github.com/gorhill/uBlock/issues/292#issuecomment-109621979
+ // Forget the value immediately, it may change outside of
+ // uBlock control.
+ delete this.originalValues[key];
+
+ // Original value was a default one
+ if ( value === undefined ) {
+ try {
+ Services.prefs.getBranch(path + '.').clearUserPref(setting);
+ } catch (ex) {
+ }
+ return;
+ }
+
+ // Reset to original value
+ this.setValue(path, setting, value);
+ },
+
+ getValue: function(path, setting) {
+ var branch = Services.prefs.getBranch(path + '.');
+ var getMethod;
+
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIPrefBranch#getPrefType%28%29
+ switch ( branch.getPrefType(setting) ) {
+ case 64: // PREF_INT
+ getMethod = 'getIntPref';
+ break;
+ case 128: // PREF_BOOL
+ getMethod = 'getBoolPref';
+ break;
+ default: // not supported
+ return;
+ }
+
+ try {
+ return branch[getMethod](setting);
+ } catch (ex) {
+ }
+ },
+
+ setValue: function(path, setting, value) {
+ var setMethod;
+ switch ( typeof value ) {
+ case 'number':
+ setMethod = 'setIntPref';
+ break;
+ case 'boolean':
+ setMethod = 'setBoolPref';
+ break;
+ default: // not supported
+ return;
+ }
+
+ try {
+ Services.prefs.getBranch(path + '.')[setMethod](setting, value);
+ } catch (ex) {
+ }
+ },
+
+ setSetting: function(setting, value) {
+ var prefName, prefVal;
+ switch ( setting ) {
+ case 'prefetching':
+ this.rememberOriginalValue('network', 'prefetch-next');
+ // http://betanews.com/2015/08/15/firefox-stealthily-loads-webpages-when-you-hover-over-links-heres-how-to-stop-it/
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=814169
+ // Sigh.
+ this.rememberOriginalValue('network.http', 'speculative-parallel-limit');
+ // https://github.com/gorhill/uBlock/issues/292
+ // "true" means "do not disable", i.e. leave entry alone
+ if ( value ) {
+ this.clear('network', 'prefetch-next');
+ this.clear('network.http', 'speculative-parallel-limit');
+ } else {
+ this.setValue('network', 'prefetch-next', false);
+ this.setValue('network.http', 'speculative-parallel-limit', 0);
+ }
+ break;
+
+ case 'hyperlinkAuditing':
+ this.rememberOriginalValue('browser', 'send_pings');
+ this.rememberOriginalValue('beacon', 'enabled');
+ // https://github.com/gorhill/uBlock/issues/292
+ // "true" means "do not disable", i.e. leave entry alone
+ if ( value ) {
+ this.clear('browser', 'send_pings');
+ this.clear('beacon', 'enabled');
+ } else {
+ this.setValue('browser', 'send_pings', false);
+ this.setValue('beacon', 'enabled', false);
+ }
+ break;
+
+ // https://github.com/gorhill/uBlock/issues/894
+ // Do not disable completely WebRTC if it can be avoided. FF42+
+ // has a `media.peerconnection.ice.default_address_only` pref which
+ // purpose is to prevent local IP address leakage.
+ case 'webrtcIPAddress':
+ if ( this.getValue('media.peerconnection', 'ice.default_address_only') !== undefined ) {
+ prefName = 'ice.default_address_only';
+ prefVal = true;
+ } else {
+ prefName = 'enabled';
+ prefVal = false;
+ }
+
+ this.rememberOriginalValue('media.peerconnection', prefName);
+ if ( value ) {
+ this.clear('media.peerconnection', prefName);
+ } else {
+ this.setValue('media.peerconnection', prefName, prefVal);
+ }
+ break;
+
+ default:
+ break;
+ }
+ },
+
+ set: function(details) {
+ for ( var setting in details ) {
+ if ( details.hasOwnProperty(setting) === false ) {
+ continue;
+ }
+ this.setSetting(setting, !!details[setting]);
+ }
+ },
+
+ restoreAll: function() {
+ var pos;
+ for ( var key in this.originalValues ) {
+ if ( this.originalValues.hasOwnProperty(key) === false ) {
+ continue;
+ }
+ pos = key.lastIndexOf('.');
+ this.clear(key.slice(0, pos), key.slice(pos + 1));
+ }
+ }
+};
+
+cleanupTasks.push(vAPI.browserSettings.restoreAll.bind(vAPI.browserSettings));
+
+/******************************************************************************/
+
+// API matches that of chrome.storage.local:
+// https://developer.chrome.com/extensions/storage
+
+vAPI.storage = (function() {
+ var db = null;
+ var vacuumTimer = null;
+
+ var close = function() {
+ if ( vacuumTimer !== null ) {
+ clearTimeout(vacuumTimer);
+ vacuumTimer = null;
+ }
+ if ( db === null ) {
+ return;
+ }
+ db.asyncClose();
+ db = null;
+ };
+
+ var open = function() {
+ if ( db !== null ) {
+ return db;
+ }
+
+ // Create path
+ var path = Services.dirsvc.get('ProfD', Ci.nsIFile);
+ path.append('extension-data');
+ if ( !path.exists() ) {
+ path.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt('0774', 8));
+ }
+ if ( !path.isDirectory() ) {
+ throw Error('Should be a directory...');
+ }
+ path.append(location.host + '.sqlite');
+
+ // Open database
+ try {
+ db = Services.storage.openDatabase(path);
+ if ( db.connectionReady === false ) {
+ db.asyncClose();
+ db = null;
+ }
+ } catch (ex) {
+ }
+
+ if ( db === null ) {
+ return null;
+ }
+
+ // Database was opened, register cleanup task
+ cleanupTasks.push(close);
+
+ // Setup database
+ db.createAsyncStatement('CREATE TABLE IF NOT EXISTS "settings" ("name" TEXT PRIMARY KEY NOT NULL, "value" TEXT);')
+ .executeAsync();
+
+ if ( vacuum !== null ) {
+ vacuumTimer = vAPI.setTimeout(vacuum, 60000);
+ }
+
+ return db;
+ };
+
+ // https://developer.mozilla.org/en-US/docs/Storage/Performance#Vacuuming_and_zero-fill
+ // Vacuum only once, and only while idle
+ var vacuum = function() {
+ vacuumTimer = null;
+ if ( db === null ) {
+ return;
+ }
+ var idleSvc = Cc['@mozilla.org/widget/idleservice;1']
+ .getService(Ci.nsIIdleService);
+ if ( idleSvc.idleTime < 60000 ) {
+ vacuumTimer = vAPI.setTimeout(vacuum, 60000);
+ return;
+ }
+ db.createAsyncStatement('VACUUM').executeAsync();
+ vacuum = null;
+ };
+
+ // Execute a query
+ var runStatement = function(stmt, callback) {
+ var result = {};
+
+ stmt.executeAsync({
+ handleResult: function(rows) {
+ if ( !rows || typeof callback !== 'function' ) {
+ return;
+ }
+
+ var row;
+
+ while ( (row = rows.getNextRow()) ) {
+ // we assume that there will be two columns, since we're
+ // using it only for preferences
+ result[row.getResultByIndex(0)] = row.getResultByIndex(1);
+ }
+ },
+ handleCompletion: function(reason) {
+ if ( typeof callback === 'function' && reason === 0 ) {
+ callback(result);
+ }
+ },
+ handleError: function(error) {
+ console.error('SQLite error ', error.result, error.message);
+ // Caller expects an answer regardless of failure.
+ if ( typeof callback === 'function' ) {
+ callback(null);
+ }
+ }
+ });
+ };
+
+ var bindNames = function(stmt, names) {
+ if ( Array.isArray(names) === false || names.length === 0 ) {
+ return;
+ }
+ var params = stmt.newBindingParamsArray();
+ var i = names.length, bp;
+ while ( i-- ) {
+ bp = params.newBindingParams();
+ bp.bindByName('name', names[i]);
+ params.addParams(bp);
+ }
+ stmt.bindParameters(params);
+ };
+
+ var clear = function(callback) {
+ if ( open() === null ) {
+ if ( typeof callback === 'function' ) {
+ callback();
+ }
+ return;
+ }
+ runStatement(db.createAsyncStatement('DELETE FROM "settings";'), callback);
+ };
+
+ var getBytesInUse = function(keys, callback) {
+ if ( typeof callback !== 'function' ) {
+ return;
+ }
+
+ if ( open() === null ) {
+ callback(0);
+ return;
+ }
+
+ var stmt;
+ if ( Array.isArray(keys) ) {
+ stmt = db.createAsyncStatement('SELECT "size" AS "size", SUM(LENGTH("value")) FROM "settings" WHERE "name" = :name');
+ bindNames(keys);
+ } else {
+ stmt = db.createAsyncStatement('SELECT "size" AS "size", SUM(LENGTH("value")) FROM "settings"');
+ }
+
+ runStatement(stmt, function(result) {
+ callback(result.size);
+ });
+ };
+
+ var read = function(details, callback) {
+ if ( typeof callback !== 'function' ) {
+ return;
+ }
+
+ var prepareResult = function(result) {
+ var key;
+ for ( key in result ) {
+ if ( result.hasOwnProperty(key) === false ) {
+ continue;
+ }
+ result[key] = JSON.parse(result[key]);
+ }
+ if ( typeof details === 'object' && details !== null ) {
+ for ( key in details ) {
+ if ( result.hasOwnProperty(key) === false ) {
+ result[key] = details[key];
+ }
+ }
+ }
+ callback(result);
+ };
+
+ if ( open() === null ) {
+ prepareResult({});
+ return;
+ }
+
+ var names = [];
+ if ( details !== null ) {
+ if ( Array.isArray(details) ) {
+ names = details;
+ } else if ( typeof details === 'object' ) {
+ names = Object.keys(details);
+ } else {
+ names = [details.toString()];
+ }
+ }
+
+ var stmt;
+ if ( names.length === 0 ) {
+ stmt = db.createAsyncStatement('SELECT * FROM "settings"');
+ } else {
+ stmt = db.createAsyncStatement('SELECT * FROM "settings" WHERE "name" = :name');
+ bindNames(stmt, names);
+ }
+
+ runStatement(stmt, prepareResult);
+ };
+
+ var remove = function(keys, callback) {
+ if ( open() === null ) {
+ if ( typeof callback === 'function' ) {
+ callback();
+ }
+ return;
+ }
+ var stmt = db.createAsyncStatement('DELETE FROM "settings" WHERE "name" = :name');
+ bindNames(stmt, typeof keys === 'string' ? [keys] : keys);
+ runStatement(stmt, callback);
+ };
+
+ var write = function(details, callback) {
+ if ( open() === null ) {
+ if ( typeof callback === 'function' ) {
+ callback();
+ }
+ return;
+ }
+
+ var stmt = db.createAsyncStatement('INSERT OR REPLACE INTO "settings" ("name", "value") VALUES(:name, :value)');
+ var params = stmt.newBindingParamsArray(), bp;
+ for ( var key in details ) {
+ if ( details.hasOwnProperty(key) === false ) {
+ continue;
+ }
+ bp = params.newBindingParams();
+ bp.bindByName('name', key);
+ bp.bindByName('value', JSON.stringify(details[key]));
+ params.addParams(bp);
+ }
+ if ( params.length === 0 ) {
+ return;
+ }
+
+ stmt.bindParameters(params);
+ runStatement(stmt, callback);
+ };
+
+ // Export API
+ var api = {
+ QUOTA_BYTES: 100 * 1024 * 1024,
+ clear: clear,
+ get: read,
+ getBytesInUse: getBytesInUse,
+ remove: remove,
+ set: write
+ };
+ return api;
+})();
+
+vAPI.cacheStorage = vAPI.storage;
+
+/******************************************************************************/
+
+// This must be executed/setup early.
+
+var winWatcher = (function() {
+ var windowToIdMap = new Map();
+ var windowIdGenerator = 1;
+ var api = {
+ onOpenWindow: null,
+ onCloseWindow: null
+ };
+
+ // https://github.com/gorhill/uMatrix/issues/586
+ // This is necessary hack because on SeaMonkey 2.40, for unknown reasons
+ // private windows do not have the attribute `windowtype` set to
+ // `navigator:browser`. As a fallback, the code here will also test whether
+ // the id attribute is `main-window`.
+ api.toBrowserWindow = function(win) {
+ var docElement = win && win.document && win.document.documentElement;
+ if ( !docElement ) {
+ return null;
+ }
+ if ( vAPI.thunderbird ) {
+ return docElement.getAttribute('windowtype') === 'mail:3pane' ? win : null;
+ }
+ return docElement.getAttribute('windowtype') === 'navigator:browser' ||
+ docElement.getAttribute('id') === 'main-window' ?
+ win : null;
+ };
+
+ api.getWindows = function() {
+ return windowToIdMap.keys();
+ };
+
+ api.idFromWindow = function(win) {
+ return windowToIdMap.get(win) || 0;
+ };
+
+ api.getCurrentWindow = function() {
+ return this.toBrowserWindow(Services.wm.getMostRecentWindow(null));
+ };
+
+ var addWindow = function(win) {
+ if ( !win || windowToIdMap.has(win) ) {
+ return;
+ }
+ windowToIdMap.set(win, windowIdGenerator++);
+ if ( typeof api.onOpenWindow === 'function' ) {
+ api.onOpenWindow(win);
+ }
+ };
+
+ var removeWindow = function(win) {
+ if ( !win || windowToIdMap.delete(win) !== true ) {
+ return;
+ }
+ if ( typeof api.onCloseWindow === 'function' ) {
+ api.onCloseWindow(win);
+ }
+ };
+
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowMediator
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowWatcher
+ // https://github.com/gorhill/uMatrix/issues/357
+ // Use nsIWindowMediator for being notified of opened/closed windows.
+ var listeners = {
+ onOpenWindow: function(aWindow) {
+ var win;
+ try {
+ win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ } catch (ex) {
+ }
+ addWindow(win);
+ },
+
+ onCloseWindow: function(aWindow) {
+ var win;
+ try {
+ win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ } catch (ex) {
+ }
+ removeWindow(win);
+ },
+
+ observe: function(aSubject, topic) {
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowWatcher#registerNotification%28%29
+ // "aSubject - the window being opened or closed, sent as an
+ // "nsISupports which can be ... QueryInterfaced to an
+ // "nsIDOMWindow."
+ var win;
+ try {
+ win = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ } catch (ex) {
+ }
+ if ( !win ) { return; }
+ if ( topic === 'domwindowopened' ) {
+ addWindow(win);
+ return;
+ }
+ if ( topic === 'domwindowclosed' ) {
+ removeWindow(win);
+ return;
+ }
+ }
+ };
+
+ (function() {
+ var winumerator, win;
+
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowMediator#getEnumerator%28%29
+ winumerator = Services.wm.getEnumerator(null);
+ while ( winumerator.hasMoreElements() ) {
+ win = winumerator.getNext();
+ if ( !win.closed ) {
+ windowToIdMap.set(win, windowIdGenerator++);
+ }
+ }
+
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWindowWatcher#getWindowEnumerator%28%29
+ winumerator = Services.ww.getWindowEnumerator();
+ while ( winumerator.hasMoreElements() ) {
+ win = winumerator.getNext()
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ if ( !win.closed ) {
+ windowToIdMap.set(win, windowIdGenerator++);
+ }
+ }
+
+ Services.wm.addListener(listeners);
+ Services.ww.registerNotification(listeners);
+ })();
+
+ cleanupTasks.push(function() {
+ Services.wm.removeListener(listeners);
+ Services.ww.unregisterNotification(listeners);
+ windowToIdMap.clear();
+ });
+
+ return api;
+})();
+
+/******************************************************************************/
+
+var getTabBrowser = function(win) {
+ return win && win.gBrowser || null;
+};
+
+/******************************************************************************/
+
+var getOwnerWindow = function(target) {
+ if ( target.ownerDocument ) {
+ return target.ownerDocument.defaultView;
+ }
+ return null;
+};
+
+/******************************************************************************/
+
+vAPI.isBehindTheSceneTabId = function(tabId) {
+ return tabId.toString() === '-1';
+};
+
+vAPI.noTabId = '-1';
+
+/******************************************************************************/
+
+vAPI.tabs = {};
+
+/******************************************************************************/
+
+vAPI.tabs.registerListeners = function() {
+ tabWatcher.start();
+};
+
+/******************************************************************************/
+
+// Firefox:
+// https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Tabbed_browser
+//
+// browser --> ownerDocument --> defaultView --> gBrowser --> browsers --+
+// ^ |
+// | |
+// +-------------------------------------------------------------------
+//
+// browser (browser)
+// contentTitle
+// currentURI
+// ownerDocument (XULDocument)
+// defaultView (ChromeWindow)
+// gBrowser (tabbrowser OR browser)
+// browsers (browser)
+// selectedBrowser
+// selectedTab
+// tabs (tab.tabbrowser-tab)
+//
+// Fennec: (what I figured so far)
+//
+// tab --> browser windows --> window --> BrowserApp --> tabs --+
+// ^ window |
+// | |
+// +---------------------------------------------------------------+
+//
+// tab
+// browser
+// [manual search to go back to tab from list of windows]
+
+vAPI.tabs.get = function(tabId, callback) {
+ var browser;
+
+ if ( tabId === null ) {
+ browser = tabWatcher.currentBrowser();
+ tabId = tabWatcher.tabIdFromTarget(browser);
+ } else {
+ browser = tabWatcher.browserFromTabId(tabId);
+ }
+
+ // For internal use
+ if ( typeof callback !== 'function' ) {
+ return browser;
+ }
+
+ if ( !browser || !browser.currentURI ) {
+ callback();
+ return;
+ }
+
+ var win = getOwnerWindow(browser);
+ var tabBrowser = getTabBrowser(win);
+
+ // https://github.com/gorhill/uMatrix/issues/540
+ // The `index` property is nowhere used by uMatrix at this point, so we
+ // will refrain from returning this information for the time being.
+
+ callback({
+ id: tabId,
+ index: undefined,
+ windowId: winWatcher.idFromWindow(win),
+ active: tabBrowser !== null && browser === tabBrowser.selectedBrowser,
+ url: browser.currentURI.asciiSpec,
+ title: browser.contentTitle
+ });
+};
+
+/******************************************************************************/
+
+vAPI.tabs.getAllSync = function(window) {
+ var win, tab;
+ var tabs = [];
+
+ for ( win of winWatcher.getWindows() ) {
+ if ( window && window !== win ) {
+ continue;
+ }
+
+ var tabBrowser = getTabBrowser(win);
+ if ( tabBrowser === null ) {
+ continue;
+ }
+
+ // This can happens if a tab-less window is currently opened.
+ // Example of a tab-less window: one opened from clicking
+ // "View Page Source".
+ if ( !tabBrowser.tabs ) {
+ continue;
+ }
+
+ for ( tab of tabBrowser.tabs ) {
+ tabs.push(tab);
+ }
+ }
+
+ return tabs;
+};
+
+/******************************************************************************/
+
+vAPI.tabs.getAll = function(callback) {
+ var tabs = [], tab;
+
+ for ( var browser of tabWatcher.browsers() ) {
+ tab = tabWatcher.tabFromBrowser(browser);
+ if ( tab === null ) {
+ continue;
+ }
+ if ( tab.hasAttribute('pending') ) {
+ continue;
+ }
+ tabs.push({
+ id: tabWatcher.tabIdFromTarget(browser),
+ url: browser.currentURI.asciiSpec
+ });
+ }
+
+ callback(tabs);
+};
+
+/******************************************************************************/
+
+// properties of the details object:
+// url: 'URL', // the address that will be opened
+// tabId: 1, // the tab is used if set, instead of creating a new one
+// index: -1, // undefined: end of the list, -1: following tab, or after index
+// active: false, // opens the tab in background - true and undefined: foreground
+// select: true // if a tab is already opened with that url, then select it instead of opening a new one
+
+vAPI.tabs.open = function(details) {
+ if ( !details.url ) {
+ return null;
+ }
+ // extension pages
+ if ( /^[\w-]{2,}:/.test(details.url) === false ) {
+ details.url = vAPI.getURL(details.url);
+ }
+
+ var tab;
+
+ if ( details.select ) {
+ var URI = Services.io.newURI(details.url, null, null);
+
+ for ( tab of this.getAllSync() ) {
+ var browser = tabWatcher.browserFromTarget(tab);
+ // https://github.com/gorhill/uBlock/issues/2558
+ if ( browser === null ) { continue; }
+
+ // Or simply .equals if we care about the fragment
+ if ( URI.equalsExceptRef(browser.currentURI) === false ) {
+ continue;
+ }
+
+ this.select(tab);
+
+ // Update URL if fragment is different
+ if ( URI.equals(browser.currentURI) === false ) {
+ browser.loadURI(URI.asciiSpec);
+ }
+ return;
+ }
+ }
+
+ if ( details.active === undefined ) {
+ details.active = true;
+ }
+
+ if ( details.tabId ) {
+ tab = tabWatcher.browserFromTabId(details.tabId);
+ if ( tab ) {
+ tabWatcher.browserFromTarget(tab).loadURI(details.url);
+ return;
+ }
+ }
+
+ // Open in a standalone window
+ if ( details.popup === true ) {
+ Services.ww.openWindow(
+ self,
+ details.url,
+ null,
+ 'location=1,menubar=1,personalbar=1,resizable=1,toolbar=1',
+ null
+ );
+ return;
+ }
+
+ var win = winWatcher.getCurrentWindow();
+ var tabBrowser = getTabBrowser(win);
+ if ( tabBrowser === null ) {
+ return;
+ }
+
+ if ( details.index === -1 ) {
+ details.index = tabBrowser.browsers.indexOf(tabBrowser.selectedBrowser) + 1;
+ }
+
+ tab = tabBrowser.loadOneTab(details.url, { inBackground: !details.active });
+
+ if ( details.index !== undefined ) {
+ tabBrowser.moveTabTo(tab, details.index);
+ }
+};
+
+/******************************************************************************/
+
+// Replace the URL of a tab. Noop if the tab does not exist.
+
+vAPI.tabs.replace = function(tabId, url) {
+ var targetURL = url;
+
+ // extension pages
+ if ( /^[\w-]{2,}:/.test(targetURL) !== true ) {
+ targetURL = vAPI.getURL(targetURL);
+ }
+
+ var browser = tabWatcher.browserFromTabId(tabId);
+ if ( browser ) {
+ browser.loadURI(targetURL);
+ }
+};
+
+/******************************************************************************/
+
+vAPI.tabs._remove = function(tab, tabBrowser) {
+ if ( tabBrowser ) {
+ tabBrowser.removeTab(tab);
+ }
+};
+
+/******************************************************************************/
+
+vAPI.tabs.remove = function(tabId) {
+ var browser = tabWatcher.browserFromTabId(tabId);
+ if ( !browser ) {
+ return;
+ }
+ var tab = tabWatcher.tabFromBrowser(browser);
+ if ( !tab ) {
+ return;
+ }
+ this._remove(tab, getTabBrowser(getOwnerWindow(browser)));
+};
+
+/******************************************************************************/
+
+vAPI.tabs.reload = function(tabId) {
+ var browser = tabWatcher.browserFromTabId(tabId);
+ if ( !browser ) {
+ return;
+ }
+
+ browser.webNavigation.reload(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
+};
+
+/******************************************************************************/
+
+vAPI.tabs.select = function(tab) {
+ if ( typeof tab !== 'object' ) {
+ tab = tabWatcher.tabFromBrowser(tabWatcher.browserFromTabId(tab));
+ }
+ if ( !tab ) {
+ return;
+ }
+
+ // https://github.com/gorhill/uBlock/issues/470
+ var win = getOwnerWindow(tab);
+ win.focus();
+
+ var tabBrowser = getTabBrowser(win);
+ if ( tabBrowser ) {
+ tabBrowser.selectedTab = tab;
+ }
+};
+
+/******************************************************************************/
+
+vAPI.tabs.injectScript = function(tabId, details, callback) {
+ var browser = tabWatcher.browserFromTabId(tabId);
+ if ( !browser ) {
+ return;
+ }
+
+ if ( typeof details.file !== 'string' ) {
+ return;
+ }
+
+ details.file = vAPI.getURL(details.file);
+ browser.messageManager.sendAsyncMessage(
+ location.host + ':broadcast',
+ JSON.stringify({
+ broadcast: true,
+ channelName: 'vAPI',
+ msg: {
+ cmd: 'injectScript',
+ details: details
+ }
+ })
+ );
+
+ if ( typeof callback === 'function' ) {
+ vAPI.setTimeout(callback, 13);
+ }
+};
+
+/******************************************************************************/
+
+var tabWatcher = (function() {
+ // TODO: find out whether we need a janitor to take care of stale entries.
+
+ // https://github.com/gorhill/uMatrix/issues/540
+ // Use only weak references to hold onto browser references.
+ var browserToTabIdMap = new WeakMap();
+ var tabIdToBrowserMap = new Map();
+ var tabIdGenerator = 1;
+
+ var indexFromBrowser = function(browser) {
+ if ( !browser ) {
+ return -1;
+ }
+ var win = getOwnerWindow(browser);
+ if ( !win ) {
+ return -1;
+ }
+ var tabBrowser = getTabBrowser(win);
+ if ( tabBrowser === null ) {
+ return -1;
+ }
+ // This can happen, for example, the `view-source:` window, there is
+ // no tabbrowser object, the browser object sits directly in the
+ // window.
+ if ( tabBrowser === browser ) {
+ return 0;
+ }
+ return tabBrowser.browsers.indexOf(browser);
+ };
+
+ var indexFromTarget = function(target) {
+ return indexFromBrowser(browserFromTarget(target));
+ };
+
+ var tabFromBrowser = function(browser) {
+ var i = indexFromBrowser(browser);
+ if ( i === -1 ) {
+ return null;
+ }
+ var win = getOwnerWindow(browser);
+ if ( !win ) {
+ return null;
+ }
+ var tabBrowser = getTabBrowser(win);
+ if ( tabBrowser === null ) {
+ return null;
+ }
+ if ( !tabBrowser.tabs || i >= tabBrowser.tabs.length ) {
+ return null;
+ }
+ return tabBrowser.tabs[i];
+ };
+
+ var browserFromTarget = function(target) {
+ if ( !target ) {
+ return null;
+ }
+ if ( target.linkedPanel ) { // target is a tab
+ target = target.linkedBrowser;
+ }
+ if ( target.localName !== 'browser' ) {
+ return null;
+ }
+ return target;
+ };
+
+ var tabIdFromTarget = function(target) {
+ var browser = browserFromTarget(target);
+ if ( browser === null ) {
+ return vAPI.noTabId;
+ }
+ var tabId = browserToTabIdMap.get(browser);
+ if ( tabId === undefined ) {
+ tabId = '' + tabIdGenerator++;
+ browserToTabIdMap.set(browser, tabId);
+ tabIdToBrowserMap.set(tabId, Cu.getWeakReference(browser));
+ }
+ return tabId;
+ };
+
+ var browserFromTabId = function(tabId) {
+ var weakref = tabIdToBrowserMap.get(tabId);
+ var browser = weakref && weakref.get();
+ return browser || null;
+ };
+
+ var currentBrowser = function() {
+ var win = winWatcher.getCurrentWindow();
+ // https://github.com/gorhill/uBlock/issues/399
+ // getTabBrowser() can return null at browser launch time.
+ var tabBrowser = getTabBrowser(win);
+ if ( tabBrowser === null ) {
+ return null;
+ }
+ return browserFromTarget(tabBrowser.selectedTab);
+ };
+
+ var removeBrowserEntry = function(tabId, browser) {
+ if ( tabId && tabId !== vAPI.noTabId ) {
+ vAPI.tabs.onClosed(tabId);
+ delete vAPI.toolbarButton.tabs[tabId];
+ tabIdToBrowserMap.delete(tabId);
+ }
+ if ( browser ) {
+ browserToTabIdMap.delete(browser);
+ }
+ };
+
+ var removeTarget = function(target) {
+ onClose({ target: target });
+ };
+
+ var getAllBrowsers = function() {
+ var browsers = [], browser;
+ for ( var [tabId, weakref] of tabIdToBrowserMap ) {
+ browser = weakref.get();
+ // TODO:
+ // Maybe call removeBrowserEntry() if the browser no longer exists?
+ if ( browser ) {
+ browsers.push(browser);
+ }
+ }
+ return browsers;
+ };
+
+ // https://developer.mozilla.org/en-US/docs/Web/Events/TabOpen
+ //var onOpen = function({target}) {
+ // var tabId = tabIdFromTarget(target);
+ // var browser = browserFromTabId(tabId);
+ // vAPI.tabs.onNavigation({
+ // frameId: 0,
+ // tabId: tabId,
+ // url: browser.currentURI.asciiSpec,
+ // });
+ //};
+
+ // https://developer.mozilla.org/en-US/docs/Web/Events/TabShow
+ var onShow = function({target}) {
+ tabIdFromTarget(target);
+ };
+
+ // https://developer.mozilla.org/en-US/docs/Web/Events/TabClose
+ var onClose = function({target}) {
+ // target is tab in Firefox, browser in Fennec
+ var browser = browserFromTarget(target);
+ var tabId = browserToTabIdMap.get(browser);
+ removeBrowserEntry(tabId, browser);
+ };
+
+ // https://developer.mozilla.org/en-US/docs/Web/Events/TabSelect
+ // This is an entry point: when creating a new tab, it is not always
+ // reported through onLocationChanged... Sigh. It is "reported" here
+ // however.
+ var onSelect = function({target}) {
+ var browser = browserFromTarget(target);
+ var tabId = browserToTabIdMap.get(browser);
+ if ( tabId === undefined ) {
+ tabId = tabIdFromTarget(target);
+ vAPI.tabs.onNavigation({
+ frameId: 0,
+ tabId: tabId,
+ url: browser.currentURI.asciiSpec
+ });
+ }
+ vAPI.setIcon(tabId, getOwnerWindow(target));
+ };
+
+ var locationChangedMessageName = location.host + ':locationChanged';
+
+ var onLocationChanged = function(e) {
+ var vapi = vAPI;
+ var details = e.data;
+
+ // Ignore notifications related to our popup
+ if ( details.url.lastIndexOf(vapi.getURL('popup.html'), 0) === 0 ) {
+ return;
+ }
+
+ var browser = e.target;
+ var tabId = tabIdFromTarget(browser);
+ if ( tabId === vapi.noTabId ) {
+ return;
+ }
+
+ // LOCATION_CHANGE_SAME_DOCUMENT = "did not load a new document"
+ if ( details.flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT ) {
+ vapi.tabs.onUpdated(tabId, {url: details.url}, {
+ frameId: 0,
+ tabId: tabId,
+ url: browser.currentURI.asciiSpec
+ });
+ return;
+ }
+
+ // https://github.com/chrisaljoudi/uBlock/issues/105
+ // Allow any kind of pages
+ vapi.tabs.onNavigation({
+ frameId: 0,
+ tabId: tabId,
+ url: details.url
+ });
+ };
+
+ var attachToTabBrowser = function(window) {
+ if ( typeof vAPI.toolbarButton.attachToNewWindow === 'function' ) {
+ vAPI.toolbarButton.attachToNewWindow(window);
+ }
+
+ var tabBrowser = getTabBrowser(window);
+ if ( tabBrowser === null ) {
+ return;
+ }
+
+ var tabContainer;
+ if ( tabBrowser.deck ) { // Fennec
+ tabContainer = tabBrowser.deck;
+ } else if ( tabBrowser.tabContainer ) { // Firefox
+ tabContainer = tabBrowser.tabContainer;
+ vAPI.contextMenu.register(document);
+ }
+
+ // https://github.com/gorhill/uBlock/issues/697
+ // Ignore `TabShow` events: unfortunately the `pending` attribute is
+ // not set when a tab is opened as a result of session restore -- it is
+ // set *after* the event is fired in such case.
+ if ( tabContainer ) {
+ tabContainer.addEventListener('TabShow', onShow);
+ tabContainer.addEventListener('TabClose', onClose);
+ // when new window is opened TabSelect doesn't run on the selected tab?
+ tabContainer.addEventListener('TabSelect', onSelect);
+ }
+ };
+
+ // https://github.com/gorhill/uBlock/issues/906
+ // Ensure the environment is ready before trying to attaching.
+ var canAttachToTabBrowser = function(window) {
+ var document = window && window.document;
+ if ( !document || document.readyState !== 'complete' ) {
+ return false;
+ }
+
+ // On some platforms, the tab browser isn't immediately available,
+ // try waiting a bit if this happens.
+ // https://github.com/gorhill/uBlock/issues/763
+ // Not getting a tab browser should not prevent from attaching ourself
+ // to the window.
+ var tabBrowser = getTabBrowser(window);
+ if ( tabBrowser === null ) {
+ return false;
+ }
+
+ return winWatcher.toBrowserWindow(window) !== null;
+ };
+
+ var onWindowLoad = function(win) {
+ deferUntil(
+ canAttachToTabBrowser.bind(null, win),
+ attachToTabBrowser.bind(null, win)
+ );
+ };
+
+ var onWindowUnload = function(win) {
+ vAPI.contextMenu.unregister(win.document);
+
+ var tabBrowser = getTabBrowser(win);
+ if ( tabBrowser === null ) {
+ return;
+ }
+
+ var tabContainer = tabBrowser.tabContainer;
+ if ( tabContainer ) {
+ tabContainer.removeEventListener('TabShow', onShow);
+ tabContainer.removeEventListener('TabClose', onClose);
+ tabContainer.removeEventListener('TabSelect', onSelect);
+ }
+
+ // https://github.com/gorhill/uBlock/issues/574
+ // To keep in mind: not all windows are tab containers,
+ // sometimes the window IS the tab.
+ var tabs;
+ if ( tabBrowser.tabs ) {
+ tabs = tabBrowser.tabs;
+ } else if ( tabBrowser.localName === 'browser' ) {
+ tabs = [tabBrowser];
+ } else {
+ tabs = [];
+ }
+
+ var browser, URI, tabId;
+ var tabindex = tabs.length, tab;
+ while ( tabindex-- ) {
+ tab = tabs[tabindex];
+ browser = browserFromTarget(tab);
+ if ( browser === null ) {
+ continue;
+ }
+ URI = browser.currentURI;
+ // Close extension tabs
+ if ( URI.schemeIs('chrome') && URI.host === location.host ) {
+ vAPI.tabs._remove(tab, getTabBrowser(win));
+ }
+ tabId = browserToTabIdMap.get(browser);
+ if ( tabId !== undefined ) {
+ removeBrowserEntry(tabId, browser);
+ tabIdToBrowserMap.delete(tabId);
+ }
+ browserToTabIdMap.delete(browser);
+ }
+ };
+
+ // Initialize map with existing active tabs
+ var start = function() {
+ var tabBrowser, tabs, tab;
+ for ( var win of winWatcher.getWindows() ) {
+ onWindowLoad(win);
+ tabBrowser = getTabBrowser(win);
+ if ( tabBrowser === null ) {
+ continue;
+ }
+ for ( tab of tabBrowser.tabs ) {
+ if ( !tab.hasAttribute('pending') ) {
+ tabIdFromTarget(tab);
+ }
+ }
+ }
+
+ winWatcher.onOpenWindow = onWindowLoad;
+ winWatcher.onCloseWindow = onWindowUnload;
+
+ vAPI.messaging.globalMessageManager.addMessageListener(
+ locationChangedMessageName,
+ onLocationChanged
+ );
+ };
+
+ var stop = function() {
+ winWatcher.onOpenWindow = null;
+ winWatcher.onCloseWindow = null;
+
+ vAPI.messaging.globalMessageManager.removeMessageListener(
+ locationChangedMessageName,
+ onLocationChanged
+ );
+
+ for ( var win of winWatcher.getWindows() ) {
+ onWindowUnload(win);
+ }
+
+ browserToTabIdMap = new WeakMap();
+ tabIdToBrowserMap.clear();
+ };
+
+ cleanupTasks.push(stop);
+
+ return {
+ browsers: getAllBrowsers,
+ browserFromTabId: browserFromTabId,
+ browserFromTarget: browserFromTarget,
+ currentBrowser: currentBrowser,
+ indexFromTarget: indexFromTarget,
+ removeTarget: removeTarget,
+ start: start,
+ tabFromBrowser: tabFromBrowser,
+ tabIdFromTarget: tabIdFromTarget
+ };
+})();
+
+/******************************************************************************/
+
+vAPI.setIcon = function(tabId, iconId, badge) {
+ // If badge is undefined, then setIcon was called from the TabSelect event
+ var win;
+ if ( badge === undefined ) {
+ win = iconId;
+ } else {
+ win = winWatcher.getCurrentWindow();
+ }
+ var tabBrowser = getTabBrowser(win);
+ if ( tabBrowser === null ) {
+ return;
+ }
+ var curTabId = tabWatcher.tabIdFromTarget(tabBrowser.selectedTab);
+ var tb = vAPI.toolbarButton;
+
+ // from 'TabSelect' event
+ if ( tabId === undefined ) {
+ tabId = curTabId;
+ } else if ( badge !== undefined ) {
+ tb.tabs[tabId] = { badge: badge, img: iconId };
+ }
+
+ if ( tabId === curTabId ) {
+ tb.updateState(win, tabId);
+ }
+};
+
+/******************************************************************************/
+
+vAPI.messaging = {
+ get globalMessageManager() {
+ return Cc['@mozilla.org/globalmessagemanager;1']
+ .getService(Ci.nsIMessageListenerManager);
+ },
+ frameScript: vAPI.getURL('frameScript.js'),
+ listeners: {},
+ defaultHandler: null,
+ NOOPFUNC: function(){},
+ UNHANDLED: 'vAPI.messaging.notHandled'
+};
+
+/******************************************************************************/
+
+vAPI.messaging.listen = function(listenerName, callback) {
+ this.listeners[listenerName] = callback;
+};
+
+/******************************************************************************/
+
+vAPI.messaging.onMessage = function({target, data}) {
+ var messageManager = target.messageManager;
+
+ if ( !messageManager ) {
+ // Message came from a popup, and its message manager is not usable.
+ // So instead we broadcast to the parent window.
+ messageManager = getOwnerWindow(
+ target.webNavigation.QueryInterface(Ci.nsIDocShell).chromeEventHandler
+ ).messageManager;
+ }
+
+ var channelNameRaw = data.channelName;
+ var pos = channelNameRaw.indexOf('|');
+ var channelName = channelNameRaw.slice(pos + 1);
+
+ var callback = vAPI.messaging.NOOPFUNC;
+ if ( data.requestId !== undefined ) {
+ callback = CallbackWrapper.factory(
+ messageManager,
+ channelName,
+ channelNameRaw.slice(0, pos),
+ data.requestId
+ ).callback;
+ }
+
+ var sender = {
+ tab: {
+ id: tabWatcher.tabIdFromTarget(target)
+ }
+ };
+
+ // Specific handler
+ var r = vAPI.messaging.UNHANDLED;
+ var listener = vAPI.messaging.listeners[channelName];
+ if ( typeof listener === 'function' ) {
+ r = listener(data.msg, sender, callback);
+ }
+ if ( r !== vAPI.messaging.UNHANDLED ) {
+ return;
+ }
+
+ // Default handler
+ r = vAPI.messaging.defaultHandler(data.msg, sender, callback);
+ if ( r !== vAPI.messaging.UNHANDLED ) {
+ return;
+ }
+
+ console.error('uMatrix> messaging > unknown request: %o', data);
+
+ // Unhandled:
+ // Need to callback anyways in case caller expected an answer, or
+ // else there is a memory leak on caller's side
+ callback();
+};
+
+/******************************************************************************/
+
+vAPI.messaging.setup = function(defaultHandler) {
+ // Already setup?
+ if ( this.defaultHandler !== null ) {
+ return;
+ }
+
+ if ( typeof defaultHandler !== 'function' ) {
+ defaultHandler = function(){ return vAPI.messaging.UNHANDLED; };
+ }
+ this.defaultHandler = defaultHandler;
+
+ this.globalMessageManager.addMessageListener(
+ location.host + ':background',
+ this.onMessage
+ );
+
+ this.globalMessageManager.loadFrameScript(this.frameScript, true);
+
+ cleanupTasks.push(function() {
+ var gmm = vAPI.messaging.globalMessageManager;
+
+ gmm.removeDelayedFrameScript(vAPI.messaging.frameScript);
+ gmm.removeMessageListener(
+ location.host + ':background',
+ vAPI.messaging.onMessage
+ );
+ });
+};
+
+/******************************************************************************/
+
+vAPI.messaging.broadcast = function(message) {
+ this.globalMessageManager.broadcastAsyncMessage(
+ location.host + ':broadcast',
+ JSON.stringify({broadcast: true, msg: message})
+ );
+};
+
+/******************************************************************************/
+
+// This allows to avoid creating a closure for every single message which
+// expects an answer. Having a closure created each time a message is processed
+// has been always bothering me. Another benefit of the implementation here
+// is to reuse the callback proxy object, so less memory churning.
+//
+// https://developers.google.com/speed/articles/optimizing-javascript
+// "Creating a closure is significantly slower then creating an inner
+// function without a closure, and much slower than reusing a static
+// function"
+//
+// http://hacksoflife.blogspot.ca/2015/01/the-four-horsemen-of-performance.html
+// "the dreaded 'uniformly slow code' case where every function takes 1%
+// of CPU and you have to make one hundred separate performance optimizations
+// to improve performance at all"
+//
+// http://jsperf.com/closure-no-closure/2
+
+var CallbackWrapper = function(messageManager, channelName, listenerId, requestId) {
+ this.callback = this.proxy.bind(this); // bind once
+ this.init(messageManager, channelName, listenerId, requestId);
+};
+
+CallbackWrapper.junkyard = [];
+
+CallbackWrapper.factory = function(messageManager, channelName, listenerId, requestId) {
+ var wrapper = CallbackWrapper.junkyard.pop();
+ if ( wrapper ) {
+ wrapper.init(messageManager, channelName, listenerId, requestId);
+ return wrapper;
+ }
+ return new CallbackWrapper(messageManager, channelName, listenerId, requestId);
+};
+
+CallbackWrapper.prototype.init = function(messageManager, channelName, listenerId, requestId) {
+ this.messageManager = messageManager;
+ this.channelName = channelName;
+ this.listenerId = listenerId;
+ this.requestId = requestId;
+};
+
+CallbackWrapper.prototype.proxy = function(response) {
+ var message = JSON.stringify({
+ requestId: this.requestId,
+ channelName: this.channelName,
+ msg: response !== undefined ? response : null
+ });
+
+ if ( this.messageManager.sendAsyncMessage ) {
+ this.messageManager.sendAsyncMessage(this.listenerId, message);
+ } else {
+ this.messageManager.broadcastAsyncMessage(this.listenerId, message);
+ }
+
+ // Mark for reuse
+ this.messageManager =
+ this.channelName =
+ this.requestId =
+ this.listenerId = null;
+ CallbackWrapper.junkyard.push(this);
+};
+
+/******************************************************************************/
+
+var httpRequestHeadersFactory = function(channel) {
+ var entry = httpRequestHeadersFactory.junkyard.pop();
+ if ( entry ) {
+ return entry.init(channel);
+ }
+ return new HTTPRequestHeaders(channel);
+};
+
+httpRequestHeadersFactory.junkyard = [];
+
+var HTTPRequestHeaders = function(channel) {
+ this.init(channel);
+};
+
+HTTPRequestHeaders.prototype.init = function(channel) {
+ this.channel = channel;
+ return this;
+};
+
+HTTPRequestHeaders.prototype.dispose = function() {
+ this.channel = null;
+ httpRequestHeadersFactory.junkyard.push(this);
+};
+
+HTTPRequestHeaders.prototype.getHeader = function(name) {
+ try {
+ return this.channel.getRequestHeader(name);
+ } catch (e) {
+ }
+ return '';
+};
+
+HTTPRequestHeaders.prototype.setHeader = function(name, newValue, create) {
+ var oldValue = this.getHeader(name);
+ if ( newValue === oldValue ) {
+ return false;
+ }
+ if ( oldValue === '' && create !== true ) {
+ return false;
+ }
+ this.channel.setRequestHeader(name, newValue, false);
+ return true;
+};
+
+/******************************************************************************/
+
+var httpObserver = {
+ classDescription: 'net-channel-event-sinks for ' + location.host,
+ classID: Components.ID('{5d2e2797-6d68-42e2-8aeb-81ce6ba16b95}'),
+ contractID: '@' + location.host + '/net-channel-event-sinks;1',
+ REQDATAKEY: location.host + 'reqdata',
+ ABORT: Components.results.NS_BINDING_ABORTED,
+ ACCEPT: Components.results.NS_SUCCEEDED,
+ // Request types:
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentPolicy#Constants
+ frameTypeMap: {
+ 6: 'main_frame',
+ 7: 'sub_frame'
+ },
+ typeMap: {
+ 1: 'other',
+ 2: 'script',
+ 3: 'image',
+ 4: 'stylesheet',
+ 5: 'object',
+ 6: 'main_frame',
+ 7: 'sub_frame',
+ 9: 'xbl',
+ 10: 'ping',
+ 11: 'xmlhttprequest',
+ 12: 'object',
+ 13: 'xml_dtd',
+ 14: 'font',
+ 15: 'media',
+ 16: 'websocket',
+ 17: 'csp_report',
+ 18: 'xslt',
+ 19: 'beacon',
+ 20: 'xmlhttprequest',
+ 21: 'imageset',
+ 22: 'web_manifest'
+ },
+ mimeTypeMap: {
+ 'audio': 15,
+ 'video': 15
+ },
+
+ get componentRegistrar() {
+ return Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+ },
+
+ get categoryManager() {
+ return Cc['@mozilla.org/categorymanager;1']
+ .getService(Ci.nsICategoryManager);
+ },
+
+ QueryInterface: (function() {
+ var {XPCOMUtils} = Cu.import('resource://gre/modules/XPCOMUtils.jsm', null);
+
+ return XPCOMUtils.generateQI([
+ Ci.nsIFactory,
+ Ci.nsIObserver,
+ Ci.nsIChannelEventSink,
+ Ci.nsISupportsWeakReference
+ ]);
+ })(),
+
+ createInstance: function(outer, iid) {
+ if ( outer ) {
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ }
+
+ return this.QueryInterface(iid);
+ },
+
+ register: function() {
+ this.pendingRingBufferInit();
+
+ // https://developer.mozilla.org/en/docs/Observer_Notifications#HTTP_requests
+ Services.obs.addObserver(this, 'http-on-modify-request', true);
+ Services.obs.addObserver(this, 'http-on-examine-response', true);
+ Services.obs.addObserver(this, 'http-on-examine-cached-response', true);
+
+ // Guard against stale instances not having been unregistered
+ if ( this.componentRegistrar.isCIDRegistered(this.classID) ) {
+ try {
+ this.componentRegistrar.unregisterFactory(this.classID, Components.manager.getClassObject(this.classID, Ci.nsIFactory));
+ } catch (ex) {
+ console.error('uMatrix> httpObserver > unable to unregister stale instance: ', ex);
+ }
+ }
+
+ this.componentRegistrar.registerFactory(
+ this.classID,
+ this.classDescription,
+ this.contractID,
+ this
+ );
+ this.categoryManager.addCategoryEntry(
+ 'net-channel-event-sinks',
+ this.contractID,
+ this.contractID,
+ false,
+ true
+ );
+ },
+
+ unregister: function() {
+ Services.obs.removeObserver(this, 'http-on-modify-request');
+ Services.obs.removeObserver(this, 'http-on-examine-response');
+ Services.obs.removeObserver(this, 'http-on-examine-cached-response');
+
+ this.componentRegistrar.unregisterFactory(this.classID, this);
+ this.categoryManager.deleteCategoryEntry(
+ 'net-channel-event-sinks',
+ this.contractID,
+ false
+ );
+ },
+
+ PendingRequest: function() {
+ this.rawType = 0;
+ this.tabId = 0;
+ this._key = ''; // key is url, from URI.spec
+ },
+
+ // If all work fine, this map should not grow indefinitely. It can have
+ // stale items in it, but these will be taken care of when entries in
+ // the ring buffer are overwritten.
+ pendingURLToIndex: new Map(),
+ pendingWritePointer: 0,
+ pendingRingBuffer: new Array(256),
+ pendingRingBufferInit: function() {
+ // Use and reuse pre-allocated PendingRequest objects = less memory
+ // churning.
+ var i = this.pendingRingBuffer.length;
+ while ( i-- ) {
+ this.pendingRingBuffer[i] = new this.PendingRequest();
+ }
+ },
+
+ // Pending request ring buffer:
+ // +-------+-------+-------+-------+-------+-------+-------
+ // |0 |1 |2 |3 |4 |5 |...
+ // +-------+-------+-------+-------+-------+-------+-------
+ //
+ // URL to ring buffer index map:
+ // { k = URL, s = ring buffer indices }
+ //
+ // s is a string which character codes map to ring buffer indices -- for
+ // when the same URL is received multiple times by shouldLoadListener()
+ // before the existing one is serviced by the network request observer.
+ // I believe the use of a string in lieu of an array reduces memory
+ // churning.
+
+ createPendingRequest: function(url) {
+ var bucket;
+ var i = this.pendingWritePointer;
+ this.pendingWritePointer = i + 1 & 255;
+ var preq = this.pendingRingBuffer[i];
+ var si = String.fromCharCode(i);
+ // Cleanup unserviced pending request
+ if ( preq._key !== '' ) {
+ bucket = this.pendingURLToIndex.get(preq._key);
+ if ( bucket.length === 1 ) {
+ this.pendingURLToIndex.delete(preq._key);
+ } else {
+ var pos = bucket.indexOf(si);
+ this.pendingURLToIndex.set(preq._key, bucket.slice(0, pos) + bucket.slice(pos + 1));
+ }
+ }
+ bucket = this.pendingURLToIndex.get(url);
+ this.pendingURLToIndex.set(url, bucket === undefined ? si : bucket + si);
+ preq._key = url;
+ return preq;
+ },
+
+ lookupPendingRequest: function(url) {
+ var bucket = this.pendingURLToIndex.get(url);
+ if ( bucket === undefined ) {
+ return null;
+ }
+ var i = bucket.charCodeAt(0);
+ if ( bucket.length === 1 ) {
+ this.pendingURLToIndex.delete(url);
+ } else {
+ this.pendingURLToIndex.set(url, bucket.slice(1));
+ }
+ var preq = this.pendingRingBuffer[i];
+ preq._key = ''; // mark as "serviced"
+ return preq;
+ },
+
+ handleRequest: function(channel, URI, tabId, rawType) {
+ var type = this.typeMap[rawType] || 'other';
+
+ var onBeforeRequest = vAPI.net.onBeforeRequest;
+ if ( onBeforeRequest.types === null || onBeforeRequest.types.has(type) ) {
+ var result = onBeforeRequest.callback({
+ parentFrameId: type === 'main_frame' ? -1 : 0,
+ tabId: tabId,
+ type: type,
+ url: URI.asciiSpec
+ });
+ if ( typeof result === 'object' ) {
+ channel.cancel(this.ABORT);
+ return true;
+ }
+ }
+
+ var onBeforeSendHeaders = vAPI.net.onBeforeSendHeaders;
+ if ( onBeforeSendHeaders.types === null || onBeforeSendHeaders.types.has(type) ) {
+ var requestHeaders = httpRequestHeadersFactory(channel);
+ onBeforeSendHeaders.callback({
+ parentFrameId: type === 'main_frame' ? -1 : 0,
+ requestHeaders: requestHeaders,
+ tabId: tabId,
+ type: type,
+ url: URI.asciiSpec
+ });
+ requestHeaders.dispose();
+ }
+
+ return false;
+ },
+
+ channelDataFromChannel: function(channel) {
+ if ( channel instanceof Ci.nsIWritablePropertyBag ) {
+ try {
+ return channel.getProperty(this.REQDATAKEY) || null;
+ } catch (ex) {
+ }
+ }
+ return null;
+ },
+
+ // https://github.com/gorhill/uMatrix/issues/165
+ // https://developer.mozilla.org/en-US/Firefox/Releases/3.5/Updating_extensions#Getting_a_load_context_from_a_request
+ // Not sure `umatrix:shouldLoad` is still needed, uMatrix does not
+ // care about embedded frames topography.
+ // Also:
+ // https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Limitations_of_chrome_scripts
+ tabIdFromChannel: function(channel) {
+ var lc;
+ try {
+ lc = channel.notificationCallbacks.getInterface(Ci.nsILoadContext);
+ } catch(ex) {
+ }
+ if ( !lc ) {
+ try {
+ lc = channel.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
+ } catch(ex) {
+ }
+ if ( !lc ) {
+ return vAPI.noTabId;
+ }
+ }
+ if ( lc.topFrameElement ) {
+ return tabWatcher.tabIdFromTarget(lc.topFrameElement);
+ }
+ var win;
+ try {
+ win = lc.associatedWindow;
+ } catch (ex) { }
+ if ( !win ) {
+ return vAPI.noTabId;
+ }
+ if ( win.top ) {
+ win = win.top;
+ }
+ var tabBrowser;
+ try {
+ tabBrowser = getTabBrowser(
+ win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell).rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow)
+ );
+ } catch (ex) { }
+ if ( !tabBrowser ) {
+ return vAPI.noTabId;
+ }
+ if ( tabBrowser.getBrowserForContentWindow ) {
+ return tabWatcher.tabIdFromTarget(tabBrowser.getBrowserForContentWindow(win));
+ }
+ // Falling back onto _getTabForContentWindow to ensure older versions
+ // of Firefox work well.
+ return tabBrowser._getTabForContentWindow ?
+ tabWatcher.tabIdFromTarget(tabBrowser._getTabForContentWindow(win)) :
+ vAPI.noTabId;
+ },
+
+ rawtypeFromContentType: function(channel) {
+ var mime = channel.contentType;
+ if ( !mime ) {
+ return 0;
+ }
+ var pos = mime.indexOf('/');
+ if ( pos === -1 ) {
+ pos = mime.length;
+ }
+ return this.mimeTypeMap[mime.slice(0, pos)] || 0;
+ },
+
+ observe: function(channel, topic) {
+ if ( channel instanceof Ci.nsIHttpChannel === false ) {
+ return;
+ }
+
+ var URI = channel.URI;
+ var channelData = this.channelDataFromChannel(channel);
+
+ if ( topic.lastIndexOf('http-on-examine-', 0) === 0 ) {
+ if ( channelData === null ) {
+ return;
+ }
+
+ var type = this.frameTypeMap[channelData[1]];
+ if ( !type ) {
+ return;
+ }
+
+ topic = 'Content-Security-Policy';
+
+ var result;
+ try {
+ result = channel.getResponseHeader(topic);
+ } catch (ex) {
+ result = null;
+ }
+
+ result = vAPI.net.onHeadersReceived.callback({
+ parentFrameId: type === 'main_frame' ? -1 : 0,
+ responseHeaders: result ? [{name: topic, value: result}] : [],
+ tabId: channelData[0],
+ type: type,
+ url: URI.asciiSpec
+ });
+
+ if ( result ) {
+ channel.setResponseHeader(
+ topic,
+ result.responseHeaders.pop().value,
+ true
+ );
+ }
+
+ return;
+ }
+
+ // http-on-modify-request
+
+ // The channel was previously serviced.
+ if ( channelData !== null ) {
+ this.handleRequest(channel, URI, channelData[0], channelData[1]);
+ return;
+ }
+
+ // The channel was never serviced.
+ var tabId;
+ var pendingRequest = this.lookupPendingRequest(URI.asciiSpec);
+ var rawType = 1;
+ var loadInfo = channel.loadInfo;
+
+ // https://github.com/gorhill/uMatrix/issues/390#issuecomment-155717004
+ if ( loadInfo ) {
+ rawType = loadInfo.externalContentPolicyType !== undefined ?
+ loadInfo.externalContentPolicyType :
+ loadInfo.contentPolicyType;
+ if ( !rawType ) {
+ rawType = 1;
+ }
+ }
+
+ if ( pendingRequest !== null ) {
+ tabId = pendingRequest.tabId;
+ // https://github.com/gorhill/uBlock/issues/654
+ // Use the request type from the HTTP observer point of view.
+ if ( rawType !== 1 ) {
+ pendingRequest.rawType = rawType;
+ } else {
+ rawType = pendingRequest.rawType;
+ }
+ } else {
+ tabId = this.tabIdFromChannel(channel);
+ }
+
+ if ( this.handleRequest(channel, URI, tabId, rawType) ) {
+ return;
+ }
+
+ if ( channel instanceof Ci.nsIWritablePropertyBag === false ) {
+ return;
+ }
+
+ // Carry data for behind-the-scene redirects
+ channel.setProperty(this.REQDATAKEY, [tabId, rawType]);
+ },
+
+ // contentPolicy.shouldLoad doesn't detect redirects, this needs to be used
+ asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
+ // If error thrown, the redirect will fail
+ try {
+ var URI = newChannel.URI;
+ if ( !URI.schemeIs('http') && !URI.schemeIs('https') ) {
+ return;
+ }
+
+ if ( newChannel instanceof Ci.nsIWritablePropertyBag === false ) {
+ return;
+ }
+
+ var channelData = this.channelDataFromChannel(oldChannel);
+ if ( channelData === null ) {
+ return;
+ }
+
+ // Carry the data on in case of multiple redirects
+ newChannel.setProperty(this.REQDATAKEY, channelData);
+ } catch (ex) {
+ // console.error(ex);
+ } finally {
+ callback.onRedirectVerifyCallback(this.ACCEPT);
+ }
+ }
+};
+
+/******************************************************************************/
+
+vAPI.net = {};
+
+/******************************************************************************/
+
+vAPI.net.registerListeners = function() {
+ this.onBeforeRequest.types = this.onBeforeRequest.types ?
+ new Set(this.onBeforeRequest.types) :
+ null;
+ this.onBeforeSendHeaders.types = this.onBeforeSendHeaders.types ?
+ new Set(this.onBeforeSendHeaders.types) :
+ null;
+
+ var shouldLoadListenerMessageName = location.host + ':shouldLoad';
+ var shouldLoadListener = function(e) {
+ var details = e.data;
+ var pendingReq = httpObserver.createPendingRequest(details.url);
+ pendingReq.rawType = details.rawType;
+ pendingReq.tabId = tabWatcher.tabIdFromTarget(e.target);
+ };
+
+ // https://github.com/gorhill/uMatrix/issues/200
+ // We need this only for Firefox 34 and less: the tab id is derived from
+ // the origin of the message.
+ if ( !vAPI.modernFirefox ) {
+ vAPI.messaging.globalMessageManager.addMessageListener(
+ shouldLoadListenerMessageName,
+ shouldLoadListener
+ );
+ }
+
+ httpObserver.register();
+
+ cleanupTasks.push(function() {
+ if ( !vAPI.modernFirefox ) {
+ vAPI.messaging.globalMessageManager.removeMessageListener(
+ shouldLoadListenerMessageName,
+ shouldLoadListener
+ );
+ }
+
+ httpObserver.unregister();
+ });
+};
+
+/******************************************************************************/
+/******************************************************************************/
+
+vAPI.toolbarButton = {
+ id: location.host + '-button',
+ type: 'view',
+ viewId: location.host + '-panel',
+ label: vAPI.app.name,
+ tooltiptext: vAPI.app.name,
+ tabs: {/*tabId: {badge: 0, img: boolean}*/},
+ init: null,
+ codePath: ''
+};
+
+/******************************************************************************/
+
+// Non-Fennec: common code paths.
+
+(function() {
+ if ( vAPI.fennec ) {
+ return;
+ }
+
+ var tbb = vAPI.toolbarButton;
+ var popupCommittedWidth = 0;
+ var popupCommittedHeight = 0;
+
+ tbb.onViewShowing = function({target}) {
+ popupCommittedWidth = popupCommittedHeight = 0;
+ target.firstChild.setAttribute('src', vAPI.getURL('popup.html'));
+ };
+
+ tbb.onViewHiding = function({target}) {
+ target.parentNode.style.maxWidth = '';
+ target.firstChild.setAttribute('src', 'about:blank');
+ };
+
+ tbb.updateState = function(win, tabId) {
+ var button = win.document.getElementById(this.id);
+
+ if ( !button ) {
+ return;
+ }
+
+ var icon = this.tabs[tabId];
+ button.setAttribute('badge', icon && icon.badge || '');
+ button.classList.toggle('off', !icon || !icon.img);
+
+ var iconId = icon && icon.img ? icon.img : 'off';
+ icon = 'url(' + vAPI.getURL('img/browsericons/icon19-' + iconId + '.png') + ')';
+ button.style.listStyleImage = icon;
+ };
+
+ tbb.populatePanel = function(doc, panel) {
+ panel.setAttribute('id', this.viewId);
+
+ var iframe = doc.createElement('iframe');
+ iframe.setAttribute('type', 'content');
+
+ panel.appendChild(iframe);
+
+ var toPx = function(pixels) {
+ return pixels.toString() + 'px';
+ };
+
+ var scrollBarWidth = 0;
+ var resizeTimer = null;
+
+ var resizePopupDelayed = function(attempts) {
+ if ( resizeTimer !== null ) {
+ return false;
+ }
+
+ // Sanity check
+ attempts = (attempts || 0) + 1;
+ if ( attempts > 1/*000*/ ) {
+ //console.error('uMatrix> resizePopupDelayed: giving up after too many attempts');
+ return false;
+ }
+
+ resizeTimer = vAPI.setTimeout(resizePopup, 10, attempts);
+ return true;
+ };
+
+ var resizePopup = function(attempts) {
+ resizeTimer = null;
+
+ panel.parentNode.style.maxWidth = 'none';
+ var body = iframe.contentDocument.body;
+
+ // https://github.com/gorhill/uMatrix/issues/301
+ // Don't resize if committed size did not change.
+ if (
+ popupCommittedWidth === body.clientWidth &&
+ popupCommittedHeight === body.clientHeight
+ ) {
+ return;
+ }
+
+ // We set a limit for height
+ var height = Math.min(body.clientHeight, 600);
+
+ // https://github.com/chrisaljoudi/uBlock/issues/730
+ // Voodoo programming: this recipe works
+ panel.style.setProperty('height', toPx(height));
+ iframe.style.setProperty('height', toPx(height));
+
+ // Adjust width for presence/absence of vertical scroll bar which may
+ // have appeared as a result of last operation.
+ var contentWindow = iframe.contentWindow;
+ var width = body.clientWidth;
+ if ( contentWindow.scrollMaxY !== 0 ) {
+ width += scrollBarWidth;
+ }
+ panel.style.setProperty('width', toPx(width));
+
+ // scrollMaxX should always be zero once we know the scrollbar width
+ if ( contentWindow.scrollMaxX !== 0 ) {
+ scrollBarWidth = contentWindow.scrollMaxX;
+ width += scrollBarWidth;
+ panel.style.setProperty('width', toPx(width));
+ }
+
+ if ( iframe.clientHeight !== height || panel.clientWidth !== width ) {
+ if ( resizePopupDelayed(attempts) ) {
+ return;
+ }
+ // resizePopupDelayed won't be called again, so commit
+ // dimentsions.
+ }
+
+ popupCommittedWidth = body.clientWidth;
+ popupCommittedHeight = body.clientHeight;
+ };
+
+ var onResizeRequested = function() {
+ var body = iframe.contentDocument.body;
+ if ( body.getAttribute('data-resize-popup') !== 'true' ) {
+ return;
+ }
+ body.removeAttribute('data-resize-popup');
+ resizePopupDelayed();
+ };
+
+ var onPopupReady = function() {
+ var win = this.contentWindow;
+
+ if ( !win || win.location.host !== location.host ) {
+ return;
+ }
+
+ if ( typeof tbb.onBeforePopupReady === 'function' ) {
+ tbb.onBeforePopupReady.call(this);
+ }
+
+ resizePopupDelayed();
+
+ var body = win.document.body;
+ body.removeAttribute('data-resize-popup');
+ var mutationObserver = new win.MutationObserver(onResizeRequested);
+ mutationObserver.observe(body, {
+ attributes: true,
+ attributeFilter: [ 'data-resize-popup' ]
+ });
+ };
+
+ iframe.addEventListener('load', onPopupReady, true);
+ };
+})();
+
+/******************************************************************************/
+
+// Firefox 28 and less
+
+(function() {
+ var tbb = vAPI.toolbarButton;
+ if ( tbb.init !== null ) {
+ return;
+ }
+ var CustomizableUI = null;
+ var forceLegacyToolbarButton = vAPI.localStorage.getBool('forceLegacyToolbarButton');
+ if ( !forceLegacyToolbarButton ) {
+ try {
+ CustomizableUI = Cu.import('resource:///modules/CustomizableUI.jsm', null).CustomizableUI;
+ } catch (ex) {
+ }
+ }
+ if ( CustomizableUI !== null ) {
+ return;
+ }
+
+ tbb.codePath = 'legacy';
+ tbb.id = 'umatrix-legacy-button'; // NOTE: must match legacy-toolbar-button.css
+ tbb.viewId = tbb.id + '-panel';
+
+ var styleSheetUri = null;
+
+ var createToolbarButton = function(window) {
+ var document = window.document;
+
+ var toolbarButton = document.createElement('toolbarbutton');
+ toolbarButton.setAttribute('id', tbb.id);
+ // type = panel would be more accurate, but doesn't look as good
+ toolbarButton.setAttribute('type', 'menu');
+ toolbarButton.setAttribute('removable', 'true');
+ toolbarButton.setAttribute('class', 'toolbarbutton-1 chromeclass-toolbar-additional');
+ toolbarButton.setAttribute('label', tbb.label);
+ toolbarButton.setAttribute('tooltiptext', tbb.label);
+
+ var toolbarButtonPanel = document.createElement('panel');
+ // NOTE: Setting level to parent breaks the popup for PaleMoon under
+ // linux (mouse pointer misaligned with content). For some reason.
+ // toolbarButtonPanel.setAttribute('level', 'parent');
+ tbb.populatePanel(document, toolbarButtonPanel);
+ toolbarButtonPanel.addEventListener('popupshowing', tbb.onViewShowing);
+ toolbarButtonPanel.addEventListener('popuphiding', tbb.onViewHiding);
+ toolbarButton.appendChild(toolbarButtonPanel);
+
+ return toolbarButton;
+ };
+
+ var addLegacyToolbarButton = function(window) {
+ // uMatrix's stylesheet lazily added.
+ if ( styleSheetUri === null ) {
+ var sss = Cc["@mozilla.org/content/style-sheet-service;1"]
+ .getService(Ci.nsIStyleSheetService);
+ styleSheetUri = Services.io.newURI(vAPI.getURL("css/legacy-toolbar-button.css"), null, null);
+
+ // Register global so it works in all windows, including palette
+ if ( !sss.sheetRegistered(styleSheetUri, sss.AUTHOR_SHEET) ) {
+ sss.loadAndRegisterSheet(styleSheetUri, sss.AUTHOR_SHEET);
+ }
+ }
+
+ var document = window.document;
+
+ // https://github.com/gorhill/uMatrix/issues/357
+ // Already installed?
+ if ( document.getElementById(tbb.id) !== null ) {
+ return;
+ }
+
+ var toolbox = document.getElementById('navigator-toolbox') ||
+ document.getElementById('mail-toolbox');
+ if ( toolbox === null ) {
+ return;
+ }
+
+ var toolbarButton = createToolbarButton(window);
+
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/toolbarpalette
+ var palette = toolbox.palette;
+ if ( palette && palette.querySelector('#' + tbb.id) === null ) {
+ palette.appendChild(toolbarButton);
+ }
+
+ // Find the place to put the button.
+ // Pale Moon: `toolbox.externalToolbars` can be undefined. Seen while
+ // testing popup test number 3:
+ // http://raymondhill.net/ublock/popup.html
+ var toolbars = toolbox.externalToolbars ? toolbox.externalToolbars.slice() : [];
+ for ( var child of toolbox.children ) {
+ if ( child.localName === 'toolbar' ) {
+ toolbars.push(child);
+ }
+ }
+
+ for ( var toolbar of toolbars ) {
+ var currentsetString = toolbar.getAttribute('currentset');
+ if ( !currentsetString ) {
+ continue;
+ }
+ var currentset = currentsetString.split(/\s*,\s*/);
+ var index = currentset.indexOf(tbb.id);
+ if ( index === -1 ) {
+ continue;
+ }
+ // This can occur with Pale Moon:
+ // "TypeError: toolbar.insertItem is not a function"
+ if ( typeof toolbar.insertItem !== 'function' ) {
+ continue;
+ }
+ // Found our button on this toolbar - but where on it?
+ var before = null;
+ for ( var i = index + 1; i < currentset.length; i++ ) {
+ before = toolbar.querySelector('[id="' + currentset[i] + '"]');
+ if ( before !== null ) {
+ break;
+ }
+ }
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Method/insertItem
+ toolbar.insertItem(tbb.id, before);
+ break;
+ }
+
+ // https://github.com/gorhill/uBlock/issues/763
+ // We are done if our toolbar button is already installed in one of the
+ // toolbar.
+ if ( palette !== null && toolbarButton.parentElement !== palette ) {
+ return;
+ }
+
+ // No button yet so give it a default location. If forcing the button,
+ // just put in in the palette rather than on any specific toolbar (who
+ // knows what toolbars will be available or visible!)
+ var navbar = document.getElementById('nav-bar');
+ if ( navbar !== null && !vAPI.localStorage.getBool('legacyToolbarButtonAdded') ) {
+ // https://github.com/gorhill/uBlock/issues/264
+ // Find a child customizable palette, if any.
+ navbar = navbar.querySelector('.customization-target') || navbar;
+ navbar.appendChild(toolbarButton);
+ navbar.setAttribute('currentset', navbar.currentSet);
+ document.persist(navbar.id, 'currentset');
+ vAPI.localStorage.setBool('legacyToolbarButtonAdded', 'true');
+ }
+ };
+
+ var canAddLegacyToolbarButton = function(window) {
+ var document = window.document;
+ if (
+ !document ||
+ document.readyState !== 'complete' ||
+ document.getElementById('nav-bar') === null
+ ) {
+ return false;
+ }
+ var toolbox = document.getElementById('navigator-toolbox') ||
+ document.getElementById('mail-toolbox');
+ return toolbox !== null && !!toolbox.palette;
+ };
+
+ var onPopupCloseRequested = function({target}) {
+ var document = target.ownerDocument;
+ if ( !document ) {
+ return;
+ }
+ var toolbarButtonPanel = document.getElementById(tbb.viewId);
+ if ( toolbarButtonPanel === null ) {
+ return;
+ }
+ // `hidePopup` reported as not existing while testing legacy button
+ // on FF 41.0.2.
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1151796
+ if ( typeof toolbarButtonPanel.hidePopup === 'function' ) {
+ toolbarButtonPanel.hidePopup();
+ }
+ };
+
+ var shutdown = function() {
+ for ( var win of winWatcher.getWindows() ) {
+ var toolbarButton = win.document.getElementById(tbb.id);
+ if ( toolbarButton ) {
+ toolbarButton.parentNode.removeChild(toolbarButton);
+ }
+ }
+
+ if ( styleSheetUri !== null ) {
+ var sss = Cc["@mozilla.org/content/style-sheet-service;1"]
+ .getService(Ci.nsIStyleSheetService);
+ if ( sss.sheetRegistered(styleSheetUri, sss.AUTHOR_SHEET) ) {
+ sss.unregisterSheet(styleSheetUri, sss.AUTHOR_SHEET);
+ }
+ styleSheetUri = null;
+ }
+
+ vAPI.messaging.globalMessageManager.removeMessageListener(
+ location.host + ':closePopup',
+ onPopupCloseRequested
+ );
+ };
+
+ tbb.attachToNewWindow = function(win) {
+ deferUntil(
+ canAddLegacyToolbarButton.bind(null, win),
+ addLegacyToolbarButton.bind(null, win)
+ );
+ };
+
+ tbb.init = function() {
+ vAPI.messaging.globalMessageManager.addMessageListener(
+ location.host + ':closePopup',
+ onPopupCloseRequested
+ );
+
+ cleanupTasks.push(shutdown);
+ };
+})();
+
+/******************************************************************************/
+
+// Firefox Australis < 36.
+
+(function() {
+ var tbb = vAPI.toolbarButton;
+ if ( tbb.init !== null ) {
+ return;
+ }
+ if ( Services.vc.compare(Services.appinfo.platformVersion, '36.0') >= 0 ) {
+ return null;
+ }
+ if ( vAPI.localStorage.getBool('forceLegacyToolbarButton') ) {
+ return null;
+ }
+ var CustomizableUI = null;
+ try {
+ CustomizableUI = Cu.import('resource:///modules/CustomizableUI.jsm', null).CustomizableUI;
+ } catch (ex) {
+ }
+ if ( CustomizableUI === null ) {
+ return;
+ }
+ tbb.codePath = 'australis';
+ tbb.CustomizableUI = CustomizableUI;
+ tbb.defaultArea = CustomizableUI.AREA_NAVBAR;
+
+ var styleURI = null;
+
+ var onPopupCloseRequested = function({target}) {
+ if ( typeof tbb.closePopup === 'function' ) {
+ tbb.closePopup(target);
+ }
+ };
+
+ var shutdown = function() {
+ CustomizableUI.destroyWidget(tbb.id);
+
+ for ( var win of winWatcher.getWindows() ) {
+ var panel = win.document.getElementById(tbb.viewId);
+ panel.parentNode.removeChild(panel);
+ win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .removeSheet(styleURI, 1);
+ }
+
+ vAPI.messaging.globalMessageManager.removeMessageListener(
+ location.host + ':closePopup',
+ onPopupCloseRequested
+ );
+ };
+
+ tbb.onBeforeCreated = function(doc) {
+ var panel = doc.createElement('panelview');
+
+ this.populatePanel(doc, panel);
+
+ doc.getElementById('PanelUI-multiView').appendChild(panel);
+
+ doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .loadSheet(styleURI, 1);
+ };
+
+ tbb.onBeforePopupReady = function() {
+ // https://github.com/gorhill/uBlock/issues/83
+ // Add `portrait` class if width is constrained.
+ try {
+ this.contentDocument.body.classList.toggle(
+ 'portrait',
+ CustomizableUI.getWidget(tbb.id).areaType === CustomizableUI.TYPE_MENU_PANEL
+ );
+ } catch (ex) {
+ /* noop */
+ }
+ };
+
+ tbb.init = function() {
+ vAPI.messaging.globalMessageManager.addMessageListener(
+ location.host + ':closePopup',
+ onPopupCloseRequested
+ );
+
+ var style = [
+ '#' + this.id + '.off {',
+ 'list-style-image: url(',
+ vAPI.getURL('img/browsericons/icon19-off.png'),
+ ');',
+ '}',
+ '#' + this.id + ' {',
+ 'list-style-image: url(',
+ vAPI.getURL('img/browsericons/icon19.png'),
+ ');',
+ '}',
+ '#' + this.viewId + ', #' + this.viewId + ' > iframe {',
+ 'width: 160px;',
+ 'height: 290px;',
+ 'overflow: hidden !important;',
+ '}',
+ '#' + this.id + '[badge]:not([badge=""])::after {',
+ 'position: absolute;',
+ 'margin-left: -16px;',
+ 'margin-top: 3px;',
+ 'padding: 1px 2px;',
+ 'font-size: 9px;',
+ 'font-weight: bold;',
+ 'color: #fff;',
+ 'background: #000;',
+ 'content: attr(badge);',
+ '}'
+ ];
+
+ styleURI = Services.io.newURI(
+ 'data:text/css,' + encodeURIComponent(style.join('')),
+ null,
+ null
+ );
+
+ this.closePopup = function(tabBrowser) {
+ CustomizableUI.hidePanelForNode(
+ tabBrowser.ownerDocument.getElementById(this.viewId)
+ );
+ };
+
+ CustomizableUI.createWidget(this);
+
+ cleanupTasks.push(shutdown);
+ };
+})();
+
+/******************************************************************************/
+
+// Firefox Australis >= 36.
+
+(function() {
+ var tbb = vAPI.toolbarButton;
+ if ( tbb.init !== null ) {
+ return;
+ }
+ if ( Services.vc.compare(Services.appinfo.platformVersion, '36.0') < 0 ) {
+ return null;
+ }
+ if ( vAPI.localStorage.getBool('forceLegacyToolbarButton') ) {
+ return null;
+ }
+ var CustomizableUI = null;
+ try {
+ CustomizableUI = Cu.import('resource:///modules/CustomizableUI.jsm', null).CustomizableUI;
+ } catch (ex) {
+ }
+ if ( CustomizableUI === null ) {
+ return null;
+ }
+ tbb.codePath = 'australis';
+ tbb.CustomizableUI = CustomizableUI;
+ tbb.defaultArea = CustomizableUI.AREA_NAVBAR;
+
+ var CUIEvents = {};
+
+ var badgeCSSRules = [
+ 'background: #000',
+ 'color: #fff'
+ ].join(';');
+
+ var updateBadgeStyle = function() {
+ for ( var win of winWatcher.getWindows() ) {
+ var button = win.document.getElementById(tbb.id);
+ if ( button === null ) {
+ continue;
+ }
+ var badge = button.ownerDocument.getAnonymousElementByAttribute(
+ button,
+ 'class',
+ 'toolbarbutton-badge'
+ );
+ if ( !badge ) {
+ continue;
+ }
+
+ badge.style.cssText = badgeCSSRules;
+ }
+ };
+
+ var updateBadge = function() {
+ var wId = tbb.id;
+ var buttonInPanel = CustomizableUI.getWidget(wId).areaType === CustomizableUI.TYPE_MENU_PANEL;
+
+ for ( var win of winWatcher.getWindows() ) {
+ var button = win.document.getElementById(wId);
+ if ( button === null ) {
+ continue;
+ }
+ if ( buttonInPanel ) {
+ button.classList.remove('badged-button');
+ continue;
+ }
+ button.classList.add('badged-button');
+ }
+
+ if ( buttonInPanel ) {
+ return;
+ }
+
+ // Anonymous elements need some time to be reachable
+ vAPI.setTimeout(updateBadgeStyle, 250);
+ }.bind(CUIEvents);
+
+ // https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/CustomizableUI.jsm#Listeners
+ CUIEvents.onCustomizeEnd = updateBadge;
+ CUIEvents.onWidgetAdded = updateBadge;
+ CUIEvents.onWidgetUnderflow = updateBadge;
+
+ var onPopupCloseRequested = function({target}) {
+ if ( typeof tbb.closePopup === 'function' ) {
+ tbb.closePopup(target);
+ }
+ };
+
+ var shutdown = function() {
+ for ( var win of winWatcher.getWindows() ) {
+ var panel = win.document.getElementById(tbb.viewId);
+ if ( panel !== null && panel.parentNode !== null ) {
+ panel.parentNode.removeChild(panel);
+ }
+ win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .removeSheet(styleURI, 1);
+ }
+
+ CustomizableUI.removeListener(CUIEvents);
+ CustomizableUI.destroyWidget(tbb.id);
+
+ vAPI.messaging.globalMessageManager.removeMessageListener(
+ location.host + ':closePopup',
+ onPopupCloseRequested
+ );
+ };
+
+ var styleURI = null;
+
+ tbb.onBeforeCreated = function(doc) {
+ var panel = doc.createElement('panelview');
+
+ this.populatePanel(doc, panel);
+
+ doc.getElementById('PanelUI-multiView').appendChild(panel);
+
+ doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .loadSheet(styleURI, 1);
+ };
+
+ tbb.onCreated = function(button) {
+ button.setAttribute('badge', '');
+ vAPI.setTimeout(updateBadge, 250);
+ };
+
+ tbb.onBeforePopupReady = function() {
+ // https://github.com/gorhill/uBlock/issues/83
+ // Add `portrait` class if width is constrained.
+ try {
+ this.contentDocument.body.classList.toggle(
+ 'portrait',
+ CustomizableUI.getWidget(tbb.id).areaType === CustomizableUI.TYPE_MENU_PANEL
+ );
+ } catch (ex) {
+ /* noop */
+ }
+ };
+
+ tbb.closePopup = function(tabBrowser) {
+ CustomizableUI.hidePanelForNode(
+ tabBrowser.ownerDocument.getElementById(tbb.viewId)
+ );
+ };
+
+ tbb.init = function() {
+ vAPI.messaging.globalMessageManager.addMessageListener(
+ location.host + ':closePopup',
+ onPopupCloseRequested
+ );
+
+ CustomizableUI.addListener(CUIEvents);
+
+ var style = [
+ '#' + this.id + '.off {',
+ 'list-style-image: url(',
+ vAPI.getURL('img/browsericons/icon19-off.png'),
+ ');',
+ '}',
+ '#' + this.id + ' {',
+ 'list-style-image: url(',
+ vAPI.getURL('img/browsericons/icon19-19.png'),
+ ');',
+ '}',
+ '#' + this.viewId + ', #' + this.viewId + ' > iframe {',
+ 'height: 290px;',
+ 'max-width: none !important;',
+ 'min-width: 0 !important;',
+ 'overflow: hidden !important;',
+ 'padding: 0 !important;',
+ 'width: 160px;',
+ '}'
+ ];
+
+ styleURI = Services.io.newURI(
+ 'data:text/css,' + encodeURIComponent(style.join('')),
+ null,
+ null
+ );
+
+ CustomizableUI.createWidget(this);
+
+ cleanupTasks.push(shutdown);
+ };
+})();
+
+/******************************************************************************/
+
+// No toolbar button.
+
+(function() {
+ // Just to ensure the number of cleanup tasks is as expected: toolbar
+ // button code is one single cleanup task regardless of platform.
+ if ( vAPI.toolbarButton.init === null ) {
+ cleanupTasks.push(function(){});
+ }
+})();
+
+/******************************************************************************/
+
+if ( vAPI.toolbarButton.init !== null ) {
+ vAPI.toolbarButton.init();
+}
+
+/******************************************************************************/
+/******************************************************************************/
+
+vAPI.contextMenu = {
+ contextMap: {
+ frame: 'inFrame',
+ link: 'onLink',
+ image: 'onImage',
+ audio: 'onAudio',
+ video: 'onVideo',
+ editable: 'onEditableArea'
+ }
+};
+
+/******************************************************************************/
+
+vAPI.contextMenu.displayMenuItem = function({target}) {
+ var doc = target.ownerDocument;
+ var gContextMenu = doc.defaultView.gContextMenu;
+ if ( !gContextMenu.browser ) {
+ return;
+ }
+
+ var menuitem = doc.getElementById(vAPI.contextMenu.menuItemId);
+ var currentURI = gContextMenu.browser.currentURI;
+
+ // https://github.com/chrisaljoudi/uBlock/issues/105
+ // TODO: Should the element picker works on any kind of pages?
+ if ( !currentURI.schemeIs('http') && !currentURI.schemeIs('https') ) {
+ menuitem.setAttribute('hidden', true);
+ return;
+ }
+
+ var ctx = vAPI.contextMenu.contexts;
+
+ if ( !ctx ) {
+ menuitem.setAttribute('hidden', false);
+ return;
+ }
+
+ var ctxMap = vAPI.contextMenu.contextMap;
+
+ for ( var context of ctx ) {
+ if (
+ context === 'page' &&
+ !gContextMenu.onLink &&
+ !gContextMenu.onImage &&
+ !gContextMenu.onEditableArea &&
+ !gContextMenu.inFrame &&
+ !gContextMenu.onVideo &&
+ !gContextMenu.onAudio
+ ) {
+ menuitem.setAttribute('hidden', false);
+ return;
+ }
+
+ if (
+ ctxMap.hasOwnProperty(context) &&
+ gContextMenu[ctxMap[context]]
+ ) {
+ menuitem.setAttribute('hidden', false);
+ return;
+ }
+ }
+
+ menuitem.setAttribute('hidden', true);
+};
+
+/******************************************************************************/
+
+vAPI.contextMenu.register = (function() {
+ var register = function(doc) {
+ if ( !this.menuItemId ) {
+ return;
+ }
+
+ // Already installed?
+ if ( doc.getElementById(this.menuItemId) !== null ) {
+ return;
+ }
+
+ var contextMenu = doc.getElementById('contentAreaContextMenu');
+ var menuitem = doc.createElement('menuitem');
+ menuitem.setAttribute('id', this.menuItemId);
+ menuitem.setAttribute('label', this.menuLabel);
+ menuitem.setAttribute('image', vAPI.getURL('img/browsericons/icon19-19.png'));
+ menuitem.setAttribute('class', 'menuitem-iconic');
+ menuitem.addEventListener('command', this.onCommand);
+ contextMenu.addEventListener('popupshowing', this.displayMenuItem);
+ contextMenu.insertBefore(menuitem, doc.getElementById('inspect-separator'));
+ };
+
+ // https://github.com/gorhill/uBlock/issues/906
+ // Be sure document.readyState is 'complete': it could happen at launch
+ // time that we are called by vAPI.contextMenu.create() directly before
+ // the environment is properly initialized.
+ var registerSafely = function(doc, tryCount) {
+ if ( doc.readyState === 'complete' ) {
+ register.call(this, doc);
+ return;
+ }
+ if ( typeof tryCount !== 'number' ) {
+ tryCount = 0;
+ }
+ tryCount += 1;
+ if ( tryCount < 8 ) {
+ vAPI.setTimeout(registerSafely.bind(this, doc, tryCount), 200);
+ }
+ };
+
+ return registerSafely;
+})();
+
+/******************************************************************************/
+
+vAPI.contextMenu.unregister = function(doc) {
+ if ( !this.menuItemId ) {
+ return;
+ }
+
+ var menuitem = doc.getElementById(this.menuItemId);
+ if ( menuitem === null ) {
+ return;
+ }
+ var contextMenu = menuitem.parentNode;
+ menuitem.removeEventListener('command', this.onCommand);
+ contextMenu.removeEventListener('popupshowing', this.displayMenuItem);
+ contextMenu.removeChild(menuitem);
+};
+
+/******************************************************************************/
+
+vAPI.contextMenu.create = function(details, callback) {
+ this.menuItemId = details.id;
+ this.menuLabel = details.title;
+ this.contexts = details.contexts;
+
+ if ( Array.isArray(this.contexts) && this.contexts.length ) {
+ this.contexts = this.contexts.indexOf('all') === -1 ? this.contexts : null;
+ } else {
+ // default in Chrome
+ this.contexts = ['page'];
+ }
+
+ this.onCommand = function() {
+ var gContextMenu = getOwnerWindow(this).gContextMenu;
+ var details = {
+ menuItemId: this.id
+ };
+
+ if ( gContextMenu.inFrame ) {
+ details.tagName = 'iframe';
+ // Probably won't work with e10s
+ details.frameUrl = gContextMenu.focusedWindow.location.href;
+ } else if ( gContextMenu.onImage ) {
+ details.tagName = 'img';
+ details.srcUrl = gContextMenu.mediaURL;
+ } else if ( gContextMenu.onAudio ) {
+ details.tagName = 'audio';
+ details.srcUrl = gContextMenu.mediaURL;
+ } else if ( gContextMenu.onVideo ) {
+ details.tagName = 'video';
+ details.srcUrl = gContextMenu.mediaURL;
+ } else if ( gContextMenu.onLink ) {
+ details.tagName = 'a';
+ details.linkUrl = gContextMenu.linkURL;
+ }
+
+ callback(details, {
+ id: tabWatcher.tabIdFromTarget(gContextMenu.browser),
+ url: gContextMenu.browser.currentURI.asciiSpec
+ });
+ };
+
+ for ( var win of winWatcher.getWindows() ) {
+ this.register(win.document);
+ }
+};
+
+/******************************************************************************/
+
+vAPI.contextMenu.remove = function() {
+ for ( var win of winWatcher.getWindows() ) {
+ this.unregister(win.document);
+ }
+
+ this.menuItemId = null;
+ this.menuLabel = null;
+ this.contexts = null;
+ this.onCommand = null;
+};
+
+/******************************************************************************/
+/******************************************************************************/
+
+var optionsObserver = (function() {
+ var addonId = 'uMatrix@raymondhill.net';
+
+ var commandHandler = function() {
+ switch ( this.id ) {
+ case 'showDashboardButton':
+ vAPI.tabs.open({ url: 'dashboard.html', index: -1 });
+ break;
+ case 'showLoggerButton':
+ vAPI.tabs.open({ url: 'logger-ui.html', index: -1 });
+ break;
+ default:
+ break;
+ }
+ };
+
+ var setupOptionsButton = function(doc, id) {
+ var button = doc.getElementById(id);
+ if ( button === null ) {
+ return;
+ }
+ button.addEventListener('command', commandHandler);
+ button.label = vAPI.i18n(id);
+ };
+
+ var setupOptionsButtons = function(doc) {
+ setupOptionsButton(doc, 'showDashboardButton');
+ setupOptionsButton(doc, 'showLoggerButton');
+ };
+
+ var observer = {
+ observe: function(doc, topic, id) {
+ if ( id !== addonId ) {
+ return;
+ }
+
+ setupOptionsButtons(doc);
+ }
+ };
+
+ // https://github.com/gorhill/uBlock/issues/948
+ // Older versions of Firefox can throw here when looking up `currentURI`.
+
+ var canInit = function() {
+ try {
+ var tabBrowser = tabWatcher.currentBrowser();
+ return tabBrowser &&
+ tabBrowser.currentURI &&
+ tabBrowser.currentURI.spec === 'about:addons' &&
+ tabBrowser.contentDocument &&
+ tabBrowser.contentDocument.readyState === 'complete';
+ } catch (ex) {
+ }
+ };
+
+ // Manually add the buttons if the `about:addons` page is already opened.
+
+ var init = function() {
+ if ( canInit() ) {
+ setupOptionsButtons(tabWatcher.currentBrowser().contentDocument);
+ }
+ };
+
+ var unregister = function() {
+ Services.obs.removeObserver(observer, 'addon-options-displayed');
+ };
+
+ var register = function() {
+ Services.obs.addObserver(observer, 'addon-options-displayed', false);
+ cleanupTasks.push(unregister);
+ deferUntil(canInit, init, { next: 463 });
+ };
+
+ return {
+ register: register,
+ unregister: unregister
+ };
+})();
+
+optionsObserver.register();
+
+/******************************************************************************/
+/******************************************************************************/
+
+vAPI.lastError = function() {
+ return null;
+};
+
+/******************************************************************************/
+/******************************************************************************/
+
+// This is called only once, when everything has been loaded in memory after
+// the extension was launched. It can be used to inject content scripts
+// in already opened web pages, to remove whatever nuisance could make it to
+// the web pages before uBlock was ready.
+
+vAPI.onLoadAllCompleted = function() {
+ for ( var browser of tabWatcher.browsers() ) {
+ browser.messageManager.sendAsyncMessage(
+ location.host + '-load-completed'
+ );
+ }
+};
+
+/******************************************************************************/
+/******************************************************************************/
+
+// Likelihood is that we do not have to punycode: given punycode overhead,
+// it's faster to check and skip than do it unconditionally all the time.
+
+var punycodeHostname = punycode.toASCII;
+var isNotASCII = /[^\x21-\x7F]/;
+
+vAPI.punycodeHostname = function(hostname) {
+ return isNotASCII.test(hostname) ? punycodeHostname(hostname) : hostname;
+};
+
+vAPI.punycodeURL = function(url) {
+ if ( isNotASCII.test(url) ) {
+ return Services.io.newURI(url, null, null).asciiSpec;
+ }
+ return url;
+};
+
+/******************************************************************************/
+/******************************************************************************/
+
+vAPI.cloud = (function() {
+ var extensionBranchPath = 'extensions.' + location.host;
+ var cloudBranchPath = extensionBranchPath + '.cloudStorage';
+
+ // https://github.com/gorhill/uBlock/issues/80#issuecomment-132081658
+ // We must use get/setComplexValue in order to properly handle strings
+ // with unicode characters.
+ var iss = Ci.nsISupportsString;
+ var argstr = Components.classes['@mozilla.org/supports-string;1']
+ .createInstance(iss);
+
+ var options = {
+ defaultDeviceName: '',
+ deviceName: ''
+ };
+
+ // User-supplied device name.
+ try {
+ options.deviceName = Services.prefs
+ .getBranch(extensionBranchPath + '.')
+ .getComplexValue('deviceName', iss)
+ .data;
+ } catch(ex) {
+ }
+
+ var getDefaultDeviceName = function() {
+ var name = '';
+ try {
+ name = Services.prefs
+ .getBranch('services.sync.client.')
+ .getComplexValue('name', iss)
+ .data;
+ } catch(ex) {
+ }
+
+ return name || window.navigator.platform || window.navigator.oscpu;
+ };
+
+ var start = function(dataKeys) {
+ var extensionBranch = Services.prefs.getBranch(extensionBranchPath + '.');
+ var syncBranch = Services.prefs.getBranch('services.sync.prefs.sync.');
+
+ // Mark config entries as syncable
+ argstr.data = '';
+ var dataKey;
+ for ( var i = 0; i < dataKeys.length; i++ ) {
+ dataKey = dataKeys[i];
+ if ( extensionBranch.prefHasUserValue('cloudStorage.' + dataKey) === false ) {
+ extensionBranch.setComplexValue('cloudStorage.' + dataKey, iss, argstr);
+ }
+ syncBranch.setBoolPref(cloudBranchPath + '.' + dataKey, true);
+ }
+ };
+
+ var push = function(datakey, data, callback) {
+ var branch = Services.prefs.getBranch(cloudBranchPath + '.');
+ var bin = {
+ 'source': options.deviceName || getDefaultDeviceName(),
+ 'tstamp': Date.now(),
+ 'data': data,
+ 'size': 0
+ };
+ bin.size = JSON.stringify(bin).length;
+ argstr.data = JSON.stringify(bin);
+ branch.setComplexValue(datakey, iss, argstr);
+ if ( typeof callback === 'function' ) {
+ callback();
+ }
+ };
+
+ var pull = function(datakey, callback) {
+ var result = null;
+ var branch = Services.prefs.getBranch(cloudBranchPath + '.');
+ try {
+ var json = branch.getComplexValue(datakey, iss).data;
+ if ( typeof json === 'string' ) {
+ result = JSON.parse(json);
+ }
+ } catch(ex) {
+ }
+ callback(result);
+ };
+
+ var getOptions = function(callback) {
+ if ( typeof callback !== 'function' ) {
+ return;
+ }
+ options.defaultDeviceName = getDefaultDeviceName();
+ callback(options);
+ };
+
+ var setOptions = function(details, callback) {
+ if ( typeof details !== 'object' || details === null ) {
+ return;
+ }
+
+ var branch = Services.prefs.getBranch(extensionBranchPath + '.');
+
+ if ( typeof details.deviceName === 'string' ) {
+ argstr.data = details.deviceName;
+ branch.setComplexValue('deviceName', iss, argstr);
+ options.deviceName = details.deviceName;
+ }
+
+ getOptions(callback);
+ };
+
+ return {
+ start: start,
+ push: push,
+ pull: pull,
+ getOptions: getOptions,
+ setOptions: setOptions
+ };
+})();
+
+/******************************************************************************/
+/******************************************************************************/
+
+vAPI.browserData = {};
+
+/******************************************************************************/
+
+// https://developer.mozilla.org/en-US/docs/HTTP_Cache
+
+vAPI.browserData.clearCache = function(callback) {
+ // PURGE_DISK_DATA_ONLY:1
+ // PURGE_DISK_ALL:2
+ // PURGE_EVERYTHING:3
+ // However I verified that no argument does clear the cache data.
+ // There is no cache2 for older versions of Firefox.
+ if ( Services.cache2 ) {
+ Services.cache2.clear();
+ } else if ( Services.cache ) {
+ Services.cache.evictEntries(Services.cache.STORE_ON_DISK);
+ }
+ if ( typeof callback === 'function' ) {
+ callback();
+ }
+};
+
+/******************************************************************************/
+
+vAPI.browserData.clearOrigin = function(/* domain */) {
+ // TODO
+};
+
+/******************************************************************************/
+/******************************************************************************/
+
+// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsICookieManager2
+// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsICookie2
+// https://developer.mozilla.org/en-US/docs/Observer_Notifications#Cookies
+
+vAPI.cookies = {};
+
+/******************************************************************************/
+
+vAPI.cookies.CookieEntry = function(ffCookie) {
+ this.domain = ffCookie.host;
+ this.name = ffCookie.name;
+ this.path = ffCookie.path;
+ this.secure = ffCookie.isSecure === true;
+ this.session = ffCookie.expires === 0;
+ this.value = ffCookie.value;
+};
+
+/******************************************************************************/
+
+vAPI.cookies.start = function() {
+ Services.obs.addObserver(this, 'cookie-changed', false);
+ Services.obs.addObserver(this, 'private-cookie-changed', false);
+ cleanupTasks.push(this.stop.bind(this));
+};
+
+/******************************************************************************/
+
+vAPI.cookies.stop = function() {
+ Services.obs.removeObserver(this, 'cookie-changed');
+ Services.obs.removeObserver(this, 'private-cookie-changed');
+};
+
+/******************************************************************************/
+
+vAPI.cookies.observe = function(subject, topic, reason) {
+ //if ( topic !== 'cookie-changed' && topic !== 'private-cookie-changed' ) {
+ // return;
+ //}
+ //
+ if ( reason === 'cleared' && typeof this.onAllRemoved === 'function' ) {
+ this.onAllRemoved();
+ return;
+ }
+ if ( subject === null ) {
+ return;
+ }
+ if ( subject instanceof Ci.nsICookie2 === false ) {
+ try {
+ subject = subject.QueryInterface(Ci.nsICookie2);
+ } catch (ex) {
+ return;
+ }
+ }
+ if ( reason === 'deleted' ) {
+ if ( typeof this.onRemoved === 'function' ) {
+ this.onRemoved(new this.CookieEntry(subject));
+ }
+ return;
+ }
+ if ( typeof this.onChanged === 'function' ) {
+ this.onChanged(new this.CookieEntry(subject));
+ }
+};
+
+/******************************************************************************/
+
+// Meant and expected to be asynchronous.
+
+vAPI.cookies.getAll = function(callback) {
+ if ( typeof callback !== 'function' ) {
+ return;
+ }
+ var onAsync = function() {
+ var out = [];
+ var enumerator = Services.cookies.enumerator;
+ var ffcookie;
+ while ( enumerator.hasMoreElements() ) {
+ ffcookie = enumerator.getNext();
+ if ( ffcookie instanceof Ci.nsICookie ) {
+ out.push(new this.CookieEntry(ffcookie));
+ }
+ }
+ callback(out);
+ };
+ vAPI.setTimeout(onAsync.bind(this), 0);
+};
+
+/******************************************************************************/
+
+vAPI.cookies.remove = function(details, callback) {
+ var uri = Services.io.newURI(details.url, null, null);
+ var cookies = Services.cookies;
+ cookies.remove(uri.asciiHost, details.name, uri.path, false, {});
+ cookies.remove( '.' + uri.asciiHost, details.name, uri.path, false, {});
+ if ( typeof callback === 'function' ) {
+ callback({
+ domain: uri.asciiHost,
+ name: details.name,
+ path: uri.path
+ });
+ }
+};
+
+/******************************************************************************/
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
diff --git a/js/vapi-client.js b/js/vapi-client.js
new file mode 100644
index 0000000..7f9521d
--- /dev/null
+++ b/js/vapi-client.js
@@ -0,0 +1,226 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* jshint esnext: true */
+/* global addMessageListener, removeMessageListener, sendAsyncMessage */
+
+// For non background pages
+
+'use strict';
+
+/******************************************************************************/
+
+(function(self) {
+
+/******************************************************************************/
+
+// https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10
+if ( self.vAPI === undefined || self.vAPI.uMatrix !== true ) {
+ self.vAPI = { uMatrix: true };
+}
+
+var vAPI = self.vAPI;
+vAPI.firefox = true;
+vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) +
+ Math.random().toString(36).slice(2);
+
+/******************************************************************************/
+
+vAPI.setTimeout = vAPI.setTimeout || function(callback, delay) {
+ return setTimeout(function() { callback(); }, delay);
+};
+
+/******************************************************************************/
+
+vAPI.shutdown = (function() {
+ var jobs = [];
+
+ var add = function(job) {
+ jobs.push(job);
+ };
+
+ var exec = function() {
+ //console.debug('Shutting down...');
+ var job;
+ while ( (job = jobs.pop()) ) {
+ job();
+ }
+ };
+
+ return {
+ add: add,
+ exec: exec
+ };
+})();
+
+/******************************************************************************/
+
+vAPI.messaging = {
+ listeners: new Set(),
+ pending: new Map(),
+ requestId: 1,
+ connected: false,
+
+ setup: function() {
+ this.addListener(this.builtinListener);
+ if ( this.toggleListenerCallback === null ) {
+ this.toggleListenerCallback = this.toggleListener.bind(this);
+ }
+ window.addEventListener('pagehide', this.toggleListenerCallback, true);
+ window.addEventListener('pageshow', this.toggleListenerCallback, true);
+ },
+
+ shutdown: function() {
+ if ( this.toggleListenerCallback !== null ) {
+ window.removeEventListener('pagehide', this.toggleListenerCallback, true);
+ window.removeEventListener('pageshow', this.toggleListenerCallback, true);
+ }
+ this.removeAllListeners();
+ //service pending callbacks
+ var pending = this.pending;
+ this.pending.clear();
+ for ( var callback of pending.values() ) {
+ if ( typeof callback === 'function' ) {
+ callback(null);
+ }
+ }
+ },
+
+ connect: function() {
+ if ( !this.connected ) {
+ if ( this.messageListenerCallback === null ) {
+ this.messageListenerCallback = this.messageListener.bind(this);
+ }
+ addMessageListener(this.messageListenerCallback);
+ this.connected = true;
+ }
+ },
+
+ disconnect: function() {
+ if ( this.connected ) {
+ removeMessageListener();
+ this.connected = false;
+ }
+ },
+
+ messageListener: function(msg) {
+ var details = JSON.parse(msg);
+ if ( !details ) {
+ return;
+ }
+
+ if ( details.broadcast ) {
+ this.sendToListeners(details.msg);
+ return;
+ }
+
+ if ( details.requestId ) {
+ var listener = this.pending.get(details.requestId);
+ if ( listener !== undefined ) {
+ this.pending.delete(details.requestId);
+ listener(details.msg);
+ return;
+ }
+ }
+ },
+ messageListenerCallback: null,
+
+ builtinListener: function(msg) {
+ if ( typeof msg.cmd === 'string' && msg.cmd === 'injectScript' ) {
+ var details = msg.details;
+ if ( !details.allFrames && window !== window.top ) {
+ return;
+ }
+ self.injectScript(details.file);
+ }
+ },
+
+ send: function(channelName, message, callback) {
+ this.connect()
+
+ message = {
+ channelName: self._sandboxId_ + '|' + channelName,
+ msg: message
+ };
+
+ if ( callback ) {
+ message.requestId = this.requestId++;
+ this.pending.set(message.requestId, callback);
+ }
+
+ sendAsyncMessage('umatrix:background', message);
+ },
+
+ toggleListener: function({type, persisted}) {
+ if ( type === 'pagehide' && !persisted ) {
+ vAPI.shutdown.exec();
+ this.shutdown();
+ return;
+ }
+
+ if ( type === 'pagehide' ) {
+ this.disconnect();
+ } else /* if ( type === 'pageshow' ) */ {
+ this.connect();
+ }
+ },
+ toggleListenerCallback: null,
+
+ sendToListeners: function(msg) {
+ for ( var listener of this.listeners ) {
+ listener(msg);
+ }
+ },
+
+ addListener: function(listener) {
+ this.listeners.add(listener);
+ this.connect()
+ },
+
+ removeListener: function(listener) {
+ this.listeners.delete(listener);
+ },
+
+ removeAllListeners: function() {
+ this.disconnect();
+ this.listeners.clear();;
+ }
+};
+
+vAPI.messaging.setup()
+
+/******************************************************************************/
+
+// No need to have vAPI client linger around after shutdown if
+// we are not a top window (because element picker can still
+// be injected in top window).
+if ( window !== window.top ) {
+ vAPI.shutdown.add(function() {
+ vAPI = null;
+ });
+}
+
+/******************************************************************************/
+
+})(this);
+
+/******************************************************************************/
diff --git a/js/vapi-common.js b/js/vapi-common.js
new file mode 100644
index 0000000..3b51d17
--- /dev/null
+++ b/js/vapi-common.js
@@ -0,0 +1,192 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global sendAsyncMessage */
+
+// For background page or non-background pages
+
+'use strict';
+
+/******************************************************************************/
+
+(function(self) {
+
+/******************************************************************************/
+
+const {Services} = Components.utils.import(
+ 'resource://gre/modules/Services.jsm',
+ null
+);
+
+// https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10
+if ( self.vAPI === undefined || self.vAPI.uMatrix !== true ) {
+ self.vAPI = { uMatrix: true };
+}
+
+var vAPI = self.vAPI;
+
+/******************************************************************************/
+
+vAPI.setTimeout = vAPI.setTimeout || function(callback, delay, extra) {
+ return setTimeout(function(a) { callback(a); }, delay, extra);
+};
+
+/******************************************************************************/
+
+// http://www.w3.org/International/questions/qa-scripts#directions
+
+var setScriptDirection = function(language) {
+ document.body.setAttribute(
+ 'dir',
+ ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(language) !== -1 ? 'rtl' : 'ltr'
+ );
+};
+
+/******************************************************************************/
+
+vAPI.download = function(details) {
+ if ( !details.url ) {
+ return;
+ }
+
+ var a = document.createElement('a');
+ a.href = details.url;
+ a.setAttribute('download', details.filename || '');
+ a.dispatchEvent(new MouseEvent('click'));
+};
+
+/******************************************************************************/
+
+vAPI.insertHTML = (function() {
+ const parser = Components.classes['@mozilla.org/parserutils;1']
+ .getService(Components.interfaces.nsIParserUtils);
+
+ // https://github.com/gorhill/uBlock/issues/845
+ // Apparently dashboard pages execute with `about:blank` principal.
+
+ return function(node, html) {
+ while ( node.firstChild ) {
+ node.removeChild(node.firstChild);
+ }
+
+ node.appendChild(parser.parseFragment(
+ html,
+ parser.SanitizerAllowStyle,
+ false,
+ Services.io.newURI('about:blank', null, null),
+ document.documentElement
+ ));
+ };
+})();
+
+/******************************************************************************/
+
+vAPI.getURL = function(path) {
+ return 'chrome://' + location.host + '/content/' + path.replace(/^\/+/, '');
+};
+
+/******************************************************************************/
+
+vAPI.i18n = (function() {
+ var stringBundle = Services.strings.createBundle(
+ 'chrome://' + location.host + '/locale/messages.properties'
+ );
+
+ return function(s) {
+ try {
+ return stringBundle.GetStringFromName(s);
+ } catch (ex) {
+ return '';
+ }
+ };
+})();
+
+setScriptDirection(navigator.language);
+
+/******************************************************************************/
+
+vAPI.closePopup = function() {
+ sendAsyncMessage(location.host + ':closePopup');
+};
+
+/******************************************************************************/
+
+// A localStorage-like object which should be accessible from the
+// background page or auxiliary pages.
+// This storage is optional, but it is nice to have, for a more polished user
+// experience.
+
+vAPI.localStorage = {
+ pbName: '',
+ pb: null,
+ str: Components.classes['@mozilla.org/supports-string;1']
+ .createInstance(Components.interfaces.nsISupportsString),
+ init: function(pbName) {
+ this.pbName = pbName;
+ this.pb = Services.prefs.getBranch(pbName);
+ },
+ getItem: function(key) {
+ try {
+ return this.pb.getComplexValue(
+ key,
+ Components.interfaces.nsISupportsString
+ ).data;
+ } catch (ex) {
+ return null;
+ }
+ },
+ setItem: function(key, value) {
+ this.str.data = value;
+ this.pb.setComplexValue(
+ key,
+ Components.interfaces.nsISupportsString,
+ this.str
+ );
+ },
+ getBool: function(key) {
+ try {
+ return this.pb.getBoolPref(key);
+ } catch (ex) {
+ return null;
+ }
+ },
+ setBool: function(key, value) {
+ this.pb.setBoolPref(key, value);
+ },
+ setDefaultBool: function(key, defaultValue) {
+ Services.prefs.getDefaultBranch(this.pbName).setBoolPref(key, defaultValue);
+ },
+ removeItem: function(key) {
+ this.pb.clearUserPref(key);
+ },
+ clear: function() {
+ this.pb.deleteBranch('');
+ }
+};
+
+vAPI.localStorage.init('extensions.' + location.host + '.');
+
+/******************************************************************************/
+
+})(this);
+
+/******************************************************************************/
diff --git a/js/vapi-popup.js b/js/vapi-popup.js
new file mode 100644
index 0000000..c1fbdbb
--- /dev/null
+++ b/js/vapi-popup.js
@@ -0,0 +1,23 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* Firefox: no platform-specific code */
diff --git a/js/xal.js b/js/xal.js
new file mode 100644
index 0000000..9dba5b9
--- /dev/null
+++ b/js/xal.js
@@ -0,0 +1,72 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 Raymond Hill
+ Copyright (C) 2019 Alessio Vanni
+
+ 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/}.
+
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+/* global chrome, µMatrix */
+
+/******************************************************************************/
+
+µMatrix.XAL = (function(){
+
+/******************************************************************************/
+
+var exports = {};
+var noopFunc = function(){};
+
+/******************************************************************************/
+
+exports.keyvalSetOne = function(key, val, callback) {
+ var bin = {};
+ bin[key] = val;
+ vAPI.storage.set(bin, callback || noopFunc);
+};
+
+/******************************************************************************/
+
+exports.keyvalGetOne = function(key, callback) {
+ vAPI.storage.get(key, callback);
+};
+
+/******************************************************************************/
+
+exports.keyvalSetMany = function(dict, callback) {
+ vAPI.storage.set(dict, callback || noopFunc);
+};
+
+/******************************************************************************/
+
+exports.keyvalRemoveOne = function(key, callback) {
+ vAPI.storage.remove(key, callback || noopFunc);
+};
+
+/******************************************************************************/
+
+exports.keyvalRemoveAll = function(callback) {
+ vAPI.storage.clear(callback || noopFunc);
+};
+
+/******************************************************************************/
+
+return exports;
+
+/******************************************************************************/
+
+})();
diff --git a/lib/publicsuffixlist.js b/lib/publicsuffixlist.js
new file mode 100644
index 0000000..a823188
--- /dev/null
+++ b/lib/publicsuffixlist.js
@@ -0,0 +1,369 @@
+/*******************************************************************************
+
+ publicsuffixlist.js - an efficient javascript implementation to deal with
+ Mozilla Foundation's Public Suffix List <http://publicsuffix.org/list/>
+ Copyright (C) 2013 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/publicsuffixlist.js */
+
+/*
+ This code is mostly dumb: I consider this to be lower-level code, thus
+ in order to ensure efficiency, the caller is responsible for sanitizing
+ the inputs.
+*/
+
+/******************************************************************************/
+
+// A single instance of PublicSuffixList is enough.
+
+;(function(root) {
+
+'use strict';
+
+/******************************************************************************/
+
+var exceptions = {};
+var rules = {};
+var selfieMagic = 'iscjsfsaolnm';
+
+// This value dictate how the search will be performed:
+// < this.cutoffLength = indexOf()
+// >= this.cutoffLength = binary search
+var cutoffLength = 256;
+var mustPunycode = /[^\w.*-]/;
+
+var onChangedListeners = [];
+
+/******************************************************************************/
+
+// In the context of this code, a domain is defined as:
+// "{label}.{public suffix}".
+// A single standalone label is a public suffix as per
+// http://publicsuffix.org/list/:
+// "If no rules match, the prevailing rule is '*' "
+// This means 'localhost' is not deemed a domain by this
+// code, since according to the definition above, it would be
+// evaluated as a public suffix. The caller is therefore responsible to
+// decide how to further interpret such public suffix.
+//
+// `hostname` must be a valid ascii-based hostname.
+
+function getDomain(hostname) {
+ // A hostname starting with a dot is not a valid hostname.
+ if ( !hostname || hostname.charAt(0) === '.' ) {
+ return '';
+ }
+ hostname = hostname.toLowerCase();
+ var suffix = getPublicSuffix(hostname);
+ if ( suffix === hostname ) {
+ return '';
+ }
+ var pos = hostname.lastIndexOf('.', hostname.lastIndexOf('.', hostname.length - suffix.length) - 1);
+ if ( pos <= 0 ) {
+ return hostname;
+ }
+ return hostname.slice(pos + 1);
+}
+
+/******************************************************************************/
+
+// Return longest public suffix.
+//
+// `hostname` must be a valid ascii-based string which respect hostname naming.
+
+function getPublicSuffix(hostname) {
+ if ( !hostname ) {
+ return '';
+ }
+ // Since we slice down the hostname with each pass, the first match
+ // is the longest, so no need to find all the matching rules.
+ var pos;
+ while ( true ) {
+ pos = hostname.indexOf('.');
+ if ( pos < 0 ) {
+ return hostname;
+ }
+ if ( search(exceptions, hostname) ) {
+ return hostname.slice(pos + 1);
+ }
+ if ( search(rules, hostname) ) {
+ return hostname;
+ }
+ if ( search(rules, '*' + hostname.slice(pos)) ) {
+ return hostname;
+ }
+ hostname = hostname.slice(pos + 1);
+ }
+ // unreachable
+}
+
+/******************************************************************************/
+
+// Look up a specific hostname.
+
+function search(store, hostname) {
+ // Extract TLD
+ var pos = hostname.lastIndexOf('.');
+ var tld, remainder;
+ if ( pos < 0 ) {
+ tld = hostname;
+ remainder = hostname;
+ } else {
+ tld = hostname.slice(pos + 1);
+ remainder = hostname.slice(0, pos);
+ }
+ var substore = store[tld];
+ if ( !substore ) {
+ return false;
+ }
+ // If substore is a string, use indexOf()
+ if ( typeof substore === 'string' ) {
+ return substore.indexOf(' ' + remainder + ' ') >= 0;
+ }
+ // It is an array: use binary search.
+ var l = remainder.length;
+ var haystack = substore[l];
+ if ( !haystack ) {
+ return false;
+ }
+ var left = 0;
+ var right = Math.floor(haystack.length / l + 0.5);
+ var i, needle;
+ while ( left < right ) {
+ i = left + right >> 1;
+ needle = haystack.substr( l * i, l );
+ if ( remainder < needle ) {
+ right = i;
+ } else if ( remainder > needle ) {
+ left = i + 1;
+ } else {
+ return true;
+ }
+ }
+ return false;
+}
+
+/******************************************************************************/
+
+// Parse and set a UTF-8 text-based suffix list. Format is same as found at:
+// http://publicsuffix.org/list/
+//
+// `toAscii` is a converter from unicode to punycode. Required since the
+// Public Suffix List contains unicode characters.
+// Suggestion: use <https://github.com/bestiejs/punycode.js> it's quite good.
+
+function parse(text, toAscii) {
+ exceptions = {};
+ rules = {};
+
+ var lineBeg = 0, lineEnd;
+ var textEnd = text.length;
+ var line, store, pos, tld;
+
+ while ( lineBeg < textEnd ) {
+ lineEnd = text.indexOf('\n', lineBeg);
+ if ( lineEnd < 0 ) {
+ lineEnd = text.indexOf('\r', lineBeg);
+ if ( lineEnd < 0 ) {
+ lineEnd = textEnd;
+ }
+ }
+ line = text.slice(lineBeg, lineEnd).trim();
+ lineBeg = lineEnd + 1;
+
+ if ( line.length === 0 ) {
+ continue;
+ }
+
+ // Ignore comments
+ pos = line.indexOf('//');
+ if ( pos >= 0 ) {
+ line = line.slice(0, pos);
+ }
+
+ // Ignore surrounding whitespaces
+ line = line.trim();
+ if ( !line ) {
+ continue;
+ }
+
+ // Is this an exception rule?
+ if ( line.charAt(0) === '!' ) {
+ store = exceptions;
+ line = line.slice(1);
+ } else {
+ store = rules;
+ }
+
+ if ( mustPunycode.test(line) ) {
+ line = toAscii(line);
+ }
+
+ // http://publicsuffix.org/list/:
+ // "... all rules must be canonicalized in the normal way
+ // for hostnames - lower-case, Punycode ..."
+ line = line.toLowerCase();
+
+ // Extract TLD
+ pos = line.lastIndexOf('.');
+ if ( pos < 0 ) {
+ tld = line;
+ } else {
+ tld = line.slice(pos + 1);
+ line = line.slice(0, pos);
+ }
+
+ // Store suffix using tld as key
+ if ( !store.hasOwnProperty(tld) ) {
+ store[tld] = [];
+ }
+ if ( line ) {
+ store[tld].push(line);
+ }
+ }
+ crystallize(exceptions);
+ crystallize(rules);
+
+ callListeners(onChangedListeners);
+}
+
+/******************************************************************************/
+
+// Cristallize the storage of suffixes using optimal internal representation
+// for future look up.
+
+function crystallize(store) {
+ var suffixes, suffix, i, l;
+
+ for ( var tld in store ) {
+ if ( !store.hasOwnProperty(tld) ) {
+ continue;
+ }
+ suffixes = store[tld].join(' ');
+ // No suffix
+ if ( !suffixes ) {
+ store[tld] = '';
+ continue;
+ }
+ // Concatenated list of suffixes less than cutoff length:
+ // Store as string, lookup using indexOf()
+ if ( suffixes.length < cutoffLength ) {
+ store[tld] = ' ' + suffixes + ' ';
+ continue;
+ }
+ // Concatenated list of suffixes greater or equal to cutoff length
+ // Store as array keyed on suffix length, lookup using binary search.
+ // I borrowed the idea to key on string length here:
+ // http://ejohn.org/blog/dictionary-lookups-in-javascript/#comment-392072
+
+ i = store[tld].length;
+ suffixes = [];
+ while ( i-- ) {
+ suffix = store[tld][i];
+ l = suffix.length;
+ if ( !suffixes[l] ) {
+ suffixes[l] = [];
+ }
+ suffixes[l].push(suffix);
+ }
+ l = suffixes.length;
+ while ( l-- ) {
+ if ( suffixes[l] ) {
+ suffixes[l] = suffixes[l].sort().join('');
+ }
+ }
+ store[tld] = suffixes;
+ }
+ return store;
+}
+
+/******************************************************************************/
+
+function toSelfie() {
+ return {
+ magic: selfieMagic,
+ rules: rules,
+ exceptions: exceptions
+ };
+}
+
+function fromSelfie(selfie) {
+ if ( typeof selfie !== 'object' || typeof selfie.magic !== 'string' || selfie.magic !== selfieMagic ) {
+ return false;
+ }
+ rules = selfie.rules;
+ exceptions = selfie.exceptions;
+ callListeners(onChangedListeners);
+ return true;
+}
+
+/******************************************************************************/
+
+var addListener = function(listeners, callback) {
+ if ( typeof callback !== 'function' ) {
+ return;
+ }
+ if ( listeners.indexOf(callback) === -1 ) {
+ listeners.push(callback);
+ }
+};
+
+var removeListener = function(listeners, callback) {
+ var pos = listeners.indexOf(callback);
+ if ( pos !== -1 ) {
+ listeners.splice(pos, 1);
+ }
+};
+
+var callListeners = function(listeners) {
+ for ( var i = 0; i < listeners.length; i++ ) {
+ listeners[i]();
+ }
+};
+
+/******************************************************************************/
+
+var onChanged = {
+ addListener: function(callback) {
+ addListener(onChangedListeners, callback);
+ },
+ removeListener: function(callback) {
+ removeListener(onChangedListeners, callback);
+ }
+};
+
+/******************************************************************************/
+
+// Public API
+
+root = root || window;
+
+root.publicSuffixList = {
+ 'version': '1.0',
+ 'parse': parse,
+ 'getDomain': getDomain,
+ 'getPublicSuffix': getPublicSuffix,
+ 'toSelfie': toSelfie,
+ 'fromSelfie': fromSelfie,
+ 'onChanged': onChanged
+};
+
+/******************************************************************************/
+
+})(this);
+
diff --git a/lib/punycode.js b/lib/punycode.js
new file mode 100644
index 0000000..ac68597
--- /dev/null
+++ b/lib/punycode.js
@@ -0,0 +1,530 @@
+/*! https://mths.be/punycode v1.3.2 by @mathias */
+;(function(root) {
+
+ /** Detect free variables */
+ var freeExports = typeof exports == 'object' && exports &&
+ !exports.nodeType && exports;
+ var freeModule = typeof module == 'object' && module &&
+ !module.nodeType && module;
+ var freeGlobal = typeof global == 'object' && global;
+ if (
+ freeGlobal.global === freeGlobal ||
+ freeGlobal.window === freeGlobal ||
+ freeGlobal.self === freeGlobal
+ ) {
+ root = freeGlobal;
+ }
+
+ /**
+ * The `punycode` object.
+ * @name punycode
+ * @type Object
+ */
+ var punycode,
+
+ /** Highest positive signed 32-bit float value */
+ maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
+
+ /** Bootstring parameters */
+ base = 36,
+ tMin = 1,
+ tMax = 26,
+ skew = 38,
+ damp = 700,
+ initialBias = 72,
+ initialN = 128, // 0x80
+ delimiter = '-', // '\x2D'
+
+ /** Regular expressions */
+ regexPunycode = /^xn--/,
+ regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
+ regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
+
+ /** Error messages */
+ errors = {
+ 'overflow': 'Overflow: input needs wider integers to process',
+ 'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
+ 'invalid-input': 'Invalid input'
+ },
+
+ /** Convenience shortcuts */
+ baseMinusTMin = base - tMin,
+ floor = Math.floor,
+ stringFromCharCode = String.fromCharCode,
+
+ /** Temporary variable */
+ key;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * A generic error utility function.
+ * @private
+ * @param {String} type The error type.
+ * @returns {Error} Throws a `RangeError` with the applicable error message.
+ */
+ function error(type) {
+ throw RangeError(errors[type]);
+ }
+
+ /**
+ * A generic `Array#map` utility function.
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} callback The function that gets called for every array
+ * item.
+ * @returns {Array} A new array of values returned by the callback function.
+ */
+ function map(array, fn) {
+ var length = array.length;
+ var result = [];
+ while (length--) {
+ result[length] = fn(array[length]);
+ }
+ return result;
+ }
+
+ /**
+ * A simple `Array#map`-like wrapper to work with domain name strings or email
+ * addresses.
+ * @private
+ * @param {String} domain The domain name or email address.
+ * @param {Function} callback The function that gets called for every
+ * character.
+ * @returns {Array} A new string of characters returned by the callback
+ * function.
+ */
+ function mapDomain(string, fn) {
+ var parts = string.split('@');
+ var result = '';
+ if (parts.length > 1) {
+ // In email addresses, only the domain name should be punycoded. Leave
+ // the local part (i.e. everything up to `@`) intact.
+ result = parts[0] + '@';
+ string = parts[1];
+ }
+ // Avoid `split(regex)` for IE8 compatibility. See #17.
+ string = string.replace(regexSeparators, '\x2E');
+ var labels = string.split('.');
+ var encoded = map(labels, fn).join('.');
+ return result + encoded;
+ }
+
+ /**
+ * Creates an array containing the numeric code points of each Unicode
+ * character in the string. While JavaScript uses UCS-2 internally,
+ * this function will convert a pair of surrogate halves (each of which
+ * UCS-2 exposes as separate characters) into a single code point,
+ * matching UTF-16.
+ * @see `punycode.ucs2.encode`
+ * @see <https://mathiasbynens.be/notes/javascript-encoding>
+ * @memberOf punycode.ucs2
+ * @name decode
+ * @param {String} string The Unicode input string (UCS-2).
+ * @returns {Array} The new array of code points.
+ */
+ function ucs2decode(string) {
+ var output = [],
+ counter = 0,
+ length = string.length,
+ value,
+ extra;
+ while (counter < length) {
+ value = string.charCodeAt(counter++);
+ if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
+ // high surrogate, and there is a next character
+ extra = string.charCodeAt(counter++);
+ if ((extra & 0xFC00) == 0xDC00) { // low surrogate
+ output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
+ } else {
+ // unmatched surrogate; only append this code unit, in case the next
+ // code unit is the high surrogate of a surrogate pair
+ output.push(value);
+ counter--;
+ }
+ } else {
+ output.push(value);
+ }
+ }
+ return output;
+ }
+
+ /**
+ * Creates a string based on an array of numeric code points.
+ * @see `punycode.ucs2.decode`
+ * @memberOf punycode.ucs2
+ * @name encode
+ * @param {Array} codePoints The array of numeric code points.
+ * @returns {String} The new Unicode string (UCS-2).
+ */
+ function ucs2encode(array) {
+ return map(array, function(value) {
+ var output = '';
+ if (value > 0xFFFF) {
+ value -= 0x10000;
+ output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
+ value = 0xDC00 | value & 0x3FF;
+ }
+ output += stringFromCharCode(value);
+ return output;
+ }).join('');
+ }
+
+ /**
+ * Converts a basic code point into a digit/integer.
+ * @see `digitToBasic()`
+ * @private
+ * @param {Number} codePoint The basic numeric code point value.
+ * @returns {Number} The numeric value of a basic code point (for use in
+ * representing integers) in the range `0` to `base - 1`, or `base` if
+ * the code point does not represent a value.
+ */
+ function basicToDigit(codePoint) {
+ if (codePoint - 48 < 10) {
+ return codePoint - 22;
+ }
+ if (codePoint - 65 < 26) {
+ return codePoint - 65;
+ }
+ if (codePoint - 97 < 26) {
+ return codePoint - 97;
+ }
+ return base;
+ }
+
+ /**
+ * Converts a digit/integer into a basic code point.
+ * @see `basicToDigit()`
+ * @private
+ * @param {Number} digit The numeric value of a basic code point.
+ * @returns {Number} The basic code point whose value (when used for
+ * representing integers) is `digit`, which needs to be in the range
+ * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
+ * used; else, the lowercase form is used. The behavior is undefined
+ * if `flag` is non-zero and `digit` has no uppercase form.
+ */
+ function digitToBasic(digit, flag) {
+ // 0..25 map to ASCII a..z or A..Z
+ // 26..35 map to ASCII 0..9
+ return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
+ }
+
+ /**
+ * Bias adaptation function as per section 3.4 of RFC 3492.
+ * http://tools.ietf.org/html/rfc3492#section-3.4
+ * @private
+ */
+ function adapt(delta, numPoints, firstTime) {
+ var k = 0;
+ delta = firstTime ? floor(delta / damp) : delta >> 1;
+ delta += floor(delta / numPoints);
+ for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
+ delta = floor(delta / baseMinusTMin);
+ }
+ return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
+ }
+
+ /**
+ * Converts a Punycode string of ASCII-only symbols to a string of Unicode
+ * symbols.
+ * @memberOf punycode
+ * @param {String} input The Punycode string of ASCII-only symbols.
+ * @returns {String} The resulting string of Unicode symbols.
+ */
+ function decode(input) {
+ // Don't use UCS-2
+ var output = [],
+ inputLength = input.length,
+ out,
+ i = 0,
+ n = initialN,
+ bias = initialBias,
+ basic,
+ j,
+ index,
+ oldi,
+ w,
+ k,
+ digit,
+ t,
+ /** Cached calculation results */
+ baseMinusT;
+
+ // Handle the basic code points: let `basic` be the number of input code
+ // points before the last delimiter, or `0` if there is none, then copy
+ // the first basic code points to the output.
+
+ basic = input.lastIndexOf(delimiter);
+ if (basic < 0) {
+ basic = 0;
+ }
+
+ for (j = 0; j < basic; ++j) {
+ // if it's not a basic code point
+ if (input.charCodeAt(j) >= 0x80) {
+ error('not-basic');
+ }
+ output.push(input.charCodeAt(j));
+ }
+
+ // Main decoding loop: start just after the last delimiter if any basic code
+ // points were copied; start at the beginning otherwise.
+
+ for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
+
+ // `index` is the index of the next character to be consumed.
+ // Decode a generalized variable-length integer into `delta`,
+ // which gets added to `i`. The overflow checking is easier
+ // if we increase `i` as we go, then subtract off its starting
+ // value at the end to obtain `delta`.
+ for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
+
+ if (index >= inputLength) {
+ error('invalid-input');
+ }
+
+ digit = basicToDigit(input.charCodeAt(index++));
+
+ if (digit >= base || digit > floor((maxInt - i) / w)) {
+ error('overflow');
+ }
+
+ i += digit * w;
+ t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+
+ if (digit < t) {
+ break;
+ }
+
+ baseMinusT = base - t;
+ if (w > floor(maxInt / baseMinusT)) {
+ error('overflow');
+ }
+
+ w *= baseMinusT;
+
+ }
+
+ out = output.length + 1;
+ bias = adapt(i - oldi, out, oldi == 0);
+
+ // `i` was supposed to wrap around from `out` to `0`,
+ // incrementing `n` each time, so we'll fix that now:
+ if (floor(i / out) > maxInt - n) {
+ error('overflow');
+ }
+
+ n += floor(i / out);
+ i %= out;
+
+ // Insert `n` at position `i` of the output
+ output.splice(i++, 0, n);
+
+ }
+
+ return ucs2encode(output);
+ }
+
+ /**
+ * Converts a string of Unicode symbols (e.g. a domain name label) to a
+ * Punycode string of ASCII-only symbols.
+ * @memberOf punycode
+ * @param {String} input The string of Unicode symbols.
+ * @returns {String} The resulting Punycode string of ASCII-only symbols.
+ */
+ function encode(input) {
+ var n,
+ delta,
+ handledCPCount,
+ basicLength,
+ bias,
+ j,
+ m,
+ q,
+ k,
+ t,
+ currentValue,
+ output = [],
+ /** `inputLength` will hold the number of code points in `input`. */
+ inputLength,
+ /** Cached calculation results */
+ handledCPCountPlusOne,
+ baseMinusT,
+ qMinusT;
+
+ // Convert the input in UCS-2 to Unicode
+ input = ucs2decode(input);
+
+ // Cache the length
+ inputLength = input.length;
+
+ // Initialize the state
+ n = initialN;
+ delta = 0;
+ bias = initialBias;
+
+ // Handle the basic code points
+ for (j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+ if (currentValue < 0x80) {
+ output.push(stringFromCharCode(currentValue));
+ }
+ }
+
+ handledCPCount = basicLength = output.length;
+
+ // `handledCPCount` is the number of code points that have been handled;
+ // `basicLength` is the number of basic code points.
+
+ // Finish the basic string - if it is not empty - with a delimiter
+ if (basicLength) {
+ output.push(delimiter);
+ }
+
+ // Main encoding loop:
+ while (handledCPCount < inputLength) {
+
+ // All non-basic code points < n have been handled already. Find the next
+ // larger one:
+ for (m = maxInt, j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+ if (currentValue >= n && currentValue < m) {
+ m = currentValue;
+ }
+ }
+
+ // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
+ // but guard against overflow
+ handledCPCountPlusOne = handledCPCount + 1;
+ if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
+ error('overflow');
+ }
+
+ delta += (m - n) * handledCPCountPlusOne;
+ n = m;
+
+ for (j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+
+ if (currentValue < n && ++delta > maxInt) {
+ error('overflow');
+ }
+
+ if (currentValue == n) {
+ // Represent delta as a generalized variable-length integer
+ for (q = delta, k = base; /* no condition */; k += base) {
+ t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+ if (q < t) {
+ break;
+ }
+ qMinusT = q - t;
+ baseMinusT = base - t;
+ output.push(
+ stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
+ );
+ q = floor(qMinusT / baseMinusT);
+ }
+
+ output.push(stringFromCharCode(digitToBasic(q, 0)));
+ bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
+ delta = 0;
+ ++handledCPCount;
+ }
+ }
+
+ ++delta;
+ ++n;
+
+ }
+ return output.join('');
+ }
+
+ /**
+ * Converts a Punycode string representing a domain name or an email address
+ * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
+ * it doesn't matter if you call it on a string that has already been
+ * converted to Unicode.
+ * @memberOf punycode
+ * @param {String} input The Punycoded domain name or email address to
+ * convert to Unicode.
+ * @returns {String} The Unicode representation of the given Punycode
+ * string.
+ */
+ function toUnicode(input) {
+ return mapDomain(input, function(string) {
+ return regexPunycode.test(string)
+ ? decode(string.slice(4).toLowerCase())
+ : string;
+ });
+ }
+
+ /**
+ * Converts a Unicode string representing a domain name or an email address to
+ * Punycode. Only the non-ASCII parts of the domain name will be converted,
+ * i.e. it doesn't matter if you call it with a domain that's already in
+ * ASCII.
+ * @memberOf punycode
+ * @param {String} input The domain name or email address to convert, as a
+ * Unicode string.
+ * @returns {String} The Punycode representation of the given domain name or
+ * email address.
+ */
+ function toASCII(input) {
+ return mapDomain(input, function(string) {
+ return regexNonASCII.test(string)
+ ? 'xn--' + encode(string)
+ : string;
+ });
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /** Define the public API */
+ punycode = {
+ /**
+ * A string representing the current Punycode.js version number.
+ * @memberOf punycode
+ * @type String
+ */
+ 'version': '1.3.2',
+ /**
+ * An object of methods to convert from JavaScript's internal character
+ * representation (UCS-2) to Unicode code points, and back.
+ * @see <https://mathiasbynens.be/notes/javascript-encoding>
+ * @memberOf punycode
+ * @type Object
+ */
+ 'ucs2': {
+ 'decode': ucs2decode,
+ 'encode': ucs2encode
+ },
+ 'decode': decode,
+ 'encode': encode,
+ 'toASCII': toASCII,
+ 'toUnicode': toUnicode
+ };
+
+ /** Expose `punycode` */
+ // Some AMD build optimizers, like r.js, check for specific condition patterns
+ // like the following:
+ if (
+ typeof define == 'function' &&
+ typeof define.amd == 'object' &&
+ define.amd
+ ) {
+ define('punycode', function() {
+ return punycode;
+ });
+ } else if (freeExports && freeModule) {
+ if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+
+ freeModule.exports = punycode;
+ } else { // in Narwhal or RingoJS v0.7.0-
+ for (key in punycode) {
+ punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
+ }
+ }
+ } else { // in Rhino or a web browser
+ root.punycode = punycode;
+ }
+
+}(this));
diff --git a/lib/yamd5.js b/lib/yamd5.js
new file mode 100644
index 0000000..582b576
--- /dev/null
+++ b/lib/yamd5.js
@@ -0,0 +1,402 @@
+/*******************************************************************************
+
+YaMD5 - Yet another MD5 hasher.
+home: https://github.com/gorhill/yamd5.js
+
+I needed an MD5 hasher, and as usual I want small code base, and fast.
+
+Originally found md5-o-matic [1]. It was fast but did not work with Unicode
+strings. Also, eventually realized it was really based on code from
+Joseph Myers [2] with no proper credits given (not nice).
+
+Then I found SparkMD5 [3], which works with Unicode strings, but at a steep
+cost to performance. Also, glancing at the code I saw avoidable redundancies
+causing the code base to be much larger than needed.
+
+So from this point I set out to write my own version, YaMD5 (sorry, I am
+not good with naming projects), of course heavily relying on the original
+code from Joseph Myers [2], and bits from SparkMD5 -- I started to work from
+SparkMD5 implementation, so there might be bits of code original to SparkMD5
+code left in a few places (like say, MD5.end()).
+
+Advantages of YaMD5:
+
+- Can handle Unicode strings
+- Natively incremental
+- Small code base
+- Fastest MD5 hasher out there so far for large input [4]
+- Even faster than versions supporting only simpler ascii strings
+
+
+ [1] https://github.com/trentmillar/md5-o-matic
+ [2] http://www.myersdaily.org/joseph/javascript/md5-text.html
+ [3] https://github.com/satazor/SparkMD5
+ [4] http://jsperf.com/md5-shootout/75
+
+So with that said, I don't know what license covers Joseph Myers' code (need
+to find out). In any case, concerning whatever original code I contributed in
+there:
+
+The MIT License (MIT)
+
+Copyright (C) 2014 Raymond Hill
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+**/
+
+/* jshint bitwise: false */
+
+(function(root) {
+
+ 'use strict';
+
+ /*
+ * Fastest md5 implementation around (JKM md5)
+ * Credits: Joseph Myers
+ *
+ * @see http://www.myersdaily.org/joseph/javascript/md5-text.html
+ * @see http://jsperf.com/md5-shootout/7
+ */
+
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
+
+ var md5cycle = function(x, k) {
+ var a = x[0],
+ b = x[1],
+ c = x[2],
+ d = x[3];
+ // ff()
+ a += (b & c | ~b & d) + k[0] - 680876936 | 0;
+ a = (a << 7 | a >>> 25) + b | 0;
+ d += (a & b | ~a & c) + k[1] - 389564586 | 0;
+ d = (d << 12 | d >>> 20) + a | 0;
+ c += (d & a | ~d & b) + k[2] + 606105819 | 0;
+ c = (c << 17 | c >>> 15) + d | 0;
+ b += (c & d | ~c & a) + k[3] - 1044525330 | 0;
+ b = (b << 22 | b >>> 10) + c | 0;
+ a += (b & c | ~b & d) + k[4] - 176418897 | 0;
+ a = (a << 7 | a >>> 25) + b | 0;
+ d += (a & b | ~a & c) + k[5] + 1200080426 | 0;
+ d = (d << 12 | d >>> 20) + a | 0;
+ c += (d & a | ~d & b) + k[6] - 1473231341 | 0;
+ c = (c << 17 | c >>> 15) + d | 0;
+ b += (c & d | ~c & a) + k[7] - 45705983 | 0;
+ b = (b << 22 | b >>> 10) + c | 0;
+ a += (b & c | ~b & d) + k[8] + 1770035416 | 0;
+ a = (a << 7 | a >>> 25) + b | 0;
+ d += (a & b | ~a & c) + k[9] - 1958414417 | 0;
+ d = (d << 12 | d >>> 20) + a | 0;
+ c += (d & a | ~d & b) + k[10] - 42063 | 0;
+ c = (c << 17 | c >>> 15) + d | 0;
+ b += (c & d | ~c & a) + k[11] - 1990404162 | 0;
+ b = (b << 22 | b >>> 10) + c | 0;
+ a += (b & c | ~b & d) + k[12] + 1804603682 | 0;
+ a = (a << 7 | a >>> 25) + b | 0;
+ d += (a & b | ~a & c) + k[13] - 40341101 | 0;
+ d = (d << 12 | d >>> 20) + a | 0;
+ c += (d & a | ~d & b) + k[14] - 1502002290 | 0;
+ c = (c << 17 | c >>> 15) + d | 0;
+ b += (c & d | ~c & a) + k[15] + 1236535329 | 0;
+ b = (b << 22 | b >>> 10) + c | 0;
+ // gg()
+ a += (b & d | c & ~d) + k[1] - 165796510 | 0;
+ a = (a << 5 | a >>> 27) + b | 0;
+ d += (a & c | b & ~c) + k[6] - 1069501632 | 0;
+ d = (d << 9 | d >>> 23) + a | 0;
+ c += (d & b | a & ~b) + k[11] + 643717713 | 0;
+ c = (c << 14 | c >>> 18) + d | 0;
+ b += (c & a | d & ~a) + k[0] - 373897302 | 0;
+ b = (b << 20 | b >>> 12) + c | 0;
+ a += (b & d | c & ~d) + k[5] - 701558691 | 0;
+ a = (a << 5 | a >>> 27) + b | 0;
+ d += (a & c | b & ~c) + k[10] + 38016083 | 0;
+ d = (d << 9 | d >>> 23) + a | 0;
+ c += (d & b | a & ~b) + k[15] - 660478335 | 0;
+ c = (c << 14 | c >>> 18) + d | 0;
+ b += (c & a | d & ~a) + k[4] - 405537848 | 0;
+ b = (b << 20 | b >>> 12) + c | 0;
+ a += (b & d | c & ~d) + k[9] + 568446438 | 0;
+ a = (a << 5 | a >>> 27) + b | 0;
+ d += (a & c | b & ~c) + k[14] - 1019803690 | 0;
+ d = (d << 9 | d >>> 23) + a | 0;
+ c += (d & b | a & ~b) + k[3] - 187363961 | 0;
+ c = (c << 14 | c >>> 18) + d | 0;
+ b += (c & a | d & ~a) + k[8] + 1163531501 | 0;
+ b = (b << 20 | b >>> 12) + c | 0;
+ a += (b & d | c & ~d) + k[13] - 1444681467 | 0;
+ a = (a << 5 | a >>> 27) + b | 0;
+ d += (a & c | b & ~c) + k[2] - 51403784 | 0;
+ d = (d << 9 | d >>> 23) + a | 0;
+ c += (d & b | a & ~b) + k[7] + 1735328473 | 0;
+ c = (c << 14 | c >>> 18) + d | 0;
+ b += (c & a | d & ~a) + k[12] - 1926607734 | 0;
+ b = (b << 20 | b >>> 12) + c | 0;
+ // hh()
+ a += (b ^ c ^ d) + k[5] - 378558 | 0;
+ a = (a << 4 | a >>> 28) + b | 0;
+ d += (a ^ b ^ c) + k[8] - 2022574463 | 0;
+ d = (d << 11 | d >>> 21) + a | 0;
+ c += (d ^ a ^ b) + k[11] + 1839030562 | 0;
+ c = (c << 16 | c >>> 16) + d | 0;
+ b += (c ^ d ^ a) + k[14] - 35309556 | 0;
+ b = (b << 23 | b >>> 9) + c | 0;
+ a += (b ^ c ^ d) + k[1] - 1530992060 | 0;
+ a = (a << 4 | a >>> 28) + b | 0;
+ d += (a ^ b ^ c) + k[4] + 1272893353 | 0;
+ d = (d << 11 | d >>> 21) + a | 0;
+ c += (d ^ a ^ b) + k[7] - 155497632 | 0;
+ c = (c << 16 | c >>> 16) + d | 0;
+ b += (c ^ d ^ a) + k[10] - 1094730640 | 0;
+ b = (b << 23 | b >>> 9) + c | 0;
+ a += (b ^ c ^ d) + k[13] + 681279174 | 0;
+ a = (a << 4 | a >>> 28) + b | 0;
+ d += (a ^ b ^ c) + k[0] - 358537222 | 0;
+ d = (d << 11 | d >>> 21) + a | 0;
+ c += (d ^ a ^ b) + k[3] - 722521979 | 0;
+ c = (c << 16 | c >>> 16) + d | 0;
+ b += (c ^ d ^ a) + k[6] + 76029189 | 0;
+ b = (b << 23 | b >>> 9) + c | 0;
+ a += (b ^ c ^ d) + k[9] - 640364487 | 0;
+ a = (a << 4 | a >>> 28) + b | 0;
+ d += (a ^ b ^ c) + k[12] - 421815835 | 0;
+ d = (d << 11 | d >>> 21) + a | 0;
+ c += (d ^ a ^ b) + k[15] + 530742520 | 0;
+ c = (c << 16 | c >>> 16) + d | 0;
+ b += (c ^ d ^ a) + k[2] - 995338651 | 0;
+ b = (b << 23 | b >>> 9) + c | 0;
+ // ii()
+ a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;
+ a = (a << 6 | a >>> 26) + b | 0;
+ d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;
+ d = (d << 10 | d >>> 22) + a | 0;
+ c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;
+ c = (c << 15 | c >>> 17) + d | 0;
+ b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;
+ b = (b << 21 |b >>> 11) + c | 0;
+ a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;
+ a = (a << 6 | a >>> 26) + b | 0;
+ d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;
+ d = (d << 10 | d >>> 22) + a | 0;
+ c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;
+ c = (c << 15 | c >>> 17) + d | 0;
+ b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;
+ b = (b << 21 |b >>> 11) + c | 0;
+ a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;
+ a = (a << 6 | a >>> 26) + b | 0;
+ d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;
+ d = (d << 10 | d >>> 22) + a | 0;
+ c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;
+ c = (c << 15 | c >>> 17) + d | 0;
+ b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;
+ b = (b << 21 |b >>> 11) + c | 0;
+ a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;
+ a = (a << 6 | a >>> 26) + b | 0;
+ d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;
+ d = (d << 10 | d >>> 22) + a | 0;
+ c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;
+ c = (c << 15 | c >>> 17) + d | 0;
+ b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;
+ b = (b << 21 | b >>> 11) + c | 0;
+
+ x[0] = a + x[0] | 0;
+ x[1] = b + x[1] | 0;
+ x[2] = c + x[2] | 0;
+ x[3] = d + x[3] | 0;
+ };
+
+ var hexChars = '0123456789abcdef';
+ var hexOut = [];
+
+ var hex = function(x) {
+ var hc = hexChars;
+ var ho = hexOut;
+ var n, offset, j;
+ for (var i = 0; i < 4; i++) {
+ offset = i * 8;
+ n = x[i];
+ for ( j = 0; j < 8; j += 2 ) {
+ ho[offset+1+j] = hc.charAt(n & 0x0F);
+ n >>>= 4;
+ ho[offset+0+j] = hc.charAt(n & 0x0F);
+ n >>>= 4;
+ }
+ }
+ return ho.join('');
+ };
+
+ var MD5 = function() {
+ this._dataLength = 0;
+ this._state = new Int32Array(4);
+ this._buffer = new ArrayBuffer(68);
+ this._bufferLength = 0;
+ this._buffer8 = new Uint8Array(this._buffer, 0, 68);
+ this._buffer32 = new Uint32Array(this._buffer, 0, 17);
+ this.start();
+ };
+
+ var stateIdentity = new Int32Array([1732584193, -271733879, -1732584194, 271733878]);
+ var buffer32Identity = new Int32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+ // Char to code point to to array conversion:
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt#Example.3A_Fixing_charCodeAt_to_handle_non-Basic-Multilingual-Plane_characters_if_their_presence_earlier_in_the_string_is_unknown
+ MD5.prototype.appendStr = function(str) {
+ var buf8 = this._buffer8;
+ var buf32 = this._buffer32;
+ var bufLen = this._bufferLength;
+ var code;
+ for ( var i = 0; i < str.length; i++ ) {
+ code = str.charCodeAt(i);
+ if ( code < 128 ) {
+ buf8[bufLen++] = code;
+ } else if ( code < 0x800 ) {
+ buf8[bufLen++] = (code >>> 6) + 0xC0;
+ buf8[bufLen++] = code & 0x3F | 0x80;
+ } else if ( code < 0xD800 || code > 0xDBFF ) {
+ buf8[bufLen++] = (code >>> 12) + 0xE0;
+ buf8[bufLen++] = (code >>> 6 & 0x3F) | 0x80;
+ buf8[bufLen++] = (code & 0x3F) | 0x80;
+ } else {
+ code = ((code - 0xD800) * 0x400) + (str.charCodeAt(++i) - 0xDC00) + 0x10000;
+ if ( code > 0x10FFFF ) {
+ throw 'Unicode standard supports code points up to U+10FFFF';
+ }
+ buf8[bufLen++] = (code >>> 18) + 0xF0;
+ buf8[bufLen++] = (code >>> 12 & 0x3F) | 0x80;
+ buf8[bufLen++] = (code >>> 6 & 0x3F) | 0x80;
+ buf8[bufLen++] = (code & 0x3F) | 0x80;
+ }
+ if ( bufLen >= 64 ) {
+ this._dataLength += 64;
+ md5cycle(this._state, buf32);
+ bufLen -= 64;
+ buf32[0] = buf32[16];
+ }
+ }
+ this._bufferLength = bufLen;
+ return this;
+ };
+
+ MD5.prototype.appendAsciiStr = function(str) {
+ var buf8 = this._buffer8;
+ var buf32 = this._buffer32;
+ var bufLen = this._bufferLength;
+ var i, j = 0;
+ for (;;) {
+ i = Math.min(str.length-j, 64-bufLen);
+ while ( i-- ) {
+ buf8[bufLen++] = str.charCodeAt(j++);
+ }
+ if ( bufLen < 64 ) {
+ break;
+ }
+ this._dataLength += 64;
+ md5cycle(this._state, buf32);
+ bufLen = 0;
+ }
+ this._bufferLength = bufLen;
+ return this;
+ };
+
+ MD5.prototype.appendByteArray = function(input) {
+ var buf8 = this._buffer8;
+ var buf32 = this._buffer32;
+ var bufLen = this._bufferLength;
+ var i, j = 0;
+ for (;;) {
+ i = Math.min(input.length-j, 64-bufLen);
+ while ( i-- ) {
+ buf8[bufLen++] = input[j++];
+ }
+ if ( bufLen < 64 ) {
+ break;
+ }
+ this._dataLength += 64;
+ md5cycle(this._state, buf32);
+ bufLen = 0;
+ }
+ this._bufferLength = bufLen;
+ return this;
+ };
+
+ MD5.prototype.start = function() {
+ this._dataLength = 0;
+ this._bufferLength = 0;
+ this._state.set(stateIdentity);
+ return this;
+ };
+
+ MD5.prototype.end = function(raw) {
+ var bufLen = this._bufferLength;
+ this._dataLength += bufLen;
+ var buf8 = this._buffer8;
+ buf8[bufLen] = 0x80;
+ buf8[bufLen+1] = buf8[bufLen+2] = buf8[bufLen+3] = 0;
+ var buf32 = this._buffer32;
+ var i = (bufLen >> 2) + 1;
+ buf32.set(buffer32Identity.subarray(i), i);
+ if (bufLen > 55) {
+ md5cycle(this._state, buf32);
+ buf32.set(buffer32Identity);
+ }
+ // Do the final computation based on the tail and length
+ // Beware that the final length may not fit in 32 bits so we take care of that
+ var dataBitsLen = this._dataLength * 8;
+ if ( dataBitsLen <= 0xFFFFFFFF ) {
+ buf32[14] = dataBitsLen;
+ } else {
+ var matches = dataBitsLen.toString(16).match(/(.*?)(.{0,8})$/);
+ var lo = parseInt(matches[2], 16);
+ var hi = parseInt(matches[1], 16) || 0;
+ buf32[14] = lo;
+ buf32[15] = hi;
+ }
+ md5cycle(this._state, buf32);
+
+ return !!raw ? this._state : hex(this._state);
+ };
+
+ // This permanent instance is to use for one-call hashing
+ var onePassHasher = new MD5();
+
+ MD5.hashStr = function(str, raw) {
+ return onePassHasher
+ .start()
+ .appendStr(str)
+ .end(raw);
+ };
+
+ MD5.hashAsciiStr = function(str, raw) {
+ return onePassHasher
+ .start()
+ .appendAsciiStr(str)
+ .end(raw);
+ };
+
+ // Self-test
+ // In some cases the fast add32 function cannot be used..
+ if ( MD5.hashStr('hello') !== '5d41402abc4b2a76b9719d911017c592' ) {
+ console.error('YaMD5> this javascript engine does not support YaMD5. Sorry.');
+ }
+
+ if ( typeof root === 'object' ) {
+ root.YaMD5 = MD5;
+ }
+ return MD5;
+})(this);
diff --git a/locale/am/messages.properties b/locale/am/messages.properties
new file mode 100644
index 0000000..060ae58
--- /dev/null
+++ b/locale/am/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Settings
+privacyPageName=Privacy
+statsPageName=Statistics
+userRulesPageName=My rules
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=About
+allPrettyName=all
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/ar/messages.properties b/locale/ar/messages.properties
new file mode 100644
index 0000000..a60fc2e
--- /dev/null
+++ b/locale/ar/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — لوحة التحكم
+loggerPageName=uMatrix — المسجل
+settingsPageName=الإعدادات
+privacyPageName=خصوصية
+statsPageName=الإحصائيات
+userRulesPageName=قواعدي
+ubiquitousRulesPageName=ملفات المستضيف
+rawSettingsPageName=المزيد
+aboutPageName=حول البرنامج
+allPrettyName=الكل
+cookiePrettyName=كوكي
+cssPrettyName=ستايل
+imagePrettyName=الصُورَة
+mediaPrettyName=ميديا
+pluginPrettyName=إضافة
+scriptPrettyName=سكربت
+xhrPrettyName=XHR
+framePrettyName=إطار
+otherPrettyName=أخرى
+matrixNoNetTrafficPrompt=لم يحدث اي اتصال بهذا التبويب حتى الآن.
+matrixMtxButtonTip=تعطيل / تمكين ميزة التصفيه لهذا النطاق.
+matrixPersistButtonTip=حفظ جميع التغييرات المؤقتة لهذا النطاق.
+matrixRevertButtonTip=الرجوع عن التغييرات المؤقتة لهذا النطاق.
+matrixReloadButton=إعادة تحميل الصفحة.
+matrix1stPartyLabel=الجزء الاول
+matrixBlacklistedHostnames=القائمة السوداء {{count}}
+matrixSwitchNoMixedContent=الاتصال الآمن الصارم
+matrixSwitchNoWorker=منع web workers
+matrixSwitchReferrerSpoof=الإحالات بالتحايل
+matrixSwitchNoscriptSpoof=محاكات وسمات <code><noscript></code>
+matrixRevertAllEntry=إعادة كافة التغييرات المؤقتة
+matrixLoggerMenuEntry=الذهاب إلى مسجل
+matrixDashboardMenuEntry=انتقل إلى لوحة التحكم الرئيسية
+matrixNoTabFound=لم يتم العثور على أي صفحة ويب
+statsPageTitle=uMatrix &ndash; إحصائيات
+statsPageGenericStats=إحصاءات عامة
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP ملف تعريف الارتباط</a> أحبطت foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP تشير</a> headers أحبطت: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'> مراجعة الارتباط التشعبي</a> أحبطت محاولات: {{count}}
+statsPageCookiesRemoved=ملفات تعريف الارتباط المحلية إزالة: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'> المخازن المحلية</a> إفراغ: {{count}}
+statsPageBrowserCacheCleared=مسح تخزين المستعرض: {{count}}
+statsPageDetailedStats=إحصائيات مفصلة
+statsPageDetailedAllPages=الكل
+statsPageDetailedBehindTheScenePage=خلف الكواليس
+statsPageOverview=نظرة عامة
+statsPageRequests=طلبات
+statsPageAllowed=السماح
+statsPageBlocked=حظر
+statsPageAll=الكل
+statsPagePages=الصفحات
+statsPageCookies=ملفات تعريف الارتباط
+statsPageCSS=ستايل
+statsPageImages=الصُوَرْ
+statsPagePlugins=الإضافات
+statsPageScripts=البرامج النصية
+statsPageXHRs=XHRs
+statsPageFrames=إطارات
+statsPageOthers=الآخرين
+statsPageDetailed=مسجل
+statsPageLogSizePrompt1=تذكر الماضي
+statsPageLogSizePrompt2=HTTP طلبات <b>لكل صفحة</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=تحديث الصفحة
+settingsPageTitle=uMatrix &ndash; إعدادات
+settingsMatrixDisplayHeader=مظهر
+settingsMatrixDisplayTextSizePrompt=حجم الخط:
+settingsMatrixDisplayTextSizeNormal=‮عادي
+settingsMatrixDisplayTextSizeLarge=كبير
+settingsMatrixDisplayColorBlind=عمي الألوان
+settingsMatrixConvenienceHeader=ملاءمة
+settingsDefaultScopeLevel=المستوى الافتراضي للمنظار:
+settingsDefaultScopeLevel0=عام
+settingsDefaultScopeLevel1=النطاق
+settingsDefaultScopeLevel2=الموقع
+settingsMatrixAutoReloadPrompt=عند إغلاق مصفوفة، وذكية تحميل علامات التبويب هذه:
+settingsMatrixAutoReloadNone=لا شيء
+settingsMatrixAutoReloadCurrent=قائم
+settingsMatrixAutoReloadAll=الكل
+settingsMatrixAutoReloadInfo=كلما قمت بإجراء تغييرات في المصفوفة التي يمكن أن تؤثر على العرض و/ أو سلوك صفحة واحدة أو أكثر،<i>uMatrix</i>سوف تحميل صفحات المتضررة تلقائيا عند إغلاق المصفوفة.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=الشفافية
+settingsIconBadgeEnabled=عرض عدد من طلبات متميزة على أيقونة
+settingsCollapseBlocked=انهيار نائبا من عناصر المحظورة
+settingsCollapseBlacklisted=تقليص مكان العناصر المدرجة في القائمة السوداء
+settingsNoscriptTagsSpoofed=محاكات وسمات <code><noscript></code> عند حظر سكريبتات الطرف الأول
+settingsCloudStorageEnabled=تمكين سحابة دعم التخزين
+privacyPageTitle=uMatrix &ndash; الخصوصية
+privacyDeleteBlockedCookiesPrompt=حذف ملفات تعريف الارتباط المحظورة.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=حذف ملفات تعريف الارتباط جلسة
+privacyDeleteNonBlockedSessionCookiesPrompt2= دقيقة بعد آخر مرة استخدمت فيها.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=مخبأ المتصفح واضح كل
+privacyClearCachePrompt2=دقائق.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup style='font-size:smaller'>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p style='font-size:smaller'>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a><br>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=<p>From Wikipedia:</p><blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote><p>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: لا سمح المحتوى المختلط.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=قواعد دائمة
+userRulesTemporaryHeader=القواعد المؤقتة
+userRulesRevert=إرجاع
+userRulesCommit=يلتزم
+userRulesEdit=تحرير
+userRulesEditSave=حفظ
+userRulesEditDicard=تخلص
+userRulesImport=إستيراد من ملف...
+userRulesExport=تصدير إلى ملف...
+userRulesFormatHint=رؤية هذه الصفحة لتركيب القاعدة.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=يتم تحميل جميع أسماء المضيفين في ملف المضيفين كما المضيفين القائمة السوداء في نطاق عالمي.
+hostsFilesStats={{blockedHostnameCount}} أسماء المضيفين منعت متميزة عن:
+hostsFilesPerFileStats={{used}} تستخدم من {{total}}
+hostsFilesLastUpdate=اخر تحديث: {{ago}}
+hostsFilesApplyChanges=تطبيق التغييرات
+hostsFilesAutoUpdatePrompt=التحديث التلقائي الملفات.
+hostsFilesUpdateNow=تحديث الآن
+hostsFilesPurgeAll=تطهير جميع مخابئ
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=تحليل
+hostsFilesExternalListPurge=مخبأ تطهير
+hostsFilesExternalListNew=يوجد إصدار جديد
+hostsFilesExternalListObsolete=عفا عليها الزمن
+rawSettingsWarning=تحذير! غير إعدادات الضبط الخام على مسؤوليتك الخاصة.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>تغيير log</a>
+aboutStorageUsed=التخزين المستخدمة: {{storageUsed}} بايت
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>توثيق</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>ضوابط</a>
+aboutCode=شفرة المصدر (GPLv3)
+aboutIssues=الخطا وقضايا
+aboutContributors=المساهمون
+aboutCodeContributors=رمز:
+aboutIssueContributors=قضايا:
+aboutTranslationContributors=الترجمات:
+aboutUserDataHeader=البيانات الخاصة بك
+aboutBackupButton=النسخ الاحتياطي لملف...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=استعادة من ملف...
+aboutRestoreConfirm=سيتم الكتابة فوق كل ما تبذلونه من إعدادات استخدام البيانات احتياطيا على {{time}}، \n\nوسوف uMatrix إعادة تشغيل. الكتابة عن الإعدادات الموجودة باستخدام البيانات احتياطيا؟?
+aboutRestoreError=البيانات لا يمكن قراءة أو غير صالح
+aboutOr=... او ...
+aboutResetButton=إعادة تعيين إلى الإعدادات الافتراضية
+aboutResetConfirm=الحذر! سيؤدي هذا إلى إزالة كافة الإعدادات المخصصة الخاصة بك. هل أنت متأكد أنك تريد المتابعة؟?
+loggerFilterInputPlaceholder=فلتر مرشح
+loggerMaxEntriesTip=الحد الأقصى لعدد المحاولات
+loggerEntryCookieDeleted=حذف الارتباط: {{value}}
+loggerEntryDeleteCookieError=فشل في حذف الارتباط: {{value}}
+loggerEntryBrowserCacheCleared=مخبأ المتصفح مسح
+loggerEntryAssetUpdated=الأصول تحديث: {{value}}
+mainBlockedPrompt1=منعت uMatrix على الصفحة التالية من التحميل:
+mainBlockedPrompt2=بسبب القاعدة التالية
+mainBlockedBack=الرجوع للخلف
+mainBlockedClose=مغلق
+commandRevertAll=إعادة كافة التغييرات المؤقتة
+commandWhitelistPageDomain=نطاق الصفحة القائمة البيضاء مؤقتا
+commandWhitelistAll=مؤقتا القائمة البيضاء فقط
+commandOpenDashboard=فتح لوحة القيادة
+elapsedOneMinuteAgo=منذ 1 دقيقة
+elapsedManyMinutesAgo={{value}} دقائق مضت
+elapsedOneHourAgo=قبل ساعه
+elapsedManyHoursAgo={{value}} منذ ساعات
+elapsedOneDayAgo=منذ يوم
+elapsedManyDaysAgo={{value}} أيام مضت
+showDashboardButton=لوحة التحكم
+showLoggerButton=مسجل
+cloudPush=التصدير إلى سحابة التخزين
+cloudPull=الاستيراد من سحابة التخزين
+cloudNoData=...\n...
+cloudDeviceNamePrompt=هذا اسم الجهاز:
+genericSubmit=تأكيد
+genericRevert=إرجاع
+errorCantConnectTo=خطأ في الشبكة: غير قادر على الاتصال {{url}}
+genericApplyChanges=تطبيق التغييرات
diff --git a/locale/bg/messages.properties b/locale/bg/messages.properties
new file mode 100644
index 0000000..54de2c6
--- /dev/null
+++ b/locale/bg/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Tабло
+loggerPageName=uMatrix — Logger
+settingsPageName=Настройки
+privacyPageName=Поверителност
+statsPageName=Статистика
+userRulesPageName=Мои правила
+ubiquitousRulesPageName=Файлове с хостове
+rawSettingsPageName=More
+aboutPageName=Относно
+allPrettyName=всичко
+cookiePrettyName=бискв
+cssPrettyName=css
+imagePrettyName=изобр
+mediaPrettyName=media
+pluginPrettyName=приставка
+scriptPrettyName=скрипт
+xhrPrettyName=XHR
+framePrettyName=рамка
+otherPrettyName=друго
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Запис на всички временни промени в тази област.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Презареждане на страницата.
+matrix1stPartyLabel=текущ домейн
+matrixBlacklistedHostnames=сайтове в черния списък: {{count}}
+matrixSwitchNoMixedContent=Само HTTPS
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Отмяна на всички временни промени
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Към контролното табло
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Статистика
+statsPageGenericStats=Обща статистика
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Изчистен е кешът в браузъра: {{count}}
+statsPageDetailedStats=Подробна статистика
+statsPageDetailedAllPages=Всичко
+statsPageDetailedBehindTheScenePage=Скрити заявки
+statsPageOverview=Общ преглед
+statsPageRequests=Заявки
+statsPageAllowed=Позволено
+statsPageBlocked=Блокирано
+statsPageAll=Всичко
+statsPagePages=Страници
+statsPageCookies=Бисквитки
+statsPageCSS=CSS
+statsPageImages=Изображения
+statsPagePlugins=Приставки
+statsPageScripts=Скриптове
+statsPageXHRs=XHR-и
+statsPageFrames=Рамки
+statsPageOthers=Други
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Запомняне на последното
+statsPageLogSizePrompt2=HTTP заявки <b>за страница</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Опресняване
+settingsPageTitle=uMatrix &ndash; Настройки
+settingsMatrixDisplayHeader=Изглед
+settingsMatrixDisplayTextSizePrompt=Големина на текста:
+settingsMatrixDisplayTextSizeNormal=Нормален
+settingsMatrixDisplayTextSizeLarge=Голям
+settingsMatrixDisplayColorBlind=Цветова схема за далтонисти
+settingsMatrixConvenienceHeader=Удобство
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=Затваряйки матрицата, започва умно презареждане на тези табове:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Текуща версия
+settingsMatrixAutoReloadAll=Всичко
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Блокирани рамки:&ensp;Цвят
+settingsSubframeOpacity=Непрозрачност
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Поверителност
+privacyDeleteBlockedCookiesPrompt=Изтриване на блокираните бисквитки.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Изтриване на неблокираните бисквитки за сесията
+privacyDeleteNonBlockedSessionCookiesPrompt2= минути след последната употреба.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Изтрийте съдържанието на <a href='https://ru.wikipedia.org/wiki/Web_storage'>локалното хранилище</a> от блокирани имена на хостове
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Изчиства кеша на браузъра на всеки
+privacyClearCachePrompt2=минути.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Само HTTPS: забранено е смесено съдържание.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Постоянни правила
+userRulesTemporaryHeader=Временни правила
+userRulesRevert=Връщане
+userRulesCommit=Commit
+userRulesEdit=Редактиране
+userRulesEditSave=Запис
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=Правилата за синтаксиса можете да видите на тази страница.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Всички сайтове във файла с хостове са заредени като блокирани на глобално ниво.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} използвано от {{total}}
+hostsFilesLastUpdate=Последно обновяване: {{ago}}
+hostsFilesApplyChanges=Прилагане на промените
+hostsFilesAutoUpdatePrompt=Автообновление на файловете от хостовете.
+hostsFilesUpdateNow=Обновяване сега
+hostsFilesPurgeAll=Очистване на всичкия кеш
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Анализиране
+hostsFilesExternalListPurge=очистване на кеша
+hostsFilesExternalListNew=налична е нова версия
+hostsFilesExternalListObsolete=остаряло
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Документация</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Позволения</a>
+aboutCode=Изходен код (GPLv3)
+aboutIssues=Бъгове и проблеми
+aboutContributors=Сътрудници
+aboutCodeContributors=Код:
+aboutIssueContributors=Проблеми:
+aboutTranslationContributors=Преводи:
+aboutUserDataHeader=Вашите данни
+aboutBackupButton=Правене на резервно копие...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Възстановяване от файл...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=Данните не могат да се прочетат или имат грешки
+aboutOr=... или ...
+aboutResetButton=Нулиране до първоначалните настройки
+aboutResetConfirm=Внимание! Това ще премахне всичките ви ръчни настройки. Наистина ли искате да продължите?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Максимален брой записи
+loggerEntryCookieDeleted=бисквитката е изтрита: {{value}}
+loggerEntryDeleteCookieError=пропадна изтриването на бисквитката: {{value}}
+loggerEntryBrowserCacheCleared=кешът в браузъра е изчистен
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix попречи зареждането на следната страница:
+mainBlockedPrompt2=Заради следното правило
+mainBlockedBack=Обратно
+mainBlockedClose=Затваряне
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Временно позволяване на домейна на страницата
+commandWhitelistAll=Временно позволение на всички
+commandOpenDashboard=Отваряне на контролното табло
+elapsedOneMinuteAgo=преди минута
+elapsedManyMinutesAgo=преди {{value}} минути
+elapsedOneHourAgo=преди час
+elapsedManyHoursAgo=преди {{value}} часа
+elapsedOneDayAgo=преди ден
+elapsedManyDaysAgo=преди {{value}} дни
+showDashboardButton=Контролно табло
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=…
+cloudDeviceNamePrompt=Името на това устройство:
+genericSubmit=Подаване
+genericRevert=Връщане
+errorCantConnectTo=Грешка в мрежата: не може да се свърже към {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/bn/messages.properties b/locale/bn/messages.properties
new file mode 100644
index 0000000..d3f39b3
--- /dev/null
+++ b/locale/bn/messages.properties
@@ -0,0 +1,179 @@
+extName=uম্যাট্রিক্স
+dashboardPageName=uম্যাট্রিক্স — ড্যাশবোর্ড
+loggerPageName=uMatrix — Logger
+settingsPageName=সেটিংস
+privacyPageName=গোপনীয়তা
+statsPageName=পরিসংখ্যান
+userRulesPageName=আমার নিয়ম
+ubiquitousRulesPageName=নিমন্ত্রক ফাইল
+rawSettingsPageName=More
+aboutPageName=সম্পর্কে
+allPrettyName=সব
+cookiePrettyName=কুকি
+cssPrettyName=css
+imagePrettyName=চিত্র
+mediaPrettyName=মিডিয়া
+pluginPrettyName=প্লাগইন
+scriptPrettyName=স্ক্রিপ্ট
+xhrPrettyName=XHR
+framePrettyName=ফ্রেম
+otherPrettyName=অন্যান্য
+matrixNoNetTrafficPrompt=এখন পর্যন্ত এই ট্যাবের জন্য কোন নেট ট্রাফিক দেখা যায়নি।
+matrixMtxButtonTip=এই ব্যাপ্তির জন্য ম্যাট্রিক্স পরিশোধন সক্রিয়/নিষ্ক্রিয় করুন।
+matrixPersistButtonTip=এই ব্যাপ্তির জন্য সব অস্থায়ী পরিবর্তনগুলি সংরক্ষণ করুন।
+matrixRevertButtonTip=এই ব্যাপ্তির জন্য অস্থায়ী পরিবর্তনগুলি প্রত্যাবর্তন করুন।
+matrixReloadButton=পৃষ্ঠাটি পুনঃলোড করুন।
+matrix1stPartyLabel=১ম-দল
+matrixBlacklistedHostnames={{count}}টি কালোতালিকাভুক্ত হোস্টেরনাম
+matrixSwitchNoMixedContent=স্ক্রিপ্ট HTTPS
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=রেফারার স্পুফিং
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=সব অস্থায়ী পরিবর্তন প্রত্যাবর্তন করুন
+matrixLoggerMenuEntry=লগারে যান
+matrixDashboardMenuEntry=ড্যাশবোর্ডে যান
+matrixNoTabFound=No web page found
+statsPageTitle=uম্যাট্রিক্স &ndash; পরিসংখ্যান
+statsPageGenericStats=জেনেরিক পরিসংখ্যান
+statsPageCookieHeadersFoiled=<a href='https://bn.wikipedia.org/wiki/এইচটিটিপি_কুকি'>এইচটিটিপি কুকি</a> হেডার ব্যর্থ: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://bn.wikipedia.org/wiki/এইচটিটিপি_রেফার'>এইচটিটিপি রেফার</a> হেডার ব্যর্থ: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>হাইপারলিঙ্ক নিরীক্ষণ</a> প্রচেষ্টা ব্যর্থ: {{count}}
+statsPageCookiesRemoved=স্থানীয় কুকি সরানো হয়েছে: {{count}}টি
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>স্থানীয় সংগ্রহস্থল</a> খালি: {{count}}টি
+statsPageBrowserCacheCleared=ব্রাউজারের ক্যাশে পরিষ্কার হয়েছে: {{count}}টি
+statsPageDetailedStats=বিস্তারিত পরিসংখ্যান
+statsPageDetailedAllPages=সব
+statsPageDetailedBehindTheScenePage=পর্দার আড়ালে
+statsPageOverview=সংক্ষিপ্ত বিবরণ
+statsPageRequests=অনুরোধ
+statsPageAllowed=অনুমোদিত
+statsPageBlocked=অবরুদ্ধ
+statsPageAll=সব
+statsPagePages=পৃষ্ঠা
+statsPageCookies=কুকি
+statsPageCSS=CSS
+statsPageImages=চিত্র
+statsPagePlugins=প্লাগইন
+statsPageScripts=স্ক্রিপ্ট
+statsPageXHRs=XHRs
+statsPageFrames=ফ্রেম
+statsPageOthers=অন্যান্য
+statsPageDetailed=লগার
+statsPageLogSizePrompt1=শেষটি মনে রাখুন
+statsPageLogSizePrompt2=<b>পাতা প্রতি</b> HTTP অনুরোধ।
+statsPageLogSizeHelp=<p>আপনি একটি ওয়েব পাতা দ্বারা তৈরিকৃত সাম্প্রতিকতম কাঁচা HTTP অনুরোধের বিস্তারিত পরিদর্শন করতে পারেন (নিচে দেখুন)।</p><p>এটি উন্নত ব্যবহারকারীদের জন্য মূলত দরকারী যারা একটি ওয়েব পাতা কি কাজ করছে তা তদন্ত করতে চান। কিন্তু এই HTTP অনুরোধে লগিং করার জন্য মেমরি প্রয়োজন, এবং আপনি যদি এই প্রযুক্তিগত তথ্যের প্রতি যত্নশীল না হোন, তাহলে মেমরির অপচয় হবে।</p><p>তাই এই ক্ষেত্র যা সর্বোচ্চ সংখ্যক আপনার সবচেয়ে সাম্প্রতিক HTTP অনুরোধ সমন্বয় করতে দেয় যার ফলে আরও পরিদর্শনের জন্য লগকৃত করা হয়।</p><p>বিস্তারিত লগিং বন্ধ করতে &ldquo;<code>0</code>&rdquo; লিখুন (এবং ফলস্বরূপ <i>uম্যাট্রিক্সের</i> মেমরির পদচিহ্ন কমে)।</p>
+statsPageRefresh=পুন:সতেজ
+settingsPageTitle=uম্যাট্রিক্স &ndash; সেটিংস
+settingsMatrixDisplayHeader=উপস্থিতি
+settingsMatrixDisplayTextSizePrompt=লেখার মাপ:
+settingsMatrixDisplayTextSizeNormal=সাধারণ
+settingsMatrixDisplayTextSizeLarge=বড়
+settingsMatrixDisplayColorBlind=বর্ণান্ধ বন্ধুত্বপূর্ণ
+settingsMatrixConvenienceHeader=সুবিধা
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=ম্যাট্রিক্স যখন বন্ধ হয়ে যায়, স্মার্ট এই ট্যাবগুলি পুনঃলোড করে:
+settingsMatrixAutoReloadNone=কিছুই না
+settingsMatrixAutoReloadCurrent=বর্তমান
+settingsMatrixAutoReloadAll=সব
+settingsMatrixAutoReloadInfo=যখনই আপনি ম্যাট্রিক্সে পরিবর্তন করবেন এটি এক বা একাধিক পাতা প্রদর্শনের আচরণ প্রভাবিত করতে পারে, আপনি ম্যাট্রিক্স বন্ধ করলে <i>uম্যাট্রিক্স</i> স্বয়ংক্রিয়ভাবে প্রভাবিত পৃষ্ঠাগুলি পুনরায় লোড করবে।
+settingsSubframeColor=অবরুদ্ধ ফ্রেম:&ensp;রং
+settingsSubframeOpacity=অস্বচ্ছতা
+settingsIconBadgeEnabled=আইকনের উপর স্বতন্ত্র অনুরোধের সংখ্যা দেখান
+settingsCollapseBlocked=অবরুদ্ধ উপাদান সংকোচনের স্থানধারক
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=ক্লাউড সঞ্চয়স্থান সমর্থন সক্রিয় করুন
+privacyPageTitle=uম্যাট্রিক্স &ndash; গোপনীয়তা
+privacyDeleteBlockedCookiesPrompt=অবরুদ্ধ কুকি মুছুন।
+privacyDeleteBlockedCookiesHelp=<p>কালোতালিকাভুক্ত কুকি <i>uম্যাট্রিক্স</i> দ্বারা আপনার ব্রাউজারে প্রবেশ করা থেকে প্রতিহত করা হয়না। তবে তাদের আপনার ব্রাউজার থেকে ছেড়ে চলে যাওয়ার সময় প্রতিহত করা হয়, যা সত্যিই গুরুত্বপূর্ণ। তারা আপনার ব্রাউজার প্রবেশ করার আগে কুকি অবরুদ্ধ না করা আপনাকে অবগত করার সুযোগ করে দেয় যে একটি সাইট কুকি ব্যবহার করার চেষ্টা করছে, এবং উপরন্তু তাদের বিষয়বস্তুর পরিদর্শন করা (যদি আপনি চান)।</p><p><i>uম্যাট্রিক্স</i> দ্বারা এইসব কালোতালিকাভুক্ত কুকি একবার দায়ী করা হলে, আপনি চাইলে তাদের আপনি আপনার ব্রাউজার থেকে মুছে ফেলতে পারেন।</p><p><b>গুরুত্বপূর্ণ তথ্য:</b> এক্সটেনশানগুলি তাদের স্বাভাবিক অপারেশনের সময় ওয়েব অনুরোধ করতে পারে। এই অনুরোধগুলির ফলাফল ব্রাউজারে কুকি তৈরি করা হচ্ছে হতে পারে। একটি কুকির উৎপত্তি সাদাতালিকাভুক্ত হোস্টনেম থেকে না হলে, <i>uম্যাট্রিক্স</i> দ্বারা কুকি ব্রাউজার থেকে সরানো হবে যদি এই অপশনটি সক্রিয় করা থাকে। তাই নিশ্চিত হোন যে একটি এক্সটেনশনের যোগাযোগকৃত হোস্টনেম(গুলি) সাদাতালিকাভুক্ত।</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=অ-অবরুদ্ধ সেশন কুকি মুছুন
+privacyDeleteNonBlockedSessionCookiesPrompt2= মিনিটের যেটি সর্বশেষ সময়ে ব্যবহৃত হয়েছিল।
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;যখন আপনি ব্রাউজারের সেশন শেষ করেন একটি সেশন কুকি তখন মুছে ফেলা হয়। সেশন কুকি অস্থায়ী মেমরির মধ্যে সংরক্ষিত হয় এবং ব্রাউজার বন্ধ করার পরে অপরিবর্তিত রাখা হয় না।&rdquo;</p><p>সেটি বাদে এটি কিছু ব্রাউজারে <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>নাও ঘটতে পারে</a>। এছাড়াও, কিছু ক্ষেত্রে, সেশন কুকি পরিস্কার করার জন্য ব্রাউজার বন্ধ হলে প্রথম দিকে যথেষ্ট নাও হতে পারে।</p>
+privacyDeleteBlockedLocalStoragePrompt=অবরুদ্ধ হোস্টনেম দ্বারা স্থাপিত <a href='https://bn.wikipedia.org/wiki/ওয়েব_স্টোরেজ'>স্থানীয় সঞ্চয়ের</a> সামগ্রী মুছে ফেলুন
+privacyDeleteBlockedLocalStorageHelp=করণীয়
+privacyClearCachePrompt1=ব্রাউজার ক্যাশে সাফ করুন প্রতি
+privacyClearCachePrompt2=মিনিটে।
+privacyClearCacheHelp=<p>কিছু কিছু ওয়েব সাইট, আপনাকে ট্র্যাকিং করতে সত্যিই নমিত, তারা আপনাকে অনুসরণ করার জন্য এমন সব কৌশল নেয় যা দেখে মনে হবে তারা আপনাকে অনুসরণ করছে না।</p><p>এই রকম কিছু কৌশল<sup style='font-size:smaller'>[১, ২]</sup><a href='https://bn.wikipedia.org/wiki/ওয়েব_ক্যাশে'>ব্রাউজারের ক্যাশে</a> নির্ভর, যার যা বিষয়বস্তু প্রায়ই দীর্ঘস্থায়ী হয়, যেহেতু খুব কমই ব্যবহারকারীদের নিয়মিত তাদের ব্রাউজারের ক্যাশে সাফ করতে সময় ব্যয় করেন।</p><p>নিয়মিত ব্রাউজারের ক্যাশে সাফ সামান্য অসুবিধা আছে (সম্ভাবত আপনি লক্ষ্য করবেন না যখন এটা ঘটে), এবং সুবিধা হল আপনার গোপনীয়তা আক্রমণ করা থেকে এইসব আপত্তিকর ব্যক্তি অনুসরণ প্রতিরোধ করা।</p><p>এই অপশন সক্রিয় করলে <i>uম্যাট্রিক্স</i> আপনার ইচ্ছামত অন্তর, এটা আপনার জন্য করবে।</p><p style='font-size:smaller'>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;ব্রাউজারের ক্যাশে&rdquo;</a><br>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;কুকিবিহীন কুকির মাধ্যমে ওয়েব ট্র্যাকিং রোধ করবে&rdquo;</a></p>
+privacyProcessRefererPrompt=<a href='https://bn.wikipedia.org/wiki/এইচটিটিপি_রেফার'>এইচটিটিপি রেফারের</a> তৃতীয় পক্ষের অনুরোধের স্ট্রিং স্পুফ করুন।
+privacyProcessRefererHelp=<p>উইকিপিডিয়া থেকে:</p><blockquote>এইচটিটিপি রেফারার একটি এইচটিটিপি হেডার ক্ষেত্র যা ওয়েবপাতার ঠিকানা চিহ্নিত করে যা অনুরোধকৃত রিসোর্সে সংযুক্ত থাকে।... <b>যেহেতু রেফারার তথ্য গোপনীয়তার নীতিমালা ভঙ্গ করতে পারে, কিছু ওয়েব ব্রাউজারে ব্যবহারকারীদের রেফারার তথ্য পাঠানো নিষ্ক্রিয় করার অনুমতি দেয়।</b></blockquote><p>যদি সেটিংসে এটি নির্বাচন করা থাকে, <i>uম্যাট্রিক্স</i> HTTP রেফারার তথ্য স্পুফ করবে যদি ডোমেইনের নামের নেট অনুরোধে HTTP রেফারারের ডোমেইনের নাম তৃতীয় পক্ষের হয়।</p>
+privacyNoMixedContentPrompt=কড়া HTTPS: মিশ্রিত সামগ্রী নিষেধ।
+privacyNoMixedContentHelp=<p><a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>মোজিলা ডেভেলপার নেটওয়ার্ক</a> থেকে:</p><blockquote>যদি HTTPS পাতা নিয়মিত, স্পষ্ট পাঠ HTTP-র মাধ্যমে বিষয়বস্তুর উদ্ধার অন্তর্ভুক্ত করে, তাহলে সংযোগ শুধুমাত্র আংশিকভাবে এনক্রিপ্ট করা হয়: এনক্রিপশনবিহীন বিষয়বস্তু স্নিফার থেকে প্রবেশযোগ্য এবং মধ্য আক্রমণকারীদের দ্বারা পরিবর্তনযোগ্য, এবং সেইজন্য সংযোগ আর সুরক্ষিত থাকে না। যখন একটি ওয়েবপাতা এই আচরণ প্রদর্শন করে, তখন এটিকে একটি মিশ্র বিষয়বস্তু পাতা বলা হয়।</blockquote>
+privacyProcessHyperlinkAuditingPrompt=সব <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>হাইপারলিঙ্ক নিরীক্ষণ</a> প্রচেষ্টা অবরুদ্ধ।
+privacyProcessHyperlinkAuditingHelp=<p>হাইপারলিঙ্ক নিরীক্ষণ একটি প্রক্রিয়া যা একটি দল, <b>যে কোন দলকে</b> একজন ব্যবহারকারীর একটি নির্দিষ্ট ওয়েব পাতার লিঙ্ক ক্লিক করা সম্পর্কে অবগত করার অনুমতি দেয়। এটি মূলত একটি ট্র্যাকিং বৈশিষ্ট্য: যা একটি ওয়েব সাইট বা কোনো তৃতীয় পক্ষকে আপনি কোন ওয়েব সাইটের কোন ওয়েব পাতা ক্লিক করেছেন তা অবগত করার অনুমতি দেয়। একমাত্র উদ্দেশ্য হল আপনার ব্রাউজিং কার্যকলাপ অনুসরণ করা।</p>
+userRulesPermanentHeader=স্থায়ী নিয়ম
+userRulesTemporaryHeader=অস্থায়ী নিয়ম
+userRulesRevert=প্রত্যাবর্তন
+userRulesCommit=কমিট
+userRulesEdit=সম্পাদনা
+userRulesEditSave=সংরক্ষণ
+userRulesEditDicard=বাতিল করুন
+userRulesImport=ফাইল থেকে আমদানি...
+userRulesExport=ফাইলে রপ্তানি...
+userRulesFormatHint=নিয়ম শব্দবিন্যাসের জন্য এই পাতাটি দেখুন।
+userRulesDefaultFileName=amar-umatrix-niyom.txt
+hostsFilesPrompt=বৈশ্বিক ব্যাপ্তিতে একটি হোস্ট ফাইলের সব হোস্টনেম কালোতালিকাভুক্ত হোস্টনেম হিসাবে লোড করা হয়।
+hostsFilesStats=এখান থেকে {{blockedHostnameCount}}টি স্বতন্ত্র অবরুদ্ধ হোস্টনেম:
+hostsFilesPerFileStats={{total}}টির মধ্যে {{used}}টি ব্যবহৃত
+hostsFilesLastUpdate=সর্বশেষ হালনাগাদ: {{ago}}
+hostsFilesApplyChanges=পরিবর্তনগুলি প্রয়োগ
+hostsFilesAutoUpdatePrompt=হোস্ট ফাইল স্বয়ংক্রিয়-হালনাগাদ করুন।
+hostsFilesUpdateNow=এখনই হালনাগাদ করুন
+hostsFilesPurgeAll=সব ক্যাশে শোধন করুন
+hostsFilesExternalListsHint=লাইন প্রতি একটি URL। &lsquo;#&rsquo; সহ উপসর্গ লাইন উপেক্ষা করা হবে। অকার্যকর URL গুলি নীরবে উপেক্ষা করা হবে।
+hostsFilesExternalListsParse=পার্স
+hostsFilesExternalListPurge=ক্যাশে শোধন করুন
+hostsFilesExternalListNew=নতুন সংস্করণ উপলব্ধ
+hostsFilesExternalListObsolete=পুরোনো
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>পরিবর্তন লগ</a>
+aboutStorageUsed=সংগ্রহস্থল ব্যবহার: {{storageUsed}} বাইট
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>নথিপত্র</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>অনুমতি</a>
+aboutCode=উত্স কোড (GPLv৩)
+aboutIssues=বাগ ও ইস্যু
+aboutContributors=অবদানকারী
+aboutCodeContributors=কোড:
+aboutIssueContributors=ইস্যু:
+aboutTranslationContributors=অনুবাদ:
+aboutUserDataHeader=আপনার উপাত্ত
+aboutBackupButton=ফাইলে ব্যাকআপ...
+aboutBackupFilename=amar-umatrix-backup.txt
+aboutRestoreButton=ফাইল থেকে পুনরুদ্ধার...
+aboutRestoreConfirm={{time}}-এর ডেটা ব্যাকআপ ব্যবহার করে আপনার সেটিংস পুনঃলিখিত হবে, এবং uম্যাট্রিক্স পুনরায় চালু হবে।\n\nডেটা ব্যাকআপ ব্যবহার করে সমস্ত বিদ্যমান সেটিংস পুনঃলিখবেন?
+aboutRestoreError=উপাত্তটি পড়া যায়নি বা অকার্যকর
+aboutOr=... বা ...
+aboutResetButton=ডিফল্ট সেটিংসে পুন:স্থাপন করুন
+aboutResetConfirm=সতর্কতা! এটি আপনার সব স্বনির্ধারিত সেটিংস সরিয়ে দেবে? আপনি কি অগ্রসর হতে চান?
+loggerFilterInputPlaceholder=পরিশোধকের অভিব্যক্তি
+loggerMaxEntriesTip=ভুক্তির সর্বাধিক সংখ্যা
+loggerEntryCookieDeleted=কুকি অপসারিত: {{value}}
+loggerEntryDeleteCookieError=কুকি অপসারণ করতে ব্যর্থ: {{value}}
+loggerEntryBrowserCacheCleared=ব্রাউজারের ক্যাশে পরিষ্কার হয়েছে
+loggerEntryAssetUpdated=অ্যাসেট হালনাগাদকৃত: {{value}}
+mainBlockedPrompt1=uম্যাট্রিক্স নিচের পৃষ্ঠা লোড করাকে প্রতিহত করেছে:
+mainBlockedPrompt2=নিম্নলিখিত নিয়মের কারণে
+mainBlockedBack=ফিরে যান
+mainBlockedClose=বন্ধ
+commandRevertAll=সব অস্থায়ী পরিবর্তন প্রত্যাবর্তন করুন
+commandWhitelistPageDomain=সাময়িকরূপে সাদাতালিকার পাতা ডোমেইন
+commandWhitelistAll=সাময়িকরূপে সাদাতালিকার সব
+commandOpenDashboard=ড্যাশবোর্ড খুলুন
+elapsedOneMinuteAgo=এক মিনিট আগে
+elapsedManyMinutesAgo={{value}} মিনিট আগে
+elapsedOneHourAgo=এক ঘণ্টা আগে
+elapsedManyHoursAgo={{value}} ঘণ্টা আগে
+elapsedOneDayAgo=এক দিন আগে
+elapsedManyDaysAgo={{value}} দিন আগে
+showDashboardButton=ড্যাশবোর্ড
+showLoggerButton=লগার
+cloudPush=ক্লাউড সঞ্চয়স্থানে রপ্তানি করুন
+cloudPull=ক্লাউড সঞ্চয়স্থান থেকে আমদানি করুন
+cloudNoData=...\n...
+cloudDeviceNamePrompt=এই ডিভাইসের নাম:
+genericSubmit=জমা দিন
+genericRevert=প্রত্যাবর্তন
+errorCantConnectTo=নেটওয়ার্ক ত্রুটি: {{url}} -এ সংযোগ করতে অক্ষম
+genericApplyChanges=Apply changes
diff --git a/locale/ca/messages.properties b/locale/ca/messages.properties
new file mode 100644
index 0000000..060ae58
--- /dev/null
+++ b/locale/ca/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Settings
+privacyPageName=Privacy
+statsPageName=Statistics
+userRulesPageName=My rules
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=About
+allPrettyName=all
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/cs/messages.properties b/locale/cs/messages.properties
new file mode 100644
index 0000000..9dedc16
--- /dev/null
+++ b/locale/cs/messages.properties
@@ -0,0 +1,179 @@
+extName=µMatrix
+dashboardPageName=µMatrix – Ovládací panel
+loggerPageName=uMatrix - logování
+settingsPageName=Nastavení
+privacyPageName=Soukromí
+statsPageName=Statistiky
+userRulesPageName=Moje pravidla
+ubiquitousRulesPageName=Soubory hostů
+rawSettingsPageName=Více
+aboutPageName=O rozšíření
+allPrettyName=vše
+cookiePrettyName=cookie
+cssPrettyName=styly
+imagePrettyName=obr.
+mediaPrettyName=média
+pluginPrettyName=plugin
+scriptPrettyName=skript
+xhrPrettyName=XHR
+framePrettyName=rámce
+otherPrettyName=jiné
+matrixNoNetTrafficPrompt=Na této záložce zatím nedošlo k žádné síťové komunikaci.
+matrixMtxButtonTip=Vypnout/zapnout filtrování matrixu pro tento rozsah.
+matrixPersistButtonTip=Uložit dočasné změny v tomto kontextu.
+matrixRevertButtonTip=Zrušit dočasné změny v tomto kontextu.
+matrixReloadButton=Obnovit stránku.
+matrix1stPartyLabel=vlastní doména
+matrixBlacklistedHostnames={{count}} blokovaných domén
+matrixSwitchNoMixedContent=Striktní HTTPS
+matrixSwitchNoWorker=Zakázat web workery
+matrixSwitchReferrerSpoof=Podvrhovat adresy odkazujících stránek
+matrixSwitchNoscriptSpoof=Falšovat <code><noscript></code> tagy
+matrixRevertAllEntry=Vrátit všechny dočasné změny
+matrixLoggerMenuEntry=Jít na záznamník
+matrixDashboardMenuEntry=Přejít na řídící panel
+matrixNoTabFound=Žádné stránky nebyly nalezeny
+statsPageTitle=µMatrix – Statistiky
+statsPageGenericStats=Obecné statistiky
+statsPageCookieHeadersFoiled=Zničené <a href='https://cs.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> hlavičky: {{count}}
+statsPageRefererHeadersFoiled=Počet neodeslaných <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> hlaviček: {{count}}
+statsPageHyperlinkAuditingFoiled=Počet zmařených <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> pokusů: {{count}}
+statsPageCookiesRemoved=Počet smazaných cookies: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Cache prohlížeče smazána: {{count}}
+statsPageDetailedStats=Podrobné statistiky
+statsPageDetailedAllPages=Všechny stránky
+statsPageDetailedBehindTheScenePage=Chromium: Pod pokličkou
+statsPageOverview=Přehled
+statsPageRequests=Spojení
+statsPageAllowed=Povoleno
+statsPageBlocked=Blokováno
+statsPageAll=Všechna
+statsPagePages=Stránky
+statsPageCookies=Cookies
+statsPageCSS=Styly
+statsPageImages=Obrázky
+statsPagePlugins=Pluginy
+statsPageScripts=Skripty
+statsPageXHRs=XHR
+statsPageFrames=Rámce
+statsPageOthers=Ostatní
+statsPageDetailed=Záznam spojení
+statsPageLogSizePrompt1=Pamatovat posledních
+statsPageLogSizePrompt2=HTTP spojení <b>na stránku</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Obnovit
+settingsPageTitle=µMatrix – Nastavení
+settingsMatrixDisplayHeader=Vzhled
+settingsMatrixDisplayTextSizePrompt=Velikost písma:
+settingsMatrixDisplayTextSizeNormal=Normální
+settingsMatrixDisplayTextSizeLarge=Velká
+settingsMatrixDisplayColorBlind=Styl pro barvoslepé
+settingsMatrixConvenienceHeader=Pohodlí
+settingsDefaultScopeLevel=Výchozí platnost:
+settingsDefaultScopeLevel0=Globální
+settingsDefaultScopeLevel1=Doména
+settingsDefaultScopeLevel2=Stránka
+settingsMatrixAutoReloadPrompt=Obnovit tyto stránky, jakmile je zavřena matice:
+settingsMatrixAutoReloadNone=Žádné
+settingsMatrixAutoReloadCurrent=Aktuální
+settingsMatrixAutoReloadAll=Všechny
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blokované rámce:&ensp;Barva
+settingsSubframeOpacity=Neprůhlednost
+settingsIconBadgeEnabled=Zobrazit počet blokovaných požadavků u ikony
+settingsCollapseBlocked=Skrýt zástupné objekty zablokovaných prvků
+settingsCollapseBlacklisted=Skrýt blokované prvky
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Zapnout podporu cloudového úložiště
+privacyPageTitle=µMatrix – Soukromí
+privacyDeleteBlockedCookiesPrompt=Mazat zablokované cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Mazat neblokované cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minut po posledním použití.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Mazat vyrovnávací paměť prohlížeče každých
+privacyClearCachePrompt2=minut.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=Z Wikipedie:<blockquote>HTTP referer je v informatice označení pro URI, ze kterého byla webová stránka navštívena. Údaj HTTP referrer zapisuje webový prohlížeč do hlavičky HTTP dotazu pro webový server, který ho může dále zpracovat. <b>Dereferrer označuje činnost, při které je ze žádosti o webovou stránku odstraněn HTTP referrer, takže není možné zjistit, odkud uživatel na webovou stránku přišel.</b></blockquote>Pokud je toto zakliknuto, <i>µMatrix</i> bude spoofovat informace o HTTP referrer pokud je jméno domény HTTP referreru třetí strany v kontextu s požadavkem.
+privacyNoMixedContentPrompt=Striktní HTTPS: zakázat smíšený obsah.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanentní pravidla
+userRulesTemporaryHeader=Dočasná pravidla
+userRulesRevert=Vrátit
+userRulesCommit=Potvrdit
+userRulesEdit=Upravit
+userRulesEditSave=Uložit
+userRulesEditDicard=Zrušit
+userRulesImport=Načíst ze souboru…
+userRulesExport=Uložit do souboru…
+userRulesFormatHint=Podívej se na tuto stránku pro syntaxi pravidel.
+userRulesDefaultFileName=moje-pravidla-umatrix.txt
+hostsFilesPrompt=Všechny jména hostů v souboru hostů jsou načteny jako zakázaná v globálním měřítku.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats=Použito {{used}} z {{total}}
+hostsFilesLastUpdate=Poslední aktualizace: {{ago}}
+hostsFilesApplyChanges=Uložit změny
+hostsFilesAutoUpdatePrompt=Automaticky aktualizovat soubory hostů.
+hostsFilesUpdateNow=Aktualizovat nyní
+hostsFilesPurgeAll=Vyčistit všechny cache
+hostsFilesExternalListsHint=Jedna URL na řádek. Řádky s prefixem &lsquo;#&rsquo; budou ignorovány. Nevalidní URL budou ignorovány.
+hostsFilesExternalListsParse=Načíst
+hostsFilesExternalListPurge=vyčistit cache
+hostsFilesExternalListNew=nová verze k dispozici
+hostsFilesExternalListObsolete=zastaralé
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Přehled změn</a>
+aboutStorageUsed=Použité místo: {{storageUsed}} bajtů
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Dokumentace</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Oprávnění</a>
+aboutCode=Zdrojový kód (GPLv3)
+aboutIssues=Chyby a problémy
+aboutContributors=Přispěvatelé
+aboutCodeContributors=Zdrojový kód:
+aboutIssueContributors=Problémy:
+aboutTranslationContributors=Překlady:
+aboutUserDataHeader=Vaše data
+aboutBackupButton=Zálohovat do souboru…
+aboutBackupFilename=moje-zaloha-umatrix.txt
+aboutRestoreButton=Obnovit ze zálohy…
+aboutRestoreConfirm=Všechna vaše nastavení budou přepsány daty zálohovanými v {{time}}, µMatrix bude restartován.\nPřepsat všechny existující nastavení zálohovanými daty?
+aboutRestoreError=Data nemohla být načtena nebo jsou neplatná
+aboutOr=nebo
+aboutResetButton=Vrátit do původního stavu
+aboutResetConfirm=Varování: Tento příkaz odstraní všechna vaše nastavení. Jste si jisti, že chcete pokračovat?
+loggerFilterInputPlaceholder=výraz(y) filtru
+loggerMaxEntriesTip=Maximální počet záznamů
+loggerEntryCookieDeleted=cookie odstraněno: {{value}}
+loggerEntryDeleteCookieError=smazání cookie se nepodařilo: {{value}}
+loggerEntryBrowserCacheCleared=cache prohlížeče vymazána
+loggerEntryAssetUpdated=asset aktualizován: {{value}}
+mainBlockedPrompt1=µMatrix zabránil následující stránce v načítání:
+mainBlockedPrompt2=Kvůli tomuto pravidlu
+mainBlockedBack=Zpět
+mainBlockedClose=Zavřít
+commandRevertAll=Zrušit všechny dočasné změny
+commandWhitelistPageDomain=Dočasně povolit doménu stránky
+commandWhitelistAll=Dočasně povolit všechny
+commandOpenDashboard=Otevřít panel
+elapsedOneMinuteAgo=před minutou
+elapsedManyMinutesAgo=před {{value}} minutami
+elapsedOneHourAgo=před hodinou
+elapsedManyHoursAgo=před {{value}} hodinami
+elapsedOneDayAgo=včera
+elapsedManyDaysAgo=před {{value}} dny
+showDashboardButton=Přehled
+showLoggerButton=Záznamník
+cloudPush=Exportovat do cloudového úložiště
+cloudPull=Importovat z cloudového úložiště
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Jméno tohoto zařízení:
+genericSubmit=Odeslat
+genericRevert=Vrátit zpět
+errorCantConnectTo=Chyba sítě: Nelze se připojit k {{url}}
+genericApplyChanges=Použít změny
diff --git a/locale/da/messages.properties b/locale/da/messages.properties
new file mode 100644
index 0000000..6dec269
--- /dev/null
+++ b/locale/da/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Kontrolpanel
+loggerPageName=uMatrix — Logger
+settingsPageName=Indstillinger
+privacyPageName=Privatliv
+statsPageName=Statistik
+userRulesPageName=Mine regler
+ubiquitousRulesPageName=Hosts-filer
+rawSettingsPageName=More
+aboutPageName=Om
+allPrettyName=alle
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=medier
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=andre
+matrixNoNetTrafficPrompt=Endnu ingen trafik registreret på denne fane.
+matrixMtxButtonTip=Deaktiver/aktiver matrix-filtrering på dette område.
+matrixPersistButtonTip=Gem alle midlertidige ændringer på dette område.
+matrixRevertButtonTip=Fortryd alle midlertidige ændringer på dette område.
+matrixReloadButton=Genindlæs siden.
+matrix1stPartyLabel=1.-parts
+matrixBlacklistedHostnames={{count}} sortlistede værtsnavne
+matrixSwitchNoMixedContent=Benyt kun HTTPS
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Forfalsking af referer
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Tilbagestil alle midlertidige ændringer
+matrixLoggerMenuEntry=Gå til logger
+matrixDashboardMenuEntry=Gå til kontrolpanel
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistik
+statsPageGenericStats=Generisk statistik
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP-cookie</a>-headere forhindret: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a>-headere forhindret: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>Hyperlink overvågnings</a> forsøg forhindret: {{count}}
+statsPageCookiesRemoved=Lokale cookies fjernet: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Lokale lagre</a> tømt: {{count}}
+statsPageBrowserCacheCleared=Browser-cacher tømt: {{count}}
+statsPageDetailedStats=Detaljeret statistik
+statsPageDetailedAllPages=Alle
+statsPageDetailedBehindTheScenePage=Baggrundsanmodninger
+statsPageOverview=Oversigt
+statsPageRequests=Forespørgsler
+statsPageAllowed=Tilladte
+statsPageBlocked=Blokerede
+statsPageAll=Alle
+statsPagePages=Sider
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Billeder
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Andre
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Husk de sidste
+statsPageLogSizePrompt2=HTTP-forespørgsler <b>per side</b>.
+statsPageLogSizeHelp=<p>Du kan inspicerer detaljerne for de seneste "rå" HTTP-forespørgsler, der er blevet foretaget af en webside (se nedenfor).</p><p>Dette er mest nyttigt for erfarne brugere, der ønsker at undersøge præcis hvad en webside har lavet. Men logningen af disse HTTP-forespørgsler kræver hukommelse, så hvis du er ligeglad med denne tekniske information, er det øgede forbrug af hukommelse spildt.</p><p>Derfor dette felt, som lader dig justere det maskimale antal nyeste HTTP-forespørgsler, som skal logges til yderligere inspektion.</p><p>Skriv &ldquo;<code>0</code>&rdquo; for at slå detaljeret logning fra (og dermed reducere hukommelsesforbruget for <i>uMatrix</i>).</p>
+statsPageRefresh=Genindlæs
+settingsPageTitle=uMatrix &ndash; Indstillinger
+settingsMatrixDisplayHeader=Udseende
+settingsMatrixDisplayTextSizePrompt=Skriftstørrelse:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Stor
+settingsMatrixDisplayColorBlind=Optimeret til farveblindhed
+settingsMatrixConvenienceHeader=Bekvemmelighed
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=Genindlæs disse faner "smart", når matricen er lukket:
+settingsMatrixAutoReloadNone=Ingen
+settingsMatrixAutoReloadCurrent=Nuværende
+settingsMatrixAutoReloadAll=Alle
+settingsMatrixAutoReloadInfo=Hver gang du ændrer noget i matricen, der kan have indflydelse på en eller flere siders udseende eller adfærd, vil <i>uMatrix</i> automatisk genindlæse de påvirkede sider, når du lukker matricen.
+settingsSubframeColor=Blokerede frames:&ensp;Farve
+settingsSubframeOpacity=Uigennemsigtighed
+settingsIconBadgeEnabled=Vis antal separate forespørgsler på ikonet
+settingsCollapseBlocked=Sammenfold pladsholder for blokerede elementer
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Aktiver lagring i skyen
+privacyPageTitle=uMatrix &ndash; Privatliv
+privacyDeleteBlockedCookiesPrompt=Slet blokerede cookies.
+privacyDeleteBlockedCookiesHelp=<p>Sortlistede cookies bliver ikke forhindret af <i>uMatrix</i> i at trænge ind i din browser. Derimod bliver de forhindret i at forlade din browser igen, hvilket faktisk er det vigtige. Ved ikke at blokere cookies inden de trænger ind i din browser, gøres det muligt for brugeren at undersøge indholdet og antallet af de cookies diverse websteder placerer i din browser.</p><p>Når disse sortlistede cookies er blevet undersøgt af <i>uMatrix</i>, kan de blive slettet, hvis brugeren ønsker det.</p><p><b>Vigtig bemærkning:</b> Udvidelser kan benytte sig af cookies i deres helt legale webkommunikation. Hvis det værtsnavn, som en cookie kommer fra, ikke er hvidlistet, vil denne cookie blive fjernet fra browseren af <i>uMatrix</i>, hvis denne indstilling er slået til. Du skal derfor sikre dig, at de værtsnavne, som en udvidelse kommunikerer med, er hvidlistede.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Slet ikke-blokerede session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutter efter de sidst er blevet brugt.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>:&ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Forvent at dette <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>ikke altid sker</a> i alle browsere. For nogen kan det også være et problem, at man skal lukke sin browser helt før disse cookies slettes.</p>
+privacyDeleteBlockedLocalStoragePrompt=Slet <a href='https://en.wikipedia.org/wiki/Web_storage'>lokal lagret</a> indhold fra blokerede hostnavne
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Slet browser cache hvert
+privacyClearCachePrompt2=minutter.
+privacyClearCacheHelp=<p>Nogle hjemmesider er meget opsatte på at tracke din færden på internettet, faktisk så meget at de er villige til at benytte sig af ikke-så-flinke metoder til at omgå de tiltag du tager for at beskytte dig selv mod tracking.</p><p>Nogle af disse metoder benytter sig af<sup style='font-size:smaller'>[1, 2]</sup> af din <a href='https://en.wikipedia.org/wiki/Web_cache'>browsers cache</a>, hvis indhold ofte bliver gemt meget længe, og ydermere er der nogle brugere der ikke sletter deres browers cache ofte.</p><p>Der er ikke rigtigt nogen negative effekter ved at slette din browers cache ofte (højst sandsynligt vil du ikke opdage det), men derimod er gevinsten stor ift. at stoppe trackers fra at invadere dit privatliv.</p><p>Vælg denne indstilling, hvis du vil have <i>uMatrix</i> til at slette din browsers cache for dig, så ofte du ønsker det.</p><p style='font-size:smaller'>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a><br>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Forfalsk<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string fra tredjeparts forespørgsler.
+privacyProcessRefererHelp=<p>Fra Wikipedia:</p><blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote><p>Hvis denne indstilling vælges, <i>vil uMatrix</i> forfalske den information som HTTP referreren indeholder, hvis netanmodningen om HTTP referreren kommer fra en tredjepart.
+privacyNoMixedContentPrompt=Streng HTTPS: forbyd blandet indhold.
+privacyNoMixedContentHelp=<p>Fra <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Bloker alle <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>hyperlink overvågnings</a>forsøg.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink overvågning er en mekanisme der gør det muligt for første eller <b>tredjepart</b>, at indsamle data om hvilke brugeren klikker på en hjemmeside. Det er altså tale om en tracking funktion, der gør informerer første eller tredjepart om, hvilke links en bruger vælger at klikke på under sit besøg på en hvilken som helst hjemmeside. Det eneste formål med hyperlink overvågning er altså, at tracke din browser aktivitet.</p>
+userRulesPermanentHeader=Permanente regler
+userRulesTemporaryHeader=Midlertidige regler
+userRulesRevert=Fortryd
+userRulesCommit=Tilføj
+userRulesEdit=Rediger
+userRulesEditSave=Gem
+userRulesEditDicard=Kassér
+userRulesImport=Importer fra fil...
+userRulesExport=Eksporter fil til...
+userRulesFormatHint=Gå til denne side for at se syntaks regler.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Alle hostnavne der forefindes i en host fil vil blive sortlistet, og blokeret overalt.
+hostsFilesStats={{blockedHostnameCount}} blokerede unikke hostnavne fra:
+hostsFilesPerFileStats={{used}} forbrug ud af {{total}}
+hostsFilesLastUpdate=Sidst opdateret: {{ago}}
+hostsFilesApplyChanges=Anvend ændringer
+hostsFilesAutoUpdatePrompt=Auto-opdater host filer.
+hostsFilesUpdateNow=Opdater nu
+hostsFilesPurgeAll=Slet alt caches
+hostsFilesExternalListsHint=Et URL per linje. Linjer der begynder med &lsquo;#&rsquo; vil blive ignoreret. Ugyldige URLs vil lydløst blive ignoreret.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=Slet cache
+hostsFilesExternalListNew=Ny version tilgængelig
+hostsFilesExternalListObsolete=Forældet
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Lagerplads brugt: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Dokumentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Tilladelser</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Fejl og problemer
+aboutContributors=Bidragydere
+aboutCodeContributors=Kode:
+aboutIssueContributors=Problemstillinger:
+aboutTranslationContributors=Oversættelser:
+aboutUserDataHeader=Din data
+aboutBackupButton=Backup til fil...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Gendan fra fil...
+aboutRestoreConfirm=Alle dine indstillinger vil blive overskrevet med data fra backupfilen fra {{time}}, herefter vil uMatrix genstarte.\n\nGendan fra backupfilen?
+aboutRestoreError=Dataen er enten ugyldig eller kunne ikke læses
+aboutOr=... eller ...
+aboutResetButton=Gendan til fabriksindstillinger
+aboutResetConfirm=Forsigtig! Dette slette alle brugerdefinerede indstillinger. Er du sikker på, at du vil fortsætte?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Det maksimale antal af linjer
+loggerEntryCookieDeleted=cookie slettet: {{value}}
+loggerEntryDeleteCookieError=Mislykkedes med at slette cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache slettet
+loggerEntryAssetUpdated=Komponenter opdateret: {{value}}
+mainBlockedPrompt1=uMatrix har blokeret den følgende webside:
+mainBlockedPrompt2=På grund af den følgende regel
+mainBlockedBack=Tilbage
+mainBlockedClose=Luk
+commandRevertAll=Fortryd alle midlertidige ændringer
+commandWhitelistPageDomain=Tillad midlertidigt alt traffik på dette domæne
+commandWhitelistAll=Tillad midlertidigt alt trafik på alle domæner
+commandOpenDashboard=Åbn kontrolpanel
+elapsedOneMinuteAgo=et minut siden
+elapsedManyMinutesAgo={{value}} minutter siden
+elapsedOneHourAgo=en time sider
+elapsedManyHoursAgo={{value}} timer siden
+elapsedOneDayAgo=en dag siden
+elapsedManyDaysAgo={{value}} dage siden
+showDashboardButton=Kontrolpanel
+showLoggerButton=Logger
+cloudPush=Eksporter til skyen
+cloudPull=Eksporter fra skyen
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Denne enheds navn:
+genericSubmit=Indsend
+genericRevert=Gendan
+errorCantConnectTo=Netværksfejl: Kan ikke oprette forbindelse til {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/de/messages.properties b/locale/de/messages.properties
new file mode 100644
index 0000000..ba60121
--- /dev/null
+++ b/locale/de/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Einstellungen
+privacyPageName=Privatsphäre
+statsPageName=Statistiken
+userRulesPageName=Meine Regeln
+ubiquitousRulesPageName=Hosts-Dateien
+rawSettingsPageName=Mehr
+aboutPageName=Über
+allPrettyName=Alle
+cookiePrettyName=Cookie
+cssPrettyName=CSS
+imagePrettyName=Grafik
+mediaPrettyName=Medien
+pluginPrettyName=Plugin
+scriptPrettyName=Skript
+xhrPrettyName=XHR
+framePrettyName=Frame
+otherPrettyName=Andere
+matrixNoNetTrafficPrompt=Bisher keine Netzwerkaktivität für diesen Tab.
+matrixMtxButtonTip=Aktiviere/deaktiviere die Matrix-Filter für diesen Geltungsbereich.
+matrixPersistButtonTip=Speichere alle temporären Änderungen für diesen Geltungsbereich.
+matrixRevertButtonTip=Entferne temporäre Änderungen für diesen Geltungsbereich.
+matrixReloadButton=Seite neu laden.\nShift drücken um den Browser Cache zu ignorieren.
+matrix1stPartyLabel=Aktuelle Domain
+matrixBlacklistedHostnames={{count}} Hostname(n) auf der Blacklist
+matrixSwitchNoMixedContent=Gemischte Inhalte verbieten
+matrixSwitchNoWorker=Web Worker verbieten
+matrixSwitchReferrerSpoof=Referrer verschleiern
+matrixSwitchNoscriptSpoof=<code><noscript></code> Tags verschleiern
+matrixRevertAllEntry=Mache alle temporären Änderungen rückgängig
+matrixLoggerMenuEntry=Gehe zum Protokoll
+matrixDashboardMenuEntry=Gehe zum Dashboard
+matrixNoTabFound=Keine Webseite gefunden
+statsPageTitle=µMatrix &ndash; Statistik
+statsPageGenericStats=Statistik nach Gattungen
+statsPageCookieHeadersFoiled=<a href='https://de.wikipedia.org/wiki/HTTP_Cookie'>HTTP Cookie</a> Header blockiert: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://de.wikipedia.org/wiki/Referrer'>HTTP Referrer</a> Header blockiert: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> Versuche vereitelt: {{count}}
+statsPageCookiesRemoved=Lokale Cookies entfernt: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local Storage</a> geleert: {{count}}
+statsPageBrowserCacheCleared=Browser Cache geleert: {{count}}
+statsPageDetailedStats=Detaillierte Statistik
+statsPageDetailedAllPages=Alle
+statsPageDetailedBehindTheScenePage=Hintergrundanfragen
+statsPageOverview=Übersicht
+statsPageRequests=Anfragen
+statsPageAllowed=Erlaubt
+statsPageBlocked=Blockiert
+statsPageAll=Alle
+statsPagePages=Seiten
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Bilder
+statsPagePlugins=Plugins
+statsPageScripts=Skripte
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Andere
+statsPageDetailed=Protokoll der Anfragen
+statsPageLogSizePrompt1=Speichere die letzten
+statsPageLogSizePrompt2=HTTP Anfragen <b>pro Seite</b>.
+statsPageLogSizeHelp=<p>Die Details der letzten HTTP Anfragen einer Webseite kannst du dir unten anschauen.</p><p>Das ist besonders nützlich für neugierige bzw. erfahrene Benutzer, die genau wissen wollen, was eine Webseite gemacht hat. Allerdings benötigt das Logging dieser HTTP Anfragen Arbeitsspeicher, und wenn du an diesen detaillierten Informationen nicht interessiert bist, belegst du diesen Speicher unnötigerweise.</p><p>Daher lässt sich mit diesem Feld die maximale Zahl der letzten HTTP Anfragen, die aufgezeichnet werden sollen, festlegen.<p><p>Gib &ldquo;<code>0</code>&rdquo; ein, um dieses detaillierte Logging ganz abzuschalten (und entsprechend die Speicherbelegung von <i>uMatrix</i> zu reduzieren).</p>
+statsPageRefresh=Neu laden
+settingsPageTitle=uMatrix &ndash; Einstellungen
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Schriftgröße:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Groß
+settingsMatrixDisplayColorBlind=Anzeige für farbenblinde Benutzer
+settingsMatrixConvenienceHeader=Komforteinstellungen
+settingsDefaultScopeLevel=Standardgeltungsbereich:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Seite
+settingsMatrixAutoReloadPrompt=Intelligentes Neuladen folgender Tabs beim Schließen der Matrix:
+settingsMatrixAutoReloadNone=Keine
+settingsMatrixAutoReloadCurrent=den Aktuellen
+settingsMatrixAutoReloadAll=Alle
+settingsMatrixAutoReloadInfo=Immer wenn du Änderungen in der Matrix durchführst, die die Anzeige und/oder das Verhalten einer oder mehrerer Seiten beeinflusst, wird <i>uMatrix</i> die betroffenen Seiten automatisch neu laden, sobald du die Matrix schließt.
+settingsSubframeColor=Blockierte Frames:&ensp;Farbe
+settingsSubframeOpacity=Undurchsuchtigkeit
+settingsIconBadgeEnabled=Zeige die Zahl der einzelnen Anfragen auf dem Icon an
+settingsCollapseBlocked=Verstecke die Platzhalter für blockierte Elemente
+settingsCollapseBlacklisted=Verstecke die Platzhalter für Elemente auf der Blacklist
+settingsNoscriptTagsSpoofed=Verschleiere <code><noscript></code> Tags wenn Skripte der aktuellen Domain blockiert werden
+settingsCloudStorageEnabled=Aktiviere die Unterstützung der Speicherung in der Cloud
+privacyPageTitle=uMatrix &ndash; Privatsphäre
+privacyDeleteBlockedCookiesPrompt=Lösche geblockte Cookies.
+privacyDeleteBlockedCookiesHelp=<p>Cookies auf der Blacklist werden von <i>uMatrix</i> nicht daran gehindert, vom Browser empfangen zu werden. Vielmehr werden sie daran gehindert, den Browser wieder zu verlassen - das ist es, worauf es wirklich ankommt. Dass Cookies nicht blockiert werden, bevor sie vom Browser empfangen werden, gibt dir die Gelegenheit, dich zu informieren, welche Seiten Cookies zu verwenden versuchen, und dir den Inhalt der Cookies anzuschauen, wenn du möchtest.</p><p>Sobald diese sich auf der Blacklist befindlichen Cookies von <i>uMatrix</i> erfasst sind, können sie aus deinem Browser gelöscht werden, falls du möchtest.</p><p><b>Wichtiger Hinweis:</b> Erweiterungen können während ihrer Benutzung Webanfragen tätigen. Diese Anfragen können dazu führen, dass Cookies im Browser erzeugt werden. Falls der Hostname, von dem das Cookie stammt, nicht auf der Whitelist ist, wird das Cookie von <i>uMatrix</i> aus dem Browser entfernt, sofern dieser Schalter aktiviert ist. Stelle daher sicher, dass Hostnamen, mit denen eine Erweiterung kommuniziert, auf der Whitelist stehen.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Lösche nicht blockierte Sitzungscookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= Minuten nach ihrer letzten Benutzung.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;Ein Sitzungscookie ... wird gelöscht, wenn die Browsersitzung beendet wird. Das Sitzungscookie wird temporär gespeichert und nicht behalten, nachdem der Browser geschlossen wurde.&rdquo;</p><p>Nur ist das möglicherweise bei einigen Browsern <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>nicht der Fall</a>. Außerdem bevorzugen es manche Benutzer, Sitzungscookies loszuwerden, bevor sie den Browser schließen.</p>
+privacyDeleteBlockedLocalStoragePrompt=Lösche <a href='http://de.wikipedia.org/wiki/Web_Storage'>Lokaler Speicher-Inhalt (Local Storage)</a> für blockierte Hostnamen
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Lösche den Browser-Cache alle
+privacyClearCachePrompt2=Minuten.
+privacyClearCacheHelp=<p>Einige Webseiten sind wirklich darauf aus, dich zu verfolgen bzw. zu tracken, und scheuen nicht davor zurück, unfeine Tricks zu verwenden, um Maßnahmen, die dieses Tracking verhindern sollen, zu unterlaufen.</p><p>Einige dieser Tricks benutzen dazu den <sup style='font-size:smaller'>[1, 2]</sup> on the <a href='https://de.wikipedia.org/wiki/Browser-Cache'>Browser Cache</a>, dessen Inhalt häufig für lange Zeit auslesbar ist, da viele Benutzer ihn nicht regelmäßig löschen.</p><p>Das regelmäßige Löschen des Browser Caches bringt kaum Nachteile mit sich (wahrscheinlich wirst du gar keine bemerken), während die Vorteile darin bestehen, dass diese fiesen Tracker davon abgehalten werden, in deine Privatsphäre einzudringen.</p><p>Aktiviere diesen Schalter, damit <i>uMatrix</i> diese Aufgabe in dem von dir gewünschten Intervall übernimmt.</p><p >[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Verschleiere den <a href='https://de.wikipedia.org/wiki/Referrer'>HTTP Referrer</a>, wenn das Ziel eine Drittseite ist.
+privacyProcessRefererHelp=<p>Aus Wikipedia:</p><blockquote>Ein Referrer ist die Internetadresse der Webseite, von der der Benutzer durch Anklicken eines Links zu der aktuellen Seite gekommen ist. ... <b>(Die Übertragung eines Referrers zu verhindern) ...ist unter anderem aus Datenschutzerwägungen interessant, da andernfalls die Möglichkeit besteht, die Referrer mit den Nutzer-IP-Adressen in Verbindung zu bringen.</b></blockquote><p>Wenn dieser Schalter aktiviert ist, wird <i>uMatrix</i> den HTTP Referrer verschleiern bzw. manipulieren, wenn der Domänenname des HTTP Referrer nicht mit dem Domänenname der anfragenden URL überein stimmt (das heißt, der Referrer stammt von einer Drittseite).
+privacyNoMixedContentPrompt=Nur HTTPS: verbiete gemischte Inhalte.
+privacyNoMixedContentHelp=<p>Übersetzt von <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>Wenn [eine] HTTPS-Seite normale Klartext-HTTP-Inhalte enthält, ist die Verbindung nur teilweise verschlüsselt: der unverschlüsselte Inhalt ist zugänglich für Schnüffler und kann durch man-in-the-middle-Attacken modifiziert werden; die Verbindung ist daher nicht mehr sicher. Wenn eine Webseite ein solches Verhalten aufweist, nennt man dies eine Seite mit gemischten Inhalten. </blockquote>
+privacyProcessHyperlinkAuditingPrompt=Blockiere alle <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> Versuche.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink-Auditing ist ein Mechanismus, der es <b>allen beteiligten Parteien</b> erlaubt herauszufinden, auf welchen Link ein Benutzer auf einer bestimmten Webseite klickt. Im Grunde ist es eine Tracking-Funktion: Es erlaubt einer Webseite oder auch Drittparteien auf dieser Webseite, sich darüber zu informieren, auf welche Links du auf eine dieser Seiten geklickt hast. Der einzige Zweck besteht darin, deine Browsing-Aktivitäten zu verfolgen.</p>
+userRulesPermanentHeader=Permanente Regeln
+userRulesTemporaryHeader=Temporäre Regeln
+userRulesRevert=Rückgängig machen
+userRulesCommit=Dauerhaft speichern
+userRulesEdit=Bearbeiten
+userRulesEditSave=Speichern
+userRulesEditDicard=Verwerfen
+userRulesImport=Aus Datei importieren...
+userRulesExport=In eine Datei exportieren ...
+userRulesFormatHint=Auf dieser Seite findest du die Syntax für die Regeln.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Alle Hostnamen einer Hosts-Datei werden als geblacklistete Hostnamen im globalen Geltungsbereich geladen.
+hostsFilesStats={{blockedHostnameCount}} eindeutig blockierte Hostnamen aus:
+hostsFilesPerFileStats={{used}} benutzt aus {{total}}
+hostsFilesLastUpdate=Letzte Aktualisierung: {{ago}}
+hostsFilesApplyChanges=Änderungen anwenden
+hostsFilesAutoUpdatePrompt=Hosts-Dateien automatisch aktualisieren.
+hostsFilesUpdateNow=Jetzt aktualisieren
+hostsFilesPurgeAll=Alle Cache-Speicher löschen
+hostsFilesExternalListsHint=Eine URL pro Zeile. Zeilen mit vorangestelltem &lsquo;#&rsquo; werden ignoriert. Ungültige URLs werden stillschweigend ignoriert.
+hostsFilesExternalListsParse=Anwenden
+hostsFilesExternalListPurge=Cache löschen
+hostsFilesExternalListNew=Neue Version verfügbar
+hostsFilesExternalListObsolete=veraltet
+rawSettingsWarning=Warnung! Die Änderung dieser Konfigurations-Einstellungen erfolgt auf eigenes Risiko.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Änderungsprotokoll</a>
+aboutStorageUsed=Benutzter Speicherplatz: {{storageUsed}} Bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Dokumentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Berechtigungen</a>
+aboutCode=Quellcode (GPLv3)
+aboutIssues=Fehler und Probleme
+aboutContributors=Mitwirkende
+aboutCodeContributors=Code:
+aboutIssueContributors=Probleme:
+aboutTranslationContributors=Übersetzungen:
+aboutUserDataHeader=Deine Daten
+aboutBackupButton=Sicherung in eine Datei...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Wiederherstellung aus einer Datei...
+aboutRestoreConfirm=Alle deine Einstellungen werden überschrieben mit Sicherungs-Daten vom {{time}}, und uMatrix wird neu gestartet.\n\nSollen alle existierenden Einstellungen mit Sicherungs-Daten überschrieben werden?
+aboutRestoreError=Die Daten konnten nicht gelesen werden oder sind ungültig
+aboutOr=... oder ...
+aboutResetButton=Auf Standardeinstellungen zurücksetzen
+aboutResetConfirm=Vorsicht! Dies wird alle deine individuellen Einstellungen entfernen. Bist du sicher, dass du fortfahren willst?
+loggerFilterInputPlaceholder=Filter-Ausdrücke
+loggerMaxEntriesTip=Maximale Zahl an Einträgen
+loggerEntryCookieDeleted=Cookie gelöscht: {{value}}
+loggerEntryDeleteCookieError=Löschen des Cookies fehlgeschlagen: {{value}}
+loggerEntryBrowserCacheCleared=Browser-Cache geleert
+loggerEntryAssetUpdated=Hosts-Dateien aktualisiert: {{value}}
+mainBlockedPrompt1=uMatrix hat das Laden der folgenden Seite verhindert:
+mainBlockedPrompt2=Aufgrund der folgenden Regel
+mainBlockedBack=Gehe zurück
+mainBlockedClose=Schließe
+commandRevertAll=Entferne alle temporären Änderungen
+commandWhitelistPageDomain=Setze die Domain temporär auf die Whitelist
+commandWhitelistAll=Setze alles temporär auf die Whitelist
+commandOpenDashboard=Dashboard öffnen
+elapsedOneMinuteAgo=vor einer Minute
+elapsedManyMinutesAgo=vor {{value}} Minuten
+elapsedOneHourAgo=vor einer Stunde
+elapsedManyHoursAgo=vor {{value}} Stunden
+elapsedOneDayAgo=vor einem Tag
+elapsedManyDaysAgo=vor {{value}} Tagen
+showDashboardButton=Dashboard
+showLoggerButton=Protokoll
+cloudPush=Export in den Cloud-Speicher
+cloudPull=Import aus dem Cloud-Speicher
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Dieser Gerätename:
+genericSubmit=Absenden
+genericRevert=Rückgängig machen
+errorCantConnectTo=Netzwerkfehler: konnte nicht zu {{url}} verbinden
+genericApplyChanges=Änderungen anwenden
diff --git a/locale/el/messages.properties b/locale/el/messages.properties
new file mode 100644
index 0000000..67fc18b
--- /dev/null
+++ b/locale/el/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Πίνακας
+loggerPageName=uMatrix — Logger
+settingsPageName=Ρυθμίσεις
+privacyPageName=Ιδιωτικό απόρρητο
+statsPageName=Στατιστικά
+userRulesPageName=Οι κανόνες μου
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=Περί
+allPrettyName=όλα
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=Εικόνα
+mediaPrettyName=media
+pluginPrettyName=Πρόσθετο
+scriptPrettyName=Δέσμη ενεργειών
+xhrPrettyName=XHR
+framePrettyName=Πλαίσιο
+otherPrettyName=Άλλο
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Επαναφόρτωση σελίδας.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Επαναφορά όλων των προσωρινών αλλαγών
+matrixLoggerMenuEntry=Πηγαίνετε στο καταγραφικό
+matrixDashboardMenuEntry=Μετάβαση στον πίνακα
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Στατιστικά
+statsPageGenericStats=Γενικά στατιστικά
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=Όλα
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Επισκόπηση
+statsPageRequests=Αιτήματα
+statsPageAllowed=Επιτρέπεται
+statsPageBlocked=Μπλοκαρισμένο
+statsPageAll=Όλα
+statsPagePages=Σελίδες
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Εικόνες
+statsPagePlugins=Πρόσθετα
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Πλαίσια
+statsPageOthers=Άλλα
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Ανανέωση
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Εμφάνιση
+settingsMatrixDisplayTextSizePrompt=Μέγεθος κειμένου:
+settingsMatrixDisplayTextSizeNormal=Κανονικό
+settingsMatrixDisplayTextSizeLarge=Μεγάλο
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=Όλα
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Διαγραφή μπλοκάρισμένων cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Απαλοιφή λανθάνουσα μνήμη του φυλλομετρητή κάθε
+privacyClearCachePrompt2=λεπτά.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/en/messages.properties b/locale/en/messages.properties
new file mode 100644
index 0000000..060ae58
--- /dev/null
+++ b/locale/en/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Settings
+privacyPageName=Privacy
+statsPageName=Statistics
+userRulesPageName=My rules
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=About
+allPrettyName=all
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/eo/messages.properties b/locale/eo/messages.properties
new file mode 100644
index 0000000..5bb1829
--- /dev/null
+++ b/locale/eo/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Panelo
+loggerPageName=uMatrix — Logger
+settingsPageName=Agordoj
+privacyPageName=Privateco
+statsPageName=Statistics
+userRulesPageName=Miaj reguloj
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=Pri
+allPrettyName=all
+cookiePrettyName=kuketo
+cssPrettyName=css
+imagePrettyName=bildo
+mediaPrettyName=media
+pluginPrettyName=kromaĵo
+scriptPrettyName=skripto
+xhrPrettyName=XHR
+framePrettyName=kadro
+otherPrettyName=alia
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reŝargi la paĝon.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Iri al protokolilo
+matrixDashboardMenuEntry=Iri al panelo
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Lokaj kuketoj forigitaj: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Lokaj konservadoj</a> malplenigitaj: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Superrigardo
+statsPageRequests=Petoj
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Paĝoj
+statsPageCookies=Kuketoj
+statsPageCSS=CSS
+statsPageImages=Bildoj
+statsPagePlugins=Kromaĵoj
+statsPageScripts=Skriptoj
+statsPageXHRs=XHR-oj
+statsPageFrames=Kadroj
+statsPageOthers=Aliaj
+statsPageDetailed=Protokolilo
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Aktualigi
+settingsPageTitle=uMatrix &ndash; Agordoj
+settingsMatrixDisplayHeader=Apero
+settingsMatrixDisplayTextSizePrompt=Tekstogrando:
+settingsMatrixDisplayTextSizeNormal=Normala
+settingsMatrixDisplayTextSizeLarge=Granda
+settingsMatrixDisplayColorBlind=Afabla por kolorblinduloj
+settingsMatrixConvenienceHeader=Komforto
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Nuna
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opakeco
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Ŝalti subtenon por nuba konservado
+privacyPageTitle=uMatrix &ndash; Privateco
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutoj.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Daŭraj reguloj
+userRulesTemporaryHeader=Nedaŭraj reguloj
+userRulesRevert=Malfari
+userRulesCommit=Konservi
+userRulesEdit=Redakti
+userRulesEditSave=Konservi
+userRulesEditDicard=Ignori
+userRulesImport=Importi el dosiero...
+userRulesExport=Eksporti al dosiero...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=miaj-umatrix-reguloj.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Lasta ĝisdatigo: {{ago}}
+hostsFilesApplyChanges=Apliki ŝanĝojn
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Ĝisdatigi nun
+hostsFilesPurgeAll=Malplenigi ĉiujn kaŝmemorojn
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Analizi
+hostsFilesExternalListPurge=malplenigi kaŝmemoron
+hostsFilesExternalListNew=nova versio disponebla
+hostsFilesExternalListObsolete=neĝisdata
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Ŝanĝoprotokolo</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Fontkodo (GPLv3)
+aboutIssues=Cimoj kaj problemoj
+aboutContributors=Kontribuantoj
+aboutCodeContributors=Kode:
+aboutIssueContributors=Problemoj:
+aboutTranslationContributors=Tradukoj:
+aboutUserDataHeader=Viaj datumoj
+aboutBackupButton=Savkopii al dosiero...
+aboutBackupFilename=mia-umatrix-savkopio.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... aŭ ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=kuketo forigita: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Reen
+mainBlockedClose=Fermi
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Malfermi la panelo
+elapsedOneMinuteAgo=antaŭ 1 minuto
+elapsedManyMinutesAgo=antaŭ {{value}} minutoj
+elapsedOneHourAgo=antaŭ 1 horo
+elapsedManyHoursAgo=antaŭ {{value}} horoj
+elapsedOneDayAgo=antaŭ 1 tago
+elapsedManyDaysAgo=antaŭ {{value}} tagoj
+showDashboardButton=Panelo
+showLoggerButton=Protokolilo
+cloudPush=Eksporti al nuba konservado
+cloudPull=Importi el nuba konservado
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Nomo de ĉi tiu aparato:
+genericSubmit=Sendi
+genericRevert=Malfari
+errorCantConnectTo=Reteraro: {{url}} ne konekteblas
+genericApplyChanges=Apply changes
diff --git a/locale/es/messages.properties b/locale/es/messages.properties
new file mode 100644
index 0000000..edf52b6
--- /dev/null
+++ b/locale/es/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Panel de control
+loggerPageName=uMatrix — Logueador
+settingsPageName=Configuración
+privacyPageName=Privacidad
+statsPageName=Estadísticas
+userRulesPageName=Mis reglas
+ubiquitousRulesPageName=Ficheros hosts
+rawSettingsPageName=More
+aboutPageName=Acerca de
+allPrettyName=todo
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=otro
+matrixNoNetTrafficPrompt=No se ha detectado tráfico de red en esta pestaña hasta el momento.
+matrixMtxButtonTip=Deshabilita/habilita el filtrado por matriz en este ámbito.
+matrixPersistButtonTip=Guardar cambios temporales para este ámbito.
+matrixRevertButtonTip=Deshacer cambios temporales para este ámbito.
+matrixReloadButton=Recargar la página.
+matrix1stPartyLabel=dominio actual
+matrixBlacklistedHostnames={{count}} nombre(s) de <i>host</i> en la lista negra
+matrixSwitchNoMixedContent=HTTPS estricto
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Falsificar Referer
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Deshacer todos los cambios temporales
+matrixLoggerMenuEntry=Ir al registro de peticiones
+matrixDashboardMenuEntry=Ir al Panel de control
+matrixNoTabFound=No se encontró la página
+statsPageTitle=uMatrix &ndash; Estadísticas
+statsPageGenericStats=Estadísticas genéricas
+statsPageCookieHeadersFoiled=Encabezados de <a href='https://es.wikipedia.org/wiki/Cookie_%28inform%C3%A1tica%29'>HTTP cookie</a> frustrados: {{count}}
+statsPageRefererHeadersFoiled=Encabezados de <a href='https://es.wikipedia.org/wiki/Referer_%28Cabecera_HTTP%29'>HTTP referer</a> frustrados: {{count}}
+statsPageHyperlinkAuditingFoiled=Intentos de <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>auditoría de hipervínculos</a> frustrados: {{count}}
+statsPageCookiesRemoved=<i>Cookies</i> locales eliminadas: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Almacenamientos locales</a> vaciados: {{count}}
+statsPageBrowserCacheCleared=Memorias caché del navegador eliminadas: {{count}}
+statsPageDetailedStats=Estadísticas detalladas
+statsPageDetailedAllPages=Todo
+statsPageDetailedBehindTheScenePage=Chromium: Behind the scene
+statsPageOverview=Sumario
+statsPageRequests=Peticiones
+statsPageAllowed=Permitidas
+statsPageBlocked=Bloquedas
+statsPageAll=Todo
+statsPagePages=Páginas
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Imágenes
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Otros
+statsPageDetailed=Registro de peticiones
+statsPageLogSizePrompt1=Recordar última(s)
+statsPageLogSizePrompt2=peticiones HTTP <b>por página</b>.
+statsPageLogSizeHelp=<p>Permite inspeccionar los detalles de las peticiones HTTP directas más recientes hechas por una página web (ver debajo).</p><p>Útil sobre todo para usuarios avanzados que deseen investigar qué, exactamente, ha estado haciendo una página. Sin embargo, registrar tales peticiones HTTP requiere memoria, que puede terminar siendo malgastada si dicha información técnica no es de su interés.</p><p>Por ello este campo le permite ajustar el número máximo de peticiones HTTP recientes que se van a registrar para una inspección ulterior.</p><p>Introduzca &ldquo;<code>0</code>&rdquo; para desactivar el registro detallado (con el consecuente ahorro de memoria de <i>uMatrix</i>).</p>
+statsPageRefresh=Actualizar
+settingsPageTitle=uMatrix &ndash; Configuración
+settingsMatrixDisplayHeader=Apariencia
+settingsMatrixDisplayTextSizePrompt=Tamaño del texto:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Grande
+settingsMatrixDisplayColorBlind=Accesible para daltónicos
+settingsMatrixConvenienceHeader=Comodidad
+settingsDefaultScopeLevel=Nivel de entorno predeterminado:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Dominio
+settingsDefaultScopeLevel2=Sitio
+settingsMatrixAutoReloadPrompt=Al cerrar la matriz, hacer recarga inteligente de las siguientes pestañas:
+settingsMatrixAutoReloadNone=Ninguna
+settingsMatrixAutoReloadCurrent=Actuales
+settingsMatrixAutoReloadAll=Todas
+settingsMatrixAutoReloadInfo=Cada vez que se hagan cambios en la matriz que puedan afectar la visualización y/o el comportamiento de una o más páginas, <i>uMatrix</i> recargará automáticamente las páginas afectadas una vez se cierre la matriz.
+settingsSubframeColor=<i>Frames</i> bloqueados:&ensp;Color
+settingsSubframeOpacity=Opacidad
+settingsIconBadgeEnabled=Mostrar el número de peticiones distintas en el icono
+settingsCollapseBlocked=Colapsar marcadores de posición de los elementos bloqueados
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Habilitar soporte de almacenamiento en la nube
+privacyPageTitle=uMatrix &ndash; Privacidad
+privacyDeleteBlockedCookiesPrompt=Eliminar <i>cookies</i> bloquedas.
+privacyDeleteBlockedCookiesHelp=<p><i>uMatrix</i> no previene la entrada al navegador de <i>cookies</i> en la lista negra, pero sí su salida, que es lo que realmente importa. El hecho de no bloquear las <i>cookies</i> antes de su entrada, da la oportunidad de saber si un sitio intenta usar <i>cookies</i>, e inspeccionar su contenido si se desea.</p><p>Una vez que tales <i>cookies</i> han sido detectadas por <i>uMatrix</i>, las mismas pueden ser eliminadas del navegador si lo decide.</p><p><b>Nota importante:</b> Las extensiones pueden hacer peticiones web como parte de su normal operación. Estas peticiones pueden resultar en la creación de <i>cookies</i> dentro del navegador. Si el nombre de <i>host</i> a partir del cual se generó la <i>cookie</i> no está en la lista de permitidos, la <i>cookie</i> será eliminada por <i>uMatrix</i> mientras esta opción esté activa. Así que asegúrese de permitir los nombres de <i>host</i> con los cuales se comunica una extensión determinada.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Elimina las <i>cookies</i> de sesión no bloqueadas
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutos a partir de la última vez que fueron usadas.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;Una <i>cookie</i> de sesión ... es eliminada una vez usted termina la sesión del navegador. La <i>cookie</i> de sesión es almacenada en la memoria temporal y no está disponible luego de cerrar el navegador.&rdquo;</p><p>Excepto que esto <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>pudiera no estar sucediendo así</a> cuando se usa un navegador basado en Chromium. Además, cerrar el navegador para limpiar las <i>cookies</i> de sesión quizás no sea lo suficientemente rápido para algunos.</p>
+privacyDeleteBlockedLocalStoragePrompt=Eliminar contenido de los nombres de dominio bloqueados <a href='https://en.wikipedia.org/wiki/Web_storage'>almacenado localmente</a>
+privacyDeleteBlockedLocalStorageHelp=POR HACER
+privacyClearCachePrompt1=Limpiar la caché del navegador cada
+privacyClearCachePrompt2=minutos.
+privacyClearCacheHelp=<p>Algunos sitios web están tan enfocados en rastrearle, que usarán ciertos trucos bastantes sucios para burlar cualquier medida que se tome para evitar ser rastreado.</p><p>Unos cuantos de estos trucos se basan<sup style='font-size:smaller'>[1, 2]</sup> en la <a href='https://es.wikipedia.org/wiki/Cach%C3%A9_web'>caché del navegador</a>, cuyo contenido como norma es perdurable, ya que raramente los usuarios se toman el trabajo de limpiarlo de forma regular.</p><p>Limpiar la caché de manera regular es bastante fácil (probablemente ni siquiera lo perciba cuando ocurra), con el beneficio de impedir a estos molestos rastreadores invadir su privacidad.</p><p>Active esta opción para que <i>uMatrix</i> lo haga por usted, en el intervalo deseado.</p><p style='font-size:smaller'>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a><br>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Falsificar información del <a href='https://es.wikipedia.org/wiki/Referer_%28Cabecera_HTTP%29'>HTTP referer</a> en las peticiones a nombres de dominios de terceros.
+privacyProcessRefererHelp=<p>Según Wikipedia:</p><blockquote>Un HTTP referer... identifica la dirección de la página web (es decir, la URI o IRI) que creó el vínculo con el recurso que está siendo solicitado. ... <b>Esto desata preocupaciones en cuanto a la privacidad, y como resultado... algunos navegadores web otorgan a sus usuarios la opción de eliminar el campo referer del encabezado de su solicitud.</b></blockquote><p>Si se selecciona esta opción, <i>uMatrix</i> falsificará la información del HTTP referer si el nombre de dominio del HTTP referer no concuerda con el nombre de dominio de la URL pedida (o sea, el creador del vínculo es un tercero respecto a la página web).</p>
+privacyNoMixedContentPrompt=HTTPS estricto: prohibir contenido mixto.
+privacyNoMixedContentHelp=<p>Según <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>Si [una] página HTTPS incluye contenido que se obtiene a través de HTTP regular, entonces la conexión está solo parcialmente encriptada: el contenido no encriptado puede ser leído mediante sniffers y modificado por ataques tipo "man-in-the-middle", lo que hace que la conexión ya no sea segura. Una página web que exhiba este comportamiento, se considera una página de contenido mixto.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Bloquear todos los intentos de <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>auditoría de hipervínculos</a>.
+privacyProcessHyperlinkAuditingHelp=<p>La auditoría de hipervínculos o enlaces es un mecanismo que permite a alguien, <b>cualquiera</b>, ser informado acerca de a que enlace accedió un usuario en una página web particular. Es esencialmente un mecanismo de rastreo: permite a un sitio web, o a un tercero, ser informado sobre cuales enlaces, en cuales páginas, fueron accedidos por el usuario. Su único motivo es rastrear la actividad de navegación del usuario.</p>
+userRulesPermanentHeader=Reglas permanentes
+userRulesTemporaryHeader=Reglas temporales
+userRulesRevert=Deshacer
+userRulesCommit=Establecer
+userRulesEdit=Editar
+userRulesEditSave=Guardar
+userRulesEditDicard=Descartar
+userRulesImport=Importar desde archivo...
+userRulesExport=Exportar a archivo...
+userRulesFormatHint=Consulte esta página para sintaxis de las reglas.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Todos los nombres de dominio en un fichero <i>hosts</i> son cargados como nombres de dominio en lista negra de manera global.
+hostsFilesStats={{blockedHostnameCount}} nombres de dominios únicos bloqueados a partir de:
+hostsFilesPerFileStats={{used}} usados de un total de {{total}}
+hostsFilesLastUpdate=Última actualización: {{ago}}
+hostsFilesApplyChanges=Aplicar cambios
+hostsFilesAutoUpdatePrompt=Actualización automática de ficheros <i>hosts</i>.
+hostsFilesUpdateNow=Actualizar ahora
+hostsFilesPurgeAll=Vaciar todas las memorias caché
+hostsFilesExternalListsHint=Una URL por línea. Las línes con prefijo &lsquo;#&rsquo; serán ignoradas. Las URL inválidas serán ignoradas de manera silenciosa.
+hostsFilesExternalListsParse=Analizar
+hostsFilesExternalListPurge=vaciar caché
+hostsFilesExternalListNew=nueva versión disponible
+hostsFilesExternalListObsolete=desactualizado
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Registro de cambios</a>
+aboutStorageUsed=Almacenamiento usado: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentación</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permisos</a>
+aboutCode=Código fuente (GPLv3)
+aboutIssues=Errores y problemas
+aboutContributors=Colaboradores
+aboutCodeContributors=Código:
+aboutIssueContributors=Problemas:
+aboutTranslationContributors=Traducciones:
+aboutUserDataHeader=Sus datos
+aboutBackupButton=Respaldar en archivo...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restaurar desde archivo...
+aboutRestoreConfirm=Todos sus ajustes serán sobrescritos usando los datos respaldados en {{time}}, y uMatrix se reiniciará.\n\n¿Sobrescribir todos los ajustes actuales usando los datos de respaldo?
+aboutRestoreError=Los datos no se pueden leer o son inválidos
+aboutOr=... o ...
+aboutResetButton=Restablecer ajustes predeterminados
+aboutResetConfirm=Atención! Esto eliminará todos sus ajustes personalizados. ¿Seguro desea continuar?
+loggerFilterInputPlaceholder=filtro(s)
+loggerMaxEntriesTip=Número máximo de registros
+loggerEntryCookieDeleted=cookie eliminada: {{value}}
+loggerEntryDeleteCookieError=fallo intentando eliminar cookie: {{value}}
+loggerEntryBrowserCacheCleared=vaciada caché del navegador
+loggerEntryAssetUpdated=recurso actualizado: {{value}}
+mainBlockedPrompt1=uMatrix previno la carga de la siguiente página:
+mainBlockedPrompt2=de acuerdo con la regla
+mainBlockedBack=Ir atrás
+mainBlockedClose=Cerrar
+commandRevertAll=Deshacer todos los cambios temporales
+commandWhitelistPageDomain=Permitir temporalmente el dominio de la página
+commandWhitelistAll=Permitir todo temporalmente
+commandOpenDashboard=Abrir Panel de control
+elapsedOneMinuteAgo=hace un minuto
+elapsedManyMinutesAgo=hace {{value}} minutos
+elapsedOneHourAgo=hace una hora
+elapsedManyHoursAgo=hace {{value}} horas
+elapsedOneDayAgo=hace un día
+elapsedManyDaysAgo=hace {{value}} días
+showDashboardButton=Panel de control
+showLoggerButton=Registro
+cloudPush=Exportar al almacenamiento en la nube
+cloudPull=Importar desde almacenamiento en la nube
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Nombre de este dispositivo:
+genericSubmit=Enviar
+genericRevert=Deshacer
+errorCantConnectTo=Error de red: Imposible conectar con {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/et/messages.properties b/locale/et/messages.properties
new file mode 100644
index 0000000..0afca45
--- /dev/null
+++ b/locale/et/messages.properties
@@ -0,0 +1,179 @@
+extName=µMatrix
+dashboardPageName=µMatrix — Esilehekülg
+loggerPageName=uMatrix — Logger
+settingsPageName=Seaded
+privacyPageName=Privaatsus
+statsPageName=Statistika
+userRulesPageName=Minu reeglid
+ubiquitousRulesPageName=Host-failid
+rawSettingsPageName=More
+aboutPageName=Teave
+allPrettyName=kõik
+cookiePrettyName=küpsis
+cssPrettyName=css
+imagePrettyName=pilt
+mediaPrettyName=media
+pluginPrettyName=meedia
+scriptPrettyName=skript
+xhrPrettyName=XHR
+framePrettyName=raam
+otherPrettyName=teised
+matrixNoNetTrafficPrompt=Selle kaardi kohta pole veel netiliiklust näha.
+matrixMtxButtonTip=Selle määratluse puhul keela/luba maatriksiline filtreerimine.\nMaatriksilise filtreerimise blokeeritud ühendused selles portaalis: {{count}}.
+matrixPersistButtonTip=Salvesta ajutiselt kõik selle määratluse muudatused.
+matrixRevertButtonTip=Eemalda selle määratluse ajutised muudatused.
+matrixReloadButton=Värskenda lehekülge.
+matrix1stPartyLabel=1. osapool
+matrixBlacklistedHostnames={{count}} hosti nimi(e) mustas nimekirjas
+matrixSwitchNoMixedContent=Range HTTPS
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Viitaja narritamine
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Mine logijasse
+matrixDashboardMenuEntry=Mine esilehele
+matrixNoTabFound=No web page found
+statsPageTitle=µMatrix &ndash; statistika
+statsPageGenericStats=Üldstatistika
+statsPageCookieHeadersFoiled=<a href='https://et.wikipedia.org/wiki/HTTP-k%C3%BCpsis'>HTTP-küpsise</a> tõrjutud päismik: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=Kõik
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Päringud
+statsPageAllowed=Lubatud
+statsPageBlocked=Blokeeritud
+statsPageAll=Kõik
+statsPagePages=Leheküljed
+statsPageCookies=Küpsised
+statsPageCSS=CSS
+statsPageImages=Pildid
+statsPagePlugins=Liidesed
+statsPageScripts=Skriptid
+statsPageXHRs=XHRid
+statsPageFrames=Raamid
+statsPageOthers=Muud
+statsPageDetailed=Logija
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normaalne
+settingsMatrixDisplayTextSizeLarge=Suur
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=Pole
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=Kõik
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/fa/messages.properties b/locale/fa/messages.properties
new file mode 100644
index 0000000..852c350
--- /dev/null
+++ b/locale/fa/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — داشبورد
+loggerPageName=uMatrix — Logger
+settingsPageName=تنظیمات
+privacyPageName=حریم خصوصی
+statsPageName=آمارها
+userRulesPageName=قوانین شخصی
+ubiquitousRulesPageName=فایل‌های هاست‌ها
+rawSettingsPageName=More
+aboutPageName=درباره
+allPrettyName=همه
+cookiePrettyName=کوکی
+cssPrettyName=css
+imagePrettyName=تصویر
+mediaPrettyName=media
+pluginPrettyName=افزونه
+scriptPrettyName=اسکریپت
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=سایر
+matrixNoNetTrafficPrompt=هیچ ترافیک شبکه‌ای برای این تب تا کنون دیده نشده است.
+matrixMtxButtonTip=فعال یا غیرفعال نمودن ماتریس فیلتر برای این محدوده.
+matrixPersistButtonTip=ذخیره نمودن همه‌ی تغییرات موقتی برای این محدوده.
+matrixRevertButtonTip=بازگرداندن تغییرات موقتی برای این محدوده.
+matrixReloadButton=بارگذاری مجدد صفحه.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} بلاک‌شده آدرس‌(های)
+matrixSwitchNoMixedContent=فقط HTTPS
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=مخفی‌نمودن ارجاع‌دهنده
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=بازگرداندن همه‌ی تغییرات موقتی
+matrixLoggerMenuEntry=رفتن به logger
+matrixDashboardMenuEntry=رفتن به داشبورد
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; آمارها
+statsPageGenericStats=آمارهای کلی
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP کوکی</a> هدرها بی‌اثر شدند: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP ارجاع‌دهنده</a> هدرها بی‌اثر شدند: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>بررسی لینک</a> تلاش‌ها بی‌اثر شدند: {{count}}
+statsPageCookiesRemoved=کوکی‌های محلی پاک‌شده: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>انباره‌های محلی</a> خالی شدند: {{count}}
+statsPageBrowserCacheCleared=کش‌های مرورگر پاک شدند: {{count}}
+statsPageDetailedStats=آمارهای جزئی
+statsPageDetailedAllPages=همه
+statsPageDetailedBehindTheScenePage=پشت‌صحنه
+statsPageOverview=نمای کلی
+statsPageRequests=درخواست‌ها
+statsPageAllowed=مجاز
+statsPageBlocked=مسدود شده
+statsPageAll=همه
+statsPagePages=صفحات
+statsPageCookies=کوکی‌ها
+statsPageCSS=CSS
+statsPageImages=تصاویر
+statsPagePlugins=افزونه‌ها
+statsPageScripts=اسکریپت‌ها
+statsPageXHRs=XHRها
+statsPageFrames=فریم‌ها
+statsPageOthers=ساير
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=به یاد سپردن آخرین
+statsPageLogSizePrompt2=درخواست‌های HTTP <b>در هر صفحه</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=تازه‌سازی
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=ظاهر
+settingsMatrixDisplayTextSizePrompt=اندازه متن:
+settingsMatrixDisplayTextSizeNormal=عادی
+settingsMatrixDisplayTextSizeLarge=بزرگ
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=راحتی
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=وقتی ماتریس بسته شد، تب‌ها را هوشمندانه دوباره بارگذاری کن:
+settingsMatrixAutoReloadNone=هیچ‌کدام
+settingsMatrixAutoReloadCurrent=کنونی
+settingsMatrixAutoReloadAll=همه
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=وضوح
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=برای‌انجام
+privacyClearCachePrompt1=پاک‌نمودن کش مرورگر هر
+privacyClearCachePrompt2=دقیقه.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=قوانین دائم
+userRulesTemporaryHeader=قوانین موقت
+userRulesRevert=بازگرداندن
+userRulesCommit=انجام‌دادن
+userRulesEdit=ويرايش
+userRulesEditSave=ذخيره
+userRulesEditDicard=صرف‌نظر
+userRulesImport=واردکردن از فایل...
+userRulesExport=ذخیره در فایل...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=اعمال تغییرات
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=اکنون به‌روزآوری شود
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=تجزیه‌کردن
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=منسوخ
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=مشکلات و مسائل
+aboutContributors=مشارکت کننده‌ها
+aboutCodeContributors=کد:
+aboutIssueContributors=مسائل:
+aboutTranslationContributors=ترجمه‌ها:
+aboutUserDataHeader=داده‌ی شما
+aboutBackupButton=پشتیبان‌گیری در فایل...
+aboutBackupFilename=پشتیبان-umatrix-من.txt
+aboutRestoreButton=بازگرداندن از فایل...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... یا ...
+aboutResetButton=بازگشت به تنظیمات پیش‌فرض
+aboutResetConfirm=هشدار! این عمل تمام تنظیمات سفارشی شما را از بین می‌برد. آیا از انجام آن مطمئن هستید؟
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=بیشترین تعداد ورودی‌ها
+loggerEntryCookieDeleted=کوکی پاک‌شده: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=کش مروگر پاک شد
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=به خاطر قانون زیر
+mainBlockedBack=برگرد
+mainBlockedClose=بستن
+commandRevertAll=بازگرداندن همه‌ی تغییرات موقتی
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=بازکردن داشبورد
+elapsedOneMinuteAgo=یک دقیقه پیش
+elapsedManyMinutesAgo={{value}} دقیقه پیش
+elapsedOneHourAgo=یک ساعت پیش
+elapsedManyHoursAgo={{value}} ساعت پیش
+elapsedOneDayAgo=یک روز پیش
+elapsedManyDaysAgo={{value}} روز پیش
+showDashboardButton=داشبورد
+showLoggerButton=Logger
+cloudPush=دخیره در فضای ابری
+cloudPull=گرفتن از فضای ابری
+cloudNoData=... ...
+cloudDeviceNamePrompt=نام دستگاه:
+genericSubmit=ثبت‌کردن
+genericRevert=بازگرداندن
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/fi/messages.properties b/locale/fi/messages.properties
new file mode 100644
index 0000000..650ccd7
--- /dev/null
+++ b/locale/fi/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Hallintapaneeli
+loggerPageName=uMatrix — Logger
+settingsPageName=Asetukset
+privacyPageName=Yksityisyys
+statsPageName=Tilastot
+userRulesPageName=Omat säännöt
+ubiquitousRulesPageName=Host tiedostot
+rawSettingsPageName=More
+aboutPageName=Tietoja
+allPrettyName=kaikki
+cookiePrettyName=keksi
+cssPrettyName=css
+imagePrettyName=kuva
+mediaPrettyName=A/V-media
+pluginPrettyName=lisäosa
+scriptPrettyName=komentosarja
+xhrPrettyName=XHR
+framePrettyName=kehys
+otherPrettyName=muut
+matrixNoNetTrafficPrompt=Ei dataliikennettä havaittu tällä välilehdellä vielä.
+matrixMtxButtonTip=Poista käytöstä/ota käyttöön matrix suodatus tälle alueelle.
+matrixPersistButtonTip=Tallenna kaikki väliaikaiset muutokset tälle alueelle.
+matrixRevertButtonTip=Palauta tilapäiset muutoksia tälle alueelle.
+matrixReloadButton=Lataa sivu uudelleen.
+matrix1stPartyLabel=1. osapuoli
+matrixBlacklistedHostnames={{count}} mustalistatut isäntänimet
+matrixSwitchNoMixedContent=Tiukka HTTPS
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Kumoa kaikki väliaikaiset muutokset
+matrixLoggerMenuEntry=Siirry lokiin
+matrixDashboardMenuEntry=Siirry hallintapaneeliin
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Tilastot
+statsPageGenericStats=Yleiset tilastot
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Yksityiskohtaiset tilastot
+statsPageDetailedAllPages=Kaikki
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Yhteenveto
+statsPageRequests=Pyynnöt
+statsPageAllowed=Sallittu
+statsPageBlocked=Estetty
+statsPageAll=Kaikki
+statsPagePages=Sivut
+statsPageCookies=Evästeet
+statsPageCSS=CSS
+statsPageImages=Kuvat
+statsPagePlugins=Laajennukset
+statsPageScripts=Komentosarjat
+statsPageXHRs=XHRs
+statsPageFrames=Kehykset
+statsPageOthers=Muut
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Muista viimeiset
+statsPageLogSizePrompt2=HTTP pyyntöä <b>sivua kohden</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Päivitä
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Ulkoasu
+settingsMatrixDisplayTextSizePrompt=Tekstikoko:
+settingsMatrixDisplayTextSizeNormal=Normaali
+settingsMatrixDisplayTextSizeLarge=Suuri
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=Ei mitään
+settingsMatrixAutoReloadCurrent=Nykyinen
+settingsMatrixAutoReloadAll=Kaikki
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Läpinäkyvyys
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Muokkaa
+userRulesEditSave=Tallenna
+userRulesEditDicard=Hylkää
+userRulesImport=Tuo tiedostosta...
+userRulesExport=Vie tiedostoon...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Käytä muutoksia
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Päivitä nyt
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parsi
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=uusi versio saatavilla
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Lähdekoodi (GPLv3)
+aboutIssues=Bugit ja ongelmat
+aboutContributors=Kehittäjät ja avustajat
+aboutCodeContributors=Koodi:
+aboutIssueContributors=Ongelmat:
+aboutTranslationContributors=Käännökset:
+aboutUserDataHeader=Tietosi
+aboutBackupButton=Varmuuskopioi tiedostoon...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Palauta tiedostosta...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Palaa
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/fil/messages.properties b/locale/fil/messages.properties
new file mode 100644
index 0000000..ac1e6b8
--- /dev/null
+++ b/locale/fil/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Mga Setting
+privacyPageName=Privacy
+statsPageName=Istatistika
+userRulesPageName=Ang aking mga panuntunan
+ubiquitousRulesPageName=Mga host file
+rawSettingsPageName=More
+aboutPageName=Tungkol sa
+allPrettyName=lahat
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=larawan
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=iba pa
+matrixNoNetTrafficPrompt=Walang nakikitang trapiko sa net para sa tab na ito sa ngayon.
+matrixMtxButtonTip=Huwag paganahin / paganahin ang pag-filter ng matris para sa saklaw na ito.
+matrixPersistButtonTip=I-save ang lahat ng mga pansamantalang pagbabago para sa saklaw na ito.
+matrixRevertButtonTip=Ibalik ang mga pansamantalang pagbabago para sa saklaw na ito.
+matrixReloadButton=I-reload ang pahina.\nPindutin ang Shift upang lampasan ang cache ng browser.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} naka-blacklist na hostname (s)
+matrixSwitchNoMixedContent=Ipagbawal ang halo-halong nilalaman
+matrixSwitchNoWorker=Ipagbawal ang mga web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Ibalik ang lahat ng mga pansamantalang pagbabago
+matrixLoggerMenuEntry=Pumunta sa logger
+matrixDashboardMenuEntry=Pumunta sa dashboard
+matrixNoTabFound=Walang nakitang pahina ng web
+statsPageTitle=uMatrix &ndash; Istatistika
+statsPageGenericStats=Generic na istatistika
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> hinabol ang mga header: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> ang mga pagtatangka ay napawalang-saysay:{{count}}
+statsPageCookiesRemoved=Inalis na mga lokal na cookies: {{count}}
+statsPageLocalStoragesCleared=<a href = 'http: //diveintohtml5.info/storage.html'> Mga lokal na storage </a> ay walang laman: {{count}}
+statsPageBrowserCacheCleared=Naka-clear ang mga cache ng browser: {{count}}
+statsPageDetailedStats=Detalyadong istatistika
+statsPageDetailedAllPages=Lahat
+statsPageDetailedBehindTheScenePage=Sa likod ng mga eksena
+statsPageOverview=Pangkalahatang-ideya
+statsPageRequests=Mga Hiling
+statsPageAllowed=Pinayagan
+statsPageBlocked=Naka-block
+statsPageAll=Lahat
+statsPagePages=Mga Pahina
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Mga Larawan
+statsPagePlugins=Mga Plugin
+statsPageScripts=Mga Script
+statsPageXHRs=Mga XHR
+statsPageFrames=Mga Frame
+statsPageOthers=Iba pa
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Tandaan ang huli
+statsPageLogSizePrompt2=Hinihiling ng HTTP sa <b> bawat pahina </ b>.
+statsPageLogSizeHelp=<p> Maaari mong siyasatin ang mga detalye ng pinakahuling mga kahilingan sa HTTP na ginawa ng isang web page (tingnan sa ibaba). </p> <p> Ito ay kapaki-pakinabang sa mga advanced na user na nais mag-imbestiga nang eksakto kung ano ang ginagawa ng isang web page. Ngunit ang pag-log ng mga kahilingan sa HTTP ay nangangailangan ng memorya, at kung hindi mo pinapahalagahan ang tungkol sa teknikal na impormasyon, pagkatapos ay ang memory ay nasayang. </p> <p> Samakatuwid ang patlang na ito na nagpapahintulot sa iyo na ayusin ang maximum na bilang ng mga pinakahuling mga kahilingan sa HTTP ay dapat ma-log para sa karagdagang inspeksyon. </p> <p> Ipasok ang &ldquo; <code> 0 </code> &rdquo; upang patayin ang detalyadong pag-log (at dahil dito ay bawasan ang memory footprint ng <i> uMatrix </i>). </p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Laki ng teksto:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Malaki
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Kaginhawaan
+settingsDefaultScopeLevel=Pangunahing antas ng saklaw:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=Kapag isinara ang matrix, ang smart na i-reload ang mga tab na ito:
+settingsMatrixAutoReloadNone=Wala
+settingsMatrixAutoReloadCurrent=Kasalukuyan
+settingsMatrixAutoReloadAll=Lahat
+settingsMatrixAutoReloadInfo=Sa tuwing gumawa ka ng mga pagbabago sa matrix na maaaring makaapekto sa display at / o pag-uugali ng isa o higit pang mga pahina, awtomatikong i-reload ng <i>uMatrix</i> ang mga apektadong pahina kapag isinara mo ang matris.
+settingsSubframeColor=Blocked frames:&ensp;Kulay
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Ipakita ang bilang ng mga natatanging kahilingan sa icon
+settingsCollapseBlocked=I-collapse ang mga placeholder ng mga naka-block na elemento
+settingsCollapseBlacklisted=I-collapse ang mga placeholder ng mga naka-blacklist na elemento
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> na mga tag kapag naka-block ang mga script ng 1st-party
+settingsCloudStorageEnabled=Paganahin ang suporta sa cloud storage
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Tanggalin ang naka-block na cookies.
+privacyDeleteBlockedCookiesHelp=<p> Ang mga blacklist na cookies ay hindi napigilan ng <i>uMatrix</i> mula sa pagpasok ng iyong browser. Gayunpaman, pinigilan sila sa pag-alis ng iyong browser, na kung saan ay talagang mahalaga. Ang hindi pagharang ng mga cookies bago sila ipasok ang iyong browser ay nagbibigay sa iyo ng pagkakataong maabisuhan na sinubukan ng isang site na gumamit ng mga cookies, at higit pa upang siyasatin ang mga nilalaman nito kung nais mo. </p><p> Kapag ang mga naka-blacklist na cookies ay na-accounted sa pamamagitan ng <i>uMatrix</i>, maaari silang alisin mula sa iyong browser kung nais mo. </p><p><b>Mahalagang paalala: </b> Maaaring gawing mga extension ang mga kahilingan sa web sa panahon ng kanilang normal na operasyon. Ang mga kahilingan na ito ay maaaring magresulta sa mga cookies na nilikha sa browser. Kung ang hostname mula sa kung saan ang isang cookie na nagmula ay hindi whitelisted, ang cookie ay aalisin mula sa browser sa pamamagitan ng <i> uMatrix </i> kung naka-check ang opsyong ito. Kaya siguraduhin na ang (mga) host na kung saan ang isang extension na nakikipag-ugnay ay whitelisted. </p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Tanggalin ang mga hindi naka-block na cookies ng session
+privacyDeleteNonBlockedSessionCookiesPrompt2= ilang minuto pagkatapos ng huling pagkakataon na ginamit na ang mga ito.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;Ang session cookie ... ay mabubura kapag tinapos mo ang session ng browser. Ang session cookie ay naka-imbak sa pansamantalang memorya at hindi mananatili pagkatapos na sarado ang browser.&rdquo;</p><p>Maliban na ito <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>maaaring hindi nangyayari</a>sa ilang mga browser. Gayundin, sa ilang, ang pagkakaroon ng pagsara sa browser upang ang mga cookies ng cookies ay maaring hindi maaaring maagang maaga.</p>
+privacyDeleteBlockedLocalStoragePrompt=Tanggalin ang nilalamang <a href='https://en.wikipedia.org/wiki/Web_storage'> lokal na imbakan </a> na itinakda ng mga naka-block na mga hostname
+privacyDeleteBlockedLocalStorageHelp=GAGAWIN
+privacyClearCachePrompt1=I-clear ang cache ng browser bawat
+privacyClearCachePrompt2=minuto.
+privacyClearCacheHelp=<p>Ang ilang mga web site ay tunay na baluktot sa pagsubaybay sa iyo, kaya magkano na sila ay gumamit ng hindi-magandang-trick upang gumana sa paligid ng anumang mga panukala mong gawin upang hindi masubaybayan.</p><p>Ang ilan sa mga trick na ito ay umaasa<sup>[1, 2]</sup> sa <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, kung aling nilalaman ay madalas na matagal na dahil hindi gaanong magagawa ng mga gumagamit ang oras upang regular na i-clear ang cache ng kanilang browser.</p><p>May kaunting abala na regular na i-clear ang cache ng browser (posibilidad na hindi mo mapansin kapag nangyari ito), at ang benepisyo ay upang maiwasan ang mga kasuklam-suklam na tagasubaybay mula sa pagsalakay sa iyong privacy.</p><p>Suriin ang opsyong ito upang magkaroon ng <i> uMatrix </i> gawin ito para sa iyo, sa pagitan na nais mo.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Ppag-iwas sa Pagsubaybay sa Web sa pamamagitan ng Cache Browser&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'> HTTP referrer </a> string ng mga kahilingan ng third-party.
+privacyProcessRefererHelp=Mula sa Wikipedia:<blockquote> Ang HTTP referer ay isang header ng HTTP na nagpapakilala sa address ng webpage na naka-link sa mapagkukunan na hiniling. ...<b>Dahil ang impormasyon ng referer ay maaaring lumalabag sa privacy, pinapayagan ng ilang mga browser ng web ang gumagamit na huwag paganahin ang pagpapadala ng impormasyon ng referer.</b></blockquote>Kung naka-check ang setting na ito,<i>uMatrix</i>ay i-spoof ang impormasyon ng HTTP referrer kung ang pangalan ng domain ng HTTP referrer ay third-party sa pangalan ng domain na net request.
+privacyNoMixedContentPrompt=Strict HTTPS: pagbawalan ang halo-halong nilalaman.
+privacyNoMixedContentHelp=<p>Mula sa <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>Kung ang [a] pahina ng HTTPS ay kinabibilangan ng nilalaman na nakuha sa pamamagitan ng regular, cleartext HTTP, at pagkatapos ang koneksyon ay bahagyang naka-encrypt lamang: ang hindi naka-encrypt na nilalaman ay naa-access sa mga sniffer at maaaring mabago ng mga man-in-the-middle attackers, at samakatuwid ang koneksyon ay hindi na-safeguarded ngayon. Kapag nagpapakita ang isang webpage ng pag-uugali na ito, ito ay tinatawag na isang halo-halong pahina ng nilalaman.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Harangan ang lahat ng<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>pag-audit ng hyperlink</a> mga pagtatangka.
+privacyProcessHyperlinkAuditingHelp=<p> Ang pag-audit ng hyperlink ay isang mekanismo na nagbibigay-daan sa isang partido, <b> anumang partido </b>, na ipaalam kung anong link ang na-click ng isang user sa isang partikular na web page. Ito ay talagang isang tampok sa pagsubaybay: pinapayagan nito ang isang web site, o anumang ikatlong partido sa web site na iyon, na ipaalam kung aling link ang iyong na-click sa alin sa mga web page nito. Ang tanging layunin ay upang masubaybayan ang iyong aktibidad sa pagba-browse. </p>
+userRulesPermanentHeader=Permanenteng panuntunan
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Ibalik
+userRulesCommit=Commit
+userRulesEdit=I-edit
+userRulesEditSave=I-save
+userRulesEditDicard=Itapon
+userRulesImport=Mag-import mula sa file...
+userRulesExport=I-export upang mag-file...
+userRulesFormatHint=Tingnan ang pahinang ito para sa syntax ng panuntunan.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Ang lahat ng mga hostname sa isang nagho-host na file ay nai-load bilang naka-blacklist na mga hostname sa global na saklaw.
+hostsFilesStats={{blockedHostnameCount}} naiiba ang nai-block na mga hostname mula sa:
+hostsFilesPerFileStats={{used}} na ginamit sa {{total}}
+hostsFilesLastUpdate=Huling pag-update: {{ago}}
+hostsFilesApplyChanges=Mag-apply ng mga pagbabago
+hostsFilesAutoUpdatePrompt=I-auto-update ang mga hosts files.
+hostsFilesUpdateNow=I-update ngayon
+hostsFilesPurgeAll=Tanggalin ang lahat ng mga cache
+hostsFilesExternalListsHint=Isang URL sa bawat linya. Mga prefix na linya na may &lsquo;#&rsquo; hindi papansinin. Ang di-wastong mga URL ay tahimik na hindi papansinin.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=mayroong bagong bersyon
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Ginamit ang imbakan: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Dokumentasyon</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Mga Pahintulot</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Mga bug at mga isyu
+aboutContributors=Mga kontribyutor
+aboutCodeContributors=Code:
+aboutIssueContributors=Mga isyu:
+aboutTranslationContributors=Mga Pagsasalin:
+aboutUserDataHeader=Ang iyong datos
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Ibalik mula sa file...
+aboutRestoreConfirm=Ang lahat ng iyong mga setting ay mapapatungan gamit ang data na naka-back up sa {{time}}, at uMatrix ay muling simulan.\n\nI-overwrite ang lahat ng umiiral na mga setting gamit ang naka-back up na data?
+aboutRestoreError=Ang data ay hindi mabasa o hindi wasto
+aboutOr=... o ...
+aboutResetButton=I-reset sa mga default na setting
+aboutResetConfirm=Mag-ingat! aalisin nito ang lahat ng iyong mga pasadyang setting. Sigurado ka bang gusto mong magpatuloy?
+loggerFilterInputPlaceholder=(mga) expression ng filter
+loggerMaxEntriesTip=Pinakamataas na bilang ng mga entry
+loggerEntryCookieDeleted=tinanggal ang cookie: {{value}}
+loggerEntryDeleteCookieError=bigo na tanggalin ang cookie: {{value}}
+loggerEntryBrowserCacheCleared=naka-clear ang cache ng browser
+loggerEntryAssetUpdated=na-update ang asset: {{value}}
+mainBlockedPrompt1=Pinigilan ng uMatrix ang sumusunod na pahina mula sa paglo-load:
+mainBlockedPrompt2=Dahil sa sumusunod na tuntunin
+mainBlockedBack=Bumalik
+mainBlockedClose=Isara
+commandRevertAll=Ibalik ang lahat ng mga pansamantalang pagbabago
+commandWhitelistPageDomain=Pansamantalang i-whitelist ang domain ng pahina
+commandWhitelistAll=Pansamantalang i-whitelist lahat
+commandOpenDashboard=Buksan ang dashboard
+elapsedOneMinuteAgo=isang minuto ang nakalipas
+elapsedManyMinutesAgo={{value}} mga minuto ang nakalipas
+elapsedOneHourAgo=isang oras ang nakalipas
+elapsedManyHoursAgo={{value}} mga oras na nakalipas
+elapsedOneDayAgo=kahapon
+elapsedManyDaysAgo={{value}} mga araw na nakalipas
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=I-export sa cloud storage
+cloudPull=Mag-import mula sa cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Ang pangalan ng device na ito:
+genericSubmit=Ipasa
+genericRevert=Ibalik
+errorCantConnectTo=Error sa network: Hindi nakakonekta sa {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/fr/messages.properties b/locale/fr/messages.properties
new file mode 100644
index 0000000..7c59fe7
--- /dev/null
+++ b/locale/fr/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Tableau de bord
+loggerPageName=uMatrix — Journal
+settingsPageName=Paramètres
+privacyPageName=Confidentialité
+statsPageName=Statistiques
+userRulesPageName=Mes règles
+ubiquitousRulesPageName=Fichiers hosts
+rawSettingsPageName=Plus
+aboutPageName=À propos
+allPrettyName=Tout
+cookiePrettyName=Cookie
+cssPrettyName=CSS
+imagePrettyName=Image
+mediaPrettyName=Média
+pluginPrettyName=Plug-in
+scriptPrettyName=Script
+xhrPrettyName=XHR
+framePrettyName=Frame
+otherPrettyName=Autre
+matrixNoNetTrafficPrompt=Aucune activité réseau n'a été observée sur cet onglet pour l'instant.
+matrixMtxButtonTip=Désactiver/Activer les filtres matriciels pour le contexte actuel
+matrixPersistButtonTip=Conserver définitivement les changements du contexte actuel
+matrixRevertButtonTip=Annuler les changements temporaires (contexte actuel)
+matrixReloadButton=Actualiser la page\nAppuyez sur Maj pour contourner le cache du navigateur
+matrix1stPartyLabel=Domaine de la page
+matrixBlacklistedHostnames={{count}} nom(s) de domaine bloqué(s)
+matrixSwitchNoMixedContent=Interdire du contenu mixte
+matrixSwitchNoWorker=Interdire les scripts de type Web worker
+matrixSwitchReferrerSpoof=Modifier le Référant HTTP
+matrixSwitchNoscriptSpoof=Modifier les balises <code><noscript></code>
+matrixRevertAllEntry=Annuler tous les changements temporaires
+matrixLoggerMenuEntry=Aller au Journal
+matrixDashboardMenuEntry=Aller au Tableau de bord
+matrixNoTabFound=Aucune page Web trouvée
+statsPageTitle=uMatrix &ndash; Statistiques
+statsPageGenericStats=Statistiques générales
+statsPageCookieHeadersFoiled=Nombre de <a href='https://fr.wikipedia.org/wiki/Cookie_(informatique)'>Cookies HTTP</a> bloqués : {{count}}
+statsPageRefererHeadersFoiled=Nombre de <a href='https://fr.wikipedia.org/wiki/R%C3%A9f%C3%A9rant'>Référants HTTP</a> bloqués : {{count}}
+statsPageHyperlinkAuditingFoiled=Nombre d'<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Audits hyperliens</a> bloqués : {{count}}
+statsPageCookiesRemoved=Nombre de Cookies locaux supprimés : {{count}}
+statsPageLocalStoragesCleared=Nombre de <a href='https://fr.wikipedia.org/wiki/Stockage_web_local'>Stockages web locaux</a> effacés : {{count}}
+statsPageBrowserCacheCleared=Nombre de fois où le cache du navigateur a été vidé : {{count}}
+statsPageDetailedStats=Statistiques détaillées
+statsPageDetailedAllPages=Toutes les pages
+statsPageDetailedBehindTheScenePage=Requêtes en coulisses du navigateur
+statsPageOverview=Vue d'ensemble
+statsPageRequests=Requêtes
+statsPageAllowed=Autorisées
+statsPageBlocked=Bloquées
+statsPageAll=Tout
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Autres
+statsPageDetailed=Journal
+statsPageLogSizePrompt1=Se souvenir des
+statsPageLogSizePrompt2=plus récentes requêtes HTTP <b>par page</b>
+statsPageLogSizeHelp=<p>Cette option vous permet de saisir le nombre de plus récentes requêtes HTTP à journaliser pour une inspection ultérieure.</p><p>Ceci est particulièrement utile pour les utilisateurs expérimentés qui veulent déterminer ce que fait précisément une page Web. Mais le processus de journalisation nécessite de la mémoire vive, et si vous ne vous souciez guère de ces informations, la mémoire utilisée dans ce but est gaspillée.</p><p>Pour désactiver cette fonctionnalité et ainsi réduire l'empreinte mémoire de <i>uMatrix</i>, saisissez &ldquo;<code>0</code>&rdquo;.</p>
+statsPageRefresh=Actualiser
+settingsPageTitle=uMatrix &ndash; Réglages
+settingsMatrixDisplayHeader=Matrice
+settingsMatrixDisplayTextSizePrompt=Taille du texte :
+settingsMatrixDisplayTextSizeNormal=Normale
+settingsMatrixDisplayTextSizeLarge=Grande
+settingsMatrixDisplayColorBlind=Mode Daltonien
+settingsMatrixConvenienceHeader=Ergonomie
+settingsDefaultScopeLevel=Niveau de contexte par défaut :
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domaine
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=À la fermeture post-édition de la matrice, recharger si nécessaire :
+settingsMatrixAutoReloadNone=Aucune page
+settingsMatrixAutoReloadCurrent=La page courante
+settingsMatrixAutoReloadAll=Toutes les pages
+settingsMatrixAutoReloadInfo=Lorsque vous avez modifié des permissions susceptibles d'affecter l'apparence et/ou le comportement d'une page Web, <i>uMatrix</i> rechargera automatiquement une ou plusieurs page(s) Web concernée(s) à la fermeture de la matrice. Si l'option Aucune page est sélectionnée, vous devrez actualiser la page Web vous-même pour appliquer les modifications.
+settingsSubframeColor=Couleur pour les <i>frames</i> bloquées :
+settingsSubframeOpacity=Opacité :
+settingsIconBadgeEnabled=Montrer le nombre de requêtes distinctes sur l'icône
+settingsCollapseBlocked=Cacher les espaces réservés aux éléments bloqués
+settingsCollapseBlacklisted=Cacher les espaces réservés aux éléments de la liste noire
+settingsNoscriptTagsSpoofed=Modifier les balises <code><noscript></code> lorsque les scripts du domaine de la page Web sont bloqués
+settingsCloudStorageEnabled=Activer le stockage des paramètres dans le nuage
+privacyPageTitle=uMatrix &ndash; Confidentialité
+privacyDeleteBlockedCookiesPrompt=Supprimer les cookies bloqués
+privacyDeleteBlockedCookiesHelp=<p><i>uMatrix</i> n'empêche pas les cookies bloqués 'd'entrer' dans votre navigateur, mais les empêche d'en 'repartir', et c'est cela qui importe. 'Laisser venir' les cookies vous permet de savoir lorsqu'un site essaye d'en utiliser, ou même d'en inspecter le contenu si vous le désirez.</p><p>Une fois que les cookies bloqués ont été pris en compte par <i>uMatrix</i>, ils peuvent être supprimés de votre navigateur si vous le souhaitez.</p><p><b>Note importante</b> : Les extensions peuvent faire des requêtes Web au cours de leurs opérations ordinaires. Ces requêtes peuvent entraîner la création de cookies dans le navigateur. Si cette fonctionnalité est activée et que le nom d'hôte originel du cookie n'est pas sur liste blanche, le cookie sera supprimé du navigateur par <i>uMatrix</i>, alors assurez-vous que le(s) nom(s) d'hôte(s) avec le(s)quel(s) l'extension interagit soit/soient sur liste blanche.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Supprimer les cookies de session non-bloqués et inutilisés depuis plus de
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>Définition W3C</a> : &ldquo;Un cookie de session [...] est effacé quand vous avez terminé la session de navigation. Le cookie de session est stocké dans la mémoire temporaire et n'est pas conservé après la fermeture du navigateur.&rdquo;</p><p>Sauf que cela <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>peut ne pas se produire</a> sur certains navigateurs. De plus, pour certaines personnes, avoir à fermer le navigateur pour nettoyer les cookies de session peut ne pas se révéler suffisant en terme de confidentialité.</p>
+privacyDeleteBlockedLocalStoragePrompt=Supprimer les <a href='http://fr.wikipedia.org/wiki/Stockage_web_local'>données locales</a> des noms d'hôtes bloqués
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Nettoyer le cache du navigateur toutes les
+privacyClearCachePrompt2=minutes
+privacyClearCacheHelp=<p>Certains sites Web sont déterminés à connaître votre vie privée en ligne, quitte à employer des méthodes peu orthodoxes pour contrer les dispositions que vous avez prises pour ne pas être victime de pistage.</p><p>Certaines de ces techniques exploitent<sup>[1, 2]</sup> le <a href='https://fr.wikipedia.org/wiki/Cache_web'>cache du navigateur</a> dont le contenu perdure souvent un bon bout de temps, puisque les utilisateurs prennent rarement le temps de fréquemment vider leur cache de navigation.</p><p>Nettoyer régulièrement le cache du navigateur est un dérangement aux yeux de certaines personnes, même si cela empêche ces envahissants pisteurs de bafouer votre confidentialité.</p><p>Cochez cette option pour que <i>uMatrix</i> s'en occupe à votre place, suivant la fréquence de votre choix.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a><br>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies”&rdquo;</a></p>
+privacyProcessRefererPrompt=Modifier les informations de <a href='https://fr.wikipedia.org/wiki/R%C3%A9f%C3%A9rant'>Référants HTTP</a> pour les tierces requêtes
+privacyProcessRefererHelp=<p>D'après Wikipédia :</p> <blockquote>Un référant HTTP est un champ dans l'entête HTTP, qui indique l'adresse de la page Web depuis laquelle on se connecte vers la ressource demandée[...] <b>Puisque les informations comprises dans un référant peuvent compromettre la vie privée, certains navigateurs Web permettent aux utilisateurs de désactiver l'envoi de ces données.</b></blockquote><p>Si ce paramètre est coché, <i>uMatrix</i> supprimera le référant HTTP si le nom de domaine du référant HTTP est différent du nom de domaine de la requête réseau (autrement dit, si vous venez d'un site différent de celui que vous visitez)</p>
+privacyNoMixedContentPrompt=Interdire du contenu mixte - Trafic HTTPS uniquement
+privacyNoMixedContentHelp=<p>D'après le <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a> :</p><blockquote>Si la page HTTPS comprend des éléments en provenance de trafic(s) HTTP, alors la connexion n'est encryptée que partiellement; le contenu non-encrypté est alors accessible aux analyseurs de paquets et peut être modifié lors d'attaques de l'homme du milieu, compromettant la sécurité de la connexion. Lorsqu'une page Web montre un tel comportement, elle est qualifiée de page Web au contenu mixte.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Bloquer toutes les tentatives d'<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>audit hyperlien</a>
+privacyProcessHyperlinkAuditingHelp=<p>L'audit hyperlien est un méchanisme qui permet à <b>n'importe quel parti</b> de connaître le lien sur lequel une personne clique dans une page Web précise. C'est surtout une fonctionnalité de pistage, qui peut donc être désactivée sans crainte.</p>
+userRulesPermanentHeader=Règles permanentes
+userRulesTemporaryHeader=Règles temporaires
+userRulesRevert=Rétablir
+userRulesCommit=Appliquer
+userRulesEdit=Éditer
+userRulesEditSave=Enregistrer
+userRulesEditDicard=Annuler
+userRulesImport=Importer à partir du fichier
+userRulesExport=Exporter vers un fichier
+userRulesFormatHint=Consultez cette page Web pour connaître la syntaxe des règles.
+userRulesDefaultFileName=mes-regles-umatrix.txt
+hostsFilesPrompt=Tout nom de domaine énuméré dans un des fichiers choisis ci-dessous est bloqué dans le contexte global (*).
+hostsFilesStats={{blockedHostnameCount}} nom(s) de domaine distinct(s) bloqué(s) depuis :
+hostsFilesPerFileStats={{used}} utilisé(s) sur un total de {{total}}
+hostsFilesLastUpdate=Dernière mise à jour effectuée il y a : {{ago}}
+hostsFilesApplyChanges=Appliquer les modifications
+hostsFilesAutoUpdatePrompt=Mettre à jour les fichiers hosts automatiquement
+hostsFilesUpdateNow=Mettre à jour maintenant
+hostsFilesPurgeAll=Vider tous les caches
+hostsFilesExternalListsHint=Une URL par ligne. Les lignes commençant par &lsquo;#&rsquo; seront ignorées. Une URL invalide sera automatiquement ignorée.
+hostsFilesExternalListsParse=Valider
+hostsFilesExternalListPurge=Vider le cache
+hostsFilesExternalListNew=Nouvelle version disponible
+hostsFilesExternalListObsolete=Potentiellement obsolète
+rawSettingsWarning=Attention, modifiez ces paramètres bruts à vos risques et périls.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Journal des changements (en Anglais)</a>
+aboutStorageUsed=Stockage utilisé : {{storageUsed}} octet(s)
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation (en Anglais)</a>
+aboutPermissions=<a href='https://github.com/gorhill/uMatrix/wiki/Permissions'>Permissions (en Anglais)</a>
+aboutCode=Code Source (Licence GPLv3, en Anglais)
+aboutIssues=Problèmes
+aboutContributors=Contributeurs
+aboutCodeContributors=Code :
+aboutIssueContributors=Problèmes :
+aboutTranslationContributors=Traductions :
+aboutUserDataHeader=Vos données
+aboutBackupButton=Enregistrer dans un fichier
+aboutBackupFilename=mes-donnees-umatrix.txt
+aboutRestoreButton=Restaurer depuis un fichier
+aboutRestoreConfirm=Toutes vos données seront remplacées par celles enregistrées le {{time}}, puis uMatrix redémarrera.\n\nDésirez-vous vraiment effectuer l'opération ?
+aboutRestoreError=Les données ne peuvent être lues ou sont invalides
+aboutOr=Ou alors, vous pouvez...
+aboutResetButton=Revenir aux paramètres par défaut
+aboutResetConfirm=Attention ! Cela supprimera tous vos paramétrages personnels. Désirez-vous vraiment réinitialiser ?
+loggerFilterInputPlaceholder=Expression(s) de filtre
+loggerMaxEntriesTip=Nombre maximum d'entrées
+loggerEntryCookieDeleted=Cookie supprimé : {{value}}
+loggerEntryDeleteCookieError=Échec lors de la suppression du cookie : {{value}}
+loggerEntryBrowserCacheCleared=Cache du navigateur nettoyé
+loggerEntryAssetUpdated=Élément mis à jour : {{value}}
+mainBlockedPrompt1=uMatrix a empêché le chargement de cette page :
+mainBlockedPrompt2=À cause de la règle suivante
+mainBlockedBack=Retour
+mainBlockedClose=Fermer
+commandRevertAll=Annuler toutes les modifications temporaires
+commandWhitelistPageDomain=Mettre temporairement en liste blanche le domaine de la page visitée
+commandWhitelistAll=Tout mettre temporairement en liste blanche
+commandOpenDashboard=Ouvrir le Tableau de bord
+elapsedOneMinuteAgo=une minute
+elapsedManyMinutesAgo={{value}} minutes
+elapsedOneHourAgo=une heure
+elapsedManyHoursAgo={{value}} heures
+elapsedOneDayAgo=un jour
+elapsedManyDaysAgo={{value}} jours
+showDashboardButton=Tableau de bord
+showLoggerButton=Journal des requêtes
+cloudPush=Exporter vers le stockage dans le nuage
+cloudPull=Importer depuis le stockage dans le nuage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Nom de ce périphérique :
+genericSubmit=Appliquer
+genericRevert=Rétablir
+errorCantConnectTo=Erreur réseau : Ne peut se connecter à {{url}}
+genericApplyChanges=Appliquer
diff --git a/locale/gu/messages.properties b/locale/gu/messages.properties
new file mode 100644
index 0000000..060ae58
--- /dev/null
+++ b/locale/gu/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Settings
+privacyPageName=Privacy
+statsPageName=Statistics
+userRulesPageName=My rules
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=About
+allPrettyName=all
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/he/messages.properties b/locale/he/messages.properties
new file mode 100644
index 0000000..51cb4f3
--- /dev/null
+++ b/locale/he/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — פאנל הקונפיגורציות
+loggerPageName=uMatrix — מתעד הבקשות
+settingsPageName=הגדרות
+privacyPageName=פרטיות
+statsPageName=סטטיסטיקות
+userRulesPageName=החוקים שלי
+ubiquitousRulesPageName=קבצי Hosts
+rawSettingsPageName=More
+aboutPageName=אודות
+allPrettyName=הכל
+cookiePrettyName=עוגייה
+cssPrettyName=CSS
+imagePrettyName=תמונה
+mediaPrettyName=מדיה
+pluginPrettyName=פלאגין
+scriptPrettyName=סקריפט
+xhrPrettyName=XHR
+framePrettyName=מסגרת
+otherPrettyName=אחר
+matrixNoNetTrafficPrompt=לא נצפו פעולות רשת בלשונית זו עד לעכשיו.
+matrixMtxButtonTip=כבה/אפשר מטריצת פילטור עבור טווח זה.
+matrixPersistButtonTip=שמור את כל השינויים הזמניים עבור טווח זה.
+matrixRevertButtonTip=מחק שינויים זמניים עבור טווח זה.
+matrixReloadButton=טען מחדש את העמוד.\nהחזק את מקש ה Shift על מנת להתעלם מזיכרון מטמון של הדפדפן.
+matrix1stPartyLabel=צד ראשון
+matrixBlacklistedHostnames={{count}} דומיין(ים) חסום(ים)
+matrixSwitchNoMixedContent=מצב HTTPS נוקשה
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=זיוף מפנה
+matrixSwitchNoscriptSpoof=זיוף תגיות <code><noscript></code>
+matrixRevertAllEntry=מחק את כל השינויים הזמניים
+matrixLoggerMenuEntry=עבור למתעד
+matrixDashboardMenuEntry=עבור לפאנל הקונפיגורציות
+matrixNoTabFound=לא נמצא עמוד
+statsPageTitle=uMatrix &ndash; סטטיסטיקות
+statsPageGenericStats=סטטיסטיקות גנריות
+statsPageCookieHeadersFoiled=כותרות (Headers) <a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> שסוכלו: {{count}}
+statsPageRefererHeadersFoiled=כותרות (Headers) <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> שסוכלו: {{count}}
+statsPageHyperlinkAuditingFoiled=ניסיונות <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> שסוכלו: {{count}}
+statsPageCookiesRemoved=מחיקות של עוגיות מקומיות: {{count}}
+statsPageLocalStoragesCleared=ריקונים של <a href='http://diveintohtml5.info/storage.html'>אחסונים לוקליים</a>: {{count}}
+statsPageBrowserCacheCleared=מחיקות של זיכרון מטמון: {{count}}
+statsPageDetailedStats=סטטיסטיקות מפורטות
+statsPageDetailedAllPages=כל הדפים
+statsPageDetailedBehindTheScenePage=מאחורי הקלעים
+statsPageOverview=סקירה כללית
+statsPageRequests=בקשות
+statsPageAllowed=מאופשרות
+statsPageBlocked=חסומות
+statsPageAll=כולן
+statsPagePages=דפים
+statsPageCookies=עוגיות
+statsPageCSS=CSS
+statsPageImages=תמונות
+statsPagePlugins=פלאגינים
+statsPageScripts=סקריפטים
+statsPageXHRs=XHRs
+statsPageFrames=מסגרות
+statsPageOthers=אחרות
+statsPageDetailed=מתעד
+statsPageLogSizePrompt1=זכור את
+statsPageLogSizePrompt2=בקשות ה HTTP <b>פר עמוד</b>.
+statsPageLogSizeHelp=<p>אתה יכול לבדוק את הפרטים של רוב בקשות HTTP הגולמיות האחרונות אשר יצאו מדף אינטרנט (ראה להלן).</p><p>זה שימושי בעיקר למשתמשים מתקדמים שרוצים לחקור בדיוק מה דף האינטרנט עשה. אבל רישום בקשות ה HTTP הללו דורש זיכרון, ואם לא אכפת לך מהמידע הטכני הזה, אז הזיכרון מתבזבז.</p><p>לכן קיימת אפשרות זו המאפשרת לך להתאים את המספר המרבי של בקשות ה HTTP הכי האחרונות שמיועדות להירשם לבדיקה נוספת.</p><p>הזן &ldquo;<code>0</code>&rdquo; כדי לכבות את הרישום המפורט (וכתוצאה מכך להפחית את השימוש בזיכרון של <i>uMatrix</i>).</p>
+statsPageRefresh=רענן
+settingsPageTitle=uMatrix &ndash; הגדרות
+settingsMatrixDisplayHeader=מטריצה
+settingsMatrixDisplayTextSizePrompt=גודל טקסט:
+settingsMatrixDisplayTextSizeNormal=רגיל
+settingsMatrixDisplayTextSizeLarge=גדול
+settingsMatrixDisplayColorBlind=ידידותי לעיוורי צבעים
+settingsMatrixConvenienceHeader=נוחות
+settingsDefaultScopeLevel=רמת הגדרות דיפולטיבית:
+settingsDefaultScopeLevel0=גלובלי
+settingsDefaultScopeLevel1=דומיין
+settingsDefaultScopeLevel2=אתר
+settingsMatrixAutoReloadPrompt=לאחר סגירת המטריצה, טען מחדש בצורה חכמה את הלשוניות הבאות:
+settingsMatrixAutoReloadNone=ללא
+settingsMatrixAutoReloadCurrent=נוכחי
+settingsMatrixAutoReloadAll=כולם
+settingsMatrixAutoReloadInfo=בכל פעם שאתה עושה שינויים במטריצה אשר יכולים להשפיע על ההצגה ו/או התנהגות של דף או יותר, <i>uMatrix</i> יטען מחדש את העמודים המושפעים אוטומטית כשתסגור את המטריצה.
+settingsSubframeColor=מסגרות חסומות:&ensp;צבע
+settingsSubframeOpacity=אטימות
+settingsIconBadgeEnabled=הראה את מספר הבקשות החסומות הייחודיות על האייקון
+settingsCollapseBlocked=הסתר את המקום שנשאר מחסימה של אלמנטים
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=זייף תגיות <code><noscript></code> כאשר סקריפטים צד-1 חסומים
+settingsCloudStorageEnabled=אפשר תמיכת אחסון ענן
+privacyPageTitle=uMatrix &ndash; פרטיות
+privacyDeleteBlockedCookiesPrompt=מחק עוגיות חסומות.
+privacyDeleteBlockedCookiesHelp=<p>עוגיות ברשימה השחורה לא נמנעות על ידי <i>uMatrix</i> מלהיכנס לדפדפן שלך. אולם הן כן מנועות מלצאת מהדפדפן שלך, וזה מה שחשוב באמת. לא לחסום עוגיות לפני שהן נכנסות לדפדפן שלך נותן לך את ההזדמנות להיות מודע לכך שאתר ניסה להשתמש בעוגיות, ויתר על כן כדי לבדוק את תוכנם, אם תרצה.</p><p>כאשר <i>uMatrix</i> נתקל בעוגייה שברשימה השחורה, הן יכולות להיות מוסרות מהדפדפן שלך אם תרצה בכך.</p><p><b>הערה חשובה:</b> תוספים יכולים לבצע בקשות רשת במהלך פעולתם הרגילה. בקשות אלה יכולות לגרום לכך שעוגיות יווצרו בדפדפן. אם הדומיין שממנה העוגייה הגיעה לא ברשימה הלבנה, העוגייה תמחק מהדפדפן על ידי <i>µMatrix</i> אם אפשרות זו מאופשרת. אז תהיה בטוח לגבי זה שהדומיין(ים) אשר איתם התוסף יכול לתקשר נמצא(ים) ברשימה הלבנה.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=מחק עוגיות סשן לא חסומות
+privacyDeleteNonBlockedSessionCookiesPrompt2= דקות אחרי הפעם האחרונה שהן היו בשימוש.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;עוגיית סשן ... נמחקת כאשר אתה מסיים הסשן של הדפדפן. עוגיית הסשן מאוכסנת בזיכרון זמני ולא נשמרת לאחר שהדפדפן נסגר.&rdquo;</p><p>אלא שהדבר <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>יכול לא לקרות</a> בדפדפנים מסויימים. כמו כן, לחלק, הפעולה של לסגור את הדפדפן על מנת שעוגיות הסשן יימחקו יכולה להיות פעולה מספיק מוקדמת.</p>
+privacyDeleteBlockedLocalStoragePrompt=מחק <a href='https://en.wikipedia.org/wiki/Web_storage'>איחסונים לוקאליים</a> שנרשמו על ידי דומיינים חסומים
+privacyDeleteBlockedLocalStorageHelp=לעשות
+privacyClearCachePrompt1=מחק את הזיכרון מטמון כל
+privacyClearCachePrompt2=דקות.
+privacyClearCacheHelp=<p>יש אתרי אינטרנט שממש רוצים לעקוב אחרייך, כל כך שהם ישתמשו בטריקים לא כל כך יפים על מנת לעקוף את כל הדברים שאתה עושה כדי לא להיות במעקב.</p><p>כמה מהטריקים האלה מתבססים<sup>[1, 2]</sup> על <a href='https://en.wikipedia.org/wiki/Web_cache'>הזיכרון מטמון של הדפדפן</a>, אשר תוכנו לרוב נמצא הרבה זמן על המחשב מכיוון שלעיתים רחוקות משתמשים ישקיעו מזמנם לנקות באופן קבוע את הזיכרון מטמון של הדפדפן שלהם.</p><p>יש אי נעימות קלה לנקות את הזיכרון מטמון של הדפדפן באופן קבוע (רוב הסיכויים שלא תשים לב כשזה קורה), והתועלת היא במניעה של העוקבים הנתעבים האלה מחדירה לפרטיות שלך.</p><p>סמן אפשרות זו בשביל ש <i>uMatrix</i> יעשה זאת בשבילך, כל כמה זמן שתרצה.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;מניעה מעקב אינטרנטי על ידי שימוש בזיכרון מטמון של הדפדפן&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;עוגיות חסרי העוגיות&rdquo;</a></p>
+privacyProcessRefererPrompt=זייף את ה <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> של בקשות צד שלישי.
+privacyProcessRefererHelp=מתוך ויקיפדיה:<blockquote>HTTP referer זוהי כותרת (header) של בקשת HTTP אשר מזהה את הכתובת של דף האינטרנט שמקושר למשאב שמבוקש. ... <b>משום שהמידע על המפנה (referer) יכול לפגוע בפרטיות, מספר דפדפנים מאפשרים למשתמש לבטל את השליחה של המידע של המפנה.</b></blockquote>אם אפשרות זו מאופשרת, <i>uMatrix</i> יזייף את המידע של ה HTTP referrer אם שם הדומיין של ה HTTP referrer הוא צד שלישי לשם הדומיין של בקשת הרשת.
+privacyNoMixedContentPrompt=מצב HTTPS נוקשה: אסור על תוכן מעורב.
+privacyNoMixedContentHelp=<p>מתוך <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p>אם דף HTTPS (מאובטח) מכיל בתוכו תוכן שהובא באמצעות בקשת HTTP, אז החיבור מוצפן באופן חלקי בלבד: התוכן הלא מוצפן נגיש להסנפה ותוקפים שנמצאים באמצע יכולים לשנות אותו, ולכן החיבור כבר לא בטוח. כאשר דף אינטרנט מציג התנהגות זו, זה נקרא דף עם תוכן מעורב.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=חסום את כל הניסיונות ל<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a>.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing זהו מנגנון אשר מאפשר לצד, <b>כל צד</b>, להיות מודע על איזה קישור בדיוק המשתמש לחץ בדף אינטרנט ספציפי. במהות שלו זוהי תכונת מעקב: היא מאפשרת לדף אינטרנט, או לכל אתר צד שלישי לדף אינטרנט זה, להיות מודע לגבי איזה קישור לחצת באיזה דף של האתר שלהם. המטרה היחידה היא לעקוב אחר פעילות הגלישה שלך.</p>
+userRulesPermanentHeader=חוקים קבועים
+userRulesTemporaryHeader=חוקים זמניים
+userRulesRevert=החזר למצב הקודם
+userRulesCommit=החל
+userRulesEdit=ערוך
+userRulesEditSave=שמור
+userRulesEditDicard=בטל
+userRulesImport=ייבא מקובץ...
+userRulesExport=ייצא לקובץ...
+userRulesFormatHint=ראה דף זה עבור הסינטקס (התחביר) של החוקים.
+userRulesDefaultFileName=חוקי-המטריצה-שלי.txt
+hostsFilesPrompt=כל הדומיינים בקובץ ה hosts נטענים כחסומים בטווח הגלובלי.
+hostsFilesStats={{blockedHostnameCount}} דומיינים שונים חסומים מ:
+hostsFilesPerFileStats={{used}} בשימוש מתוך {{total}}
+hostsFilesLastUpdate=עדכון אחרון: {{ago}}
+hostsFilesApplyChanges=החל שינויים
+hostsFilesAutoUpdatePrompt=עדכן אוטומטית את קבצי ה hosts.
+hostsFilesUpdateNow=עדכן כעת
+hostsFilesPurgeAll=נקה את כל זיכרוני המטמון
+hostsFilesExternalListsHint=כתובת אחת בכל שורה. שורות עם קידומת &lsquo;#&rsquo; לא יפורשו. כתובות לא חוקיות לא יפורשו ולא תהיה התראה לכך.
+hostsFilesExternalListsParse=פרסר
+hostsFilesExternalListPurge=נקה זיכרון מטמון
+hostsFilesExternalListNew=גרסה חדשה זמינה
+hostsFilesExternalListObsolete=לא עדכני
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>רשימת שינויים</a>
+aboutStorageUsed=אחסון בשימוש: {{storageUsed}} בתים
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>דוקומנטציה</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>הרשאות</a>
+aboutCode=קוד מקור (GPLv3)
+aboutIssues=באגים ובעיות
+aboutContributors=תורמים
+aboutCodeContributors=קוד:
+aboutIssueContributors=בעיות:
+aboutTranslationContributors=תרגומים:
+aboutUserDataHeader=המידע שלך
+aboutBackupButton=גבה לקובץ...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=שחזר מקובץ...
+aboutRestoreConfirm=כל ההגדרות שלך יוחלפו בנתונים שגובו ב {{time}}, ו uMatrix יופעל מחדש.\n\nהאם להחליף את כל ההגדרות הקיימות בנתונים המגובים?
+aboutRestoreError=לא הוצלח לקרוא את המידע או שהינו פגום
+aboutOr=... או ...
+aboutResetButton=אפס להגדרות ברירת מחדל
+aboutResetConfirm=אזהרה! פעולה זו תוביל למחיקה של כל ההגדרות המותאמות אישית שלך. האם אתה בטוח שאתה רוצה להמשיך?
+loggerFilterInputPlaceholder=ביטוי(י) פילטור
+loggerMaxEntriesTip=מספר רשומות מקסימליות
+loggerEntryCookieDeleted=עוגייה נמחקה: {{value}}
+loggerEntryDeleteCookieError=נכשל במחיקת העוגייה: {{value}}
+loggerEntryBrowserCacheCleared=זיכרון מטמון נמחק
+loggerEntryAssetUpdated=הנכס עודכן: {{value}}
+mainBlockedPrompt1=uMatrix מנע מהעמוד הבא מלהיטען:
+mainBlockedPrompt2=בגלל החוק הבא
+mainBlockedBack=חזור
+mainBlockedClose=סגור
+commandRevertAll=מחק את כל ההגדרות הזמניות
+commandWhitelistPageDomain=זמנית הרשה את דף הדומיין
+commandWhitelistAll=זמנית הרשה הכל
+commandOpenDashboard=פתח את פאנל הקונפיגורציות
+elapsedOneMinuteAgo=לפני דקה
+elapsedManyMinutesAgo=לפני {{value}} דקות
+elapsedOneHourAgo=לפני שעה
+elapsedManyHoursAgo=לפני {{value}} שעות
+elapsedOneDayAgo=לפני יום
+elapsedManyDaysAgo=לפני {{value}} ימים
+showDashboardButton=פאנל הקונפיגורציות
+showLoggerButton=מתעד
+cloudPush=ייצא לאחסון ענן
+cloudPull=ייבא מתוך אחסון ענן
+cloudNoData=...\n...
+cloudDeviceNamePrompt=שם התקן זה:
+genericSubmit=שלח
+genericRevert=שחזר
+errorCantConnectTo=שגיאת רשת: לא הוצלח להתחבר ל {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/hi/messages.properties b/locale/hi/messages.properties
new file mode 100644
index 0000000..b79e657
--- /dev/null
+++ b/locale/hi/messages.properties
@@ -0,0 +1,179 @@
+extName=µमैट्रिक्स
+dashboardPageName=µमैट्रिक्स— डैशबोर्ड
+loggerPageName=uMatrix — Logger
+settingsPageName=सेटिंग्स
+privacyPageName=गोपनीयता
+statsPageName=आंकड़े
+userRulesPageName=मेरे नियम
+ubiquitousRulesPageName=होस्ट फ़ाइलें
+rawSettingsPageName=More
+aboutPageName=जानकारी
+allPrettyName=सभी
+cookiePrettyName=कुकीज़
+cssPrettyName=सीएसएस
+imagePrettyName=छवि
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=लिपि
+xhrPrettyName=XHR
+framePrettyName=ढांचा
+otherPrettyName=अन्य
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=सभी
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=संक्षिप्त अवलोकन
+statsPageRequests=Requests
+statsPageAllowed=अनुमति है
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=अनुरोध प्रवेश
+statsPageLogSizePrompt1=पिछले याद रखें
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=रिफ्रेश
+settingsPageTitle=µMatrix &ndash; सेटिंग्स
+settingsMatrixDisplayHeader=दिखावट
+settingsMatrixDisplayTextSizePrompt=पाठ का आकार
+settingsMatrixDisplayTextSizeNormal=नोर्मल
+settingsMatrixDisplayTextSizeLarge=बड़ा
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=कोई नहीं
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/hr/messages.properties b/locale/hr/messages.properties
new file mode 100644
index 0000000..060ae58
--- /dev/null
+++ b/locale/hr/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Settings
+privacyPageName=Privacy
+statsPageName=Statistics
+userRulesPageName=My rules
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=About
+allPrettyName=all
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/hu/messages.properties b/locale/hu/messages.properties
new file mode 100644
index 0000000..6c055ac
--- /dev/null
+++ b/locale/hu/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Vezérlőpult
+loggerPageName=uMatrix — Napló
+settingsPageName=Beállítások
+privacyPageName=Adatvédelem
+statsPageName=Statisztika
+userRulesPageName=Saját szabályok
+ubiquitousRulesPageName=Tiltólisták
+rawSettingsPageName=Több
+aboutPageName=Névjegy
+allPrettyName=mind
+cookiePrettyName=sütik
+cssPrettyName=css
+imagePrettyName=kép
+mediaPrettyName=média
+pluginPrettyName=bővítmény
+scriptPrettyName=szkript
+xhrPrettyName=XHR
+framePrettyName=keret
+otherPrettyName=egyéb
+matrixNoNetTrafficPrompt=Eddig még nincs ehhez a laphoz tartozó adatátvitel.
+matrixMtxButtonTip=Mátrixszűrés engedélyezése/tiltása ebben a hatáskörben.
+matrixPersistButtonTip=Minden ideiglenes változtatás mentése ebben a hatáskörben.
+matrixRevertButtonTip=Ideiglenes változtatások visszavonása ebben a hatáskörben.
+matrixReloadButton=Oldal újratöltése.
+matrix1stPartyLabel=Aktuális domain
+matrixBlacklistedHostnames={{count}} tiltott hostnév
+matrixSwitchNoMixedContent=Kevert tartalom tiltása
+matrixSwitchNoWorker=Web worker-ek tiltása
+matrixSwitchReferrerSpoof=<code>Referer</code> header hamisítása
+matrixSwitchNoscriptSpoof=<code><noscript></code> tagek felülírása
+matrixRevertAllEntry=Minden ideiglenes változtatás visszavonása
+matrixLoggerMenuEntry=Napló megnyitása
+matrixDashboardMenuEntry=Irány a vezérlőpult
+matrixNoTabFound=Nem található weboldal
+statsPageTitle=uMatrix &ndash; Statisztika
+statsPageGenericStats=Általános statisztikák
+statsPageCookieHeadersFoiled=<a href='https://hu.wikipedia.org/wiki/HTTP_cookie'>HTTP süti</a> header-ek blokkolva: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://hu.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> header-ek blokkolva: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/links.html#hyperlink-auditing'>Hiperlink-auditálási</a> kísérletek blokkolva: {{count}}
+statsPageCookiesRemoved={{count}} helyi süti eltávolítva
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Helyi tárolók</a> kiürítve: {{count}}
+statsPageBrowserCacheCleared=Böngésző gyorsítótára törölve: {{count}}
+statsPageDetailedStats=Részletes statisztika
+statsPageDetailedAllPages=Mind
+statsPageDetailedBehindTheScenePage=A háttérben
+statsPageOverview=Áttekintés
+statsPageRequests=Lekérések
+statsPageAllowed=Engedélyezett
+statsPageBlocked=Blokkolt
+statsPageAll=Mind
+statsPagePages=Oldalak
+statsPageCookies=Sütik
+statsPageCSS=CSS
+statsPageImages=Képek
+statsPagePlugins=Bővítmények
+statsPageScripts=Szkriptek
+statsPageXHRs=XHR-ek
+statsPageFrames=Keretek
+statsPageOthers=Egyebek
+statsPageDetailed=Napló
+statsPageLogSizePrompt1=Emlékezz az utolsó
+statsPageLogSizePrompt2=darab HTTP kérésre <b>oldalanként</b>.
+statsPageLogSizeHelp=<p>A <i>uMatrix</i> lehetőséget ad a weboldalak által generált legutóbbi nyers HTTP lekérések részleteinek megtekintésére.</p><p>Ez leginkább haladó felhasználók számára hasznos, akik szeretnének képben lenni a weboldal tényleges működésével.</p><p>Természetesen a HTTP lekérések naplózása memóriát foglal, ezért amennyiben ilyen részletes technikai információkra nincs szükség, abban az esetben érdemes ezt a funkciót kikapcsolni memóriamegtakarítás céljából.</p><p>Ebben a mezőben megadható a maximálisan eltárolt HTTP lekérések száma; &ldquo;<code>0</code>&rdquo;-ra állítva ez a funkció kikapcsolásra kerül.</p>
+statsPageRefresh=Frissítés
+settingsPageTitle=uMatrix &ndash; Beállítások
+settingsMatrixDisplayHeader=Megjelenés
+settingsMatrixDisplayTextSizePrompt=Betűméret:
+settingsMatrixDisplayTextSizeNormal=Normál
+settingsMatrixDisplayTextSizeLarge=Nagy
+settingsMatrixDisplayColorBlind=Színtévesztőbarát mód
+settingsMatrixConvenienceHeader=Kényelmi beállítások
+settingsDefaultScopeLevel=Hatáskör alapértelmezett szintje:
+settingsDefaultScopeLevel0=Globális
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Weboldal
+settingsMatrixAutoReloadPrompt=A mátrix bezárásával a következő lapok intelligens újratöltése:
+settingsMatrixAutoReloadNone=Nincs
+settingsMatrixAutoReloadCurrent=Jelenlegi
+settingsMatrixAutoReloadAll=Mind
+settingsMatrixAutoReloadInfo=Amennyiben a mátrixban olyan változtatások történnek, amelyek egy, vagy több oldal megjelenítését vagy működését befolyásolják, a mátrix bezárása után a <i>uMatrix</i> automatikusan újra fogja tölteni az érintett oldalakat.
+settingsSubframeColor=Blokkolt keret:&ensp;Szín
+settingsSubframeOpacity=Áttetszőség
+settingsIconBadgeEnabled=Jelenjen meg az egyedi lekérések száma az ikonon
+settingsCollapseBlocked=Blokkolt elemek helyőrzőinek elrejtése
+settingsCollapseBlacklisted=Tiltott elemek helyőrzőinek elrejtése
+settingsNoscriptTagsSpoofed=<code><noscript></code> tagek tartalmának felülírása, ha az aktuális domain szkriptjei blokkolva vannak
+settingsCloudStorageEnabled=Felhőszolgáltatások támogatása
+privacyPageTitle=uMatrix &ndash; Adatvédelem
+privacyDeleteBlockedCookiesPrompt=Blokkolt sütik törlése.
+privacyDeleteBlockedCookiesHelp=<p>A <i>uMatrix</i> a tiltólistás sütik böngészőből való távozását tiltja csak meg, a böngészőbe érkezésüket nem akadályozza meg. Ezúton tudomás szerezhető arról, ha egy oldal sütiket akar tárolni, valamint azok tartalma is megtekinthetővé válik.</p><p>Ezen opció bejelölésével a <i>uMatrix</i> automatikusan eltávolítja a beérkezett tiltott sütiket.</p><p><b>Fontos:</b> Nemcsak oldalak, hanem kiegészítők is indíthatnak lekérdezéseket működésük során, amelyek sütik létrehozásához vezethetnek. Amennyiben ez az opció engedélyezve van, továbbá a lekérésekhez tartozó hostneveken a sütik blokkolva vannak, akkor azok el lesznek távolítva a <i>uMatrix</i> által. Ez egyes kiegészítőknél hibás működést eredményezhet, amíg az általuk használt hostnevekről kapott sütik nincsenek engedélyezve.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Nem blokkolt munkamenet sütik törlése
+privacyDeleteNonBlockedSessionCookiesPrompt2= perccel az utolsó használatuk után.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A munkamenet süti ... eltávolításra kerül a böngészési munkamenettel befejezően. A munkamenet süti ideiglenes memóriában tárolódik és a böngésző bezárásával törlésre kerül.&rdquo;</p><p>Viszont pár böngészőnél <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>előfordulhat, hogy nem ez történik</a>, valamint nem minden esetben elfogadható megoldás a böngésző csak a munkamenet sütik törléséért való bezárása.</p>
+privacyDeleteBlockedLocalStoragePrompt=Blokkolt hostnevek által <a href='https://en.wikipedia.org/wiki/Web_storage'>helyileg tárolt</a> adatok törlése
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Gyorsítótárak ürítése
+privacyClearCachePrompt2=percenként.
+privacyClearCacheHelp=<p>Egyes weboldalak annyira elszántan követni szeretnék a felhasználóikat, hogy képesek nem kifejezetten szép trükköket is bevetni a követés ellen tett lépéseik kikerülése érdekében.</p><p>Pár ilyen trükk<sup>[1, 2]</sup> a <a href='https://en.wikipedia.org/wiki/Web_cache'>böngésző gyorsítótárát</a> használja fel, tekintve, hogy a felhasználók ritkán ürítik ki azt, így gyakran sokáig eltárolásra kerülnek ezek a követők.</p><p>A böngésző gyorsítótárának gyakori ürítése kevés hátránnyal jár, (legtöbbször fel sem tűnik,) ráadásul megállítja ezeket az ellenszenves követéseket a személyes adatok védelme érdekében.</p><p>Ezen opció engedélyezésekor a <i>uMatrix</i> kiüríti a böngészó gyorsítótárát a megadott időközönként.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=A <a href='https://hu.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> header meghamisítása harmadik fél felé küldött lekérések esetében.
+privacyProcessRefererHelp=Wikipediából:<blockquote>A Referer egy olyan HTTP header, ami meghatározza annak a weboldalnak címét, ami a lekért oldalra mutatott. ... <b>Mivel ez az információ a személyes adatok védelmét sértheti, néhány böngésző megengedi a felhasználóknak, hogy kikapcsolják ennek az információnak az elküldését.</b></blockquote>Ezen beállítás engedélyezésekor a <i>uMatrix</i> meg fogja hamisítani a referer információt abban az esetben, ha a referer domain neve harmadik félnek számít a lekérés domain nevéhez képest.
+privacyNoMixedContentPrompt=Szigorú HTTPS: kevert tartalom tiltása.
+privacyNoMixedContentHelp=<p>A <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>ből:</p><blockquote>Amennyiben [egy] HTTPS oldal sima, szöveges HTTP lekérésen kereszül elért elemeket tartalmaz, akkor a kapcsolat csak részben titkosított: a titkosítatlan tartalom a kapcsolatot lehallgató egyének számára is elérhető, valamint megváltoztatható man-in-the-middle támadás által, ezért a kapcsolat nem tekinthető védettnek ezek fényében. Amikor egy oldal ilyen jelenséget produkál, akkor azt kevert tartalmú oldalnak hívjuk.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Minden <a href='https://html.spec.whatwg.org/multipage/links.html#hyperlink-auditing'>hiperlink-auditálási</a> kísérlet blokkolása.
+privacyProcessHyperlinkAuditingHelp=<p>A hiperlink-auditálás egy olyan mechanizmus, ami egy hardmadik- <b>vagy bármely</b> fél számára lehetővé teszi, hogy visszajelzést kapjanak egy a felhasználó által az oldalon lévő linkre történő kattintásról. Lényegében ez egy követési módszer: lehetővé teszi egy oldalnak, vagy bármely harmadik fél számára, hogy tudomást szerezzenek arról, hogy a felhasználó melyik linkre kattintott az oldalon. Egyetlen célja, hogy a felhasználó böngészési aktivitását nyomonkövesse.</p>
+userRulesPermanentHeader=Állandó szabályok
+userRulesTemporaryHeader=Átmeneti szabályok
+userRulesRevert=Visszaállítás
+userRulesCommit=Alkalmaz
+userRulesEdit=Szerkesztés
+userRulesEditSave=Mentés
+userRulesEditDicard=Elvetés
+userRulesImport=Importálás fájlból...
+userRulesExport=Exportálás fájlba...
+userRulesFormatHint=A szintaktikai szabályok az alábbi oldalon találhatók.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Egy hosts fájlban található összes bejegyzés tiltott hostnév lesz a globális hatáskörre nézve.
+hostsFilesStats={{blockedHostnameCount}} különböző hostnév blokkolva a következő listák alapján:
+hostsFilesPerFileStats={{used}} használatban {{total}} közül
+hostsFilesLastUpdate=Utoljára frissítve: {{ago}}
+hostsFilesApplyChanges=Módosítások alkalmazása
+hostsFilesAutoUpdatePrompt=Hosts fájlok automatikus frissítése.
+hostsFilesUpdateNow=Frissítés
+hostsFilesPurgeAll=Minden gyorsítótár ürítése
+hostsFilesExternalListsHint=Soronként egy URL. A &lsquo;#&rsquo; karakterrel kezdődő sorok figyelmen kívül lesznek hagyva. Az érvénytelen URL-ek hibaüzenet nélkül figyelmen kívül lesznek hagyva.
+hostsFilesExternalListsParse=Elemzés
+hostsFilesExternalListPurge=gyorsítótár ürítése
+hostsFilesExternalListNew=új verzió érhető el
+hostsFilesExternalListObsolete=elavult
+rawSettingsWarning=Vigyázat! Ezen nyers beállítások megváltoztatása csak saját felelősségre.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Változási napló</a>
+aboutStorageUsed=Tárolóhely használat: {{storageUsed}} byte
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Dokumentáció</a>
+aboutPermissions=<a href='https://github.com/gorhill/uMatrix/wiki/About-the-required-permissions'>Engedélyek</a>
+aboutCode=Forráskód (GPLv3)
+aboutIssues=Bugok és problémák
+aboutContributors=Közreműködők
+aboutCodeContributors=Kód:
+aboutIssueContributors=Problémák:
+aboutTranslationContributors=Fordítások:
+aboutUserDataHeader=Beállítások kezelése
+aboutBackupButton=Mentés fájlba...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Visszaállítás fájlból...
+aboutRestoreConfirm=Minden jelenlegi beállítás felülírásra kerül a {{time}} dátumú biztonsági mentésben található beállítással, majd ezt követően a uMatrix újraindul.\n\nBiztosan felülírjam a jelenlegi beállításokat a megadott biztonsági mentéssel?
+aboutRestoreError=Nem olvasható vagy érvénytelen adat
+aboutOr=... vagy ...
+aboutResetButton=Alapbeállítások visszaállítása
+aboutResetConfirm=Vigyázat! A kiegészítő minden egyéni beállítása eltávolításra fog kerülni. Biztosan ezt tegyem?
+loggerFilterInputPlaceholder=szűrő kifejezés(ek)
+loggerMaxEntriesTip=Maximális bejegyzések száma
+loggerEntryCookieDeleted=süti törölve: {{value}}
+loggerEntryDeleteCookieError=süti törlése sikertelen: {{value}}
+loggerEntryBrowserCacheCleared=böngésző gyorsítótára kiürítve
+loggerEntryAssetUpdated=host adatok frissítve: {{value}}
+mainBlockedPrompt1=A uMatrix megakadályozta a következő oldal betöltését:
+mainBlockedPrompt2=A következő szabály miatt
+mainBlockedBack=Vissza
+mainBlockedClose=Bezár
+commandRevertAll=Minden ideiglenes változtatás törlése
+commandWhitelistPageDomain=Az oldal domainjének ideiglenes engedélyezése
+commandWhitelistAll=Minden domain ideiglenes engedélyezése
+commandOpenDashboard=Vezérlőpult megnyitása
+elapsedOneMinuteAgo=egy perce
+elapsedManyMinutesAgo={{value}} perce
+elapsedOneHourAgo=egy órája
+elapsedManyHoursAgo={{value}} órája
+elapsedOneDayAgo=egy napja
+elapsedManyDaysAgo={{value}} napja
+showDashboardButton=Vezérlőpult
+showLoggerButton=Napló
+cloudPush=Exportálás a felhőszolgáltatásba
+cloudPull=Importálás a felhőszolgáltatásból
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Eszköz neve:
+genericSubmit=Küldés
+genericRevert=Visszaállítás
+errorCantConnectTo=Hálózati hiba: {{url}} betöltése sikertelen
+genericApplyChanges=Módosítások alkalmazása
diff --git a/locale/id/messages.properties b/locale/id/messages.properties
new file mode 100644
index 0000000..50eac49
--- /dev/null
+++ b/locale/id/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dasbor
+loggerPageName=uMatrix — Logger
+settingsPageName=Pengaturan
+privacyPageName=Privasi
+statsPageName=Statistik
+userRulesPageName=Aturan saya
+ubiquitousRulesPageName=Berkas host
+rawSettingsPageName=More
+aboutPageName=Tentang
+allPrettyName=semua
+cookiePrettyName=kuki
+cssPrettyName=css
+imagePrettyName=gmbr
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=skrip
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=lain
+matrixNoNetTrafficPrompt=Sejauh ini tidak terlihat lalu lintas jaringan untuk tab ini.
+matrixMtxButtonTip=Nonaktif/aktifkan penyaringan matriks untuk lingkup ini.
+matrixPersistButtonTip=Simpan semua perubahan sementara untuk lingkup ini.
+matrixRevertButtonTip=Kembalikan perubahan sementara untuk lingkup ini.
+matrixReloadButton=Muat ulang laman.
+matrix1stPartyLabel=pihak pertama
+matrixBlacklistedHostnames={{count}} nama host dicekal
+matrixSwitchNoMixedContent=HTTPS Ketat
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Pemalsuan perujuk
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Kembalikan semua ubahan sementara
+matrixLoggerMenuEntry=Kunjungi pencatat
+matrixDashboardMenuEntry=Kunjungi dasbor
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistik
+statsPageGenericStats=Statistik umum
+statsPageCookieHeadersFoiled=Tajuk <a href='https://id.wikipedia.org/wiki/Kuki_HTTP'>kuki HTTP</a> digagalkan: {{count}}
+statsPageRefererHeadersFoiled=Tajuk <a href='https://en.wikipedia.org/wiki/HTTP_referer'>perujuk HTTP</a> digagalkan: {{count}}
+statsPageHyperlinkAuditingFoiled=Percobaan <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>pengauditan pranala</a> digagalkan: {{count}}
+statsPageCookiesRemoved=Kuki lokal dibuang: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Penyimpanan lokal</a> dikosongkan: {{count}}
+statsPageBrowserCacheCleared=Tembolok peramban dibersihkan: {{count}}
+statsPageDetailedStats=Statistik terperinci
+statsPageDetailedAllPages=Semua
+statsPageDetailedBehindTheScenePage=Di balik layar
+statsPageOverview=Tinjauan
+statsPageRequests=Permintaan
+statsPageAllowed=Diizinkan
+statsPageBlocked=Diblokir
+statsPageAll=Semua
+statsPagePages=Halaman
+statsPageCookies=Kuki
+statsPageCSS=CSS
+statsPageImages=Gambar
+statsPagePlugins=Plugin
+statsPageScripts=Skrip
+statsPageXHRs=XHR
+statsPageFrames=Frame
+statsPageOthers=Lainnya
+statsPageDetailed=Pencatat
+statsPageLogSizePrompt1=Ingat
+statsPageLogSizePrompt2=permintaan HTTP yang terakhir <b>per halaman</b>.
+statsPageLogSizeHelp=<p>Anda dapat memeriksa rincian terkini permintaan mentah HTTP yang telah dibuat oleh halaman web (lihat di bawah).</p><p>Hal ini sangat berguna bagi para pengguna tingkat lanjut yang ingin menyelidiki secara pasti apa yang halaman web telah lakukan. Akan tetapi, mencatat permintaan HTTP ini memerlukan tambahan memori, dan jika anda tidak peduli tentang informasi teknis ini, maka memori terbuang dengan percuma.</p><p>Oleh karena itu, ruas ini memungkinkan anda mengatur jumlah maksimum permintaan HTTP terkini yang akan dicatat untuk pemeriksaan lebih lanjut.</p><p>Masukkan &ldquo;<code>0</code>&rdquo; untuk mematikan pencatatan terperinci (dan berarti berkurangnya penggunaan memori <i>uMatrix</i>).</p>
+statsPageRefresh=Segarkan
+settingsPageTitle=uMatrix &ndash; Pengaturan
+settingsMatrixDisplayHeader=Tampilan
+settingsMatrixDisplayTextSizePrompt=Ukuran teks:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Besar
+settingsMatrixDisplayColorBlind=Ramah buta warna
+settingsMatrixConvenienceHeader=Nyaman
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=Ketika matriks ditutup, muat ulang dengan cerdas tab berikut:
+settingsMatrixAutoReloadNone=Nihil
+settingsMatrixAutoReloadCurrent=Saat ini
+settingsMatrixAutoReloadAll=Semua
+settingsMatrixAutoReloadInfo=Kapanpun anda membuat perubahan pada matriks yang dapat mempengaruhi tampilan dan/atau perilaku satu atau lebih halaman, <i>uMatrix</i> akan memuat ulang halaman yang terdampak secara otomatis ketika anda menutup matriks.
+settingsSubframeColor=Frame diblokir:&ensp;Warna
+settingsSubframeOpacity=Kepekatan
+settingsIconBadgeEnabled=Tampilkan jumlah permintaan yang berbeda pada ikon
+settingsCollapseBlocked=Runtuhkan tempat elemen yang diblokir
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Fungsikan dukungan penyimpanan awan
+privacyPageTitle=uMatrix &ndash; Privasi
+privacyDeleteBlockedCookiesPrompt=Hapus kuki yang diblokir.
+privacyDeleteBlockedCookiesHelp=<p>Kuki dalam daftar hitam tidak dicegah oleh <i>uMatrix</i> untuk masuk ke peramban anda. Namun mereka dicegah untuk meninggalkan peramban anda, dan itulah yang terpenting. Tidak memblokir kuki sebelum mereka masuk ke peramban anda memberikan anda kesempatan mendapatkan informasi bahwa situs tersebut mencoba untuk menggunakan kuki, dan lebih lanjut memeriksa isi kuki tersebut jika anda inginkan.</p><p>Setelah kuki dalam daftar hitam ini dicatat oleh <i>uMatrix</i>, mereka dapat dibuang dari peramban anda jika anda menginginkannya.</p><p><b>Catatan penting:</b> Ekstensi dapat membuat permintaan web selama operasi normal ekstensi berjalan. Permintaan ini dapat mengakibatkan beberapa kuki dibuat dalam peramban. Jika nama host dari mana kuki berasal tidak dalam daftar putih, kuki akan dibuang dari peramban oleh <i>uMatrix</i> jika opsi berikut dicentang. Jadi, pastikan nama host dengan yang ekstensi berkomunikasi masuk dalam daftar putih.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Hapus kuki sesi yang tidak diblokir
+privacyDeleteNonBlockedSessionCookiesPrompt2= menit setelah terakhir kali mereka digunakan.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;Kuki sesi ... dihapus ketika anda mengakhiri sesi peramban. Kuki sesi disimpan di dalam memori sementara dan tidak tinggal setelah peramban ditutup.&rdquo;</p><p>Kecuali hal tersebut <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>mungkin tidak akan terjadi</a> pada beberapa peramban. Juga, bagi beberapa, menutup peramban agar kuki sesi dibersihkan mungkin tidak cukup cepat.</p>
+privacyDeleteBlockedLocalStoragePrompt=Hapus muatan <a href='https://en.wikipedia.org/wiki/Web_storage'>penyimpanan lokal</a> yang disimpan oleh nama host yang diblokir
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Bersihkan tembolok peramban setiap
+privacyClearCachePrompt2=menit.
+privacyClearCacheHelp=<p>Beberapa situs sangat lihai dalam melacak anda, amat sangat lihai sampai-sampai mereka akan gunakan cara-cara yang tidak baik untuk menghindari apapun tindakan yang anda lakukan agar tidak dilacak.</p><p>Beberapa cara-cara ini mengandalkan<sup>[1, 2]</sup> <a href='https://en.wikipedia.org/wiki/Web_cache'>tembolok peramban</a>, yang muatannya terkadang bertahan lama karena jarang pengguna menyempatkan waktu secara berkala membersihkan tembolok peramban mereka.</p><p>Ada sedikit ketidaknyamanan untuk membersihkan tembolok peramban secara berkala (kemungkinan adalah anda tidak akan tahu ketika hal tersebut terjadi), dan keuntungannya adalah untuk mencegah pelacak yang menjengkelkan ini melanggar privasi anda.</p><p>Centang opsi berikut untuk membuat <i>uMatrix</i> melakukannya untuk anda, pada selang waktu yang anda inginkan.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Mencegah Pelacakan Web melalui Tembolok Web&rdquo;</a>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Kuki tanpa kuki&rdquo;</a></p>
+privacyProcessRefererPrompt=Palsukan untai <a href='https://en.wikipedia.org/wiki/HTTP_referer'>perujuk HTTP</a> pada permintaan pihak ketiga.
+privacyProcessRefererHelp=Dari Wikipedia:<blockquote>Perujuk HTTP adalah sebuah ruas tajuk HTTP yang mengidentifikasi alamat dari halaman web yang bertautan dengan sumber daya yang sedang diminta. ... <b>Karena informasi perujuk dapat melanggar privasi, beberapa peramban memperbolehkan pengguna untuk menonaktifkan pengiriman informasi perujuk.</b></blockquote>Jika aturan ini dicentang, <i>uMatrix</i> akan memalsukan informasi perujuk HTTP jika nama domain perujuk HTTP adalah pihak ketiga dari nama domain jaringan yang diminta.
+privacyNoMixedContentPrompt=HTTPS Ketat: larang konten campuran.
+privacyNoMixedContentHelp=<p>Dari <a href='https://developer.mozilla.org/id/docs/Security/MixedContent'>Jaringan Pengembang Mozilla</a>:</p><blockquote>Jika halaman HTTPS mengandung muatan yang diambil melalui HTTP umum / berteks jelas, maka koneksi hanya terenkripsi sebagian: muatan yang tidak terenkripsi tersedia bagi para pengendus jaringan dan dapat diubah oleh serangan orang-ditengah, dan oleh karena itu koneksi menjadi tidak lagi aman. Ketika halaman web memperlihatkan perilaku seperti ini, hal ini disebut &ldquo;halaman dengan muatan campuran tidak aman&rdquo;.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Blokir semua percobaan <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>pengauditan pranala</a>.
+privacyProcessHyperlinkAuditingHelp=<p>Pengauditan pranala adalah mekanisme yang memperbolehkan satu pihak, <b>pihak manapun</b>, mendapatkan informasi tentang tautan mana yang pengguna klik pada halaman web tertentu. Hal ini pada dasarnya adalah fitur pelacakan: memperbolehkan sebuah situs web, atau pihak ketiga dari situs web tersebut, mendapatkan informasi tentang tautan mana di halaman yang mana yang anda klik pada situs web tersebut. Tujuannya adalah untuk melacak aktifitas penjelajahan anda.</p>
+userRulesPermanentHeader=Aturan permanen
+userRulesTemporaryHeader=Aturan sementara
+userRulesRevert=Kembalikan
+userRulesCommit=Tetapkan
+userRulesEdit=Sunting
+userRulesEditSave=Simpan
+userRulesEditDicard=Batal
+userRulesImport=Impor dari berkas...
+userRulesExport=Ekspor ke berkas...
+userRulesFormatHint=Lihat laman ini untuk aturan sintaksis.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Semua nama host di dalam berkas host dimuat sebagai nama host yang dicekal dalam lingkup global.
+hostsFilesStats={{blockedHostnameCount}} nama host yang berbeda diblokir dari:
+hostsFilesPerFileStats={{used}} digunakan dari {{total}}
+hostsFilesLastUpdate=Terakhir diperbarui: {{ago}}
+hostsFilesApplyChanges=Terapkan perubahan
+hostsFilesAutoUpdatePrompt=Otomatis perbarui berkas host.
+hostsFilesUpdateNow=Perbarui sekarang
+hostsFilesPurgeAll=Bersihkan semua tembolok
+hostsFilesExternalListsHint=Satu URL per baris. Baris yang diawali dengan &lsquo;#&rsquo; akan diabaikan. URL yang tidak valid akan diabaikan tanpa peringatan.
+hostsFilesExternalListsParse=Uraikan
+hostsFilesExternalListPurge=bersihkan tembolok
+hostsFilesExternalListNew=versi baru tersedia
+hostsFilesExternalListObsolete=usang
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Catatan perubahan</a>
+aboutStorageUsed=Penyimpanan digunakan: {{storageUsed}} bita
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Dokumentasi</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Perizinan</a>
+aboutCode=Kode sumber (GPLv3)
+aboutIssues=Kutu dan masalah
+aboutContributors=Kontributor
+aboutCodeContributors=Kode:
+aboutIssueContributors=Masalah:
+aboutTranslationContributors=Transliterasi:
+aboutUserDataHeader=Data anda
+aboutBackupButton=Cadangkan ke berkas...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Pulihkan dari berkas...
+aboutRestoreConfirm=Semua pengaturan anda akan ditimpa menggunakan data yang dicadangkan pada {{time}}, dan uMatrix akan memulai ulang.\n\nTimpa semua aturan yang ada menggunakan data yang dicadangkan?
+aboutRestoreError=Data tidak dapat dibaca atau tidak valid
+aboutOr=... atau ...
+aboutResetButton=Kembalikan ke pengaturan bawaan
+aboutResetConfirm=Peringatan! ini akan membuang semua pengaturan tersesuai anda. Apakah anda yakin ingin melanjutkan?
+loggerFilterInputPlaceholder=ekspresi penyaring
+loggerMaxEntriesTip=Jumlah maksimum entri
+loggerEntryCookieDeleted=kuki dihapus: {{value}}
+loggerEntryDeleteCookieError=gagal menghapus kuki: {{value}}
+loggerEntryBrowserCacheCleared=tembolok peramban dibersihkan
+loggerEntryAssetUpdated=aset diperbarui: {{value}}
+mainBlockedPrompt1=uMatrix telah mencegah laman berikut dimuat:
+mainBlockedPrompt2=Disebabkan oleh aturan berikut
+mainBlockedBack=Kembali
+mainBlockedClose=Tutup
+commandRevertAll=Kembalikan semua perubahan sementara
+commandWhitelistPageDomain=Sementara daftar-putihkan ranah halaman
+commandWhitelistAll=Sementara daftar-putihkan semua
+commandOpenDashboard=Buka dasbor
+elapsedOneMinuteAgo=semenit yang lalu
+elapsedManyMinutesAgo={{value}} menit yang lalu
+elapsedOneHourAgo=sejam yang lalu
+elapsedManyHoursAgo={{value}} jam yang lalu
+elapsedOneDayAgo=sehari yang lalu
+elapsedManyDaysAgo={{value}} hari yang lalu
+showDashboardButton=Dasbor
+showLoggerButton=Pencatat
+cloudPush=Ekspor ke penyimpanan awan
+cloudPull=Impor dari penyimpanan awan
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Nama perangkat ini:
+genericSubmit=Kirim
+genericRevert=Kembalikan
+errorCantConnectTo=Galat jaringan: Tidak bisa terhubung ke {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/it/messages.properties b/locale/it/messages.properties
new file mode 100644
index 0000000..2874a27
--- /dev/null
+++ b/locale/it/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Pannello di controllo
+loggerPageName=uMatrix — Registro
+settingsPageName=Opzioni
+privacyPageName=Privacy
+statsPageName=Statistiche
+userRulesPageName=Regole personalizzate
+ubiquitousRulesPageName=File di Host
+rawSettingsPageName=Altro
+aboutPageName=Info
+allPrettyName=tutto
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=immagine
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=altro
+matrixNoNetTrafficPrompt=Nessun traffico visto per questa tab, per ora.
+matrixMtxButtonTip=Disabilita/abilita filtro a matrice per questa pagina.\nRichieste bloccate dal filtro a matrice in questa pagina: {{count}}.
+matrixPersistButtonTip=Salva tutti i cambiamenti temporanei per questa pagina.
+matrixRevertButtonTip=Rimuovi i cambiamenti temporanei per questa pagina.
+matrixReloadButton=Ricarica la pagina.
+matrix1stPartyLabel=Dominio corrente
+matrixBlacklistedHostnames=Hostname in blacklist
+matrixSwitchNoMixedContent=Proibisci i contenuti misti
+matrixSwitchNoWorker=Proibisci i web worker
+matrixSwitchReferrerSpoof=Spoof referer
+matrixSwitchNoscriptSpoof=Spoof dei tag <code><noscript></code>
+matrixRevertAllEntry=Ripristina tutte le modifiche temporanee
+matrixLoggerMenuEntry=Vai al registro delle richieste
+matrixDashboardMenuEntry=Vai al pannello di controllo
+matrixNoTabFound=Nessuna pagina trovata
+statsPageTitle=uMatrix &ndash; Statistiche
+statsPageGenericStats=Statistiche generali
+statsPageCookieHeadersFoiled=<a href='http://it.wikipedia.org/wiki/Cookie'>HTTP cookie</a> headers bloccati: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://it.wikipedia.org/wiki/Referer'>HTTP referer</a> headers bloccati: {{count}}
+statsPageHyperlinkAuditingFoiled=Tentativi sventati di <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>revisione hyperlink</a>: {{count}}
+statsPageCookiesRemoved=Cookies locali eliminati: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> svuotati: {{count}}
+statsPageBrowserCacheCleared=Cache del browser pulite: {{count}}
+statsPageDetailedStats=Statistiche dettagliate
+statsPageDetailedAllPages=Tutto
+statsPageDetailedBehindTheScenePage=Dietro le quinte
+statsPageOverview=Panoramica
+statsPageRequests=Richieste
+statsPageAllowed=Consentite
+statsPageBlocked=Bloccate
+statsPageAll=Tutto
+statsPagePages=Pagine
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Immagini
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Altro
+statsPageDetailed=Registro delle richieste
+statsPageLogSizePrompt1=Ricorda le ultime
+statsPageLogSizePrompt2=richieste HTTP <b>per pagina</b>.
+statsPageLogSizeHelp=<p>Puoi ispezionare i dettagli della richiesta HTTP raw più recente fatta da una pagina web (vedi sotto).</p><p>Questo è utile soprattutto per gli utenti avanzati che vogliono investigare su cosa fa esattamente una pagina web. Tuttavia registrare queste richieste HTTP richiede memoria, e se non ti interessano le informazioni tecniche, allora è uno spreco di memoria.</p><p>Ecco questo campo per regolare il massimo numero di richieste HTTP recenti da registrare per ulteriori ispezioni.</p><p>Inserisci &ldquo;<code>0</code>&rdquo; per disattivare la registrazione (e di conseguenza ridurre l'ingombro di memoria di <i>uMatrix</i>).</p>
+statsPageRefresh=Aggiorna
+settingsPageTitle=uMatrix &ndash; Impostazioni
+settingsMatrixDisplayHeader=Aspetto
+settingsMatrixDisplayTextSizePrompt=Dimensione del testo:
+settingsMatrixDisplayTextSizeNormal=Normale
+settingsMatrixDisplayTextSizeLarge=Grande
+settingsMatrixDisplayColorBlind=Adatto ai daltonici
+settingsMatrixConvenienceHeader=Comodità
+settingsDefaultScopeLevel=Ambito predefinito:
+settingsDefaultScopeLevel0=Globale
+settingsDefaultScopeLevel1=Dominio
+settingsDefaultScopeLevel2=Sito
+settingsMatrixAutoReloadPrompt=Quando la matrice è chiusa, ricarica queste schede:
+settingsMatrixAutoReloadNone=Nessuna
+settingsMatrixAutoReloadCurrent=Pagina corrente
+settingsMatrixAutoReloadAll=Tutte
+settingsMatrixAutoReloadInfo=Ogni volta che effettui modifiche nella matrice che possono influenzare la visualizzazione e/o il comportamento di una o più pagine, <i>uMatrix</i> ricaricherà automaticamente le pagine in questione quando chiudi la matrice.
+settingsSubframeColor=Frames bloccati:&ensp;Colore
+settingsSubframeOpacity=Opacità
+settingsIconBadgeEnabled=Mostra il numero di richieste sull'icona
+settingsCollapseBlocked=Nascondi lo spazio riservato agli elementi bloccati
+settingsCollapseBlacklisted=Nascondi lo spazio riservato agli elementi bloccati
+settingsNoscriptTagsSpoofed=Falsifica i tag <code><noscript></code> quando gli script del dominio sono bloccati
+settingsCloudStorageEnabled=Attiva il supporto all'archiviazione sul cloud
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Cancella cookies bloccati.
+privacyDeleteBlockedCookiesHelp=<p><i>uMatrix</i> non può impedire ai cookie bloccati di entrare nel tuo browser. Tuttavia impedisce che lascino il tuo browser, che è la cosa che conta. Non bloccare i cookie prima che entrino nel tuo browser ti dà la possibilità di sapere che un sito ha provato ad utilizzare cookie, e anche di ispezionarne il contenuto se lo desideri.</p><p>Quando questi cookie bloccati vengono presi in gestione da <i>uMatrix</i>, possono essere rimossi dal browser se lo desideri.</p><p><b>Nota importante:</b> Le estensioni possono fare richieste web durante il loro normale funzionamento. Queste richieste possono portare alla creazione di cookie nel browser. Se il nome dell'host da cui proviene un cookie non è presente nella lista bianca, il cookie verrà rimosso dal browser da <i>uMatrix</i> se l'opzione è selezionata. Assicurati quindi che il/i nome/i dell'host con cui un'estensione comunica sia in lista bianca.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Elimina i cookie di sessione non bloccati
+privacyDeleteNonBlockedSessionCookiesPrompt2= minuti dopo l'ultima volta che sono stati usati.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;Un cookie di sessione ... viene cancellato al termine della sessione del browser. Il cookie di sessione è memorizzato nella memoria temporanea e non viene conservato dopo la chiusura del browser.&rdquo;</p><p>A parte il fatto che ciò <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>potrebbe non accadere</a> in alcuni browser. Inoltre, per alcuni, dover chiudere il browser per eliminare i cookie di sessione potrebbe non essere abbastanza presto.</p>
+privacyDeleteBlockedLocalStoragePrompt=Elimina il contenuto del <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> dei nomi host bloccati
+privacyDeleteBlockedLocalStorageHelp=DA FARE
+privacyClearCachePrompt1=Elimina la cache del browser ogni
+privacyClearCachePrompt2=minuti.
+privacyClearCacheHelp=<p>Alcuni siti sono davvero inclini a tracciarti, tanto che utilizzeranno trucchi non molto carini per sorpassare qualsiasi misura prendi per non essere monitorato.</p><p>Alcuni di questi trucchi si basano<sup>[1, 2]</sup> sulla <a href='https://it.wikipedia.org/wiki/Web_cache'>cache del browser</a>, il quale contenuto è spesso di lunga durata, dato che raramente gli utenti si prendono il tempo di cancellare regolarmente la cache del loro browser.</p><p>C'è poco disagio nel cancellare la cache del browser regolarmente (probabilmente non te ne renderai conto), e il vantaggio è quello di evitare che questi infelici tracker invadano la tua privacy.</p><p>Seleziona questa opzione se vuoi che <i>uMatrix</i> lo faccia per te, a intervalli scelti.</p><p>[1]<a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a> [2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Falsifica (spoof) la stringa <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> di richieste di terze parti.
+privacyProcessRefererHelp=Da Wikipedia (en):<blockquote>Un referer HTTP è un campo di intestazione HTTP che identifica l'indirizzo della pagina web di provenienza verso la risorsa richiesta. ... <b>Poiché le informazioni del referer possono violare la privacy, alcuni browser consentono all'utente di disabilitare l'invio delle informazioni del referer.</b></blockquote>Se questa opzione è selezionata, <i>uMatrix</i> falsificherà (spoof) le informazioni del referrer HTTP se il nome di dominio del referrer HTTP è di terze parti.
+privacyNoMixedContentPrompt=Strict HTTPS: proibisci i contenuti misti.
+privacyNoMixedContentHelp=<p>Da <a href='https://developer.mozilla.org/it/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>Se [a] la pagina HTTPS include contenuti recuperati attraverso richieste HTTP regolari (non crittografate), la connessione risulterà solo parzialmente criptata: il contenuto non crittografato è accessibile a sniffer e potrebbe essere modificato da attacchi man-in-the-middle. In questo caso la connessione non è più considerata protetta. Quando una pagina web presenta questo comportamento si parla di una pagina con Contenuto Misto.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Blocca tutti i tentativi di <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>revisione degli hyperlink</a>.
+privacyProcessHyperlinkAuditingHelp=<p>La revisione degli hyperlink è un meccanismo che consente a una parte, <b>qualsiasi parte</b>, di essere informata su quale link ha cliccato l'utente in una pagina web. Si tratta essenzialmente di una funzione di tracciamento: permette a un sito, o a qualsiasi terza parte di quel sito, di essere informato su quale link hai cliccato su quale delle sue pagine web. L'unico scopo è quello di tracciare la tua attività di navigazione.</p>
+userRulesPermanentHeader=Regole permanenti
+userRulesTemporaryHeader=Regole temporanee
+userRulesRevert=Ripristina
+userRulesCommit=Applica
+userRulesEdit=Modifica
+userRulesEditSave=Salva
+userRulesEditDicard=Annulla
+userRulesImport=Importa da file...
+userRulesExport=Esporta in un file...
+userRulesFormatHint=Vedi questa pagina per la sintassi delle regole.
+userRulesDefaultFileName=mie-regole-umatrix.txt
+hostsFilesPrompt=Tutti i nomi in un file hosts vengono caricati come nomi host bloccati in ambito generale.
+hostsFilesStats={{blockedHostnameCount}} nomi host bloccati da:
+hostsFilesPerFileStats={{used}} usati su {{total}}
+hostsFilesLastUpdate=Ultimo aggiornamento: {{ago}}
+hostsFilesApplyChanges=Salva i cambiamenti
+hostsFilesAutoUpdatePrompt=Aggiornamento automatico files hosts.
+hostsFilesUpdateNow=Aggiorna ora
+hostsFilesPurgeAll=Cancella tutte le cache
+hostsFilesExternalListsHint=Un URL per riga. Le righe che cominciano con &lsquo;#&rsquo; saranno ignorate. Gli Url non validi saranno silenziosamente ignorati.
+hostsFilesExternalListsParse=Analizza
+hostsFilesExternalListPurge=elimina cache
+hostsFilesExternalListNew=nuova versione disponibile
+hostsFilesExternalListObsolete=obsoleta
+rawSettingsWarning=Attenzione! Modifica queste impostazioni grezze a tuo rischio.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Spazio utilizzato: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentazione</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permessi</a>
+aboutCode=Codice sorgente (GPLv3)
+aboutIssues=Bugs e problemi
+aboutContributors=Collaboratori
+aboutCodeContributors=Codice:
+aboutIssueContributors=Problemi:
+aboutTranslationContributors=Traduzioni:
+aboutUserDataHeader=Tuoi dati
+aboutBackupButton=Crea file di backup...
+aboutBackupFilename=mio-umatrix-backup.txt
+aboutRestoreButton=Ripristina dal file...
+aboutRestoreConfirm=Tutte le impostazioni verranno sovrascritte utilizzando i dati di backup del {{time}}, e uMatrix sarà riavviato. \n\nSovrascrivere tutte le impostazioni esistenti con quelle del backup?
+aboutRestoreError=I dati non possono essere letti o sono invalidi
+aboutOr=... o ...
+aboutResetButton=Torna ai parametri di default
+aboutResetConfirm=Attenzione! Questo rimuoverà tutte le impostazioni personalizzate. Sei sicuro di voler procedere?
+loggerFilterInputPlaceholder=espressione/i filtro
+loggerMaxEntriesTip=Numero massimo di voci
+loggerEntryCookieDeleted=cookie eliminato: {{value}}
+loggerEntryDeleteCookieError=impossibile eliminare il cookie: {{value}}
+loggerEntryBrowserCacheCleared=cache del browser svuotata
+loggerEntryAssetUpdated=asset aggiornato: {{value}}
+mainBlockedPrompt1=uMatrix ha impedito alla seguente pagina di caricarsi:
+mainBlockedPrompt2=A causa della regola seguente
+mainBlockedBack=Torna indietro
+mainBlockedClose=Chiudi
+commandRevertAll=Ripristina tutte le modifiche temporanee
+commandWhitelistPageDomain=Permetti temporaneamente il dominio
+commandWhitelistAll=Permetti temporaneamente tutto
+commandOpenDashboard=Apri la dashboard
+elapsedOneMinuteAgo=un minuto fa
+elapsedManyMinutesAgo={{value}} minuti fa
+elapsedOneHourAgo=un'ora fa
+elapsedManyHoursAgo={{value}} ore fa
+elapsedOneDayAgo=un giorno fa
+elapsedManyDaysAgo={{value}} giorni fa
+showDashboardButton=Pannello di controllo
+showLoggerButton=Registro
+cloudPush=Esporta nell'archivio cloud
+cloudPull=Importa dall'archivio cloud
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Il nome del dispositivo:
+genericSubmit=Invia
+genericRevert=Ripristina
+errorCantConnectTo=Errore di rete: Impossibile connettersi a {{url}}
+genericApplyChanges=Applica modifiche
diff --git a/locale/ja/messages.properties b/locale/ja/messages.properties
new file mode 100644
index 0000000..054ed9e
--- /dev/null
+++ b/locale/ja/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — ダッシュボード
+loggerPageName=uMatrix — Logger
+settingsPageName=設定
+privacyPageName=プライバシー
+statsPageName=統計
+userRulesPageName=My ルール
+ubiquitousRulesPageName=ホストファイル
+rawSettingsPageName=詳細
+aboutPageName=uMatrix について
+allPrettyName=すべて
+cookiePrettyName=Cookie
+cssPrettyName=CSS
+imagePrettyName=画像
+mediaPrettyName=Media
+pluginPrettyName=Media
+scriptPrettyName=Script
+xhrPrettyName=XHR
+framePrettyName=Frame
+otherPrettyName=その他
+matrixNoNetTrafficPrompt=このタブではネットワークトラフィックを検出できません
+matrixMtxButtonTip=このスコープの Matrix フィルタの有効/無効を切り替える
+matrixPersistButtonTip=このスコープの一時的な変更をすべて保存する
+matrixRevertButtonTip=このスコープの一時的な変更を元に戻す
+matrixReloadButton=このページを再読み込みする\nShift キーを押しながら実行すると、キャッシュを無視した再読み込みを行います
+matrix1stPartyLabel=1st Party
+matrixBlacklistedHostnames={{count}} 件がブラックリストと一致
+matrixSwitchNoMixedContent=混在コンテンツをブロック
+matrixSwitchNoWorker=Web Worker をブロック
+matrixSwitchReferrerSpoof=リファラの偽装
+matrixSwitchNoscriptSpoof=<code><noscript></code> タグの偽装
+matrixRevertAllEntry=すべての一時的な変更を元に戻す
+matrixLoggerMenuEntry=Logger を開く
+matrixDashboardMenuEntry=ダッシュボードを開く
+matrixNoTabFound=ページが見つかりません
+statsPageTitle=uMatrix &ndash; 統計
+statsPageGenericStats=一般的な統計
+statsPageCookieHeadersFoiled=拒否した <a href='https://en.wikipedia.org/wiki/HTTP_cookie'>Cookie</a> の数: {{count}}
+statsPageRefererHeadersFoiled=拒否した <a href='https://en.wikipedia.org/wiki/HTTP_referer'>リファラ</a> の数: {{count}}
+statsPageHyperlinkAuditingFoiled=拒否した <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>ハイパーリンク監査</a> の数: {{count}}
+statsPageCookiesRemoved=削除した Cookie の数: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storage</a> を空にした数: {{count}}
+statsPageBrowserCacheCleared=ブラウザキャッシュを消去した数: {{count}}
+statsPageDetailedStats=詳細な統計
+statsPageDetailedAllPages=すべて
+statsPageDetailedBehindTheScenePage=バックグラウンドリクエスト
+statsPageOverview=概要
+statsPageRequests=リクエスト
+statsPageAllowed=許可
+statsPageBlocked=ブロック中
+statsPageAll=すべて
+statsPagePages=ページ
+statsPageCookies=Cookie
+statsPageCSS=CSS
+statsPageImages=画像
+statsPagePlugins=Plugins
+statsPageScripts=スクリプト
+statsPageXHRs=XHRs
+statsPageFrames=フレーム
+statsPageOthers=その他
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=ページあたり直近の
+statsPageLogSizePrompt2=<b>1ページあたり</b> の HTTP 要求
+statsPageLogSizeHelp=<p>Web ページ(下記参照)による直近の HTTP リクエストの詳細を調べることができます。</p><p>これは、Web ページが何をしていたかを正確に調べたい上級ユーザーにとってとても便利です。しかし、これらの HTTP リクエストをログに記録するのは多くのメモリーを必要とします。もしあなたがこの技術情報を必要としない場合、メモリーが浪費されています。</p><p>このフィールドではログに記録される直近の HTTP リクエストの上限を調整することができます。</p><p>ログ記録を無効化したい(そして <i>uMatrix</i> のメモリー消費量を削減したい)場合は &ldquo;<code>0</code>&rdquo; を入力してください。</p>
+statsPageRefresh=再読み込み
+settingsPageTitle=uMatrix &ndash; 設定
+settingsMatrixDisplayHeader=外観
+settingsMatrixDisplayTextSizePrompt=文字の大きさ:
+settingsMatrixDisplayTextSizeNormal=標準
+settingsMatrixDisplayTextSizeLarge=大きい
+settingsMatrixDisplayColorBlind=色覚障害の人に見やすい色で表示する
+settingsMatrixConvenienceHeader=利便性
+settingsDefaultScopeLevel=デフォルトのスコープレベル:
+settingsDefaultScopeLevel0=グローバル
+settingsDefaultScopeLevel1=ドメイン
+settingsDefaultScopeLevel2=サイト
+settingsMatrixAutoReloadPrompt=Matrix を閉じたとき、これらのタブを再読み込みする:
+settingsMatrixAutoReloadNone=なし
+settingsMatrixAutoReloadCurrent=現在
+settingsMatrixAutoReloadAll=すべて
+settingsMatrixAutoReloadInfo=1 ページ以上に影響を与える変更をしてから Matrix を閉じたときは、<i>uMatrix</i> は影響をうけるページを再読み込みします
+settingsSubframeColor=ブロック中のframe:&ensp;Color
+settingsSubframeOpacity=不透明度
+settingsIconBadgeEnabled=アイコンにリクエスト数を表示する
+settingsCollapseBlocked=ブロックした要素のプレースホルダを表示しない
+settingsCollapseBlacklisted=明示的に拒否した要素のプレースホルダを表示しない
+settingsNoscriptTagsSpoofed=訪れているサイトのスクリプトがブロックされている場合に <code><noscript></code> タグを偽装する
+settingsCloudStorageEnabled=クラウドストレージのサポートを有効にする
+privacyPageTitle=uMatrix &ndash; プライバシー
+privacyDeleteBlockedCookiesPrompt=ブロックした Cookie を削除する
+privacyDeleteBlockedCookiesHelp=<p><i>uMatrix</i> は、ブロックされる設定の Cookie であってもブラウザに入ってくることは許可し、出て行くことをブロックします(後者が本当に必要なことです)。ブラウザに入ってくるのを許可することで、そのサイトが Cookie を使おうとしていることをユーザが確認でき、さらにその内容を調べることもできます。</p><p><i>uMatrix</i> でこれらの Cookie について確認したあと、必要であればそれらをブラウザから削除しても構いません。</p><p><b>重要な注意点:</b> ブラウザの拡張機能はその機能の一部として Web リクエストをおこなうことがあり、場合によってはブラウザ内に Cookie が保存されます。このオプションをオンにした場合、該当のホストを許可しておかないと <i>uMatrix</i> によって Cookie が削除されます。拡張機能が通信するホストを許可しておくことを忘れないでください。</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=ブロックしていないセッション Cookie を削除する (
+privacyDeleteNonBlockedSessionCookiesPrompt2= 分間使用されていない場合)
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;ブラウザを終了するとセッション Cookie は消去されます。セッション Cookie は一時的に保存され、ブラウザが閉じられた後には保持されません。&rdquo;</p><p><a href='https://code.google.com/p/chromium/issues/detail?id=128513'>この動きをしない</a> ブラウザもあります。また、セッション Cookie を消去するためだけに、ブラウザを閉じたくないということがあるかもしれません。</p>
+privacyDeleteBlockedLocalStoragePrompt=ブロックしているホストが保存した <a href='https://en.wikipedia.org/wiki/Web_storage'>Local storage</a> を削除する
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=ブラウザのキャッシュをクリアする(
+privacyClearCachePrompt2=分おき)
+privacyClearCacheHelp=<p>ユーザの行動を追跡するのに必死な Web サイトもあります。こうした Web サイトは行儀の悪い方法を使って、追跡されないようにするための対策を回避しようとします。</p><p>こうした手法の一部は、<a href='https://en.wikipedia.org/wiki/Web_cache'>ブラウザのキャッシュ</a>に依存 <sup>[1, 2]</sup> しています。なぜなら、ブラウザのキャッシュを消去する人はまれで、多くの場合に長期間にわたって残り続けるためです。</p><p>定期的にブラウザのキャッシュを消去することによる不都合はほとんどありません(あるとすれば、消去されるときユーザが気づかないということです)。メリットは、こうした不愉快なトラッカーによってあなたのプライバシーが侵害されるのを防げるという点です。</p><p>このオプションを設定することで、任意の間隔で <i>uMatrix</i> がブラウザのキャッシュを消去するようになります。</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=サードパーティーリクエストの <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP リファラ</a> を偽装する
+privacyProcessRefererHelp=Wikipedia から引用:<blockquote>HTTP リファラは、要求しているリソースにリンクしたウェブページのアドレスを識別する HTTP ヘッダのフィールドです。<b>リファラはプライバシーを侵害するおそれがあるため、一部の Web ブラウザでは、ユーザーがリファラの送信を無効にすることができます</b>。</blockquote>この設定をオンにすると、HTTP リファラのドメイン名がアクセスしている Web サイトのドメイン名と異なる場合(サードパーティリクエストである場合)、<i>uMatrix</i> は HTTP リファラを偽装します。
+privacyNoMixedContentPrompt=厳密な HTTPS: 混在コンテンツをブロックする
+privacyNoMixedContentHelp=<p><a href='https://developer.mozilla.org/ja/docs/Security/%E6%B7%B7%E5%9C%A8%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84'>Mozilla Developer Network</a> から引用:</p><blockquote>HTTPS のページの中に平文の HTTP で送られてくるコンテンツが含まれている場合、混在コンテンツと呼ばれます。このようなページは部分的にしか暗号化されておらず、盗聴者や中間者攻撃者が暗号化されていないコンテンツにアクセスできてしまいます。つまり、ページは安全ではありません。</blockquote>
+privacyProcessHyperlinkAuditingPrompt=すべての <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>ハイパーリンク監査</a> を拒否する
+privacyProcessHyperlinkAuditingHelp=<p>ハイパーリンクの監査は、ユーザーが特定の Web ページ上でどのリンクをクリックしたかを <b>不特定の誰か</b> に通知することを可能にする仕組みです。これは本質的にトラッキングの機能です。あなたがあるリンクをクリックしたとき、それを Web サイト(または第三者)に通知します。この機能の唯一の目的は、あなたのブラウジングを追跡することです。</p>
+userRulesPermanentHeader=永続的なルール
+userRulesTemporaryHeader=一時的なルール
+userRulesRevert=元に戻す
+userRulesCommit=コミット
+userRulesEdit=編集
+userRulesEditSave=保存
+userRulesEditDicard=破棄
+userRulesImport=ファイルからインポート...
+userRulesExport=ファイルへエクスポート...
+userRulesFormatHint=ルールの構文については、このページを参照してください。
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=ホストファイルに記載されたすべてのホストへの、あらゆるリクエストはブロックされます。
+hostsFilesStats={{blockedHostnameCount}} 件のホストをブロックしています:
+hostsFilesPerFileStats={{total}} 件のうち {{used}} 件を使用中
+hostsFilesLastUpdate=最終更新日: {{ago}}
+hostsFilesApplyChanges=変更を適用する
+hostsFilesAutoUpdatePrompt=ホストファイルを自動で更新する
+hostsFilesUpdateNow=今すぐアップデート
+hostsFilesPurgeAll=すべてのキャッシュをクリア
+hostsFilesExternalListsHint=1 行にひとつずつ記述してください。&lsquo;#&rsquo; で始まる行は無視されます。無効な URL は警告なしに無視されます。
+hostsFilesExternalListsParse=読み込む
+hostsFilesExternalListPurge=キャッシュのクリア
+hostsFilesExternalListNew=新しいバージョンを利用できます
+hostsFilesExternalListObsolete=古い
+rawSettingsWarning=警告! これらの内部設定はご自身のリスクにより変更すること。
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>変更履歴</a>
+aboutStorageUsed=使用中のストレージ: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>ドキュメント</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>パーミッションについて</a>
+aboutCode=ソースコード (GPLv3)
+aboutIssues=バグや問題点
+aboutContributors=協力者
+aboutCodeContributors=ソースコード:
+aboutIssueContributors=問題点:
+aboutTranslationContributors=翻訳:
+aboutUserDataHeader=データ
+aboutBackupButton=ファイルにバックアップ...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=ファイルから復元...
+aboutRestoreConfirm=すべての設定は {{time}} のバックアップデータで上書きされ、uMatrix が再起動します。\n\nバックアップデータで上書きしますか?
+aboutRestoreError=データを読み込めなかったか、無効なデータです
+aboutOr=... もしくは ...
+aboutResetButton=初期設定に戻す
+aboutResetConfirm=注意! 設定はすべて削除されます。続行してもよろしいですか?
+loggerFilterInputPlaceholder=フィルタする
+loggerMaxEntriesTip=エントリの最大数
+loggerEntryCookieDeleted=削除した Cookie:{{value}}
+loggerEntryDeleteCookieError=Cookie の削除に失敗しました:{{value}}
+loggerEntryBrowserCacheCleared=ブラウザのキャッシュを削除しました
+loggerEntryAssetUpdated=リソースを更新しました:{{value}}
+mainBlockedPrompt1=uMatrix はこのページの読み込みをブロックしています:
+mainBlockedPrompt2=以下のルールに従う
+mainBlockedBack=戻る
+mainBlockedClose=閉じる
+commandRevertAll=すべての一時的な変更を元に戻す
+commandWhitelistPageDomain=このページのドメインを一時的にホワイトリストに登録する
+commandWhitelistAll=一時的にすべてホワイトリストに登録
+commandOpenDashboard=ダッシュボードを開く
+elapsedOneMinuteAgo=1 分前
+elapsedManyMinutesAgo={{value}} 分前
+elapsedOneHourAgo=1 時間前
+elapsedManyHoursAgo={{value}} 時間前
+elapsedOneDayAgo=1 日前
+elapsedManyDaysAgo={{value}} 日前
+showDashboardButton=ダッシュボード
+showLoggerButton=Logger
+cloudPush=クラウドストレージにエクスポート
+cloudPull=クラウドストレージからインポート
+cloudNoData=...\n...
+cloudDeviceNamePrompt=このデバイスの名称:
+genericSubmit=登録する
+genericRevert=元に戻す
+errorCantConnectTo=ネットワークエラー: {{url}} に接続できません
+genericApplyChanges=変更を適用
diff --git a/locale/kn/messages.properties b/locale/kn/messages.properties
new file mode 100644
index 0000000..060ae58
--- /dev/null
+++ b/locale/kn/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Settings
+privacyPageName=Privacy
+statsPageName=Statistics
+userRulesPageName=My rules
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=About
+allPrettyName=all
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/ko/messages.properties b/locale/ko/messages.properties
new file mode 100644
index 0000000..b85f569
--- /dev/null
+++ b/locale/ko/messages.properties
@@ -0,0 +1,179 @@
+extName=µMatrix
+dashboardPageName=µMatrix — 대시보드
+loggerPageName=uMatrix — 로그
+settingsPageName=설정
+privacyPageName=개인정보
+statsPageName=통계
+userRulesPageName=내 규칙
+ubiquitousRulesPageName=호스트 파일
+rawSettingsPageName=More
+aboutPageName=정보
+allPrettyName=전체
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=이 탭에서는 네트워크 트래픽을 감지 할 수 없습니다.
+matrixMtxButtonTip=현재 페이지에 대한 matrix 사용 여부 설정
+matrixPersistButtonTip=현재 페이지 임시 변경 사항 저장
+matrixRevertButtonTip=현재 페이지 임시 변경 사항 초기화
+matrixReloadButton=페이지 새로고침.\nShift를 누르면서 클릭 할 경우 브라우저 캐시를 건너 뜀
+matrix1stPartyLabel=최상위 도메인
+matrixBlacklistedHostnames={{count}} 블럭된 호스트 이름
+matrixSwitchNoMixedContent=전체 HTTPS
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=참조 주소 위조
+matrixSwitchNoscriptSpoof=<code><noscript></code> 태그 위조
+matrixRevertAllEntry=모든 임시 변경 사항 초기화
+matrixLoggerMenuEntry=로그로 이동
+matrixDashboardMenuEntry=대시보드로 이동
+matrixNoTabFound=웹 페이지를 찾을 수 없습니다.
+statsPageTitle=uMatrix &ndash; 통계
+statsPageGenericStats=일반 통계
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=로컬 쿠기 삭제: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>로컬 저장소</a> 삭제한 수: {{count}}
+statsPageBrowserCacheCleared=브라우저 케시 삭제: {{count}}
+statsPageDetailedStats=상세 통계
+statsPageDetailedAllPages=전체
+statsPageDetailedBehindTheScenePage=백그라운드 요청
+statsPageOverview=요약
+statsPageRequests=리퀘스트
+statsPageAllowed=허용
+statsPageBlocked=차단
+statsPageAll=전체
+statsPagePages=페이지
+statsPageCookies=Cookie
+statsPageCSS=CSS
+statsPageImages=image
+statsPagePlugins=plugin
+statsPageScripts=script
+statsPageXHRs=XHRs
+statsPageFrames=frame
+statsPageOthers=other
+statsPageDetailed=로그
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP 요청<b>페이지 당</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=새로고침
+settingsPageTitle=µMatrix &ndash; 설정
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=글자 크기:
+settingsMatrixDisplayTextSizeNormal=보통
+settingsMatrixDisplayTextSizeLarge=큼
+settingsMatrixDisplayColorBlind=색약자 설정
+settingsMatrixConvenienceHeader=편의 설정
+settingsDefaultScopeLevel=기본 범위
+settingsDefaultScopeLevel0=전체
+settingsDefaultScopeLevel1=도메인
+settingsDefaultScopeLevel2=사이트
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=없음
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=전체
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=아이콘에 요청 수 표시
+settingsCollapseBlocked=차단 된 요청과 관련된 페이지 콘텐츠를 숨깁니다.
+settingsCollapseBlacklisted=블랙리스트와 관련된 페이지 콘텐츠 숨김
+settingsNoscriptTagsSpoofed=웹 페이지의 도메인 스크립트 차단 시 <code><noscript></code> 태그 수정
+settingsCloudStorageEnabled=클라우드 저장소 활성화
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=차단된 쿠키 삭제
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=차단되지 않은 세션 쿠키 삭제
+privacyDeleteNonBlockedSessionCookiesPrompt2= 분 동안 사용되지 않은 경우
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=차단 호스트 이름으로 설정된 <a href='https://en.wikipedia.org/wiki/Web_storage'>로컬 저장소</a> 삭제
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=설정한 시간마다 브라우저 캐시 지우기:
+privacyClearCachePrompt2=분
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=제 3 자 요청의 <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP 리퍼러</a> 문자열을 위조 합니다.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=전체 HTTPS: 혼합 된 콘텐츠 금지
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=모든 <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'> 하이퍼 링크 추적 </a> 거부
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=저장된 규칙
+userRulesTemporaryHeader=임시 규칙
+userRulesRevert=되돌리기
+userRulesCommit=저장
+userRulesEdit=편집
+userRulesEditSave=저장
+userRulesEditDicard=삭제
+userRulesImport=파일에서 가져오기
+userRulesExport=파일로 내보내기...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=호스트 파일에있는 도메인은 글로벌 블랙리스트에 포함됩니다.
+hostsFilesStats=차단된 호스트 네임: {{blockedHostnameCount}}
+hostsFilesPerFileStats={{total}} 개 중 {{used}} 개 사용 중
+hostsFilesLastUpdate=마지막 업데이트: {{ago}}
+hostsFilesApplyChanges=변경사항 적용
+hostsFilesAutoUpdatePrompt=호스트 파일 자동 업데이트
+hostsFilesUpdateNow=지금 업데이트하기
+hostsFilesPurgeAll=케시 초기화
+hostsFilesExternalListsHint=한 줄에 하나의 URL. 시작이 &lsquo;#&rsquo; 일 경우 적용되지 않습니다. 유효하지 않은 URL은 자동으로 무시됩니다.
+hostsFilesExternalListsParse=읽기
+hostsFilesExternalListPurge=케시 지우기
+hostsFilesExternalListNew=새로운 버전이 나왔습니다
+hostsFilesExternalListObsolete=구버전입니다
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>변경사항</a>
+aboutStorageUsed=저장공간 사용: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>문서</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>확장기능 요구 권한</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=오류 혹은 이슈
+aboutContributors=기여자
+aboutCodeContributors=코드:
+aboutIssueContributors=이슈:
+aboutTranslationContributors=번역:
+aboutUserDataHeader=사용자 데이터
+aboutBackupButton=파일로 백업하기...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=파일에서 복원...
+aboutRestoreConfirm={{time}} 에 백업된 데이터가 모든 설정을 덮어쓰기한 뒤, uMatrix이 재시작합니다.\n\n존재하는 모든 설정을 백업 데이터로 덮어쓰시겠습니까?
+aboutRestoreError=데이터를 읽을 수 없거나 유효하지 않습니다.
+aboutOr=... 혹은 ...
+aboutResetButton=기본값 설정으로 초기화
+aboutResetConfirm=주의! 모든 개인설정이 제거됩니다. 정말로 계속하시겠습니까?
+loggerFilterInputPlaceholder=필터링
+loggerMaxEntriesTip=최대 항목 수
+loggerEntryCookieDeleted=쿠키 삭제: {{value}}
+loggerEntryDeleteCookieError=삭제 못 한 쿠키: {{value}}
+loggerEntryBrowserCacheCleared=브라우저 케시 삭제 완료
+loggerEntryAssetUpdated=자원 업데이트 완료:{{value}}
+mainBlockedPrompt1=uMatrix가 다음 페이지를 불러오지 못하게했습니다.
+mainBlockedPrompt2=다음 규칙에 따라
+mainBlockedBack=뒤로 가기
+mainBlockedClose=닫기
+commandRevertAll=모든 임시 변경 사항 초기화
+commandWhitelistPageDomain=이 페이지의 도메인을 임시로 화이트리스트에 등록 하였습니다.
+commandWhitelistAll=임시로 모두 허용 목록에 추가
+commandOpenDashboard=대시보드 열기
+elapsedOneMinuteAgo=1분 전
+elapsedManyMinutesAgo={{value}} 분 전
+elapsedOneHourAgo=한시간 전
+elapsedManyHoursAgo={{value}} 시간 전
+elapsedOneDayAgo=어제
+elapsedManyDaysAgo={{value}} 일 전
+showDashboardButton=대시보드
+showLoggerButton=로그
+cloudPush=클라우드에 저장
+cloudPull=클라우드 저장소에서 내려받기
+cloudNoData=...\n...
+cloudDeviceNamePrompt=디바이스 이름:
+genericSubmit=저장
+genericRevert=되돌리기
+errorCantConnectTo=네트워크 오류: 연결할 수 없습니다 {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/lt/messages.properties b/locale/lt/messages.properties
new file mode 100644
index 0000000..060ae58
--- /dev/null
+++ b/locale/lt/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Settings
+privacyPageName=Privacy
+statsPageName=Statistics
+userRulesPageName=My rules
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=About
+allPrettyName=all
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/lv/messages.properties b/locale/lv/messages.properties
new file mode 100644
index 0000000..554eb95
--- /dev/null
+++ b/locale/lv/messages.properties
@@ -0,0 +1,179 @@
+extName=µMatrix
+dashboardPageName=µMatrix — Vadības panelis
+loggerPageName=uMatrix — Logger
+settingsPageName=Iestatījumi
+privacyPageName=Konfidencialitāte
+statsPageName=Statistika
+userRulesPageName=Mani noteikumi
+ubiquitousRulesPageName=Hostu faili
+rawSettingsPageName=More
+aboutPageName=Par
+allPrettyName=viss
+cookiePrettyName=sīkdatne
+cssPrettyName=css
+imagePrettyName=attēls
+mediaPrettyName=media
+pluginPrettyName=spraudnis
+scriptPrettyName=skripts
+xhrPrettyName=XHR
+framePrettyName=kadrs
+otherPrettyName=cits
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=Viss
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Pārskats
+statsPageRequests=Pieprasījumi
+statsPageAllowed=Atļauts
+statsPageBlocked=Bloķēts
+statsPageAll=Viss
+statsPagePages=Lapas
+statsPageCookies=Sīkdatnes
+statsPageCSS=CSS
+statsPageImages=Attēli
+statsPagePlugins=Spraudņi
+statsPageScripts=Skripti
+statsPageXHRs=XHRs
+statsPageFrames=Rāmji
+statsPageOthers=Citi
+statsPageDetailed=Pieprasījumu žurnāls
+statsPageLogSizePrompt1=Atcerēties pēdējo
+statsPageLogSizePrompt2=HTTP pieprasījumi <b>pa lapai</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Atjaunot
+settingsPageTitle=µMatrix &ndash; Iestatījumi
+settingsMatrixDisplayHeader=Izskats
+settingsMatrixDisplayTextSizePrompt=Teksta lielums:
+settingsMatrixDisplayTextSizeNormal=Normāls
+settingsMatrixDisplayTextSizeLarge=Liels
+settingsMatrixDisplayColorBlind=Daltoniķiem draudzīgs
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=Neviens
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Necaurredzamība
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=µMatrix &ndash; Konfidencialitāte
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minūtes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Atgriezt
+userRulesCommit=Commit
+userRulesEdit=Rediģēt
+userRulesEditSave=Saglabāt
+userRulesEditDicard=Atcelt
+userRulesImport=Import from file...
+userRulesExport=Eksportēt uz failu...
+userRulesFormatHint=Apskatīt šo lapu priekš noteikumu sintakses.
+userRulesDefaultFileName=mani-umatriksa-noteikumi.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} izmantoti no kopējiem {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Lietot izmaiņas
+hostsFilesAutoUpdatePrompt=Automātiski atjaunot resursdatora failus.
+hostsFilesUpdateNow=Atjaunināt tagad
+hostsFilesPurgeAll=Iztīrīt visas kešatmiņas
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parsēt
+hostsFilesExternalListPurge=Iztīrīt kešatmiņu
+hostsFilesExternalListNew=pieejama jauna versija
+hostsFilesExternalListObsolete=novecojis
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Izmaiņu žurnāls</a>
+aboutStorageUsed=Izmantotā atmiņa: {{storageUsed}} baiti
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Dokumentācija</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Atļaujas</a>
+aboutCode=Pirmkods (GPLv3)
+aboutIssues=Kļūdas un problēmas
+aboutContributors=Atbalstītāji
+aboutCodeContributors=Kods:
+aboutIssueContributors=Problēmas:
+aboutTranslationContributors=Tulkojumi:
+aboutUserDataHeader=Jūsu dati
+aboutBackupButton=Dublēt uz failu...
+aboutBackupFilename=mana-umatriksa-rezerves-kopija.txt
+aboutRestoreButton=Atjaunot no faila...
+aboutRestoreConfirm=Visi jūsu iestatījumi tiks pārrakstīti izmantojot rezerves kopijas datus {{time}} and µMatriks tiks restartēts.\n\nPārrakstīt visus esošos iestatījumus izmantojot rezerves kopijas datus?
+aboutRestoreError=Datus nevarēja nolasīt vai tie ir nederīgi
+aboutOr=... vai ...
+aboutResetButton=Atgriezt noklusējuma iestatījumus
+aboutResetConfirm=Uzmanību! tiks atcelti visi jūsu izveidotie iestatījumi. Vai vēlaties turpināt?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Atcelt visas pagaidu izmaiņas
+commandWhitelistPageDomain=Uz laiku atļaut web lapas domēnu
+commandWhitelistAll=Uz laiku atļaut visu
+commandOpenDashboard=Atvērt vadības paneli
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Tīkla kļūda: Nespēja savienoties ar {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/ml/messages.properties b/locale/ml/messages.properties
new file mode 100644
index 0000000..7b94f19
--- /dev/null
+++ b/locale/ml/messages.properties
@@ -0,0 +1,179 @@
+extName=യുമാട്രിക്സ്‌
+dashboardPageName=യുമാട്രിക്സ്‌ — ഡാഷ്ബോര്‍ഡ്
+loggerPageName=uMatrix — Logger
+settingsPageName=സെറ്റിംഗ്സ്
+privacyPageName=പ്രൈവസി
+statsPageName=സ്റ്ററ്റിസ്റ്റിക്സ്‌
+userRulesPageName=എന്‍റെ നിയമങ്ങള്‍
+ubiquitousRulesPageName=ഹോസ്റ്റ് ഫയലുകള്‍
+rawSettingsPageName=More
+aboutPageName=യുമാട്രിക്സിനെ കുറിച്ച്
+allPrettyName=എല്ലാം
+cookiePrettyName=കുക്കി
+cssPrettyName=സിഎസ്എസ്‌
+imagePrettyName=ചിത്രം
+mediaPrettyName=media
+pluginPrettyName=പ്ലഗ്ഗിന്‍
+scriptPrettyName=സ്ക്രിപ്റ്റ്
+xhrPrettyName=എക്സ്‌എച്ച്ആര്‍
+framePrettyName=ഫ്രെയിം
+otherPrettyName=മറ്റുള്ളവ
+matrixNoNetTrafficPrompt=ഈ ടാബില്‍ ഇതുവരെ ഒരു ഗതാഗതവും ഉണ്ടായിട്ടില്ല.
+matrixMtxButtonTip=ഈ സ്കോപ്പില്‍ മാട്രിക്സ്‌ ഫില്‍ട്ടറിംഗ് എനേബിള്‍/ഡിസേബിള്‍ ചെയ്യുക.
+matrixPersistButtonTip=ഈ സ്കോപ്പിലെ എല്ലാ താത്കാലിക മാറ്റങ്ങളും സൂക്ഷിക്കുക.
+matrixRevertButtonTip=ഈ സ്കോപ്പിലെ എല്ലാ താത്കാലിക മാറ്റങ്ങളും ഉപേക്ഷിക്കുക.
+matrixReloadButton=പേജ്‌ ഒന്നു കൂടി ലോഡ് ചെയ്യുക.
+matrix1stPartyLabel=ഒന്നാം കക്ഷി
+matrixBlacklistedHostnames={{count}} കരിംപട്ടികയില്‍ ഉള്ള ഹോസ്റ്റുകള്‍
+matrixSwitchNoMixedContent=സ്ട്രിക്റ്റ് എച്ച്ടിടിപിഎസ്
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=റഫറര്‍ അനുകരണം
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=എല്ലാ താത്കാലിക മാറ്റങ്ങളും ഉപേക്ഷിക്കുക
+matrixLoggerMenuEntry=ലോഗ്ഗറിലേക്ക് പോകുക
+matrixDashboardMenuEntry=ഡാഷ്ബോര്‍ഡിലേക്ക് പോകുക
+matrixNoTabFound=No web page found
+statsPageTitle=യുമാട്രിക്സ്‌ &ndash; സ്റ്ററ്റിസ്റ്റിക്സ്‌
+statsPageGenericStats=ജനറിക്ക് സ്റ്ററ്റിസ്റ്റിക്സ്‌
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>എച്ച്റ്റിറ്റിപി കുക്കി</a> തടഞ്ഞ ഹെഡറുകള്‍: {{count}}
+statsPageRefererHeadersFoiled=തടഞ്ഞ <a href='https://en.wikipedia.org/wiki/HTTP_referer'>എച്ച്റ്റിറ്റിപി</a> ഹെഡറുകള്‍:{{count}}
+statsPageHyperlinkAuditingFoiled=തടഞ്ഞ <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>ഹൈപ്പര്‍ലിങ്ക് ഓടിറ്റിംഗ്</a> ശ്രമങ്ങള്‍: {{count}}
+statsPageCookiesRemoved=തുടച്ചുനീക്കിയ ലോക്കല്‍ കുക്കികള്‍: {{count}}
+statsPageLocalStoragesCleared=കാലിയാക്കപ്പെട്ട<a href='http://diveintohtml5.info/storage.html'>ലോക്കല്‍ സ്റ്റോറെജ്</a>:{{count}}
+statsPageBrowserCacheCleared=ക്ലിയര്‍ ചെയ്യപ്പെട്ട ബ്രൌസര്‍ കാഷ്: {{count}}
+statsPageDetailedStats=വിശദമായ സ്റ്ററ്റിസ്റ്റിക്സ്‌
+statsPageDetailedAllPages=എല്ലാം
+statsPageDetailedBehindTheScenePage=സീനിനു പിന്നില്‍
+statsPageOverview=ഓവര്‍വ്യൂ
+statsPageRequests=അപേക്ഷകള്‍
+statsPageAllowed=അനുവദനീയമായവ
+statsPageBlocked=തടഞ്ഞിരിക്കുന്നവ
+statsPageAll=എല്ലാം
+statsPagePages=പേജുകള്‍
+statsPageCookies=കുക്കികള്‍
+statsPageCSS=സിഎസ്എസ്
+statsPageImages=ചിത്രങ്ങള്‍
+statsPagePlugins=പ്ലഗ്ഗിന്നുകള്‍
+statsPageScripts=സ്ക്രിപ്റ്റുകള്‍
+statsPageXHRs=എക്സ്‌എച്ച്ആറുകള്‍
+statsPageFrames=ഫ്രെയിമുകള്‍
+statsPageOthers=മറ്റുള്ളവ
+statsPageDetailed=ലോഗ്ഗര്‍
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=ഒന്നും അല്ല
+settingsMatrixAutoReloadCurrent=ഇപ്പോഴത്തെത്
+settingsMatrixAutoReloadAll=എല്ലാം
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=ഒപ്പാസിറ്റി
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=ചെയ്യാനുള്ളവ
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=മിനിറ്റുകള്‍.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=സ്ഥിര നിയമങ്ങള്‍
+userRulesTemporaryHeader=താത്കാലിക നിയമങ്ങള്‍
+userRulesRevert=റിവേര്‍ട്ട്
+userRulesCommit=കമ്മിറ്റ്
+userRulesEdit=എഡിറ്റ്‌
+userRulesEditSave=സൂക്ഷിക്കുക
+userRulesEditDicard=കളയുക
+userRulesImport=ഫയലില്‍ നിന്നും ഇമ്പോര്‍ട്ട് ചെയ്യുക...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=പാര്‍സ് ചെയ്യുക
+hostsFilesExternalListPurge=കാഷ് നശിപ്പിക്കുക
+hostsFilesExternalListNew=പുതിയ വെര്‍ഷന്‍ ലഭ്യമാണ്
+hostsFilesExternalListObsolete=കാലഹരണപ്പെട്ടത്
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>മാറ്റത്തിന്റെ കുറിപ്പുകള്‍</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>അനുവാദങ്ങള്‍</a>
+aboutCode=സോര്‍സ് കോഡ് (ജിപിഎല്‍ വി3)
+aboutIssues=ബഗ്ഗുകളും പ്രശ്നങ്ങളും
+aboutContributors=സംഭാവന ചെയ്തവര്‍
+aboutCodeContributors=കോഡ്:
+aboutIssueContributors=പ്രശ്നങ്ങള്‍:
+aboutTranslationContributors=പരിഭാഷകള്‍:
+aboutUserDataHeader=നിങ്ങളുടെ ഡാറ്റ
+aboutBackupButton=ഫയലിലേക്ക് ബാക്ക്‌ അപ് ചെയ്യുക...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=ഫയലില്‍ നിന്നും റീസ്റ്റോര്‍ ചെയ്യുക...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... അല്ലെങ്കില്‍ ...
+aboutResetButton=ഡീഫാള്‍ട്ട് സെറ്റിംഗ്സിലേക്ക് റീസ്റ്റോര്‍ ചെയ്യുക
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=പിന്നിലേക്ക്‌ പോകുക
+mainBlockedClose=അടയ്ക്കുക
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=ഡാഷ്ബോര്‍ഡ് തുറക്കുക
+elapsedOneMinuteAgo=ഒരു മിനിറ്റ് മുന്‍പ്
+elapsedManyMinutesAgo={{value}} മിനിറ്റുകള്‍ മുന്‍പ്
+elapsedOneHourAgo=ഒരു മണിക്കൂര്‍ മുന്‍പ്
+elapsedManyHoursAgo={{value}} മണിക്കൂറുകള്‍ മുന്‍പ്
+elapsedOneDayAgo=ഒരു ദിവസം മുന്‍പ്
+elapsedManyDaysAgo={{value}} ദിവസങ്ങള്‍ക്ക്മുന്‍പ്
+showDashboardButton=ഡാഷ്ബോര്‍ഡ്
+showLoggerButton=ലോഗ്ഗര്‍
+cloudPush=ക്ലൌഡ് സ്റ്റോറേജിലേക്ക് എക്സ്പോര്‍ട്ട്‌ ചെയ്യുക
+cloudPull=ക്ലൌഡ് സ്റ്റോറേജില്‍ നിന്ന് ഇമ്പോര്‍ട്ട് ചെയ്യുക
+cloudNoData=...\n...
+cloudDeviceNamePrompt=ഈ ഉപകരണത്തിന്‍റെ പേര്:
+genericSubmit=സബ്മിറ്റ്
+genericRevert=റിവേര്‍ട്ട്
+errorCantConnectTo=നെറ്റ്‌വര്‍ക്ക് പ്രശ്നം: {{url}} ലേക്ക് കണക്റ്റ് ചെയ്യാന്‍ ഒക്കുന്നില്ല
+genericApplyChanges=Apply changes
diff --git a/locale/mr/messages.properties b/locale/mr/messages.properties
new file mode 100644
index 0000000..060ae58
--- /dev/null
+++ b/locale/mr/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Settings
+privacyPageName=Privacy
+statsPageName=Statistics
+userRulesPageName=My rules
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=About
+allPrettyName=all
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/ms/messages.properties b/locale/ms/messages.properties
new file mode 100644
index 0000000..060ae58
--- /dev/null
+++ b/locale/ms/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Settings
+privacyPageName=Privacy
+statsPageName=Statistics
+userRulesPageName=My rules
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=About
+allPrettyName=all
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/nb/messages.properties b/locale/nb/messages.properties
new file mode 100644
index 0000000..87d0f6c
--- /dev/null
+++ b/locale/nb/messages.properties
@@ -0,0 +1,179 @@
+extName=µMatrix
+dashboardPageName=uMatrix - Instrumentpanel
+loggerPageName=uMatrix — Logger
+settingsPageName=Innstillinger
+privacyPageName=Personvern
+statsPageName=Statistikk
+userRulesPageName=Mine regelsett
+ubiquitousRulesPageName=Vertsfiler
+rawSettingsPageName=More
+aboutPageName=Om
+allPrettyName=alt
+cookiePrettyName=kake
+cssPrettyName=CSS
+imagePrettyName=bilde
+mediaPrettyName=medie
+pluginPrettyName=tillegg
+scriptPrettyName=skript
+xhrPrettyName=XHR
+framePrettyName=ramme
+otherPrettyName=annet
+matrixNoNetTrafficPrompt=Ingen nettverkstrafikk oppdaget for denne fanen så langt.
+matrixMtxButtonTip=Skru på/av matrisefiltrering for dette virkefeltet.
+matrixPersistButtonTip=Lagre alle midlertidige endringer for dette virkefeltet.
+matrixRevertButtonTip=Tilbakestill midlertidige endringer for dette virkefeltet.
+matrixReloadButton=Gjeninnlast denne siden.
+matrix1stPartyLabel=førsteparts
+matrixBlacklistedHostnames={{count}} svartelistede vertsnavn
+matrixSwitchNoMixedContent=Streng HTTPS
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Lurendreiing av referent
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Tilbakestill alle midlertidige endringer
+matrixLoggerMenuEntry=Gå til loggføring
+matrixDashboardMenuEntry=Gå til instrumentpanel
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistikk
+statsPageGenericStats=Alminnelig statistikk
+statsPageCookieHeadersFoiled=<a href='https://no.wikipedia.org/wiki/Informasjonskapsel'>HTTP-kake</a> hoder lurt: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP-referent</a>hoder lurt: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>Hyperlenke-revisjon</a>forsøk lurt:{{count}}
+statsPageCookiesRemoved={{count}} lokale kaker fjernet
+statsPageLocalStoragesCleared={{count}} <a href='http://diveintohtml5.info/storage.html'>lokallager</a>tømt
+statsPageBrowserCacheCleared={{count}} nettlesermellomlager tømt
+statsPageDetailedStats=Detaljert statistikk
+statsPageDetailedAllPages=Alt
+statsPageDetailedBehindTheScenePage=Bak sceneteppet
+statsPageOverview=Oversikt
+statsPageRequests=Forespørsler
+statsPageAllowed=Tillatt
+statsPageBlocked=Blokkert
+statsPageAll=Alt
+statsPagePages=Sider
+statsPageCookies=Kaker
+statsPageCSS=CSS
+statsPageImages=Bilder
+statsPagePlugins=Tillegg
+statsPageScripts=Skript
+statsPageXHRs=XHR-er
+statsPageFrames=Rammer
+statsPageOthers=Annet
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Husket de siste
+statsPageLogSizePrompt2=HTTP-forespørsler <b>per side</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Gjenoppfrisk
+settingsPageTitle=uMatrix &ndash; innstillinger
+settingsMatrixDisplayHeader=Utseende
+settingsMatrixDisplayTextSizePrompt=Tekststørrelse:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Stor
+settingsMatrixDisplayColorBlind=Fargeblindhets-vennlig
+settingsMatrixConvenienceHeader=Bekvemmelighet
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=Ved lukking av matrisen, utfør smart gjeninnlasting av disse fanene:
+settingsMatrixAutoReloadNone=Ingen
+settingsMatrixAutoReloadCurrent=Gjeldende
+settingsMatrixAutoReloadAll=Alle
+settingsMatrixAutoReloadInfo=Når du gjør endringer i matrisen som kan ha innvirkning på visningen og/eller oppførselen til én eller flere sider vil <i>uMatrix</i> gjeninnlaste sider som omfattes av dette når du lukker matrisen.
+settingsSubframeColor=Blokkerte rammer:&ensp;Farge
+settingsSubframeOpacity=Dekkevne
+settingsIconBadgeEnabled=Vis antall entydige forespørsler på miniatyrbildet
+settingsCollapseBlocked=Fold sammen plassholderen tilhørende blokkerte elementer
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Skru på støtte for sky-lagring
+privacyPageTitle=uMatrix &ndash; personvern
+privacyDeleteBlockedCookiesPrompt=Slettede blokkerte kaker.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Slett ikke-blokkerte kaker for denne økta
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutter etter sist gang de ble brukt.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Slett <a href='https://en.wikipedia.org/wiki/Web_storage'>lokalt innhold</a> satt av blokkerte vertsnavn
+privacyDeleteBlockedLocalStorageHelp=Skal gjøres
+privacyClearCachePrompt1=Tøm nettleserens mellomlager hvert
+privacyClearCachePrompt2=minutt.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Lur <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP-referent</a> strengen til tredjeparts-forespørsler.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Streng HTTPS: forby blandet innhold.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanente regelsett
+userRulesTemporaryHeader=Midlertidige regler
+userRulesRevert=Tilbakestill
+userRulesCommit=Send inn
+userRulesEdit=Rediger
+userRulesEditSave=Lagre
+userRulesEditDicard=Avvis
+userRulesImport=Importer fra fil…
+userRulesExport=Eksporter til fil…
+userRulesFormatHint=Vis siden for regel-syntaks.
+userRulesDefaultFileName=mine-umatrix-regler.txt
+hostsFilesPrompt=Alle vertsnavn i en vertsnavnsfil blir innlastet som svartelistede vertsnavn i det globale virkefeltet.
+hostsFilesStats={{blockedHostnameCount}} entydig blokkerte vertsnavn fra:
+hostsFilesPerFileStats={{used}} brukt av {{total}}
+hostsFilesLastUpdate={{ago}} siste oppdatering
+hostsFilesApplyChanges=Bruk
+hostsFilesAutoUpdatePrompt=Auto-oppdatering av vertsfiler.
+hostsFilesUpdateNow=Oppdater nå
+hostsFilesPurgeAll=Rens alle mellomlager
+hostsFilesExternalListsHint=Én nettadresse per linje. Linjes innledet med &lsquo;#&rsquo; vil bli ignorert. Ugyldige nettadresser vil bli ignorert i all stillhet.
+hostsFilesExternalListsParse=Fortolk
+hostsFilesExternalListPurge=rens mellomlager
+hostsFilesExternalListNew=ny versjon tilgjengelig
+hostsFilesExternalListObsolete=utdatert
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Endringslogg</a>
+aboutStorageUsed={{storageUsed}} Byte lagringsplass brukt
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Dokumentasjon</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Tillatelser</a>
+aboutCode=Kildekode (GPLv3)
+aboutIssues=Feil og mangler
+aboutContributors=Bidragsytere
+aboutCodeContributors=Kode:
+aboutIssueContributors=Mangler:
+aboutTranslationContributors=Oversettelser:
+aboutUserDataHeader=Din data
+aboutBackupButton=Sikkerhetskopier til fil…
+aboutBackupFilename=min-umatrix-sikkerhetskopi.txt
+aboutRestoreButton=Gjenopprett fra fil…
+aboutRestoreConfirm=Alle dine innstillinger vil bli overskrevet ved bruk av data sikkerhetskopiert {{time}}, og uMatrix vil starte på ny.\n\nOverskriv alle eksisterende innstillinger med sikkerhetskopiert data?
+aboutRestoreError=Dataen kunne ikke leses eller er ugyldig
+aboutOr=… eller …
+aboutResetButton=Tilbakestill til forvalgte innstillinger
+aboutResetConfirm=Advarsel! Dette vil fjerne alle dine egenkomponerte innstillinger. Bekreft at du vil fortsette.
+loggerFilterInputPlaceholder=filter-uttrykk
+loggerMaxEntriesTip=Maksimalt antall oppføringer
+loggerEntryCookieDeleted=kake slettet: {{value}}
+loggerEntryDeleteCookieError=kunne ikke slette kake: {{value}}
+loggerEntryBrowserCacheCleared=nettlesermellomlager tømt
+loggerEntryAssetUpdated=verdigjenstand oppdatert: {{value}}
+mainBlockedPrompt1=uMatrix har forhindret innlasting av følgende side:
+mainBlockedPrompt2=I kraft av følgende regel
+mainBlockedBack=Gå tilbake
+mainBlockedClose=Lukk
+commandRevertAll=Omgjør alle midlertidige endringer
+commandWhitelistPageDomain=Midlertidig hvitlist sidens domene
+commandWhitelistAll=Midlertidig hvitlisting av alt
+commandOpenDashboard=Åpne instrumentpanel
+elapsedOneMinuteAgo=ett minutt siden
+elapsedManyMinutesAgo={{value}} minutter siden
+elapsedOneHourAgo=en time siden
+elapsedManyHoursAgo={{value}} timer siden
+elapsedOneDayAgo=en dag siden
+elapsedManyDaysAgo={{value}} dager siden
+showDashboardButton=Instrumentpanel
+showLoggerButton=Loggføring
+cloudPush=Eksporter til skylager
+cloudPull=Importer fra skylager
+cloudNoData=…\n…
+cloudDeviceNamePrompt=Enhetens navn:
+genericSubmit=Send inn
+genericRevert=Tilbakestill
+errorCantConnectTo=Nettverksfeil: Kan ikke koble til {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/nl/messages.properties b/locale/nl/messages.properties
new file mode 100644
index 0000000..54f6600
--- /dev/null
+++ b/locale/nl/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix – Dashboard
+loggerPageName=uMatrix – Logger
+settingsPageName=Instellingen
+privacyPageName=Privacy
+statsPageName=Statistieken
+userRulesPageName=Mijn regels
+ubiquitousRulesPageName=Hosts-bestanden
+rawSettingsPageName=Meer
+aboutPageName=Over
+allPrettyName=alle
+cookiePrettyName=cookie
+cssPrettyName=CSS
+imagePrettyName=afb.
+mediaPrettyName=media
+pluginPrettyName=plug-in
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=overig
+matrixNoNetTrafficPrompt=Voor dit tabblad is nog geen netwerkverkeer gedetecteerd.
+matrixMtxButtonTip=Matrixfiltering voor dit bereik in-/uitschakelen
+matrixPersistButtonTip=Alle tijdelijke wijzigingen voor dit bereik opslaan
+matrixRevertButtonTip=Tijdelijke wijzigingen voor dit bereik ongedaan maken
+matrixReloadButton=De pagina vernieuwen.\nDruk op Shift om de browsercache te omzeilen.
+matrix1stPartyLabel=Huidige domein
+matrixBlacklistedHostnames={{count}} geblackliste hostnamen
+matrixSwitchNoMixedContent=Gemengde inhoud blokkeren
+matrixSwitchNoWorker=Web workers blokkeren
+matrixSwitchReferrerSpoof=<code>Referer</code>-header vervalsen
+matrixSwitchNoscriptSpoof=<code><noscript></code>-tags vervalsen
+matrixRevertAllEntry=Alle tijdelijke wijzigingen ongedaan maken
+matrixLoggerMenuEntry=Naar logger
+matrixDashboardMenuEntry=Naar dashboard
+matrixNoTabFound=Geen webpagina gevonden
+statsPageTitle=uMatrix &ndash; Statistieken
+statsPageGenericStats=Algemene statistieken
+statsPageCookieHeadersFoiled=Geweerde <a href='https://nl.wikipedia.org/wiki/HTTP_cookie'>HTTP-cookie</a>-headers: {{count}}
+statsPageRefererHeadersFoiled=Geweerde <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP-referer</a>-headers: {{count}}
+statsPageHyperlinkAuditingFoiled=Verijdelde <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a>-pogingen: {{count}}
+statsPageCookiesRemoved=Verwijderde lokale cookies: {{count}}
+statsPageLocalStoragesCleared=Gewiste <a href='http://diveintohtml5.info/storage.html'>lokale opslagcaches</a>: {{count}}
+statsPageBrowserCacheCleared=Gewiste browsercaches: {{count}}
+statsPageDetailedStats=Gedetailleerde statistieken
+statsPageDetailedAllPages=Alle
+statsPageDetailedBehindTheScenePage=Achter de schermen
+statsPageOverview=Overzicht
+statsPageRequests=Aanvragen
+statsPageAllowed=Toegestaan
+statsPageBlocked=Geblokkeerd
+statsPageAll=Alle
+statsPagePages=Pagina’s
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Afbeeldingen
+statsPagePlugins=Plug-ins
+statsPageScripts=Scripts
+statsPageXHRs=XHR’s
+statsPageFrames=Frames
+statsPageOthers=Overige
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=De laatste
+statsPageLogSizePrompt2=HTTP-aanvragen <b>per pagina</b> onthouden
+statsPageLogSizeHelp=<p>Van de meest recente HTTP-aanvragen die door een website zijn gedaan, kunt u de details inspecteren.</p><p>Dit is vooral nuttig voor nieuwsgierige (ook ervaren) gebruikers die willen onderzoeken wat een webpagina precies heeft gedaan. Het registreren van deze HTTP-aanvragen vereist echter geheugen, en als u niets om deze technische informatie geeft, wordt geheugen verspild.</p><p>Vandaar dit veld, waarmee u het maximale aantal meest recente HTTP-aanvragen kunt instellen dat voor nader onderzoek wordt geregistreerd.</p><p>Vul &lsquo;<code>0</code>&rsquo; in om gedetailleerde registratie uit te zetten (en daardoor de geheugenfootprint van <i>uMatrix</i> te verlagen).</p>
+statsPageRefresh=Vernieuwen
+settingsPageTitle=uMatrix &ndash; Instellingen
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Tekstgrootte:
+settingsMatrixDisplayTextSizeNormal=Normaal
+settingsMatrixDisplayTextSizeLarge=Groot
+settingsMatrixDisplayColorBlind=Vriendelijk voor kleurenblinden
+settingsMatrixConvenienceHeader=Gemak
+settingsDefaultScopeLevel=Standaard bereikniveau:
+settingsDefaultScopeLevel0=Globaal
+settingsDefaultScopeLevel1=Domein
+settingsDefaultScopeLevel2=Website
+settingsMatrixAutoReloadPrompt=Wanneer de matrix wordt gesloten, deze tabbladen slim vernieuwen:
+settingsMatrixAutoReloadNone=Geen
+settingsMatrixAutoReloadCurrent=Huidige
+settingsMatrixAutoReloadAll=Alle
+settingsMatrixAutoReloadInfo=Als u wijzigingen aanbrengt die de weergave en/of het gedrag van een of meer pagina’s kunnen beïnvloeden, zal <i>uMatrix</i> de betreffende pagina’s automatisch vernieuwen zodra u de matrix sluit.
+settingsSubframeColor=Geblokkeerde frames:&ensp;kleur
+settingsSubframeOpacity=Ondoorzichtigheid
+settingsIconBadgeEnabled=Het aantal afzonderlijke aanvragen op het pictogram tonen
+settingsCollapseBlocked=Tijdelijke aanduiding van geblokkeerde elementen samenvouwen
+settingsCollapseBlacklisted=Tijdelijke aanduiding van geblackliste elementen samenvouwen
+settingsNoscriptTagsSpoofed=<code><noscript></code>-tags vervalsen wanneer scripts van het huidige domein worden geblokkeerd
+settingsCloudStorageEnabled=Ondersteuning voor cloudopslag inschakelen
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Geblokkeerde cookies verwijderen
+privacyDeleteBlockedCookiesHelp=<p><i>uMatrix</i> voorkomt niet dat geblackliste cookies door uw browser worden ontvangen. Er wordt echter wel voorkomen dat ze uw browser verlaten, wat het belangrijkste is. Het niet blokkeren van cookies voordat ze door uw browser worden ontvangen biedt de mogelijkheid te worden geïnformeerd wanneer een website cookies probeerde te gebruiken, en daarnaast eventueel de inhoud ervan te inspecteren.</p><p>Als deze geblackliste cookies eenmaal door <i>uMatrix</i> zijn verwerkt, kunnen ze desgewenst uit uw browser worden verwijderd.</p><p><b>Belangrijke opmerking:</b> extensies kunnen tijdens hun normale werking webaanvragen doen. Door deze aanvragen kunnen cookies in de browser worden aangemaakt. Als de hostnaam waarvan een cookie afkomstig is zich niet op de whitelist bevindt, zal het cookie als deze optie is aangevinkt door <i>uMatrix</i> uit de browser worden verwijderd. Zorg er daarom voor dat de namen van de hosts waarmee een extensie communiceert zich op de whitelist bevinden.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Niet-geblokkeerde sessiecookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minuten na de laatste keer dat ze zijn gebruikt verwijderen
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;Een sessiecookie wordt gewist zodra u de browsersessie beëindigt. Een sessiecookie wordt in tijdelijk geheugen opgeslagen en niet behouden nadat de browser wordt gesloten.&rdquo;</p><p>Het is mogelijk dat dit <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>niet in alle browsers gebeurt</a>. Ook vinden sommige gebruikers het moeten sluiten van de browser om sessiecookies te wissen misschien niet vroeg genoeg.
+privacyDeleteBlockedLocalStoragePrompt=Inhoud van <a href='https://en.wikipedia.org/wiki/Web_storage'>lokale opslag</a> die door geblokkeerde hostnamen is ingesteld verwijderen
+privacyDeleteBlockedLocalStorageHelp=TE DOEN
+privacyClearCachePrompt1=Browsercache om de
+privacyClearCachePrompt2=minuten wissen
+privacyClearCacheHelp=<p>Sommige websites zijn er zo op gericht om u te traceren dat ze onvriendelijke trucs gebruiken om maatregelen die u neemt om niet te worden gevolgd te omzeilen.</p><p>Sommige van deze trucs gebruiken<sup>[1, 2]</sup> de <a href='https://en.wikipedia.org/wiki/Web_cache'>browsercache</a>, waarvan de inhoud vaak lang aanwezig is, aangezien gebruikers zelden de tijd nemen om hun browsercache regelmatig te wissen.</p><p>Het regelmatig wissen van de browsercache biedt weinig ongemakken (waarschijnlijk merkt u het niet als dit gebeurt), en het voordeel is dat wordt voorkomen dat deze onaangename trackers inbreuk op uw privacy maken.</p><p>Vink deze optie aan om dit met een gewenst interval door <i>uMatrix</i> te laten doen.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&lsquo;Preventing Web Tracking via the Browser Cache&rsquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&lsquo;Cookieless cookies&rsquo;</a></p>
+privacyProcessRefererPrompt=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP-referer</a>-string van aanvragen van derden vervalsen
+privacyProcessRefererHelp=Van Wikipedia:<blockquote>Een HTTP-referer is een HTTP-headerveld dat het adres aanduidt van de webpagina die naar de opgevraagde bron verwijst. ... <b>Omdat referer-informatie de privacy kan schenden, kunnen gebruikers in sommige webbrowsers het verzenden van de referer-informatie uitschakelen.</b></blockquote>Als deze optie is aangevinkt, zal <i>uMatrix</i> de HTTP-referer vervalsen (lees: manipuleren) als de domeinnaam van de HTTP-referer een derde partij is voor de domeinnaam van de netwerkaanvraag.
+privacyNoMixedContentPrompt=Strict HTTPS: gemengde inhoud blokkeren
+privacyNoMixedContentHelp=<p>Van <a href='https://developer.mozilla.org/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>Als een HTTPS-pagina inhoud bevat die door normale cleartext-HTTP is opgehaald, is de verbinding slechts gedeeltelijk versleuteld: de onversleutelde inhoud is toegankelijk voor ‘sniffers’ en kan door ‘man-in-the-middle’-aanvallers worden aangepast, en de verbinding is daarom niet meer beschermd. Wanneer een webpagina dit gedrag vertoont, wordt deze een pagina met gemengde inhoud genoemd.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Alle <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a>-pogingen blokkeren
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is een mechanisme waarmee een partij, <b>ongeacht welke</b>, kan worden geïnformeerd over de koppeling op een bepaalde webpagina waarop een gebruiker heeft geklikt. Dit is in essentie een trackingfunctie: het kan een website of derde partijen ten opzichte van die website informeren over op welke koppeling en op welke pagina’s van de website u hebt geklikt. Het enige doel ervan is het volgen van uw browseractiviteit.</p>
+userRulesPermanentHeader=Permanente regels
+userRulesTemporaryHeader=Tijdelijke regels
+userRulesRevert=Ongedaan maken
+userRulesCommit=Vastleggen
+userRulesEdit=Bewerken
+userRulesEditSave=Opslaan
+userRulesEditDicard=Annuleren
+userRulesImport=Importeren uit bestand...
+userRulesExport=Exporteren naar bestand...
+userRulesFormatHint=Bekijk deze pagina voor regelsyntaxis.
+userRulesDefaultFileName=mijn-umatrix-regels.txt
+hostsFilesPrompt=Alle hostnamen in een hosts-bestand worden in het globale bereik als geblackliste hostnamen geladen.
+hostsFilesStats={{blockedHostnameCount}} afzonderlijke geblokkeerde hostnamen van:
+hostsFilesPerFileStats={{used}} van {{total}} gebruikt
+hostsFilesLastUpdate=Laatst bijgewerkt: {{ago}}
+hostsFilesApplyChanges=Wijzigingen toepassen
+hostsFilesAutoUpdatePrompt=Hosts-bestanden automatisch bijwerken
+hostsFilesUpdateNow=Nu bijwerken
+hostsFilesPurgeAll=Alle caches legen
+hostsFilesExternalListsHint=Eén URL per regel. Regels beginnend met &lsquo;#&rsquo; worden genegeerd. Ongeldige URL’s worden zonder mededeling genegeerd.
+hostsFilesExternalListsParse=Toepassen
+hostsFilesExternalListPurge=cache legen
+hostsFilesExternalListNew=nieuwe versie beschikbaar
+hostsFilesExternalListObsolete=verouderd
+rawSettingsWarning=Waarschuwing! U wijzigt deze configuratie-instellingen op eigen risico.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Wijzigingenlogboek</a>
+aboutStorageUsed=Gebruikte opslag: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentatie</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Toestemmingen</a>
+aboutCode=Broncode (GPLv3)
+aboutIssues=Bugs en problemen
+aboutContributors=Medewerkers
+aboutCodeContributors=Code:
+aboutIssueContributors=Problemen:
+aboutTranslationContributors=Vertalingen:
+aboutUserDataHeader=Uw gegevens
+aboutBackupButton=Back-up maken naar bestand...
+aboutBackupFilename=mijn-umatrix-back-up.txt
+aboutRestoreButton=Terugzetten uit bestand...
+aboutRestoreConfirm=Al uw instelling zullen met back-upgegevens van {{time}} worden overschreven, en uMatrix zal worden herstart.\n\nWilt u alle bestaande instellingen met back-upgegevens overschrijven?
+aboutRestoreError=De gegevens konden niet worden gelezen of zijn ongeldig
+aboutOr=... of ...
+aboutResetButton=Standaardinstellingen terugzetten
+aboutResetConfirm=Waarschuwing! Hierdoor worden alle aangepaste instellingen verwijderd. Weet u zeker dat u wilt doorgaan?
+loggerFilterInputPlaceholder=filterexpressie(s)
+loggerMaxEntriesTip=Maximale aantal vermeldingen
+loggerEntryCookieDeleted=cookie verwijderd: {{value}}
+loggerEntryDeleteCookieError=cookie verwijderen mislukt: {{value}}
+loggerEntryBrowserCacheCleared=browsercache gewist
+loggerEntryAssetUpdated=hosts-bestand bijgewerkt: {{value}}
+mainBlockedPrompt1=uMatrix heeft voorkomen dat de volgende pagina werd geladen:
+mainBlockedPrompt2=vanwege de volgende regel
+mainBlockedBack=Teruggaan
+mainBlockedClose=Sluiten
+commandRevertAll=Alle tijdelijke wijzigingen ongedaan maken
+commandWhitelistPageDomain=Paginadomein tijdelijk toestaan
+commandWhitelistAll=Alles tijdelijk toestaan
+commandOpenDashboard=Dashboard openen
+elapsedOneMinuteAgo=een minuut geleden
+elapsedManyMinutesAgo={{value}} minuten geleden
+elapsedOneHourAgo=een uur geleden
+elapsedManyHoursAgo={{value}} uur geleden
+elapsedOneDayAgo=een dag geleden
+elapsedManyDaysAgo={{value}} dagen geleden
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Exporteren naar cloudopslag
+cloudPull=Importeren uit cloudopslag
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Naam van dit apparaat:
+genericSubmit=Verzenden
+genericRevert=Ongedaan maken
+errorCantConnectTo=Netwerkfout: kan geen verbinding maken met {{url}}
+genericApplyChanges=Wijzigingen toepassen
diff --git a/locale/pl/messages.properties b/locale/pl/messages.properties
new file mode 100644
index 0000000..e5e7672
--- /dev/null
+++ b/locale/pl/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix – Panel sterowania
+loggerPageName=uMatrix — Dziennik
+settingsPageName=Ustawienia
+privacyPageName=Prywatność
+statsPageName=Statystyki
+userRulesPageName=Moje reguły
+ubiquitousRulesPageName=Pliki hosts
+rawSettingsPageName=Więcej
+aboutPageName=O rozszerzeniu
+allPrettyName=wszystko
+cookiePrettyName=ciastko
+cssPrettyName=CSS
+imagePrettyName=obrazek
+mediaPrettyName=media
+pluginPrettyName=wtyczka
+scriptPrettyName=skrypt
+xhrPrettyName=XHR
+framePrettyName=ramka
+otherPrettyName=inne
+matrixNoNetTrafficPrompt=Brak aktywności sieciowej dla tej karty.
+matrixMtxButtonTip=Wyłącz/włącz filtrowanie matrycowe dla tego kontekstu.
+matrixPersistButtonTip=Zapisz wszystkie tymczasowe zmiany dla tego kontekstu.
+matrixRevertButtonTip=Usuń tymczasowe zmiany dla tej witryny.
+matrixReloadButton=Odśwież stronę.\nPrzytrzymaj Shift aby ominąć cache.
+matrix1stPartyLabel=ta domena
+matrixBlacklistedHostnames={{count}} zablokowanych hostów
+matrixSwitchNoMixedContent=Zabroń mieszanej zawartości
+matrixSwitchNoWorker=Zabroń web workerów
+matrixSwitchReferrerSpoof=Podrabianie nagłówka <code>referer</code>
+matrixSwitchNoscriptSpoof=Podrabianie tagów <code><noscript></code>
+matrixRevertAllEntry=Cofnij wszystkie tymczasowe zmiany
+matrixLoggerMenuEntry=Przejdź do dziennika żądań
+matrixDashboardMenuEntry=Przejdź do panelu sterowania
+matrixNoTabFound=Nie znaleziono strony
+statsPageTitle=uMatrix &ndash; Statystyki
+statsPageGenericStats=Ogólne statystyki
+statsPageCookieHeadersFoiled=Powstrzymane nagłówki <a href='https://pl.wikipedia.org/wiki/HTTP_cookie'>ciasteczek</a>: {{count}}
+statsPageRefererHeadersFoiled=Powstrzymane nagłówki <a href='https://pl.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a>: {{count}}
+statsPageHyperlinkAuditingFoiled=Powstrzymane próby <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>audytowania linków [ang.]</a>: {{count}}
+statsPageCookiesRemoved=Usuniętych lokalnych ciasteczek: {{count}}
+statsPageLocalStoragesCleared=Opróżnione <a href='http://diveintohtml5.info/storage.html'>magazyny lokalne</a>: {{count}}
+statsPageBrowserCacheCleared=Opróżnione pamięci podręczne przeglądarki: {{count}}
+statsPageDetailedStats=Szczegółowe statystyki
+statsPageDetailedAllPages=Wszystkie
+statsPageDetailedBehindTheScenePage=Ukryte połączenia
+statsPageOverview=Podsumowanie
+statsPageRequests=Żądania
+statsPageAllowed=Dozwolony
+statsPageBlocked=Zablokowany
+statsPageAll=Wszystkie
+statsPagePages=Strony
+statsPageCookies=Ciasteczka
+statsPageCSS=CSS
+statsPageImages=Obrazy
+statsPagePlugins=Wtyczki
+statsPageScripts=Skrypty
+statsPageXHRs=XHRy
+statsPageFrames=Ramki
+statsPageOthers=Inne
+statsPageDetailed=Dziennik
+statsPageLogSizePrompt1=Zapamiętaj ostatnie
+statsPageLogSizePrompt2=żądań HTTP <b>na stronę</b>.
+statsPageLogSizeHelp=<p>Możesz kontrolować szczegóły ostatnich żądań HTTP, które zostały wykonane przez daną stronę (zobacz niżej).</p><p>Jest to przydatne dla zaawansowanych użytkowników, którzy chcieliby zobaczyć co dokładnie jest pobierane. Jednakże rejestrowanie żądań HTTP wymaga pamięci RAM, więc jeśli nie zależy Ci na tej technicznej informacji, pamięć jest po prostu marnowana.</p><p>Stąd to pole, które pozwoli Ci dostosować ilość ostatnich żądań HTTP które zostaną zarejestrowane.</p><p>Wpisz &ldquo;<code>0</code>&rdquo;, aby wyłączyć szczegółowe rejestrowanie (dzięki temu zmniejszysz użycie pamięci RAM przez <i>uMatrix</i>).</p>
+statsPageRefresh=Odśwież
+settingsPageTitle=uMatrix &ndash; Ustawienia
+settingsMatrixDisplayHeader=Matryca
+settingsMatrixDisplayTextSizePrompt=Rozmiar tekstu:
+settingsMatrixDisplayTextSizeNormal=Normalny
+settingsMatrixDisplayTextSizeLarge=Duży
+settingsMatrixDisplayColorBlind=Schemat kolorów przyjazny daltonistom
+settingsMatrixConvenienceHeader=Udogodnienia
+settingsDefaultScopeLevel=Domyślny poziom kontekstu:
+settingsDefaultScopeLevel0=globalny
+settingsDefaultScopeLevel1=domena
+settingsDefaultScopeLevel2=strona
+settingsMatrixAutoReloadPrompt=Kiedy okno uMatrix zostanie zamknięte, inteligentnie odśwież następujące karty:
+settingsMatrixAutoReloadNone=Żaden
+settingsMatrixAutoReloadCurrent=Aktualny
+settingsMatrixAutoReloadAll=Wszystkie
+settingsMatrixAutoReloadInfo=Kiedy wprowadzasz w matrycy zmiany, które mogą wpływać na wyświetlanie lub zachowanie jednej lub kilku stron, <i>uMatrix</i> odświeży te strony automatycznie, kiedy okno zostanie zamknięte.
+settingsSubframeColor=Zablokowane ramki:&ensp;Kolor
+settingsSubframeOpacity=Przezroczystość
+settingsIconBadgeEnabled=Pokaż liczbę odrębnych żądań na ikonie rozszerzenia
+settingsCollapseBlocked=Ukryj symbole zastępcze zablokowanych elementów
+settingsCollapseBlacklisted=Ukryj symbole zastępcze czarno-listowanych elementów
+settingsNoscriptTagsSpoofed=Podrabiaj tagi <code><noscript></code> gdy skrypty z aktualnej domeny są zablokowane
+settingsCloudStorageEnabled=Włącz możliwość zapisywania ustawień w chmurze
+privacyPageTitle=uMatrix &ndash; Prywatność
+privacyDeleteBlockedCookiesPrompt=Usuń zablokowane ciasteczka.
+privacyDeleteBlockedCookiesHelp=<p><i>uMatrix</i>nie zapobiega dostawaniu się ciasteczek z czarnej listy do twojej przeglądarki. Jednakże zapobiega opuszczaniu jej przez nie, co ostatecznie się liczy. Nieblokowanie ciasteczek zanim dotrą do twojej przeglądarki, daje możliwość poinformowania cię gdy strona próbuje używać ciastek, a nawet pozwala przejrzeć ich zawartość gdy tego chcesz.</p><p>Kiedy te czarnolistowane ciasteczka zostaną sklasyfikowane przez <i>uMatrix</i>, mogą, jeśli sobie tego życzysz, zostać usunięte z przeglądarki.</p><p><b>Ważna informacja:</b> Rozszerzenia mogą wykonywać żądania sieciowe podczas ich normalnej pracy. Te żądania mogą spowodować zapisanie ciasteczek w przeglądarce. Jeśli nazwa hosta z której pochodzi ciasteczko nie jest na białej liście, ciasteczko zostanie usunięte z przeglądarki przez <i>uMatrix</i> jeśli ta opcja jest zaznaczona. Upewnij się, że nazwy hostów z którymi komunikuje się rozszerzenie są na białej liście.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Usuń nie zablokowane ciasteczka sesyjne
+privacyDeleteNonBlockedSessionCookiesPrompt2= minut od ostatniego użycia.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C [ang.]</a>: &ldquo;Ciasteczko sesyjne ... jest usuwane kiedy kończysz sesję przeglądarki. Ciasteczko sesyjne przechowywane jest w pamięci tymczasowej i nie jest zachowywane po zamknięciu przeglądarki.&rdquo;</p><p>Poza tym, że <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>nie zawsze to działa</a> w niektórych przeglądarkach. Również, dla niektórych użytkowników, zamknięcie przeglądarki by usunąć ciasteczka może nie być wystarczająco szybkie.</p>
+privacyDeleteBlockedLocalStoragePrompt=Usuwaj zawartość <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> ustawioną przez zablokowane hosty
+privacyDeleteBlockedLocalStorageHelp=Do zrobienia
+privacyClearCachePrompt1=Wyczyść pamięć podręczną przeglądarki co
+privacyClearCachePrompt2=minut.
+privacyClearCacheHelp=<p>Niektóre witryny internetowe są tak zdecydowane by cię śledzić, że używają niezbyt ładnych trików aby obejść jakiekolwiek zabezpieczenia zastosujesz, by nie być śledzonym.</p><p>Kilka z tych trików<sup>[1, 2]</sup> opiera się na wykorzystaniu <a href='https://en.wikipedia.org/wiki/Web_cache'>pamięci podręcznej przeglądarki [ang.]</a>, której zawartość jest często dostępna bardzo długo, ponieważ użytkownicy rzadko ją usuwają.</p><p>Wady regularnego czyszczenia pamięci podręcznej są niewielkie (prawdopodobnie nie zauważysz, gdy to się stanie), a zaletą jest zapobieganie tym nieznośnym szpiegom naruszania twojej prywatności.</p><p>Zaznacz tą opcję by <i>uMatrix</i> robił to za ciebie, z częstotliwością jaką sobie życzysz.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Zapobieganie śledzeniu z wykorzystaniem pamięci podręcznej przeglądarki&rdquo; [ang.]</a>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Bezciasteczkowe ciasteczka&rdquo;[ang.]</a></p>
+privacyProcessRefererPrompt=Fałszuj nagłówek <a href='https://pl.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> dla domen zewnętrznych.
+privacyProcessRefererHelp=Z Wikipedii [ang.]:<blockquote>HTTP referer jest nagłówkiem HTTP który identyfikuje adres strony która linkuje do zasobu będącego wywoływanym. ... <b>Ponieważ informacje o stronie odsyłającej mogą zagrażać prywatności, niektóre przeglądarki pozwalają na wyłączenie wysyłania tej informacji.</b></blockquote>Jeśli ta opcja jest zaznaczona, <i>uMatrix</i> będzie fałszował informację o refererze jeśli domena referera jest inna niż domena żądania sieciowego.
+privacyNoMixedContentPrompt=Ścisły HTTPS: blokuj mieszaną zawartość.
+privacyNoMixedContentHelp=<p>Z <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network [ang.]</a>:</p><blockquote>Jeżeli strona HTTPS zawiera elementy pobrane przez zwykły, nie szyfrowany protokół HTTP, połączenie szyfrowane jest tylko częściowo: elementy nieszyfrowane są podatne na podsłuch i mogą być zmodyfikowane w ataku man-in-the-middle, i dlatego połączenie nie jest już bezpieczne.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Blokuj wszystkie próby <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>audytowania linków [ang.]</a>.
+privacyProcessHyperlinkAuditingHelp=<p>Audytowanie linków to mechanizm wysyłający informację o linkach klikniętych przez użytkownika na stronie internetowej. Jest to funkcja przeznaczona głównie do śledzenia: pozwala witrynie internetowej lub dowolnej stronie trzeciej, być informowanym o linkach jakie kliknąłeś na stronie internetowej. Jedynym celem jest śledzenie twojej aktywności w sieci.</p>
+userRulesPermanentHeader=Stałe zasady
+userRulesTemporaryHeader=Tymczasowe zasady
+userRulesRevert=Przywróć
+userRulesCommit=Zatwierdź
+userRulesEdit=Edytuj
+userRulesEditSave=Zapisz
+userRulesEditDicard=Odrzuć
+userRulesImport=Importuj z pliku...
+userRulesExport=Eksportuj do pliku...
+userRulesFormatHint=Składnię reguł znajdziesz tutaj.
+userRulesDefaultFileName=umatrix-moje-reguly.txt
+hostsFilesPrompt=Wszystkie nazwy hostów z plików hosts są traktowane jako czarno-listowane globalnie.
+hostsFilesStats={{blockedHostnameCount}} blokowanych unikalnych hostów z:
+hostsFilesPerFileStats=użytych {{used}} z {{total}}
+hostsFilesLastUpdate=Ostatnia aktualizacja: {{ago}}
+hostsFilesApplyChanges=Zastosuj zmiany
+hostsFilesAutoUpdatePrompt=Automatycznie aktualizuj pliki hosts.
+hostsFilesUpdateNow=Aktualizuj teraz
+hostsFilesPurgeAll=Przeczyść całą pamięć podręczną
+hostsFilesExternalListsHint=Jeden URL na linię. Linie zaczynające się od &lsquo;#&rsquo; będą traktowane jak komentarz. Niepoprawne URLe będą ignorowane.
+hostsFilesExternalListsParse=Przetwórz
+hostsFilesExternalListPurge=wyczyść pamięć podręczną
+hostsFilesExternalListNew=dostępna nowsza wersja
+hostsFilesExternalListObsolete=nieaktualna
+rawSettingsWarning=Ostrzeżenie! Te ustawienia zmieniasz na własne ryzyko.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Historia zmian</a>
+aboutStorageUsed=Wykorzystana pamięć: {{storageUsed}} bajtów
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Dokumentacja</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Uprawnienia</a>
+aboutCode=Kod źródłowy (GPLv3)
+aboutIssues=Błędy i problemy
+aboutContributors=Współtwórcy
+aboutCodeContributors=Kod:
+aboutIssueContributors=Problemy:
+aboutTranslationContributors=Tłumaczenia:
+aboutUserDataHeader=Twoje dane
+aboutBackupButton=Zapisz kopię zapasową...
+aboutBackupFilename=umatrix-kopia-zapasowa.txt
+aboutRestoreButton=Przywróć kopię zapasową...
+aboutRestoreConfirm=Wszystkie ustawienia zostaną nadpisane danymi z kopii z {{time}}, a następnie uMatrix zostanie zrestartowany.\n\nNadpisać aktualne ustawienia używając danych z kopii?
+aboutRestoreError=Dane nie mogą być odczytane lub są niepoprawne
+aboutOr=... lub ...
+aboutResetButton=Przywróć ustawienia domyślne
+aboutResetConfirm=Uwaga! Twoje ustawienia zostaną usunięte. Czy na pewno chcesz kontynuować?
+loggerFilterInputPlaceholder=filtruj wpisy dziennika
+loggerMaxEntriesTip=Maksymalna ilość wpisów
+loggerEntryCookieDeleted=ciastko usunięte: {{value}}
+loggerEntryDeleteCookieError=Nie udało się usunąć ciasteczka: {{value}}
+loggerEntryBrowserCacheCleared=pamięć podręczna przeglądarki wyczyszczona
+loggerEntryAssetUpdated=zaktualizowano: {{value}}
+mainBlockedPrompt1=uMatrix nie pozwolił załadować się następującej stronie:
+mainBlockedPrompt2=Z powodu tej reguły
+mainBlockedBack=Wstecz
+mainBlockedClose=Zamknij
+commandRevertAll=Cofnij wszystkie tymczasowe zmiany
+commandWhitelistPageDomain=Tymczasowo dodaj tą domenę na białą listę
+commandWhitelistAll=Tymczasowo dodaj wszystko na białą listę
+commandOpenDashboard=Otwórz panel sterowania
+elapsedOneMinuteAgo=minutę temu
+elapsedManyMinutesAgo={{value}} minut temu
+elapsedOneHourAgo=godzinę temu
+elapsedManyHoursAgo={{value}} godzin temu
+elapsedOneDayAgo=dzień temu
+elapsedManyDaysAgo={{value}} dni temu
+showDashboardButton=Panel sterowania
+showLoggerButton=Dziennik żądań
+cloudPush=Eksportuj do chmury
+cloudPull=Importuj z chmury
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Nazwa tego urządzenia:
+genericSubmit=Zatwierdź
+genericRevert=Przywróć
+errorCantConnectTo=Błąd sieci: nie można połączyć z {{url}}
+genericApplyChanges=Zastosuj zmiany
diff --git a/locale/pt-BR/messages.properties b/locale/pt-BR/messages.properties
new file mode 100644
index 0000000..433c5d1
--- /dev/null
+++ b/locale/pt-BR/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Painel
+loggerPageName=uMatrix — Registro
+settingsPageName=Preferências
+privacyPageName=Privacidade
+statsPageName=Estatísticas
+userRulesPageName=Minhas regras
+ubiquitousRulesPageName=Arquivos de hosts
+rawSettingsPageName=Mais
+aboutPageName=Sobre
+allPrettyName=tudo
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=imagem
+mediaPrettyName=mídia
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=outro
+matrixNoNetTrafficPrompt=Não foi encontrado tráfego de rede nesta aba até agora.
+matrixMtxButtonTip=Desabilitar/habilitar filtragem pela matrix para este escopo.
+matrixPersistButtonTip=Salvar todas mudanças temporárias para este escopo.
+matrixRevertButtonTip=Remover mudanças temporárias para este escopo.
+matrixReloadButton=Recarregar a página.\nPressione Shift para ignorar o cache do navegador.
+matrix1stPartyLabel=1° Nível
+matrixBlacklistedHostnames={{count}} hostname(s) na lista negra
+matrixSwitchNoMixedContent=Proibir conteúdo misto
+matrixSwitchNoWorker=Proibir web workers
+matrixSwitchReferrerSpoof=Disfarçar <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Disfarçar <code><noscript></code> tags
+matrixRevertAllEntry=Reverter todas mudanças temporárias
+matrixLoggerMenuEntry=Ir para o registro
+matrixDashboardMenuEntry=Ir para o painel
+matrixNoTabFound=Nenhuma página de web encontrada
+statsPageTitle=uMatrix &ndash; Estatísticas
+statsPageGenericStats=Estatísticas Genéricas
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> cabeçalhos frustrados: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> cabeçalhos frustrados: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> tentativas falhas: {{count}}
+statsPageCookiesRemoved=Cookies locais removidos: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Armazenamento local</a> esvaziados: {{count}}
+statsPageBrowserCacheCleared=Caches do navegador limpas: {{count}}
+statsPageDetailedStats=Estatísticas detalhadas
+statsPageDetailedAllPages=Todos
+statsPageDetailedBehindTheScenePage=Por trás das cenas
+statsPageOverview=Visão Geral
+statsPageRequests=Pedidos
+statsPageAllowed=Permitidos
+statsPageBlocked=Bloqueados
+statsPageAll=Tudo
+statsPagePages=Páginas
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Imagens
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Outros
+statsPageDetailed=Registro
+statsPageLogSizePrompt1=Lembrar dos últimos
+statsPageLogSizePrompt2=Pedidos HTTP <b>por página</b>.
+statsPageLogSizeHelp=<p> Você pode inspecionar os detalhes das mais recentes solicitações HTTP matérias que têm sido feitas por uma página web (veja abaixo). </p><p> Isto é útil para usuários avançados que querem investigar exatamente o que uma página da web tem vindo a fazer. Mas registrando essas solicitações HTTP requer memória, e se você não se preocupam com esta informação técnica, a memória está sendo desperdiçado. </p><p> Por isso, neste domínio, que permite ajustar o número máximo dos mais recentes solicitações HTTP que deve ser registada para inspeção adicional </p><p> Digite &ldquo;.<code>0</code>&rdquo; para desativar o log detalhado (e, consequentemente, reduzir o consumo de memória de <i>uMatrix</i>).</p>
+statsPageRefresh=Atualizar
+settingsPageTitle=uMatrix &ndash; Configurações
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Tamanho do texto:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Grande
+settingsMatrixDisplayColorBlind=Cor amigável para daltônicos
+settingsMatrixConvenienceHeader=Conveniência
+settingsDefaultScopeLevel=Nível padrão do escopo:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domínio
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=Quando a matrix é fechada, recarregue inteligentemente essas abas:
+settingsMatrixAutoReloadNone=Vazio
+settingsMatrixAutoReloadCurrent=Atual
+settingsMatrixAutoReloadAll=Todos
+settingsMatrixAutoReloadInfo=Sempre que você fizer mudanças na matriz que podem afetar a visualização e/ou comportamento de uma ou mais páginas, <i>uMatrix</i> recarregará páginas afetadas automaticamente quando você fechar a matriz.
+settingsSubframeColor=Frames bloqueados:&ensp;Cor
+settingsSubframeOpacity=Opacidade
+settingsIconBadgeEnabled=Mostrar o número de pedidos distintos sob o ícone
+settingsCollapseBlocked=Agrupar espaços reservados à elementos bloqueados
+settingsCollapseBlacklisted=Agrupar espaços reservados à elementos da lista negra
+settingsNoscriptTagsSpoofed=Disfarçar <code><noscript></code> tags quando scripts de 1° nível são bloqueados
+settingsCloudStorageEnabled=Habilitar suporte à armazenagem em nuvem
+privacyPageTitle=uMatrix &ndash; Privacidade
+privacyDeleteBlockedCookiesPrompt=Apagar os cookies bloqueados.
+privacyDeleteBlockedCookiesHelp=<p>Cookies na lista negra não são impedidos pelo <i>uMatrix</i> de entrar em seu navegador. No entanto, eles estão impedidos de ficar no seu navegador, que é o que realmente importa. Não bloquear os cookies antes de introduzir o seu navegador lhe dá a oportunidade de ser informado de que um site tentou usar cookies, e além disso, para inspecionar o seu conteúdo, se desejar.</p><p>Uma vez que esses cookies lista negra foram contabilizados por <i>uMatrix</i>, eles podem ser removidos do seu navegador, se assim o desejar.</p><p><b>Nota importante:</b> As extensões podem fazer solicitações da web durante o curso de sua operação normal. Estes pedidos podem resultar em cookies que está sendo criado no navegador. Se o nome da máquina a partir de onde se originam um biscoito não está na lista branca, o cookie será removido a partir do navegador pelo <i>uMatrix</i> se esta opção estiver marcada. Então não se esqueça que o nome da máquina (s) com que uma extensão comunicar está na lista branca.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Excluir cookies da sessão não-bloqueados
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutos após a última vez que eles tenham sido utilizados.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;Um cookie de sessão ... é apagado quando você termina a sessão do navegador. O cookie de sessão é armazenado na memória temporária e não é mantido após o navegador for fechado.&rdquo;</p><p>Exceto para este <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>não pode estar acontecendo</a> quando utilizar um navegador baseado em Chromium. Além disso, para alguns, é necessário fechar o navegador para limpar os cookies da sessão pode não ser suficientemente cedo.</p>
+privacyDeleteBlockedLocalStoragePrompt=Apagar <a href='https://en.wikipedia.org/wiki/Web_storage'>armazenamento local</a> conteúdo definido por hostnames bloqueadas
+privacyDeleteBlockedLocalStorageHelp=À FAZER
+privacyClearCachePrompt1=Limpar o cache do navegador à cada
+privacyClearCachePrompt2=minutos.
+privacyClearCacheHelp=<p>Alguns sites são realmente empenhados no seu rastreamento, tanto que eles vão usar truques não tão agradáveis ​​para contornar as medidas de tomar, a fim de não ser rastreado.</p><p>Alguns desses truques é usar<sup>[1, 2]</sup> no <a href='https://en.wikipedia.org/wiki/Web_cache'>cache do navegador</a>, que o conteúdo é muitas vezes de longa duração já que raramente os usuários irão perder seu tempo para limpar regularmente o cache de seu navegador.</p><p>Há pouco inconveniente em limpar o cache do navegador regularmente (mais provável é que você não vai notar quando isso acontece), e o benefício é para evitar que esses rastreadores detestáveis ​​de invadir a sua privacidade.</p><p>Marque esta opção para o <i>uMatrix</i> fazer isso por você, no intervalo que você desejar.</p><p>[1]<a\nhref='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Disfarçar linhas <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> dos pedidos de terceiros.
+privacyProcessRefererHelp=Do Wikipedia:<blockquote>HTTP referer é um campo de cabeçalho HTTP que identifica o endereço da página que ligado ao recurso que está sendo solicitado. ... <b>Porque a informação do referer pode violar a privacidade, alguns navegadores da Web permitem que o usuário desativar o envio de informações do referer.</b></blockquote>Se essa configuração for selecionada, <i>uMatrix</i> irá remover as informações HTTP referer se o nome do domínio do HTTP referrer é de terceiros para o nome de domínio da solicitação.
+privacyNoMixedContentPrompt=HTTPS Estrito: proibir conteúdo misto.
+privacyNoMixedContentHelp=<p>A partir de <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>Se [uma] página HTTPS inclui conteúdo recuperado através de regular, HTTP texto puro, então a conexão é criptografada apenas parcialmente: o conteúdo não criptografado é acessível a sniffers e pode ser modificado por hackers intermediários, e, portanto, a conexão não é mais segura. Quando uma página da Web exibe este comportamento, ele é chamado de uma página de conteúdo misto.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Bloquear todas tentativas de <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a>.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing é um mecanismo que permite que um ente, <b>qualquer ente</b>, de ser informado sobre o que vincular um usuário clicou em uma página web em particular. É essencialmente um recurso de rastreamento: ele permite que um site, ou qualquer terceira parte para esse site, para ser informado sobre qual link você clicou em que uma das suas páginas web. O único objetivo é rastrear sua atividade de navegação.</p>
+userRulesPermanentHeader=Regras permanentes
+userRulesTemporaryHeader=Regras temporárias
+userRulesRevert=Reverter
+userRulesCommit=Aplicar
+userRulesEdit=Editar
+userRulesEditSave=Salvar
+userRulesEditDicard=Descartar
+userRulesImport=Importar do arquivo...
+userRulesExport=Exportar para arquivo...
+userRulesFormatHint=Veja está página para regras de sintaxe.
+userRulesDefaultFileName=minhas-regras-umatrix.txt
+hostsFilesPrompt=Todos os hostnames no arquivo de hosts são carregados como hostnames da lista negra no escopo global.
+hostsFilesStats={{blockedHostnameCount}} hostnames distintos bloqueados de:
+hostsFilesPerFileStats={{used}} usado do total de {{total}}
+hostsFilesLastUpdate=Última atualização: {{ago}}
+hostsFilesApplyChanges=Aplicar mudanças
+hostsFilesAutoUpdatePrompt=Auto-atualizar os arquivos hosts.
+hostsFilesUpdateNow=Atualizar agora
+hostsFilesPurgeAll=Limpar todos caches
+hostsFilesExternalListsHint=Uma URL por linha. Linhas pré-fixadas com &lsquo;#&rsquo; irão ser ignoradas. URLs inválidas irão ser silenciosamente ignoradas.
+hostsFilesExternalListsParse=Analisar
+hostsFilesExternalListPurge=limpar cache
+hostsFilesExternalListNew=nova versão disponível
+hostsFilesExternalListObsolete=desatualizado
+rawSettingsWarning=Cuidado! Mude essas configurações por sua própria conta e risco.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Armazenamento usado: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentação</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissões</a>
+aboutCode=Código Fonte (GPLv3)
+aboutIssues=Bugs e problemas
+aboutContributors=Colaboradores
+aboutCodeContributors=Código:
+aboutIssueContributors=Problemas:
+aboutTranslationContributors=Traduções:
+aboutUserDataHeader=Seus dados
+aboutBackupButton=Salvar em arquivo...
+aboutBackupFilename=meu-backup-umatrix.txt
+aboutRestoreButton=Restaurar do arquivo...
+aboutRestoreConfirm=Todas as suas definições irão ser sobrescritas pelos dados salvos em {{time}}, e o uMatrix irá reiniciar.\n\nSubstituir todas a configuração atual usando dados de backup?
+aboutRestoreError=Os dados não podem ser lidos ou são inválidos
+aboutOr=... ou ...
+aboutResetButton=Restaurar as configurações para o padrão
+aboutResetConfirm=Aviso! isso irá remover todas as suas configurações personalizadas. Você está certo que deseja continuar?
+loggerFilterInputPlaceholder=filtrar expressão(ões)
+loggerMaxEntriesTip=Número máximo de entradas
+loggerEntryCookieDeleted=cookie apagado: {{value}}
+loggerEntryDeleteCookieError=falha ao apagar cookie: {{value}}
+loggerEntryBrowserCacheCleared=cache do navegador limpo
+loggerEntryAssetUpdated=recurso atualizado: {{value}}
+mainBlockedPrompt1=uMatrix impediu que a seguinte página fosse carregada:
+mainBlockedPrompt2=Devido à seguinte regra
+mainBlockedBack=Voltar
+mainBlockedClose=Fechar
+commandRevertAll=Remover todas mudanças temporárias
+commandWhitelistPageDomain=Adicionar o domínio à lista branca temporariamente
+commandWhitelistAll=Adicionar tudo à lista branca temporariamente
+commandOpenDashboard=Abrir Painel
+elapsedOneMinuteAgo=há um minuto atrás
+elapsedManyMinutesAgo=há {{value}} minuto(s) atrás
+elapsedOneHourAgo=há uma hora atrás
+elapsedManyHoursAgo=há {{value}} hora(s) atrás
+elapsedOneDayAgo=há um dia atrás
+elapsedManyDaysAgo=há {{value}} dia(s) atrás
+showDashboardButton=Painel
+showLoggerButton=Registro
+cloudPush=Exportar para armazenagem em nuvem
+cloudPull=Importar da armazenagem em nuvem
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Para este dispositivo:
+genericSubmit=Enviar
+genericRevert=Reverter
+errorCantConnectTo=Erro na rede: Não foi possível conectar ao {{url}}
+genericApplyChanges=Aplicar mudanças
diff --git a/locale/pt-PT/messages.properties b/locale/pt-PT/messages.properties
new file mode 100644
index 0000000..85e5d56
--- /dev/null
+++ b/locale/pt-PT/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Painel
+loggerPageName=uMatrix — Logger
+settingsPageName=Definições
+privacyPageName=Privacidade
+statsPageName=Estatísticas
+userRulesPageName=As minhas regras
+ubiquitousRulesPageName=Ficheiros hosts
+rawSettingsPageName=More
+aboutPageName=Sobre
+allPrettyName=tudo
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=imagem
+mediaPrettyName=mídia
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=outro
+matrixNoNetTrafficPrompt=Não foi encontrado tráfego nesta aba até agora.
+matrixMtxButtonTip=Desactiva/Activa filtração matriz neste escopo.\nPedidos bloqueados através da filtração da matriz nesta página: {{count}}.
+matrixPersistButtonTip=Guardar todas as modificações temporárias para este escopo.
+matrixRevertButtonTip=Reverte todas as modificações temporárias para este escopo.
+matrixReloadButton=Recarregar a página.
+matrix1stPartyLabel=1º domínio
+matrixBlacklistedHostnames={{count}} hostname(s) na lista negra
+matrixSwitchNoMixedContent=HTTPS rigoroso
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Falsificação do referrer
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Reverter todas as modificações temporárias
+matrixLoggerMenuEntry=Aceder ao registo de pedidos
+matrixDashboardMenuEntry=Aceder ao painel de controlo
+matrixNoTabFound=Nenhuma página encontrada
+statsPageTitle=uMatrix &ndash; Estatísticas
+statsPageGenericStats=Estatísticas genéricas
+statsPageCookieHeadersFoiled=<a href='https://pt.wikipedia.org/wiki/Cookie_HTTP'>Cookie HTTP</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://pt.wikipedia.org/wiki/HTTP_referrer'>Referer HTTP</a> headers destruídos: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Auditoria de hyperlink</a> tentativas destruídas: {{count}}
+statsPageCookiesRemoved=Cookies locais removidos: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Armazenamento local</a> esvaziado: {{count}}
+statsPageBrowserCacheCleared=Caches do browser limpas: {{count}}
+statsPageDetailedStats=Estatísticas detalhadas
+statsPageDetailedAllPages=Tudo
+statsPageDetailedBehindTheScenePage=Por trás das cenas
+statsPageOverview=Sinopse
+statsPageRequests=Pedidos
+statsPageAllowed=Permitido
+statsPageBlocked=Bloqueado
+statsPageAll=Tudo
+statsPagePages=Páginas
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Imagens
+statsPagePlugins=Plug-ins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Outros
+statsPageDetailed=Registo de pedidos
+statsPageLogSizePrompt1=Lembrar o último
+statsPageLogSizePrompt2=Pedidos HTTP <b>por página</b>.
+statsPageLogSizeHelp=<p>Pode inspecionar os detalhes brutos dos mais recentes pedidos HTTP que foram feitos pela página web (ver em baixo).</p><p>Isto é útil principalmente para utilizadores avançados que querem investigar precisamente o que cada página web tem feito. No entanto, registar esses pedidos HTTP requere memória, e se não se interessa por esta especificação técnica, então memória está a ser consumida.</p><p>Daí este campo, que deixa ajustar o número máximo de pedidos HTTP recentes que são registados para inspecção futura.</p><p>Escreve &ldquo;<code>0</code>&rdquo; para desactivar o registo detalhado (e consequentemente reduzir a pegada da memória do <i>uMatrix</i>).</p>
+statsPageRefresh=Atualizar
+settingsPageTitle=uMatrix &ndash; Definições
+settingsMatrixDisplayHeader=Aparência
+settingsMatrixDisplayTextSizePrompt=Tamanho do texto:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Grande
+settingsMatrixDisplayColorBlind=Modo daltónico
+settingsMatrixConvenienceHeader=Conveniência
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domínio
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=Quando a matriz é fechada, recarrega estas abas inteligentemente:
+settingsMatrixAutoReloadNone=Nenhuma
+settingsMatrixAutoReloadCurrent=Atual
+settingsMatrixAutoReloadAll=Todas
+settingsMatrixAutoReloadInfo=Quando fizer modificações na matriz que podem afectar a exibição e/ou comportamento de uma, ou mais páginas, o <i>uMatrix</i> irá recarregar as páginas afectadas automaticamente quando fechar a matriz.
+settingsSubframeColor=Frames bloqueadas:&ensp;Cor
+settingsSubframeOpacity=Opacidade
+settingsIconBadgeEnabled=Mostra o número de pedidos distintos no ícone
+settingsCollapseBlocked=Colapsar espaço reservado dos elementos bloqueados
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Ativar suporte para armazenamento na nuvem
+privacyPageTitle=uMatrix &ndash; Privacidade
+privacyDeleteBlockedCookiesPrompt=Apagar cookies bloqueados.
+privacyDeleteBlockedCookiesHelp=<p>Cookies na lista negra não são proibídos pelo <i>uMatrix</i> de entrar no seu browser. No entanto, são proibídos de sair do seu browser, que é o que realmente importa. Não proibir cookies de entrarem no seu browser dá-lhe a oportunidade de ser informado que um site tentou usá-los, e poderá inspeccionar os seus conteúdos, se assim o quiser.</p><p>Uma vez que estes cookies da lista negra tenham sido registados pelo <i>uMatrix</i>, podem ser removidos do seu browser, se assim o desejar.</p><p><b>Nota importante:</b> Extensões podem fazer pedidos web durante o seu decurso normal de operação. Estes pedidos podem resultar na criação de cookies no seu browser. Se o domínio de onde o cookie provém estiver na lista negra, este será removido do browser pelo <i>uMatrix</i>, se esta opção estiver seleccionada. Portanto confirme que o nome do(s) site(s) com o(s) quais a extensão comunica não está(ão) na lista negra.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Apagar cookies não bloqueados desta sessão
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutos após a última vez que foram usados.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;Um cookie da sessão ... é apagado quando fecha o browser. O cookie da sessão é guardado em memória temporária e não é mantido após o browser ter sido fechado.&rdquo;</p><p>Excepto que isto <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>poderá não acontecer</a> em alguns browsers. No entanto, para alguns, ter de fechar o browser, para que os cookies da sessão sejam eliminados, pode não ser cedo suficiente.</p>
+privacyDeleteBlockedLocalStoragePrompt=Apagar <a href='https://en.wikipedia.org/wiki/Web_storage'>armazenamento local</a> de conteúdo guardado por domínios bloqueados
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Limpar a cache do navegador a cada
+privacyClearCachePrompt2=minutos.
+privacyClearCacheHelp=<p>Alguns sites estão realmente empenhados em o monitorizar. Tanto, que por vezes usam truques sujos para contornar as medidas que pôs em práctica para não o ser.</p><p>Alguns desses truques dependem<sup style='font-size:smaller'>[1, 2]</sup> da <a href='https://en.wikipedia.org/wiki/Web_cache'>cache do browser</a>, cujo conteúdo costuma durar, pois a maioria dos utilizadores raremente perde tempo a limpá-la regularmente.</p><p>Há pouca inconveniência em limpar a cache do browser (a probabilidade é de nem reparar quando acontece), e o benefício é prevenir esses trackers desagradáveis de invadirem a sua privacidade.</p><p>Seleccione esta opção para que o <i>uMatrix</i> o faça por si, num intervalo de tempo por si especificado.</p><p style='font-size:smaller'>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Prevenir monitoração na web através da cache do browser&rdquo;</a><br>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Falsifica a string do <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> de pedidos de terceiros.
+privacyProcessRefererHelp=<p>Do Wikipedia: </p><blockquote>O HTTP referrer é um campo de cabeçalho HTTP que identifica o endereço da página web que liga ao recurso sendo solicitado. Pela verificação do referer, a nova página web pode ver de onde a requisição se originou. ...<b>Pelo facto desta informação poder violar a privacidade, alguns browsers deixam que o utilizador desactive o envio da informação do referrer. </b></blockquote><p>Se esta opção estiver seleccionada, <i>uMatrix</i> irá falsificar a informação do HTTP referrer, se o nome do domínio do mesmo for parte de um domínio de terceiros, relativamente ao nome do domínio do pedido original.
+privacyNoMixedContentPrompt=HTTPS rigoroso: proíbir conteúdo misto.
+privacyNoMixedContentHelp=<p>Da <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>Se [a] página HTTPS inclui conteúdo obtido através de HTTP regular, então a conexão está apenas encriptada parcialmente: o conteúdo não encriptado está acessível a sniffers e pode ser modificado por ataques man-in-the-middle e, portanto, a conexão não está protegida. Quando uma página web exibe este comportamento, é chamada uma página de conteúdo misto.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Bloqueia todas as tentativas de <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>auditoria de hiperlinks</a>.
+privacyProcessHyperlinkAuditingHelp=<p>Auditoria de hiperlinks é um mecanismo que permite que uma entidade, <b>qualquer entidade</b>, seja informada do link em que um utilizador clicou, numa determinada página web. É essencialmente monitorização: permite que um site, ou terceiros, relativos a esse site, sejam informados do link em que clicou, numa das suas páginas web. O único propósito disto é a monitorização da sua actividade.</p>
+userRulesPermanentHeader=Regras permanentes
+userRulesTemporaryHeader=Regras temporárias
+userRulesRevert=Reverter
+userRulesCommit=Aplicar
+userRulesEdit=Editar
+userRulesEditSave=Guardar
+userRulesEditDicard=Rejeitar
+userRulesImport=Importar do ficheiro...
+userRulesExport=Exportar para ficheiro...
+userRulesFormatHint=Veja esta página para a sintaxe das regras.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Todos os nomes dos hosts num ficheiro hosts são carregados como domínios de lista negra no escopo global.
+hostsFilesStats={{blockedHostnameCount}} sites distintos bloqueados:
+hostsFilesPerFileStats={{used}} utilizados de {{total}}
+hostsFilesLastUpdate=Última update: {{ago}}
+hostsFilesApplyChanges=Aplicar alterações
+hostsFilesAutoUpdatePrompt=Actualiza automaticamente os ficheiros hosts.
+hostsFilesUpdateNow=Actualizar agora
+hostsFilesPurgeAll=Limpar todas as caches
+hostsFilesExternalListsHint=Um URL por linha. Linhas prefixadas com &lsquo;#&rsquo; serão ignoradas. URLs invalidos serão ignorados silenciosamente.
+hostsFilesExternalListsParse=Analisar
+hostsFilesExternalListPurge=limpar cache
+hostsFilesExternalListNew=nova versão disponível
+hostsFilesExternalListObsolete=desactualizada
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Espaço usado: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentação</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissões</a>
+aboutCode=Código fonte (GPLv3)
+aboutIssues=Bugs e problemas
+aboutContributors=Colaboradores
+aboutCodeContributors=Código:
+aboutIssueContributors=Problemas:
+aboutTranslationContributors=Traduções:
+aboutUserDataHeader=Os seus dados
+aboutBackupButton=Guardar cópia de segurança para um ficheiro...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restaurar a partir de um ficheiro...
+aboutRestoreConfirm=Todas as suas definições irão ser substituídas usando a cópia de segurança guardada em {{time}}, e o uMatrix irá-se reiniciar.\n\nSubstituir configuração existente usando dados da cópia de segurança?
+aboutRestoreError=Os dados não puderam ser lidos ou são inválidos
+aboutOr=... ou ...
+aboutResetButton=Repor as definições predefinidas
+aboutResetConfirm=Aviso! isto irá remover todas as sua definições. Tem a certeza que pretende proceder?
+loggerFilterInputPlaceholder=filtrar expressão(ões)
+loggerMaxEntriesTip=Número máximo de entradas
+loggerEntryCookieDeleted=cookie apagado: {{value}}
+loggerEntryDeleteCookieError=falha ao eliminar cookie: {{value}}
+loggerEntryBrowserCacheCleared=cache do browser apagada
+loggerEntryAssetUpdated=recurso actualizado: {{value}}
+mainBlockedPrompt1=uMatrix impediu a seguinte página de ser carregada:
+mainBlockedPrompt2=Devido à seguinte regra
+mainBlockedBack=Voltar
+mainBlockedClose=Fechar
+commandRevertAll=Reverter todas as modificações temporárias
+commandWhitelistPageDomain=Adicionar domínio da página à lista branca temporariamente
+commandWhitelistAll=Adicionar tudo à lista branca temporariamente
+commandOpenDashboard=Abrir painel
+elapsedOneMinuteAgo=há um minuto atrás
+elapsedManyMinutesAgo=há {{value}} minuto(s) atrás
+elapsedOneHourAgo=há uma hora atrás
+elapsedManyHoursAgo=há {{value}} hora(s) atrás
+elapsedOneDayAgo=há um dia atrás
+elapsedManyDaysAgo=há {{value}} dia(s) atrás
+showDashboardButton=Painel
+showLoggerButton=Logger
+cloudPush=Exportar para armazenamento em nuvem
+cloudPull=Importar para armazenamento em nuvem
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Nome deste dispositivo:
+genericSubmit=Submeter
+genericRevert=Desfazer
+errorCantConnectTo=Erro de rede: Incapaz de se conectar a {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/ro/messages.properties b/locale/ro/messages.properties
new file mode 100644
index 0000000..59d6235
--- /dev/null
+++ b/locale/ro/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Panou de control
+loggerPageName=uMatrix — Logger
+settingsPageName=Opțiuni
+privacyPageName=Intimitate
+statsPageName=Statistici
+userRulesPageName=Regulile mele
+ubiquitousRulesPageName=Fișiere de gazde
+rawSettingsPageName=More
+aboutPageName=Despre
+allPrettyName=tot
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=imagine
+mediaPrettyName=media
+pluginPrettyName=modul
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=cadru
+otherPrettyName=altele
+matrixNoNetTrafficPrompt=Deocamdată nu a fost detectat trafic pentru această filă.
+matrixMtxButtonTip=Dezactivează/activează filtrarea matriceală pentru acest context.
+matrixPersistButtonTip=Salvează toate modificările temporare pentru acest context.
+matrixRevertButtonTip=Anulează modificările temporare pentru acest context.
+matrixReloadButton=Reîncarcă pagina.
+matrix1stPartyLabel=sait primar
+matrixBlacklistedHostnames={{count}} gazde blocate
+matrixSwitchNoMixedContent=HTTPS strict
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Alterare referent
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Elimină toate modificările temporare
+matrixLoggerMenuEntry=Mergi la jurnal
+matrixDashboardMenuEntry=Mergi la panoul de control
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistici
+statsPageGenericStats=Statistici generice
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Cookie-uri locale șterse: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Statistici detaliate
+statsPageDetailedAllPages=Tot
+statsPageDetailedBehindTheScenePage=În spatele scenei
+statsPageOverview=Privire de ansamblu
+statsPageRequests=Cereri
+statsPageAllowed=Permis
+statsPageBlocked=Blocat
+statsPageAll=Tot
+statsPagePages=Pagini
+statsPageCookies=Cookie
+statsPageCSS=CSS
+statsPageImages=Imagini
+statsPagePlugins=Module
+statsPageScripts=Scripturi
+statsPageXHRs=XHR
+statsPageFrames=Cadre
+statsPageOthers=Altele
+statsPageDetailed=Jurnal
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=Cereri HTTP <b>per pagină</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Reîncarcă
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Aspect
+settingsMatrixDisplayTextSizePrompt=Dimensiunea textului:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Mare
+settingsMatrixDisplayColorBlind=Accesibil pentru daltonism
+settingsMatrixConvenienceHeader=Comoditate
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=Nimic
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=Tot
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Cadre blocate:&ensp;Culoare
+settingsSubframeOpacity=Opacitate
+settingsIconBadgeEnabled=Arată numărul cererilor distincte pe pictogramă
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Șterge cookie-urile blocate.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Șterge cookie-urile de sesiune neblocate
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=DE FĂCUT
+privacyClearCachePrompt1=Șterge memoria tampon a navigatorului la fiecare
+privacyClearCachePrompt2=minute.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=HTTPS strict: interzice conținut mixt.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Reguli permanente
+userRulesTemporaryHeader=Reguli temporare
+userRulesRevert=Înlătură
+userRulesCommit=Aplică
+userRulesEdit=Modifică
+userRulesEditSave=Salvează
+userRulesEditDicard=Elimină
+userRulesImport=Încarcă din fișier...
+userRulesExport=Exportă în fișier...
+userRulesFormatHint=Vizitează această pagină pentru sintaxa regulilor.
+userRulesDefaultFileName=regulile-mele-umatrix.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Aplică modificările
+hostsFilesAutoUpdatePrompt=Actualizează automat fișierele de gazde.
+hostsFilesUpdateNow=Actualizează acum
+hostsFilesPurgeAll=Șterge tot din memoria tampon
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Analizează
+hostsFilesExternalListPurge=șterge memoria tampon
+hostsFilesExternalListNew=o nouă versiune disponibilă
+hostsFilesExternalListObsolete=învechit
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Jurnalul de modificări</a>
+aboutStorageUsed=Spațiu de stocare utilizat: {{storageUsed}} octeți
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentație</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permisiuni</a>
+aboutCode=Cod sursă (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contribuitori
+aboutCodeContributors=Cod:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Traduceri:
+aboutUserDataHeader=Datele tale
+aboutBackupButton=Salvează în fișier...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... sau ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie șters: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Înapoi
+mainBlockedClose=Închide
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Deschide panoul de control
+elapsedOneMinuteAgo=acum un minut
+elapsedManyMinutesAgo=acum {{value}} minute
+elapsedOneHourAgo=acum o oră
+elapsedManyHoursAgo=acum {{value}} ore
+elapsedOneDayAgo=acum o zi
+elapsedManyDaysAgo=acum {{value}} zile
+showDashboardButton=Panou de control
+showLoggerButton=Jurnal
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/ru/messages.properties b/locale/ru/messages.properties
new file mode 100644
index 0000000..900a8bc
--- /dev/null
+++ b/locale/ru/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Панель управления
+loggerPageName=uMatrix — Журнал сетевых запросов
+settingsPageName=Настройки
+privacyPageName=Приватность
+statsPageName=Статистика
+userRulesPageName=Мои правила
+ubiquitousRulesPageName=Hosts файлы
+rawSettingsPageName=Ещё
+aboutPageName=О расширении
+allPrettyName=все
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=Нет сетевой активности на этой вкладке.
+matrixMtxButtonTip=Откл/вкл фильтрацию запросов для данной области
+matrixPersistButtonTip=Сохранить все временные изменения в этой области.
+matrixRevertButtonTip=Отменить временные изменения для этой области.
+matrixReloadButton=Перезагрузить страницу.\nУдерживайте Shift, чтобы проигнорировать кэш.
+matrix1stPartyLabel=текущий сайт
+matrixBlacklistedHostnames=запрещённые сайты: {{count}}
+matrixSwitchNoMixedContent=Только HTTPS
+matrixSwitchNoWorker=Запретить web workers
+matrixSwitchReferrerSpoof=Подмена referrer
+matrixSwitchNoscriptSpoof=Подмена <code><noscript></code> тегов
+matrixRevertAllEntry=Отменить все временные изменения
+matrixLoggerMenuEntry=Перейти к журналу запросов
+matrixDashboardMenuEntry=Перейти в панель управления
+matrixNoTabFound=Страница не найдена
+statsPageTitle=uMatrix &ndash; Статистика
+statsPageGenericStats=Общая статистика
+statsPageCookieHeadersFoiled=Обработано заголовков <a href='https://ru.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a>: {{count}}
+statsPageRefererHeadersFoiled=Обработано заголовков <a href='https://ru.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a>: {{count}}
+statsPageHyperlinkAuditingFoiled=Обработано попыток <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>аудита гиперссылок</a>: {{count}}
+statsPageCookiesRemoved=Удалено файлов cookie: {{count}}
+statsPageLocalStoragesCleared=Очищено <a href='http://diveintohtml5.info/storage.html'>локальных хранилищ</a>: {{count}}
+statsPageBrowserCacheCleared=Очищено кэшей браузера: {{count}}
+statsPageDetailedStats=Подробная статистика
+statsPageDetailedAllPages=Все
+statsPageDetailedBehindTheScenePage=Скрытые запросы
+statsPageOverview=Общие сведения
+statsPageRequests=Запросы
+statsPageAllowed=Принято
+statsPageBlocked=Отклонено
+statsPageAll=Всего
+statsPagePages=Веб-страницы
+statsPageCookies=Файлы cookie
+statsPageCSS=CSS
+statsPageImages=Изображения
+statsPagePlugins=Плагины
+statsPageScripts=Скрипты
+statsPageXHRs=XHR
+statsPageFrames=Фреймы
+statsPageOthers=Прочее
+statsPageDetailed=Журнал запросов
+statsPageLogSizePrompt1=Запоминать последние
+statsPageLogSizePrompt2=HTTP запросов <b>на страницу</b>.
+statsPageLogSizeHelp=<p>Вы можете детально изучить последние необработанные HTTP запросы, которые были сделаны веб-страницей (см. ниже).</p><p>Это особенно полезно для продвинутых пользователей, которые хотят узнать, что конкретно делала веб-страница. Однако журналирование этих HTTP запросов увеличивает потребление памяти, и если вас не интересует эта техническая информация, то это - трата памяти.</p><p>Это поле позволяет вам настроить максимальное число последних HTTP запросов, которые будут храниться в журнале для последующего изучения.</p><p>Введите &ldquo;<code>0</code>&rdquo; чтобы отключить журналирование (и соответственно уменьшить потребление памяти <i>uMatrix</i>).</p>
+statsPageRefresh=Обновить
+settingsPageTitle=uMatrix &ndash; Настройки
+settingsMatrixDisplayHeader=Матрица
+settingsMatrixDisplayTextSizePrompt=Размер текста:
+settingsMatrixDisplayTextSizeNormal=Средний
+settingsMatrixDisplayTextSizeLarge=Крупный
+settingsMatrixDisplayColorBlind=Цветовая схема для дальтоников
+settingsMatrixConvenienceHeader=Удобство
+settingsDefaultScopeLevel=Область применения по умолчанию:
+settingsDefaultScopeLevel0=Глобально
+settingsDefaultScopeLevel1=Домен
+settingsDefaultScopeLevel2=Сайт
+settingsMatrixAutoReloadPrompt=Когда матрица закроется, обновить эти вкладки:
+settingsMatrixAutoReloadNone=(Пусто)
+settingsMatrixAutoReloadCurrent=Текущая версия
+settingsMatrixAutoReloadAll=Все
+settingsMatrixAutoReloadInfo=Когда вы вносите изменения в матрицу запросов, которые могут повлиять на отображение и/или поведение одной или нескольких страниц, <i>uMatrix</i> перезагрузит эти страницы автоматически, когда вы закроете панель.
+settingsSubframeColor=Заблокированные фреймы:&ensp;Цвет
+settingsSubframeOpacity=Непрозрачность
+settingsIconBadgeEnabled=Показывать число отдельных запросов на иконке
+settingsCollapseBlocked=Скрывать оставшееся от заблокированных элементов место
+settingsCollapseBlacklisted=Скрывать оставшееся место от элементов из черного списка
+settingsNoscriptTagsSpoofed=Подменять <code><noscript></code> теги, когда скрипты главного домена заблокированы
+settingsCloudStorageEnabled=Включить поддержку облачного хранилища
+privacyPageTitle=uMatrix &ndash; Приватность
+privacyDeleteBlockedCookiesPrompt=Удалять заблокированные куки.
+privacyDeleteBlockedCookiesHelp=<p>Запрещенные куки не блокируются <i>uMatrix</i> при входе в ваш браузер. Однако их передача из браузера предотвращается - вот что действительно важно. Неблокировка куки файлов до того, как они сохраняются браузером дает вам возможность узнать, что сайт пытался использовать куки, и потом изучить их содержимое, если вы хотите.</p><p>Как только эти запрещенные куки составлены <i>uMatrix</i>, они могут быть удалены из браузера, по вашему желанию.</p><p><b>Важное замечание:</b> Расширения могут осуществлять веб-запросы во время своей обычной работы. Эти запросы могут порождать создание куки файлов в браузере. Если имя хоста (сайта), от которого создается куки не в белом списке, то <i>uMatrix</i> удалит эти куки из браузера, если эта опция включена. Поэтому убедитесь, что имена хостов (сайтов) с которыми соединяется расширение добавлено в белый список.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Удалять незаблокированные сессионные куки через
+privacyDeleteNonBlockedSessionCookiesPrompt2= минут после последнего использования.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;Сеансовые файлы cookie... удаляются при завершении сеанса браузера. Они хранятся во временной памяти и не сохраняются после закрытия браузера.&rdquo;</p><p>Однако в некоторых браузерах этого <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>может не произойти</a>. Кроме того, может возникнуть потребность в удалении сеансовых файлов cookie до закрытия браузера.</p>
+privacyDeleteBlockedLocalStoragePrompt=Удалять содержимое <a href='https://ru.wikipedia.org/wiki/Web_storage'>локального хранилища</a> заблокированных сайтов.
+privacyDeleteBlockedLocalStorageHelp=Планируется сделать
+privacyClearCachePrompt1=Очищать кэш браузера каждые
+privacyClearCachePrompt2=минут.
+privacyClearCacheHelp=<p>Некоторые сайты идут на всевозможные ухищрения для того, чтобы обойти любые меры, предпринятые для защиты от отслеживания.</p><p>Некоторые ухищрения основаны<sup>[1, 2]</sup> на использовании <a href='https://en.wikipedia.org/wiki/Web_cache'>кэша браузера</a>, содержимое которого зачастую хранится длительное время, потому что пользователи нерегулярно его очищают.</p><p>Регулярная очистка кэша предотвращает отслеживание, однако делать это вручную не всегда удобно.</p><p>Включение данной опции позволит <i>uMatrix</i> делать это за вас с заданным интервалом времени.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Подменять строку <a href='https://ru.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> в запросах к сторонним источникам.
+privacyProcessRefererHelp=Материал из Википедии:<blockquote>HTTP referer — это HTTP заголовок, который указывает адрес страницы, содержащей ссылку на запрошенный ресурс. ... <b>Поскольку эта информация может нарушать конфиденциальность, некоторые браузеры позволяют запретить её передачу.</b></blockquote>Если эта опция включена, <i>uMatrix</i> будет подменять HTTP referer, если сайт, указанный в HTTP referer, является сторонним по отношению к запрашиваемому ресурсу.
+privacyNoMixedContentPrompt=Только HTTPS: запретить смешанное содержимое.
+privacyNoMixedContentHelp=<p>Материал из <a href='https://developer.mozilla.org/ru-RU/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>Если страница, переданная по HTTPS, содержит контент, передаваемый по обычному, открытому HTTP, соединение считается частично зашифрованным: то, что передаётся по HTTP, можно перехватить и изменить, вследствие чего соединение уже не считается защищённым. Такие страницы называются <em>страницами со смешанным содержимым</em>.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Блокировать все попытки <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>аудита гиперссылок</a>.
+privacyProcessHyperlinkAuditingHelp=<p>Проверка гиперссылок — это механизм, позволяющий <b>любой стороне</b> получать информацию о том, по каким ссылкам переходил пользователь на каждой конкретной веб-странице. Единственная цель — отслеживание вашей интернет-активности.</p>
+userRulesPermanentHeader=Постоянные правила
+userRulesTemporaryHeader=Временные правила
+userRulesRevert=Вернуть
+userRulesCommit=Утвердить
+userRulesEdit=Изменить
+userRulesEditSave=Сохранить
+userRulesEditDicard=Сбросить
+userRulesImport=Импорт из файла...
+userRulesExport=Экспорт в файл...
+userRulesFormatHint=Синтаксис правил можно посмотреть на этой странице.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Каждый список содержит перечень сайтов, запрещённых на глобальном уровне.
+hostsFilesStats={{blockedHostnameCount}} отдельных хостов заблокировано из:
+hostsFilesPerFileStats={{used}} использовано из {{total}}
+hostsFilesLastUpdate=Последнее обновление: {{ago}}
+hostsFilesApplyChanges=Применить изменения
+hostsFilesAutoUpdatePrompt=Автообновление списков сайтов.
+hostsFilesUpdateNow=Обновить сейчас
+hostsFilesPurgeAll=Очистить все кэши
+hostsFilesExternalListsHint=Один URL на строку. Строки, начинающиеся с символа &lsquo;#&rsquo;, будут проигнорированы. Некорректные ссылки будут пропущены.
+hostsFilesExternalListsParse=Обработать
+hostsFilesExternalListPurge=очистить кэш
+hostsFilesExternalListNew=доступна новая версия
+hostsFilesExternalListObsolete=список устарел
+rawSettingsWarning=Внимание! Изменяйте эти настройки конфигурации на свой страх и риск.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Список изменений</a>
+aboutStorageUsed=Локальное хранилище: {{storageUsed}} байт
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Документация</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Разрешения</a>
+aboutCode=Исходный код (GPLv3)
+aboutIssues=Баги и проблемы
+aboutContributors=Участники
+aboutCodeContributors=Код:
+aboutIssueContributors=Проблемы:
+aboutTranslationContributors=Переводы:
+aboutUserDataHeader=Ваши данные
+aboutBackupButton=Сделать резервный файл...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Восстановить из файла...
+aboutRestoreConfirm=Все ваши настройки будут перезаписаны резервными данными от {{time}}, и uMatrix перезагрузится.\n\nИзменить все настройки, используя эту резервную копию?
+aboutRestoreError=Данные не могут быть прочитаны или содержат ошибки
+aboutOr=... или ...
+aboutResetButton=Сброс до настроек по умолчанию
+aboutResetConfirm=Внимание! Это сбросит все ваши ручные настройки. Вы уверены, что хотите продолжить?
+loggerFilterInputPlaceholder=фильтровать выражение(я)
+loggerMaxEntriesTip=Максимальное число записей
+loggerEntryCookieDeleted=куки удален: {{value}}
+loggerEntryDeleteCookieError=не удалось удалить куки: {{value}}
+loggerEntryBrowserCacheCleared=кэш браузера очищен
+loggerEntryAssetUpdated=актив обновлен: {{value}}
+mainBlockedPrompt1=uMatrix предотвратил загрузку следующей страницы:
+mainBlockedPrompt2=Из-за следующего правила
+mainBlockedBack=Назад
+mainBlockedClose=Закрыть
+commandRevertAll=Отменить все временные изменения
+commandWhitelistPageDomain=Временно разрешить загрузку страницы
+commandWhitelistAll=Временно добавить все в белый список
+commandOpenDashboard=Открыть панель управления
+elapsedOneMinuteAgo=минуту назад
+elapsedManyMinutesAgo={{value}} минут назад
+elapsedOneHourAgo=час назад
+elapsedManyHoursAgo={{value}} часов назад
+elapsedOneDayAgo=день назад
+elapsedManyDaysAgo={{value}} дней назад
+showDashboardButton=Панель управления
+showLoggerButton=Журнал запросов
+cloudPush=Экспорт в облачное хранилище
+cloudPull=Импорт из облачного хранилища
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Имя этого устройства:
+genericSubmit=Принять
+genericRevert=Вернуть
+errorCantConnectTo=Ошибка сети: Не удается соединиться с {{url}}
+genericApplyChanges=Применить изменения
diff --git a/locale/sk/messages.properties b/locale/sk/messages.properties
new file mode 100644
index 0000000..060ae58
--- /dev/null
+++ b/locale/sk/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Settings
+privacyPageName=Privacy
+statsPageName=Statistics
+userRulesPageName=My rules
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=About
+allPrettyName=all
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/sl/messages.properties b/locale/sl/messages.properties
new file mode 100644
index 0000000..a926471
--- /dev/null
+++ b/locale/sl/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Nadzorna plošča
+loggerPageName=uMatrix — Logger
+settingsPageName=Nastavitve
+privacyPageName=Zasebnost
+statsPageName=Statistika
+userRulesPageName=Moja pravila
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=O razširitvi
+allPrettyName=vse
+cookiePrettyName=piškotek
+cssPrettyName=css
+imagePrettyName=slika
+mediaPrettyName=media
+pluginPrettyName=vtičnik
+scriptPrettyName=skript
+xhrPrettyName=XHR
+framePrettyName=okvir
+otherPrettyName=ostalo
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Strog HTTPS
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=Vse
+statsPagePages=Strani
+statsPageCookies=Piškotki
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/sr/messages.properties b/locale/sr/messages.properties
new file mode 100644
index 0000000..ac80ae4
--- /dev/null
+++ b/locale/sr/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Контролна табла
+loggerPageName=uMatrix — Евиденција
+settingsPageName=Подешавања
+privacyPageName=Приватност
+statsPageName=Статистика
+userRulesPageName=Моја правила
+ubiquitousRulesPageName=Хост датотеке
+rawSettingsPageName=Више
+aboutPageName=О програму
+allPrettyName=све
+cookiePrettyName=колачић
+cssPrettyName=css
+imagePrettyName=слика
+mediaPrettyName=медији
+pluginPrettyName=прикључак
+scriptPrettyName=скрипта
+xhrPrettyName=XHR
+framePrettyName=оквир
+otherPrettyName=остало
+matrixNoNetTrafficPrompt=Нема мрежних активности на овој картици.
+matrixMtxButtonTip=Омогући/онемогући матрично филтрирање за ову област.
+matrixPersistButtonTip=Сачувај све привремене измене за ову област.
+matrixRevertButtonTip=Врати привремене измене за ову област.
+matrixReloadButton=Поново учитај страницу.
+matrix1stPartyLabel=Тренутни домен
+matrixBlacklistedHostnames=забрањених сајтова: {{count}}
+matrixSwitchNoMixedContent=Забрани мешовити садржај
+matrixSwitchNoWorker=Забрани веб радне скрипте
+matrixSwitchReferrerSpoof=Referrer лажирање
+matrixSwitchNoscriptSpoof=Лажирање <code><noscript></code> тагова
+matrixRevertAllEntry=Врати све привремене измене
+matrixLoggerMenuEntry=Иди на евиденцију
+matrixDashboardMenuEntry=Иди на контролну таблу
+matrixNoTabFound=Ниједна веб страница није пронађена
+statsPageTitle=uMatrix &ndash; Статистика
+statsPageGenericStats=Општа статистика
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> блокираних заглавља: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> блокираних заглавља: {{count}}
+statsPageHyperlinkAuditingFoiled=Блокираних покушаја <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>надгледања хипервеза</a>: {{count}}
+statsPageCookiesRemoved=Уклоњено локалних колачића: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Локалних складишта</a> испражњено: {{count}}
+statsPageBrowserCacheCleared=Очишћено кеша прегледача: {{count}}
+statsPageDetailedStats=Детаљна статистика
+statsPageDetailedAllPages=Све
+statsPageDetailedBehindTheScenePage=Скривени захтеви
+statsPageOverview=Преглед
+statsPageRequests=Захтеви
+statsPageAllowed=Дозвољено
+statsPageBlocked=Блокирано
+statsPageAll=Све
+statsPagePages=Странице
+statsPageCookies=Колачићи
+statsPageCSS=CSS
+statsPageImages=Слике
+statsPagePlugins=Прикључци
+statsPageScripts=Скрипте
+statsPageXHRs=XHR
+statsPageFrames=Оквири
+statsPageOthers=Остало
+statsPageDetailed=Евиденција
+statsPageLogSizePrompt1=Запамти последње
+statsPageLogSizePrompt2=HTTP захтеви <b>по страници</b>.
+statsPageLogSizeHelp=<p>Можете прегледати детаље најновијих HTTP захтева креираних од стране веб странице (видети испод).</p><p>Ово је нарочито корисно напредним корисницима који желе да сазнају шта је тачно страница радила. Али за евидентирање ових HTTP захтева потребна је меморија, па ако вам није битна ова техничка информација, то значи повећање заузећа меморије.</p><p>Дакле, ово поље вам омогућава да подесите максимални број најновијих HTTP захтева које треба да се евидентирају ради даљег испитивања.</p><p>Унесите &ldquo;<code>0</code>&rdquo; за искључивање детаљног евидентирања (и смањења заузећа меморије од стране додатка <i>uMatrix</i>).</p>
+statsPageRefresh=Освежи
+settingsPageTitle=uMatrix &ndash; Подешавања
+settingsMatrixDisplayHeader=Изглед
+settingsMatrixDisplayTextSizePrompt=Величина текста:
+settingsMatrixDisplayTextSizeNormal=Нормалан
+settingsMatrixDisplayTextSizeLarge=Велики
+settingsMatrixDisplayColorBlind=Шема боја за далтонисте
+settingsMatrixConvenienceHeader=Погодност
+settingsDefaultScopeLevel=Подразумевани ниво опсега:
+settingsDefaultScopeLevel0=Глобално
+settingsDefaultScopeLevel1=Домен
+settingsDefaultScopeLevel2=Сајт
+settingsMatrixAutoReloadPrompt=Приликом затварања матрице, паметно обнови ове картице:
+settingsMatrixAutoReloadNone=Ниједан
+settingsMatrixAutoReloadCurrent=Тренутни
+settingsMatrixAutoReloadAll=Све
+settingsMatrixAutoReloadInfo=Када год начините измене у матрици које могу утицати на приказ и/или понашање једне или више страница, <i>uMatrix</i> ће аутоматски поново учитати погођене странице када будете затворили матрицу.
+settingsSubframeColor=Блокираних оквира:&ensp;Боја
+settingsSubframeOpacity=Непрозирност
+settingsIconBadgeEnabled=Прикажи број појединачних захтева на иконици
+settingsCollapseBlocked=Скупи држач блокираних елемената
+settingsCollapseBlacklisted=Скупи држач елемената на црној листи
+settingsNoscriptTagsSpoofed=Лажирај <code><noscript></code> тагове када су скрипте прве стране блокиране
+settingsCloudStorageEnabled=Омогући подршку складиштења у облаку
+privacyPageTitle=uMatrix &ndash; Приватност
+privacyDeleteBlockedCookiesPrompt=Избриши блокиране колачиће.
+privacyDeleteBlockedCookiesHelp=<p><i>uMatrix</i> не спречава улазак блокираних колачића у ваш прегледач али их спречава да га напусте и то је оно што је у ствари важно. Неблокирање колачића пре него што уђу у прегледач вам даје могућност да будете упознати са тим да сајт покушава да користи колачиће, као и да испитујете њихов садржај, уколико то желите.</p><p>Чим <i>uMatrix</i> обрачуна ове колачиће, можете их уклонити ако то желите.</p><p><b>Важна напомена</b>Проширења могу креирати веб захтеве током свог нормалног рада. Ови захтеви могу довести до креирања колачића у прегледачу. Ако сајт одакле потиче колачић није на белој листи, онда ће <i>uMatrix</i> уклонити колачић из прегледача ако је ова опција омогућена. Зато будите сигурни да сајт са којим проширење комуницира буде на белој листи.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Избриши неблокиране колачиће сесије
+privacyDeleteNonBlockedSessionCookiesPrompt2= минута након последњег коришћења.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;Колачић сесије ... се брише када завршите сесију прегледача. Колачић сесије се чува у привременој меморији и не задржава се после затварања прегледача.&rdquo;</p><p>С тим да се ово<a href='https://code.google.com/p/chromium/issues/detail?id=128513'>можда неће десити</a> у појединим прегледачима. Такође, затварање прегледача због брисања колачића сесије можда неће бити довољно брзо.</p>
+privacyDeleteBlockedLocalStoragePrompt=Избриши садржај <a href='https://ru.wikipedia.org/wiki/Web_storage'>локалног складишта</a> блокираних сајтова
+privacyDeleteBlockedLocalStorageHelp=Листа задатака
+privacyClearCachePrompt1=Очисти кеш прегледача сваких
+privacyClearCachePrompt2=минута.
+privacyClearCacheHelp=<p>Неки веб сајтови су стварно решени да вас прате, толико да ће користити не баш лепе трикове да би заобишли све мере које предузимате да бисте избегли праћење.</p><p>Неко од ових трикова се заснивају<sup style='font-size:smaller'>[1, 2]</sup>на<a href='https://en.wikipedia.org/wiki/Web_cache'>кешу прегледача</a>,чији је садржај често дуготрајан, пошто корисници ретко одвајају мало времена да редовно чисте кеш прегледача.</p><p>Мало је непријатно редовно чистити кеш прегледача, а корист се огледа у томе да се спрече ови одвратни трагачи да упадају у вашу приватност.</p><p>Омогућите ову опцију да <i>uMatrix</i> то ради уместо вас, у временском интервалу који желите.</p><p style='font-size:smaller'>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Спречавање веб праћења преко кеша прегледача&rdquo;</a><br>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Лажирај низ <a href='https://ru.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> за захтеве трећих страна.
+privacyProcessRefererHelp=<p>Са Википедије</p><blockquote>HTTP referer је HTTP заглавље које идентификује адресу веб странице који је повезан са извором који се тражи. ... <b>Пошто ове информације могу нарушити приватност, неки прегледачи дозвољавају корисницима да онемогуће слање ових информација.</b></blockquote><p>Ако је ова опција омогућена, <i>uMatrix</i> ће лажирати информацију ако је сајт, наведен у HTTP referrer, трећа страна у односу на захтевани.
+privacyNoMixedContentPrompt=Само HTTPS: забрани мешани садржај.
+privacyNoMixedContentHelp=<p>Са<a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>Ако [a] HTTPS страница укључује садржај који се преузима преко редовног,отвореног HTTP, веза је делимично кодирана: некодираном садржају се може приступити и може бити мењан, тако да веза више није безбедна. Таква веб страница се зове страница са мешаним садржајем.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Блокирај све покушаје <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>надгледања хипервеза</a>.
+privacyProcessHyperlinkAuditingHelp=<p>Надгледање хипервеза је механизам који дозвољава страни, <b>било којој страни</b>, да се информише о томе коју везу је корисник отворио на одређеној страници. То је у суштини функција праћења: дозвољава веб сајту или некој трећој страни да се информише о томе коју везу сте отворили на којој веб страници. Једина сврха је праћење активности корисника.</p>
+userRulesPermanentHeader=Трајна правила
+userRulesTemporaryHeader=Привремена правила
+userRulesRevert=Врати
+userRulesCommit=Потврди
+userRulesEdit=Уреди
+userRulesEditSave=Сачувај
+userRulesEditDicard=Одбаци
+userRulesImport=Увези из датотеке...
+userRulesExport=Извези као датотеку...
+userRulesFormatHint=Правила синтаксе можете погледати на овој страници.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Сви сајтови у хост датотеци су учитани као блокирани на глобалном нивоу.
+hostsFilesStats={{blockedHostnameCount}} појединачних блокираних хостова из:
+hostsFilesPerFileStats={{used}} искоришћено од {{total}}
+hostsFilesLastUpdate=Последње ажурирање: {{ago}}
+hostsFilesApplyChanges=Примени измене
+hostsFilesAutoUpdatePrompt=Аутоматски обнови хост датотеке.
+hostsFilesUpdateNow=Ажурирај сада
+hostsFilesPurgeAll=Очисти сав кеш
+hostsFilesExternalListsHint=Један URL по линији. Линије које почињу симболом &lsquo;#&rsquo; ће бити игнорисане. Неважеће адресе ће бити тихо игнорисане.
+hostsFilesExternalListsParse=Анализирај
+hostsFilesExternalListPurge=очисти кеш
+hostsFilesExternalListNew=доступна нова верзија
+hostsFilesExternalListObsolete=застарело
+rawSettingsWarning=Упозорење! Мењате ова конфигурацијска подешавања на сопствени ризик.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Списак измена</a>
+aboutStorageUsed=Искоришћеност складишта: {{storageUsed}} бајтова
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Документација</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Дозволе</a>
+aboutCode=Изворни код (GPLv3)
+aboutIssues=Грешке и проблеми
+aboutContributors=Сарадници
+aboutCodeContributors=Код:
+aboutIssueContributors=Проблеми:
+aboutTranslationContributors=Преводи:
+aboutUserDataHeader=Ваши подаци
+aboutBackupButton=Сачувај резервну копију...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Обнови из датотеке...
+aboutRestoreConfirm=Сва ваша подешавања ће бити замењена резервном копијом од {{time}}, и uMatrix ће бити поново покренут.\n\nЗаменити сва постојећа подешавања резервном копијом?
+aboutRestoreError=Подаци се не могу прочитати или су неважећи
+aboutOr=... или ...
+aboutResetButton=Врати на подразумевана подешавања
+aboutResetConfirm=Пажња! Ово ће уклонити ваша прилагођена подешавања. Да ли сте сигурни да желите да наставите?
+loggerFilterInputPlaceholder=филтрирање израза
+loggerMaxEntriesTip=Највећи број уноса
+loggerEntryCookieDeleted=колачића избрисано: {{value}}
+loggerEntryDeleteCookieError=неуспелих брисања колачића: {{value}}
+loggerEntryBrowserCacheCleared=кеш прегледача очишћен
+loggerEntryAssetUpdated=ажурираних средстава: {{value}}
+mainBlockedPrompt1=uMatrix је спречио учитавање следеће странице:
+mainBlockedPrompt2=Због следећег правила
+mainBlockedBack=Назад
+mainBlockedClose=Затвори
+commandRevertAll=Врати све привремене измене
+commandWhitelistPageDomain=Привремено дозволи домен странице
+commandWhitelistAll=Привремено дозволи све
+commandOpenDashboard=Отвори контролну таблу
+elapsedOneMinuteAgo=пре једног минута
+elapsedManyMinutesAgo=пре {{value}} минута
+elapsedOneHourAgo=пре сат времена
+elapsedManyHoursAgo=пре {{value}} сата/и
+elapsedOneDayAgo=пре једног дана
+elapsedManyDaysAgo=пре {{value}} дана
+showDashboardButton=Контролна табла
+showLoggerButton=Евиденција
+cloudPush=Извези у складиште у облаку
+cloudPull=Увези из складишта у облаку
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Име овог уређаја:
+genericSubmit=Проследи
+genericRevert=Врати
+errorCantConnectTo=Грешка на мрежи: Ни може се повезати на {{url}}
+genericApplyChanges=Примени промене
diff --git a/locale/sv/messages.properties b/locale/sv/messages.properties
new file mode 100644
index 0000000..67173d9
--- /dev/null
+++ b/locale/sv/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Översiktspanel
+loggerPageName=uMatrix — Logger
+settingsPageName=Inställningar
+privacyPageName=Integritet
+statsPageName=Statistik
+userRulesPageName=Mina regler
+ubiquitousRulesPageName=Värdnamnsfiler
+rawSettingsPageName=More
+aboutPageName=Om
+allPrettyName=alla
+cookiePrettyName=kakor
+cssPrettyName=css
+imagePrettyName=bild
+mediaPrettyName=medium
+pluginPrettyName=tillägg
+scriptPrettyName=skript
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=övrigt
+matrixNoNetTrafficPrompt=Ingen nättrafik har hittills upptäckts för den här fliken.
+matrixMtxButtonTip=Stäng av/sätt på matrisfiltrering i detta omfång.
+matrixPersistButtonTip=Spara tillfälliga ändringar för detta omfång.
+matrixRevertButtonTip=Återställ tillfälliga ändringar i detta omfång.
+matrixReloadButton=Ladda om sidan.\nHåll ner Skift tangenten för att kringgå webbläsarens cache.
+matrix1stPartyLabel=Förstapart
+matrixBlacklistedHostnames={{count}} svartlistade värdnamn
+matrixSwitchNoMixedContent=Strikt HTTPS
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Hänvisningsförvanskning
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Återställ alla tillfälliga ändringar
+matrixLoggerMenuEntry=Visa logg
+matrixDashboardMenuEntry=Visa översiktspanel
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistik
+statsPageGenericStats=Generell statistik
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP-kaksidhuvuden</a> förhindrade: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP-referenssidhuvuden</a> förhindrade: {{count}}
+statsPageHyperlinkAuditingFoiled=Försök till <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>granskning av hyperlänkar</a> förhindrade: {{count}}
+statsPageCookiesRemoved=Lokala kakor borttagna: {{count}}
+statsPageLocalStoragesCleared=Tömda <a href='http://diveintohtml5.info/storage.html'>lokala lagringar</a>: {{count}}
+statsPageBrowserCacheCleared=Cacherensningar i webläsaren: {{count}}
+statsPageDetailedStats=Detaljerad statistik
+statsPageDetailedAllPages=Alla
+statsPageDetailedBehindTheScenePage=Bakom kulissen
+statsPageOverview=Översikt
+statsPageRequests=Förfrågningar
+statsPageAllowed=Tillåtna
+statsPageBlocked=Blockerade
+statsPageAll=Alla
+statsPagePages=Sidor
+statsPageCookies=Kakor
+statsPageCSS=CSS
+statsPageImages=Bilder
+statsPagePlugins=Tillägg
+statsPageScripts=Skript
+statsPageXHRs=XHR:er
+statsPageFrames=Frames
+statsPageOthers=Övriga
+statsPageDetailed=Logg
+statsPageLogSizePrompt1=Kom ihåg sista
+statsPageLogSizePrompt2=HTTP-förfrågningar <b>per sida</b>.
+statsPageLogSizeHelp=<p>Du kan inspektera de senaste råa HTTP-frågningarna gjorda från en sida (se nedan).</p><p>Det är mest till för erfarna användare som vill undersöka exakt vad webbsidan har gjort. Loggningen kräver dock plats och om du inte bryr dig om tekniska detaljer så kommer det slösa på minnet.</p><p>Därför finns detta fält som låter dig justera hur många av de senaste HTTP-förfrågningarna som som loggas.</p><p>Ange &ldquo;<code>0</code>&rdquo; för att stänga av detaljerad loggning (och således minska minneskravet från <i>uMatrix</i>).</p>
+statsPageRefresh=Uppdatera
+settingsPageTitle=uMatrix &ndash; Inställningar
+settingsMatrixDisplayHeader=Utseende
+settingsMatrixDisplayTextSizePrompt=Textstorlek:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Stor
+settingsMatrixDisplayColorBlind=Färgblindsanpassning
+settingsMatrixConvenienceHeader=Bekvämlighet
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=När matrisen är stängd, ladda om dessa flikar intelligent:
+settingsMatrixAutoReloadNone=Inga
+settingsMatrixAutoReloadCurrent=Aktuell
+settingsMatrixAutoReloadAll=Alla
+settingsMatrixAutoReloadInfo=När du gör ändringar i matrisen som påverkar hur andra sidor ser ut eller beter sig kommer <i>uMatrix</i> ladda om de berörda sidorna automatiskt när du stänger matrisen.
+settingsSubframeColor=Blockerade frames:&ensp;Färg
+settingsSubframeOpacity=Genomskinlighet
+settingsIconBadgeEnabled=Visa antalet distinkta förfrågningar i ikonen
+settingsCollapseBlocked=Kollapsa blockerade objekt
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Stöd för lagring i molnet
+privacyPageTitle=uMatrix &ndash; Integritet
+privacyDeleteBlockedCookiesPrompt=Ta bort blockerade kakor.
+privacyDeleteBlockedCookiesHelp=<p>Svartlistade kakor hindras inte från att hamna i din webbläsare av <i>uMatrix</i>. De är dock skyddade från att lämna webbläsaren, vilket är det som räknas. Genom att inte blockera kakor från att hamna i din webbläsare får du möjligheten att se om en sida försökt att använda sig av kakor och du kan undersöka kakornas innehåll.</p><p>När kakorna har passerat <i>uMatrix</i>, kan de raderas om du så önskar.</p><p><b>Viktigt:</b> Webbläsartillägg kan göra förfrågningar till webben medan de arbetar. Dessa förfrågningar kan resultera i att kakor skapas i webbläsaren. Om värdnamnet för vilken en kaka kommer ifrån inte är vitlistat kommer kakan att tas bort av <i>uMatrix</i> om det är så inställt. Därför är det viktigt att kontrollera att värdnamnen som tillägg använder för att kommunicera är vitlistade.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Ta bort icke-blockerade sessionskakor
+privacyDeleteNonBlockedSessionCookiesPrompt2= minuter efter att de senast användes.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;En sessionskaka ... raderas efter webbläsarens session avslutas. Sessionskakan lagras i ett temporärt minne och sparas inte när webbläsaren stängs.&rdquo;</p><p>Förutom att det <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>kanske inte alltid fungerar så</a> i vissa webbläsare. För vissa personer är det inte tidigt nog att rensa kakorna när webbläsaren stängs.</p>
+privacyDeleteBlockedLocalStoragePrompt=Radera <a href='https://en.wikipedia.org/wiki/Web_storage'>lokala lagringar</a> för blockerade värdar
+privacyDeleteBlockedLocalStorageHelp=ATT GÖRA
+privacyClearCachePrompt1=Rensa webbläsarens cache var
+privacyClearCachePrompt2=minut.
+privacyClearCacheHelp=<p>Vissa webbplatser är fast beslutna att spåra dig, till den grad att de använda osjysta metoder för att ta sig runt dina försök att inte bli spårad.</p><p>Några av dessa metoder<sup style='font-size:smaller'>[1, 2]</sup> förlitar sig på <a href='https://en.wikipedia.org/wiki/Web_cache'>webbläsarcachen</a>, vars innehåll ofta är långvarigt eftersom användare sällar tar sig tiden att regelbundet rensa deras cache.</p><p>Det finns knappt några nackdelar med att rensa cachet i webbläsar regelbundet (troligen märker du inte ens när det sker) och fördelen är att du förhindrar att dessa irriterande förföljare kränker din integritet.</p><p>Kryssa i detta alternativ för att låta <i>uMatrix</i> göra detta åt dig, i det intervall du ställer in.</p><p style='font-size:smaller'>[1]<a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a><br>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Förvanska <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP-hänvisning</a> för tredjepartsförfrågningar.
+privacyProcessRefererHelp=<p>Översatt från Wikipedia:</p><blockquote>En HTTP-hänvisning (s.k. referer) är den delen i ett HTTP-sidhuvud som identifierar adressen på den sida som länkade till den nuvarande sidan som begärs. ... <b>Eftersom hänvisningen kan inskränka på integriteten tillåter vissa webbläsare användare att låta bli att skicka hänvisningsinformationen.</b></blockquote><p>Om inställningen är påslagen kommer <i>uMatrix</i> att lura domänen med en annan HTTP-hänvisningnsinformation i sidhuvudet, om det gäller en tredje partsdomän till domänen som gjort nätförfrågan.
+privacyNoMixedContentPrompt=Strikt HTTPS: förbjud blandat innehåll.
+privacyNoMixedContentHelp=<p>Översatt från <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>Om en HTTPS-sida innehåller material som hämtas med oskyddad HTTP, kommer anslutningen endast vara delvis krypterad: det okrypterade materialet kan användas av nätverksanalyseringsprogram och ändras av en okänd mellanhand. Därför är inte anslutningen längre säker. När en webbsida har detta beteende, kallas det för en sida med blandat innehåll.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Blockera alla försök till <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>granskning av hyperlänkar</a>.
+privacyProcessHyperlinkAuditingHelp=<p>Granskning av hyperlänkar är en mekanism som tillåter en part, <b>vilken som helst</b>, att få tillgång till information om en användare har klickat på en särskild webbplats. Det är praktiskt taget en spårningsfunktion: det möjliggör för en websida, eller en tredjepart till den sidan, att få information om vilken sida du var på och vilken länk du har tryckt på. Syftet är endast att kartlägga ditt beteende när du surfar.</p>
+userRulesPermanentHeader=Permanenta regler
+userRulesTemporaryHeader=Tillfälliga regler
+userRulesRevert=Återställ
+userRulesCommit=Spara
+userRulesEdit=Redigera
+userRulesEditSave=Spara
+userRulesEditDicard=Avbryt
+userRulesImport=Importera från fil...
+userRulesExport=Exportera till fil...
+userRulesFormatHint=Läs denna sida för regelsyntax.
+userRulesDefaultFileName=mina-umatrix-regler.txt
+hostsFilesPrompt=Alla värdnamn i värdnamnsfilen är laddade för att svartlista värdnamn i det globala tillämpningsområdet.
+hostsFilesStats={{blockedHostnameCount}} distinkt blockerade värdnamn från:
+hostsFilesPerFileStats={{used}} använda av {{total}}
+hostsFilesLastUpdate=Senast uppdaterad: {{ago}}
+hostsFilesApplyChanges=Verkställ ändringar
+hostsFilesAutoUpdatePrompt=Uppdatera värdnamsfiler automatiskt.
+hostsFilesUpdateNow=Uppdatera nu
+hostsFilesPurgeAll=Töm alla cachar
+hostsFilesExternalListsHint=En adress per rad. Rader som börjar med &lsquo;#&rsquo; ignoreras. Inkorrekta adresser ignoreras utan varning.
+hostsFilesExternalListsParse=Läs in
+hostsFilesExternalListPurge=rensa cache
+hostsFilesExternalListNew=ny version tillgänglig
+hostsFilesExternalListObsolete=inaktuell
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Ändringslogg</a>
+aboutStorageUsed=Använt utrymme: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Dokumentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Tillstånd</a>
+aboutCode=Källkod (GPLv3)
+aboutIssues=Buggar och problem
+aboutContributors=Medhjälpare
+aboutCodeContributors=Kod:
+aboutIssueContributors=Problem:
+aboutTranslationContributors=Översättningar:
+aboutUserDataHeader=Din data
+aboutBackupButton=Säkerhetskopia till fil...
+aboutBackupFilename=min-umatrix-säkerhetskopia.txt
+aboutRestoreButton=Återställ från fil...
+aboutRestoreConfirm=Alla dina inställningar kommer att skrivas över med data från {{time}} och uMatrix kommer att startas om.\n\nVill du skriva över de existerande inställningarna med data från säkerhetskopian?
+aboutRestoreError=Data kunde inte läsas in ordentligt
+aboutOr=... eller ...
+aboutResetButton=Återställ till förvalda inställningar
+aboutResetConfirm=Obs! Detta kommer att ta bort alla dina egna inställningar. Är du säkert på att du vill fortsätta?
+loggerFilterInputPlaceholder=filteruttryck
+loggerMaxEntriesTip=Högst antal inlägg
+loggerEntryCookieDeleted=borttagna kakor: {{value}}
+loggerEntryDeleteCookieError=misslyckades med att ta bort kaka: {{value}}
+loggerEntryBrowserCacheCleared=webbläsarens cache rensades
+loggerEntryAssetUpdated=komponent uppdaterad: {{value}}
+mainBlockedPrompt1=uMatrix har förhindrat denna sida från att laddas:
+mainBlockedPrompt2=På grund av den här regeln
+mainBlockedBack=Gå tillbaka
+mainBlockedClose=Stäng
+commandRevertAll=Återställ alla tillfälliga ändringar
+commandWhitelistPageDomain=Vitlista siddomän tillfälligt
+commandWhitelistAll=Vitlista alla tillfälligt
+commandOpenDashboard=Öppna översiktspanel
+elapsedOneMinuteAgo=en minut sedan
+elapsedManyMinutesAgo={{value}} minuter sedan
+elapsedOneHourAgo=en timme sedan
+elapsedManyHoursAgo={{value}} timmar sedan
+elapsedOneDayAgo=en dag sedan
+elapsedManyDaysAgo={{value}} dagar sedan
+showDashboardButton=Översiktspanel
+showLoggerButton=Logg
+cloudPush=Exportera till molnet
+cloudPull=Importera från molnet
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Namn på denna enhet:
+genericSubmit=Skicka
+genericRevert=Återställ
+errorCantConnectTo=Nätverksproblem: kan inte ansluta till {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/sw/messages.properties b/locale/sw/messages.properties
new file mode 100644
index 0000000..060ae58
--- /dev/null
+++ b/locale/sw/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Settings
+privacyPageName=Privacy
+statsPageName=Statistics
+userRulesPageName=My rules
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=About
+allPrettyName=all
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/ta/messages.properties b/locale/ta/messages.properties
new file mode 100644
index 0000000..060ae58
--- /dev/null
+++ b/locale/ta/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Settings
+privacyPageName=Privacy
+statsPageName=Statistics
+userRulesPageName=My rules
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=About
+allPrettyName=all
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/te/messages.properties b/locale/te/messages.properties
new file mode 100644
index 0000000..7cba035
--- /dev/null
+++ b/locale/te/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=ఐచ్చికాలు
+privacyPageName=గోప్యత
+statsPageName=గణాంకాలు
+userRulesPageName=నా నియమాలు
+ubiquitousRulesPageName=హోస్ట్ ఫైల్లు
+rawSettingsPageName=More
+aboutPageName=మా గురించి
+allPrettyName=అన్ని
+cookiePrettyName=కుక్కి
+cssPrettyName=సిఎస్ఎస్
+imagePrettyName=చిత్రం
+mediaPrettyName=media
+pluginPrettyName=పొడిగింపు
+scriptPrettyName=స్క్రిప్ట్
+xhrPrettyName=XHR
+framePrettyName=ఫ్రేమ్
+otherPrettyName=ఇతర
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=అన్ని
+statsPagePages=పేజీలు
+statsPageCookies=కుక్కీలు
+statsPageCSS=సిఎస్ఎస్
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=ఇతరులు
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=ఏదీకాదు
+settingsMatrixAutoReloadCurrent=ప్రస్తుత
+settingsMatrixAutoReloadAll=అన్ని
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=స్పష్టత
+settingsIconBadgeEnabled=చిహ్నంపై విభిన్న అభ్యర్దనల సంఖ్య చూపుము
+settingsCollapseBlocked=నిరోధించబడిన అంశాలయోక్క సంగ్రాహకాలను దాచుము
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=నిమిషాలు.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=శాశ్వత నియమాలు
+userRulesTemporaryHeader=తాత్కాలిక నియమాలు
+userRulesRevert=Revert
+userRulesCommit=భద్రపరచు
+userRulesEdit=సవరించు
+userRulesEditSave=భద్రపరచు
+userRulesEditDicard=వదిలివేయు
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=మార్పులను అమలుపరచు
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=ఇప్పుడే నవీకరించు
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=ఒక రోజు క్రితం
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/th/messages.properties b/locale/th/messages.properties
new file mode 100644
index 0000000..060ae58
--- /dev/null
+++ b/locale/th/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Dashboard
+loggerPageName=uMatrix — Logger
+settingsPageName=Settings
+privacyPageName=Privacy
+statsPageName=Statistics
+userRulesPageName=My rules
+ubiquitousRulesPageName=Hosts files
+rawSettingsPageName=More
+aboutPageName=About
+allPrettyName=all
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=image
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=other
+matrixNoNetTrafficPrompt=No net traffic seen for this tab so far.
+matrixMtxButtonTip=Disable/enable matrix filtering for this scope.
+matrixPersistButtonTip=Save all temporary changes for this scope.
+matrixRevertButtonTip=Revert temporary changes for this scope.
+matrixReloadButton=Reload the page.\nPress Shift to bypass the browser cache.
+matrix1stPartyLabel=1st-party
+matrixBlacklistedHostnames={{count}} blacklisted hostname(s)
+matrixSwitchNoMixedContent=Forbid mixed content
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Spoof <code>Referer</code> header
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Revert all temporary changes
+matrixLoggerMenuEntry=Go to logger
+matrixDashboardMenuEntry=Go to dashboard
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Statistics
+statsPageGenericStats=Generic statistics
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}
+statsPageCookiesRemoved=Local cookies removed: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Local storages</a> emptied: {{count}}
+statsPageBrowserCacheCleared=Browser caches cleared: {{count}}
+statsPageDetailedStats=Detailed statistics
+statsPageDetailedAllPages=All
+statsPageDetailedBehindTheScenePage=Behind the scene
+statsPageOverview=Overview
+statsPageRequests=Requests
+statsPageAllowed=Allowed
+statsPageBlocked=Blocked
+statsPageAll=All
+statsPagePages=Pages
+statsPageCookies=Cookies
+statsPageCSS=CSS
+statsPageImages=Images
+statsPagePlugins=Plugins
+statsPageScripts=Scripts
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Others
+statsPageDetailed=Logger
+statsPageLogSizePrompt1=Remember the last
+statsPageLogSizePrompt2=HTTP requests <b>per page</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Refresh
+settingsPageTitle=uMatrix &ndash; Settings
+settingsMatrixDisplayHeader=Matrix
+settingsMatrixDisplayTextSizePrompt=Text size:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Large
+settingsMatrixDisplayColorBlind=Color-blind friendly
+settingsMatrixConvenienceHeader=Convenience
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=When the matrix is closed, smart reload these tabs:
+settingsMatrixAutoReloadNone=None
+settingsMatrixAutoReloadCurrent=Current
+settingsMatrixAutoReloadAll=All
+settingsMatrixAutoReloadInfo=Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Opacity
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=uMatrix &ndash; Privacy
+privacyDeleteBlockedCookiesPrompt=Delete blocked cookies.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Delete non-blocked session cookies
+privacyDeleteNonBlockedSessionCookiesPrompt2= minutes after the last time they have been used.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Clear browser cache every
+privacyClearCachePrompt2=minutes.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Permanent rules
+userRulesTemporaryHeader=Temporary rules
+userRulesRevert=Revert
+userRulesCommit=Commit
+userRulesEdit=Edit
+userRulesEditSave=Save
+userRulesEditDicard=Discard
+userRulesImport=Import from file...
+userRulesExport=Export to file...
+userRulesFormatHint=See this page for rule syntax.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.
+hostsFilesStats={{blockedHostnameCount}} distinct blocked hostnames from:
+hostsFilesPerFileStats={{used}} used out of {{total}}
+hostsFilesLastUpdate=Last update: {{ago}}
+hostsFilesApplyChanges=Apply changes
+hostsFilesAutoUpdatePrompt=Auto-update hosts files.
+hostsFilesUpdateNow=Update now
+hostsFilesPurgeAll=Purge all caches
+hostsFilesExternalListsHint=One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.
+hostsFilesExternalListsParse=Parse
+hostsFilesExternalListPurge=purge cache
+hostsFilesExternalListNew=new version available
+hostsFilesExternalListObsolete=outdated
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>
+aboutStorageUsed=Storage used: {{storageUsed}} bytes
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>
+aboutCode=Source code (GPLv3)
+aboutIssues=Bugs and issues
+aboutContributors=Contributors
+aboutCodeContributors=Code:
+aboutIssueContributors=Issues:
+aboutTranslationContributors=Translations:
+aboutUserDataHeader=Your data
+aboutBackupButton=Backup to file...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Restore from file...
+aboutRestoreConfirm=All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?
+aboutRestoreError=The data could not be read or is invalid
+aboutOr=... or ...
+aboutResetButton=Reset to default settings
+aboutResetConfirm=Caution! this will remove all your custom settings. Are you sure you want to proceed?
+loggerFilterInputPlaceholder=filter expression(s)
+loggerMaxEntriesTip=Maximum number of entries
+loggerEntryCookieDeleted=cookie deleted: {{value}}
+loggerEntryDeleteCookieError=failed to delete cookie: {{value}}
+loggerEntryBrowserCacheCleared=browser cache cleared
+loggerEntryAssetUpdated=asset updated: {{value}}
+mainBlockedPrompt1=uMatrix has prevented the following page from loading:
+mainBlockedPrompt2=Because of the following rule
+mainBlockedBack=Go back
+mainBlockedClose=Close
+commandRevertAll=Revert all temporary changes
+commandWhitelistPageDomain=Temporarily whitelist page domain
+commandWhitelistAll=Temporarily whitelist all
+commandOpenDashboard=Open dashboard
+elapsedOneMinuteAgo=a minute ago
+elapsedManyMinutesAgo={{value}} minutes ago
+elapsedOneHourAgo=an hour ago
+elapsedManyHoursAgo={{value}} hours ago
+elapsedOneDayAgo=a day ago
+elapsedManyDaysAgo={{value}} days ago
+showDashboardButton=Dashboard
+showLoggerButton=Logger
+cloudPush=Export to cloud storage
+cloudPull=Import from cloud storage
+cloudNoData=...\n...
+cloudDeviceNamePrompt=This device name:
+genericSubmit=Submit
+genericRevert=Revert
+errorCantConnectTo=Network error: Unable to connect to {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/tr/messages.properties b/locale/tr/messages.properties
new file mode 100644
index 0000000..5c11bfc
--- /dev/null
+++ b/locale/tr/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Kontrol Paneli
+loggerPageName=uMatrix — Günlükçü
+settingsPageName=Ayarlar
+privacyPageName=Gizlilik
+statsPageName=İstatistikler
+userRulesPageName=Kurallarım
+ubiquitousRulesPageName=Alan adı dosyaları
+rawSettingsPageName=Daha
+aboutPageName=Hakkında
+allPrettyName=tümü
+cookiePrettyName=çerez
+cssPrettyName=css
+imagePrettyName=resim
+mediaPrettyName=medya
+pluginPrettyName=eklenti
+scriptPrettyName=betik
+xhrPrettyName=XHR
+framePrettyName=çerçeve
+otherPrettyName=diğer
+matrixNoNetTrafficPrompt=Bu sekme için şimdiye kadar hiç ağ trafiği görülmedi.
+matrixMtxButtonTip=Bu kapsam için matris süzgecini devre dışı bırak/etkinleştir.
+matrixPersistButtonTip=Bu kapsam için geçici değişikliklerin tümünü kaydet.
+matrixRevertButtonTip=Bu kapsam için geçici değişiklikleri geri al.
+matrixReloadButton=Sayfayı yeniden yükle.\nTarayıcı önbelleğini yok sayarak yeniden yüklemek için Üst Karakter'e basın.
+matrix1stPartyLabel=1. taraf
+matrixBlacklistedHostnames={{count}} kara listelenmiş alan adı
+matrixSwitchNoMixedContent=Katı HTTPS
+matrixSwitchNoWorker=Web işçilerini yasakla
+matrixSwitchReferrerSpoof=Başvuran aldat
+matrixSwitchNoscriptSpoof=<code><noscript></code> etiketi aldat
+matrixRevertAllEntry=Geçici değişikliklerin tümünü geri al
+matrixLoggerMenuEntry=Günlükçüye git
+matrixDashboardMenuEntry=Kontrol paneline git
+matrixNoTabFound=Hiçbir web sayfası bulunamadı
+statsPageTitle=uMatrix &ndash; İstatistikleri
+statsPageGenericStats=Genel istatistikler
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP çerez</a> başlığı önlendi: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP başvuran</a> başlığı önlendi: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hiperbağlaç denetim</a> girişimi önlendi: {{count}}
+statsPageCookiesRemoved=Yerel çerezler kaldırıldı: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Yerel depolar</a> boşaltıldı: {{count}}
+statsPageBrowserCacheCleared=Tarayıcı önbelleği temizlendi: {{count}}
+statsPageDetailedStats=Ayrıntılı istatistikler
+statsPageDetailedAllPages=Tümü
+statsPageDetailedBehindTheScenePage=Perde arkası
+statsPageOverview=Genel Bakış
+statsPageRequests=İstekler
+statsPageAllowed=İzin verilen
+statsPageBlocked=Engellenmiş
+statsPageAll=Tümü
+statsPagePages=Sayfalar
+statsPageCookies=Çerezler
+statsPageCSS=CSS
+statsPageImages=Resimler
+statsPagePlugins=Eklentiler
+statsPageScripts=Betikler
+statsPageXHRs=XHR'ler
+statsPageFrames=Çerçeveler
+statsPageOthers=Diğerleri
+statsPageDetailed=Günlükçü
+statsPageLogSizePrompt1=<b>Sayfa başına</b> son
+statsPageLogSizePrompt2=HTTP isteğini hatırla.
+statsPageLogSizeHelp=<p>Bir web sayfası tarafından en son yapılan işlenmemiş HTTP isteklerinin ayrıntılarını denetleyebilirsiniz (aşağıya bakın).</p><p>Bu en çok ileri düzey kullanıcıların bir web sayfasının tam olarak ne yaptığını incelemerinde kullanışlı olur. Fakat bu HTTP istek günlüklüklerinin tutulması bellek kullanımı arttırır ve bu teknik bilgiye ihtiyacınız yoksa bellek boşa kullanılmış olur.</p><p>Bundan dolayı en fazla günlüğü tutulacak HTTP istek sayısını bu alandan ayarlayabilirsiniz.</p><p>&ldquo;<code>0</code>&rdquo; girerek ayrıntılı günlük tutulmasını kapatabilirsiniz (ve netice olarak <i>uMatrix'in</i> bellek kullanımını düşürürsünüz).</p>
+statsPageRefresh=Yenile
+settingsPageTitle=uMatrix &ndash; Ayarları
+settingsMatrixDisplayHeader=Matris
+settingsMatrixDisplayTextSizePrompt=Metin boyutu:
+settingsMatrixDisplayTextSizeNormal=Normal
+settingsMatrixDisplayTextSizeLarge=Büyük
+settingsMatrixDisplayColorBlind=Renk körü dostu
+settingsMatrixConvenienceHeader=Uygunluk
+settingsDefaultScopeLevel=Varsayılan kapsam seviyesi:
+settingsDefaultScopeLevel0=Genel
+settingsDefaultScopeLevel1=Alan adı
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=Matris kapatıldığında şu sekmeleri yeniden yükle:
+settingsMatrixAutoReloadNone=Hiçbiri
+settingsMatrixAutoReloadCurrent=Şu anki
+settingsMatrixAutoReloadAll=Tümü
+settingsMatrixAutoReloadInfo=Matristen sayfa görünümünü ve/veya davranışını etkileyebilecek değişiklikler yaptığınızda, <i>uMatrix</i> bunlardan etkilenen sayfaları matris kapatıldığında otomatik olarak yeniden yükleyecektir.
+settingsSubframeColor=Engellenmiş çerçeveler:&ensp;Color
+settingsSubframeOpacity=Saydamlık
+settingsIconBadgeEnabled=Farklı isteklerin sayısını simge üzerinde göster
+settingsCollapseBlocked=Engellenmiş elementlerin yertutucularını daralt
+settingsCollapseBlacklisted=Kara listelenmiş ögelerin yertutucularını daralt
+settingsNoscriptTagsSpoofed=1. taraf betikler engellendiğinde <code><noscript></code> etiketlerini aldat
+settingsCloudStorageEnabled=Bulut depo desteğini etkinleştir
+privacyPageTitle=uMatrix &ndash; Gizlilik
+privacyDeleteBlockedCookiesPrompt=Engellenmiş çerezleri sil.
+privacyDeleteBlockedCookiesHelp=<p>Kara listelenmiş çerezlerin tarayıcınıza girmesi <i>uMatrix</i> tarafından önlenmez. Ancak tarayıcınızdan çıkışları önlenir, önemli olan da budur. Çerezlerin tarayıcınıza girişi önlenmeyerek sitenin çerez kullanımı hakkında bilgi sahibi olma fırsatını elde edersiniz ve üstelik dilerseniz içeriklerini de denetleyebilirsiniz.</p><p>Bu engellenmiş çerezler bir kere <i>uMatrix</i> tarafından raporlandıktan sonra eğer isterseniz tabi tarayıcınızdan kaldırılabilir.</p><p><b>Önemli not:</b> Eklentiler normal işleyişleri sırasında web isteği yapabilir. Bu istekler tarayıcınızda çerez oluşumuna neden olur. Bu seçenek işaretli ve çerezin geldiği alan adı beyaz listede değilse bu çerez <i>uMatrix</i> tarafından tarayıcınızdan kaldırılacaktır. Bu yüzden eklentilerin iletişim kurduğu alan ad(lar)ının beyaz listede bulunduğundan emin olunuz.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Engellenmemiş oturum çerezlerini son kullanımlarından
+privacyDeleteNonBlockedSessionCookiesPrompt2= dakika sonra sil.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;Bir oturum çerezi ... tarayıcı oturumu sonlandığında silinir. Oturum çerezi geçici bellekte depolanır ve tarayıcı kapandıktan sonra muhafaza edilmez.&rdquo;</p><p>Bazı tarayıcıları kullanırken <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>bu durum gerçekleşmeyebilir.</a> Ayrıca, bazılarımız için, oturum çerezlerinin temizlenmesi için tarayıcının kapatılması yeteri kadar erken olmayabilir.</p>
+privacyDeleteBlockedLocalStoragePrompt=Engellenmiş alan adları tarafından oluşturulmuş <a href='https://en.wikipedia.org/wiki/Web_storage'>Yerel depolama</a> içeriğini sil
+privacyDeleteBlockedLocalStorageHelp=Yapılacaklar
+privacyClearCachePrompt1=Tarayıcı önbelleğini her
+privacyClearCachePrompt2=dakikada bir temizle.
+privacyClearCacheHelp=<p>Bazı web siteleri sizi izlemekte çok kararlıdır, izlenmemek için aldığınız her ne önlem varsa bunları aşmak için hoş olmayan hileli yöntemler kullanacaklardır.</p><p>Bu hilelerden bazıları <a href='https://en.wikipedia.org/wiki/Web_cache'>tarayıcı önbelleği</a> <sup>[1, 2]</sup>'ye dayalıdır, kullanıcı düzenli olarak tarayıcısının geçmişini temizlemediği için içerik çok uzun süre tarayıcıda kalır.</p><p>Tarayıcı önbelleğini düzenli olarak temizlemek biraz zahmetlidir (temizlendiğinde ise muhtemelen farkına varmazsınız) ve faydası ise kötü izleyicilerin gizliliğinizi ihlal etmesinin önüne geçmiş olursunuz.</p><p>Bu seçeneği işaretleyerek belirli aralıklarla bu işlemi sizin yerinize <i>uMatrix</i> yapabilir.</p><p>[1]<a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Tarayıcı Önbelleği ile Web İzlemesini Önlemek&rdquo;</a>\n[2]<a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Çerezsiz çerezler&rdquo;</a></p>
+privacyProcessRefererPrompt=Üçüncü taraf isteklerin <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP başvuran</a> bilgisini aldat.
+privacyProcessRefererHelp=Wikipedia'dan:<blockquote>HTTP başvuran bilgisi bir web sayfasına hangi bağlantıdan geldiğinizi içeren HTTP başlığıdır. ... <b>Çünkü başvuran bilgisi gizliliğinizi ihlal edebilir, bazı web tarayıcıları bu bilginin gönderimini devre dışı bırakmanıza izin verebilir.</b></blockquote>Bu ayar işaretliyse, <i>uMatrix</i> HTTP başvuran bilgisinin alan adı, istek yapılan alan adına göre üçüncü taraf ise bu bilgiyi aldatacaktır.
+privacyNoMixedContentPrompt=Katı HTTPS: Karışık içeriği yasakla.
+privacyNoMixedContentHelp=<p><a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Geliştiriciler Ağ'ından</a>:</p><blockquote>HTTPS sayfası sıradan bir içerik barındırıyorsa, düz metin HTTP, o bağlantı yalnızca kısmen şifrelenmiştir: şifrelenmemiş içerik, koklayıcı erişimine açık ve araya giren saldırıları tarafından değiştirilebilir durumdadır, bu nedenle artık bağlantı güvence altında değildir. Bu davranışı sergileyen web sayfasına, karışık içerikli sayfa denir.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Tüm <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hiperbağlaç denetim</a> girişimlerini engelle.
+privacyProcessHyperlinkAuditingHelp=<p>Köprü denetimi, kullanıcının web sayfasında hangi bağlantıya tıkladığını bir tarafa bildiren, <b>herhangi bir tarafa</b>, mekanizmadır. Esasen bir izleme özelliğidir: Bir web sitesinin veya o siteye bağlı üçüncü tarafın o sitenin hangi sayfasında hangi bağlantıya tıkladığınızı bilmesine olanak sağlar. Bunun tek amacı sizin tarayıcı etkinliğinizi izlemektir.</p>
+userRulesPermanentHeader=Kalıcı kurallar
+userRulesTemporaryHeader=Geçici kurallar
+userRulesRevert=Geri al
+userRulesCommit=İşle
+userRulesEdit=Düzenle
+userRulesEditSave=Kaydet
+userRulesEditDicard=İptal et
+userRulesImport=Dosyadan içe aktar...
+userRulesExport=Dosyaya aktar...
+userRulesFormatHint=Kural sözdizimi için bu sayfaya bakın.
+userRulesDefaultFileName=umatrix-kurallarim.txt
+hostsFilesPrompt=Alan adı dosyasındaki tüm alan adları genel kapsamda kara listeye yüklenir.
+hostsFilesStats={{blockedHostnameCount}} farklı engellenmiş alan adı:
+hostsFilesPerFileStats={{total}} süzgecin {{used}} adedi kullanıldı
+hostsFilesLastUpdate=Son güncelleme: {{ago}}
+hostsFilesApplyChanges=Değişiklikleri uygula
+hostsFilesAutoUpdatePrompt=Alan adı dosyalarını otomatik olarak güncelle.
+hostsFilesUpdateNow=Şimdi güncelle
+hostsFilesPurgeAll=Tüm önbellekleri temizle
+hostsFilesExternalListsHint=Satır başına tek URL. &lsquo;#&rsquo; işareti ile başlayan satırlar yok sayılacaktır. Geçersiz URL'ler sessizcene yok sayılacaktır.
+hostsFilesExternalListsParse=Çözümle
+hostsFilesExternalListPurge=önbelleği temizle
+hostsFilesExternalListNew=yeni sürüm mevcut
+hostsFilesExternalListObsolete=eski
+rawSettingsWarning=Uyarı! Bu ham yapılandırma ayarlarını değiştirmenin sorumluluğu size aittir.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Değişim günlüğü</a>
+aboutStorageUsed=Kullanılan depolama: {{storageUsed}} bayt
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Belgeler</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>İzinler</a>
+aboutCode=Kaynak kodu (GPLv3)
+aboutIssues=Hatalar ve sorunlar
+aboutContributors=Katkıda bulunanlar
+aboutCodeContributors=Kod:
+aboutIssueContributors=Sorunlar:
+aboutTranslationContributors=Çeviriler:
+aboutUserDataHeader=Verileriniz
+aboutBackupButton=Dosyaya yedekle...
+aboutBackupFilename=umatrix-yedeklerim.txt
+aboutRestoreButton=Dosyadan geri yükle...
+aboutRestoreConfirm={{time}} zamanında yedeklenmiş veri kullanılarak tüm ayarlarınızın üzerine yazılacak ve uMatrix yeniden başlayacak.\n\nYedeklenmiş veri kullanılarak ayarlarınızın üzerine yazılsın mı?
+aboutRestoreError=Veri okunamadı veya geçersiz
+aboutOr=... veya ...
+aboutResetButton=Varsayılan ayarlara sıfırla
+aboutResetConfirm=Uyarı! Bu işlem tüm kişisel ayarlarınızı kaldıracaktır. Devam etmek istediğinizden emin misiniz?
+loggerFilterInputPlaceholder=süzgeç ifadeleri
+loggerMaxEntriesTip=Maksimum giriş sayısı
+loggerEntryCookieDeleted=çerez silindi: {{value}}
+loggerEntryDeleteCookieError=çerez silme başarısız: {{value}}
+loggerEntryBrowserCacheCleared=tarayıcı önbelleği temizlendi
+loggerEntryAssetUpdated=liste güncellendi: {{value}}
+mainBlockedPrompt1=uMatrix aşağıdaki sayfaların yüklenmesini engelledi:
+mainBlockedPrompt2=Bu kural nedeniyle
+mainBlockedBack=Geri git
+mainBlockedClose=Kapat
+commandRevertAll=Geçici değişikliklerin tümünü geri al
+commandWhitelistPageDomain=Sayfa alan adını geçici olarak beyaz listeye ekle
+commandWhitelistAll=Tümünü geçici olarak beyaz listeye ekle
+commandOpenDashboard=Kontrol paneli aç
+elapsedOneMinuteAgo=bir dakika önce
+elapsedManyMinutesAgo={{value}} dakika önce
+elapsedOneHourAgo=bir saat önce
+elapsedManyHoursAgo={{value}} saat önce
+elapsedOneDayAgo=bir gün önce
+elapsedManyDaysAgo={{value}} gün önce
+showDashboardButton=Kontrol Paneli
+showLoggerButton=Günlükçü
+cloudPush=Bulut depoya aktar
+cloudPull=Bulut depodan al
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Bu cihazın adı:
+genericSubmit=Gönder
+genericRevert=Geri al
+errorCantConnectTo=Ağ hatası: {{url}} adresine bağlanılamıyor
+genericApplyChanges=Değişiklikleri uygula
diff --git a/locale/uk/messages.properties b/locale/uk/messages.properties
new file mode 100644
index 0000000..d5b5a67
--- /dev/null
+++ b/locale/uk/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — Налаштування
+loggerPageName=uMatrix — Logger
+settingsPageName=Налаштування
+privacyPageName=Конфіденційність
+statsPageName=Статистика
+userRulesPageName=Мої правила
+ubiquitousRulesPageName=Файли hosts
+rawSettingsPageName=More
+aboutPageName=Про програму
+allPrettyName=усе
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=зображення
+mediaPrettyName=media
+pluginPrettyName=плагін
+scriptPrettyName=скрипт
+xhrPrettyName=XHR
+framePrettyName=фрейм
+otherPrettyName=інше
+matrixNoNetTrafficPrompt=У цій вкладці ще не було трафіку.
+matrixMtxButtonTip=Вимкнути/увімкнути фільтрацію для цього сайту.
+matrixPersistButtonTip=Зберегти усі тимчасові зміни для цього сайту.
+matrixRevertButtonTip=Відмінити усі тимчасові зміни для цього сайту.
+matrixReloadButton=Перезавантажити сторінку.
+matrix1stPartyLabel=поточний сайт
+matrixBlacklistedHostnames=Сайтів заблоковано: {{count}}
+matrixSwitchNoMixedContent=Строге HTTPS
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Підміна referrer
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Відмінити усі тимчасові зміни
+matrixLoggerMenuEntry=Перейти до журналу
+matrixDashboardMenuEntry=Налаштування
+matrixNoTabFound=No web page found
+statsPageTitle=uMatrix &ndash; Статистика
+statsPageGenericStats=Загальна статистика
+statsPageCookieHeadersFoiled=Відредаговано заголовків <a href='http://uk.wikipedia.org/wiki/Куки'>кукі</a>: {{count}}
+statsPageRefererHeadersFoiled=Відредаговано заголовків <a href='https://en.wikipedia.org/wiki/HTTP_referer'>referer</a>: {{count}}
+statsPageHyperlinkAuditingFoiled=Заблоковано спроб <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>аудиту посилань</a>: {{count}}
+statsPageCookiesRemoved=Видалено локальних кукі: {{count}}
+statsPageLocalStoragesCleared=Спорожнено <a href='http://diveintohtml5.info/storage.html'>локальних сховищ</a>: {{count}}
+statsPageBrowserCacheCleared=Кешів очищено: {{count}}
+statsPageDetailedStats=Детальна статистика
+statsPageDetailedAllPages=Усе
+statsPageDetailedBehindTheScenePage=Приховані запити
+statsPageOverview=Огляд
+statsPageRequests=Запити
+statsPageAllowed=Дозволено
+statsPageBlocked=Заблоковано
+statsPageAll=Усе
+statsPagePages=Сторінки
+statsPageCookies=Кукі
+statsPageCSS=CSS
+statsPageImages=Зображення
+statsPagePlugins=Плагіни
+statsPageScripts=Скрипти
+statsPageXHRs=XHRs
+statsPageFrames=Frames
+statsPageOthers=Інше
+statsPageDetailed=Журнал
+statsPageLogSizePrompt1=Запам’ятати останнє
+statsPageLogSizePrompt2=HTTP запитів <b>на сторінку</b>.
+statsPageLogSizeHelp=<p>Ви можете переглянути деталі про останні необроблені запити HTTP, що були зроблені сторінкою (див. нижче).</p><p>Це набільш корисно для просунутих користувачів, які хочуть перевірити, що конкретно робила дана веб-сторінка. Однак, збереження цих запитів HTTP потребує додаткової пам'яті, тож якщо вам не потрібна технічна інформація, пам'ять буде витрачатись намарно. </p><p>Отож, це поле, що дозволяє вам змінити максимальну кількість останніх запитів HTTP, що будуть збережені для подальшої перевірки.</p><p>Введіть &ldquo;<code>0</code>&rdquo; щоб вимкнути детальне логування (і, як наслідок, зменшити витрати пам'яті <i>uMatrix</i>'ом).</p>
+statsPageRefresh=Оновити
+settingsPageTitle=uMatrix &ndash; Налаштування
+settingsMatrixDisplayHeader=Вигляд
+settingsMatrixDisplayTextSizePrompt=Розмір тексту:
+settingsMatrixDisplayTextSizeNormal=Середній
+settingsMatrixDisplayTextSizeLarge=Великий
+settingsMatrixDisplayColorBlind=Контрастні кольори
+settingsMatrixConvenienceHeader=Зручність
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=Після закриття matrix перезавантажувати ці вкладки:
+settingsMatrixAutoReloadNone=Жодних
+settingsMatrixAutoReloadCurrent=Поточний
+settingsMatrixAutoReloadAll=Усе
+settingsMatrixAutoReloadInfo=Щоразу, коли ви робите зміни в матриці, які можуть змінити вигляд і/або поведінку однієї, або більше сторінок <i>uMatrix</i> перевантажить змінені сторінки після закриття матриці.
+settingsSubframeColor=Blocked frames:&ensp;Color
+settingsSubframeOpacity=Затінення
+settingsIconBadgeEnabled=Show the number of distinct requests on the icon
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Увімкнути хмарне зберігання
+privacyPageTitle=uMatrix &ndash; Приватність
+privacyDeleteBlockedCookiesPrompt=Видаляти заблоковані кукі.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Видаляти незаблоковані кукі сесій через
+privacyDeleteNonBlockedSessionCookiesPrompt2= хвилин простою.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=Перелік завдань
+privacyClearCachePrompt1=Ощищувати кеш оглядача кожні
+privacyClearCachePrompt2=хвилин.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=Strict HTTPS: forbid mixed content.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Постійні правила
+userRulesTemporaryHeader=Тимчасові правила
+userRulesRevert=Повернути
+userRulesCommit=Затвердити
+userRulesEdit=Редагувати
+userRulesEditSave=Зберегти
+userRulesEditDicard=Відхилити
+userRulesImport=Імпортувати з файлу...
+userRulesExport=Експортувати в файл...
+userRulesFormatHint=Дивися цю сторінку з правилами синтаксису.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Всі імена з файлу hosts були занесені до чорного списку у глобальному діапазоні.
+hostsFilesStats={{blockedHostnameCount}} унікальних імен заблоковано з:
+hostsFilesPerFileStats={{used}} використано з {{total}}
+hostsFilesLastUpdate=Останнє оновлення: {{ago}}
+hostsFilesApplyChanges=Застосувати зміни
+hostsFilesAutoUpdatePrompt=Автоматичне оновлення файлів hosts.
+hostsFilesUpdateNow=Оновити зараз
+hostsFilesPurgeAll=Відмінити усі зміни
+hostsFilesExternalListsHint=Один URL на рядок. Рядки що починаються з &lsquo;#&rsquo; будуть проігноровані. Неправильні URL будуть проігноровані.
+hostsFilesExternalListsParse=Обробити
+hostsFilesExternalListPurge=очистити кеш
+hostsFilesExternalListNew=є нова версія
+hostsFilesExternalListObsolete=застаріла
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Перелік змін</a>
+aboutStorageUsed=Байтів використано у сховищі: {{storageUsed}}
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Документація</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Дозволи</a>
+aboutCode=Текст програми (GPLv3)
+aboutIssues=Проблеми і помилки
+aboutContributors=Учасники
+aboutCodeContributors=Код:
+aboutIssueContributors=Проблеми:
+aboutTranslationContributors=Переклади:
+aboutUserDataHeader=Ваші дані
+aboutBackupButton=Зберегти резервну копію...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Відновити з файлу...
+aboutRestoreConfirm=Усі налаштування будуть замінені даними з резервної копії від {{time}}, і uMatrix буде перезавантажено.\n\nЗамінити усі існуючі дані?
+aboutRestoreError=Дані неправильні або недоступні
+aboutOr=... або ...
+aboutResetButton=Скинути до заводських налаштувань
+aboutResetConfirm=Увага! Ваші ручні налаштування буде видалено. Продовжити?
+loggerFilterInputPlaceholder=фільтр(и)
+loggerMaxEntriesTip=Максимальна кількість записів
+loggerEntryCookieDeleted=кукі видалено: {{value}}
+loggerEntryDeleteCookieError=не вдалося видалити кукі: {{value}}
+loggerEntryBrowserCacheCleared=кеш браузера очищено
+loggerEntryAssetUpdated=компонент оновлено: {{value}}
+mainBlockedPrompt1=uMatrix заблокував завантаження сторінки:
+mainBlockedPrompt2=Через наступне правило
+mainBlockedBack=Назад
+mainBlockedClose=Закрити
+commandRevertAll=Відмінити усі тимчасові зміни
+commandWhitelistPageDomain=Тимчасово додати домен у білий список
+commandWhitelistAll=Тимчасово розблокувати усе
+commandOpenDashboard=Відкрити налаштування
+elapsedOneMinuteAgo=хвилину тому
+elapsedManyMinutesAgo={{value}} хвилин тому
+elapsedOneHourAgo=годину тому
+elapsedManyHoursAgo={{value}} годин тому
+elapsedOneDayAgo=день тому
+elapsedManyDaysAgo={{value}} днів тому
+showDashboardButton=Налаштування
+showLoggerButton=Журнал
+cloudPush=Експорт до хмарного сховища
+cloudPull=Імпорт з хмарного сховища
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Назва цього пристрою:
+genericSubmit=Відправити
+genericRevert=Повернути
+errorCantConnectTo=Помилка мережі: Неможливо під’єднатися до {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/vi/messages.properties b/locale/vi/messages.properties
new file mode 100644
index 0000000..35e0c88
--- /dev/null
+++ b/locale/vi/messages.properties
@@ -0,0 +1,179 @@
+extName=µMatrix
+dashboardPageName=µMatrix — Bảng điều khiển
+loggerPageName=uMatrix — Logger
+settingsPageName=Cài đặt
+privacyPageName=Riêng tư
+statsPageName=Thống kê
+userRulesPageName=Quy tắc riêng
+ubiquitousRulesPageName=Tập tin máy chủ
+rawSettingsPageName=More
+aboutPageName=Thông tin
+allPrettyName=tất cả
+cookiePrettyName=cookie
+cssPrettyName=css
+imagePrettyName=ảnh
+mediaPrettyName=media
+pluginPrettyName=plugin
+scriptPrettyName=script
+xhrPrettyName=XHR
+framePrettyName=frame
+otherPrettyName=khác
+matrixNoNetTrafficPrompt=Chưa phát hiện lưu lượng mạng trong thẻ này.
+matrixMtxButtonTip=Vô hiệu/kích hoạt lọc ma trận cho phạm vi này.\nYêu cầu đã chặn thông qua lọc ma trận trên trang này: {{count}}.
+matrixPersistButtonTip=Lưu tất cả thay đổi tạm thời cho phạm vi này.
+matrixRevertButtonTip=Xoá thay đổi tạm thời của phạm vị này.
+matrixReloadButton=Tải lại trang.
+matrix1stPartyLabel=bên thứ nhất
+matrixBlacklistedHostnames={{count}} tên máy chủ trong danh sách đen
+matrixSwitchNoMixedContent=HTTPS nghiêm ngặt
+matrixSwitchNoWorker=Forbid web workers
+matrixSwitchReferrerSpoof=Giả tham chiếu
+matrixSwitchNoscriptSpoof=Spoof <code><noscript></code> tags
+matrixRevertAllEntry=Hủy các thay đổi tạm thời
+matrixLoggerMenuEntry=Đến nhật ký
+matrixDashboardMenuEntry=Đến bảng điều khiển
+matrixNoTabFound=No web page found
+statsPageTitle=Thống kê µMatrix
+statsPageGenericStats=Thống kê tổng quát
+statsPageCookieHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>Đầu mục cookie HTTP</a> bị chặn: {{count}}
+statsPageRefererHeadersFoiled=<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>Đầu mục tham chiếu HTTP</a> bị chặn: {{count}}
+statsPageHyperlinkAuditingFoiled=<a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>Cố gắng kiểm tra siêu liên kết</a> bị chặn: {{count}}
+statsPageCookiesRemoved=Cookie trên máy đã xoá: {{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>Lưu trữ trên máy</a> đã làm rỗng: {{count}}
+statsPageBrowserCacheCleared=Bộ nhớ đệm trình duyệt đã xoá: {{count}}
+statsPageDetailedStats=Thống kê chi tiết
+statsPageDetailedAllPages=Tất cả
+statsPageDetailedBehindTheScenePage=Yêu cầu ẩn
+statsPageOverview=Tổng quan
+statsPageRequests=Yêu cầu
+statsPageAllowed=Đã cho phép
+statsPageBlocked=Đã chặn
+statsPageAll=Tất cả
+statsPagePages=Trang
+statsPageCookies=Cookie
+statsPageCSS=CSS
+statsPageImages=Ảnh
+statsPagePlugins=Plugin
+statsPageScripts=Script
+statsPageXHRs=XHR
+statsPageFrames=Frame
+statsPageOthers=Khác
+statsPageDetailed=Nhật ký yêu cầu
+statsPageLogSizePrompt1=Nhớ mục cuối
+statsPageLogSizePrompt2=Yêu cầu HTTP <b>mỗi trang</b>.
+statsPageLogSizeHelp=<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>
+statsPageRefresh=Tải lại
+settingsPageTitle=Cài đặt µMatrix
+settingsMatrixDisplayHeader=Diện mạo
+settingsMatrixDisplayTextSizePrompt=Cỡ chữ:
+settingsMatrixDisplayTextSizeNormal=Thường
+settingsMatrixDisplayTextSizeLarge=Lớn
+settingsMatrixDisplayColorBlind=Thân thiện với người mù màu
+settingsMatrixConvenienceHeader=Tiện lợi
+settingsDefaultScopeLevel=Default scope level:
+settingsDefaultScopeLevel0=Global
+settingsDefaultScopeLevel1=Domain
+settingsDefaultScopeLevel2=Site
+settingsMatrixAutoReloadPrompt=Khi đã đóng ma trận, tải lại thông minh những thẻ này:
+settingsMatrixAutoReloadNone=Không có
+settingsMatrixAutoReloadCurrent=Hiện tại
+settingsMatrixAutoReloadAll=Tất cả
+settingsMatrixAutoReloadInfo=Bất cứ khi nào bạn tạo ra thay đổi trong ma trận có thể ảnh hưởng đến việc hiển thị và/hoặc hành vi của một hoặc nhiều trang, <i>µMatrix</i> sẽ tự tải lại trang được ảnh hưởng khi bạn đóng ma trận.
+settingsSubframeColor=Đã chặn frame:&ensp;Màu
+settingsSubframeOpacity=Độ mờ
+settingsIconBadgeEnabled=Hiển thị số lượng yêu cầu riêng biệt trên biểu tượng
+settingsCollapseBlocked=Collapse placeholder of blocked elements
+settingsCollapseBlacklisted=Collapse placeholder of blacklisted elements
+settingsNoscriptTagsSpoofed=Spoof <code><noscript></code> tags when 1st-party scripts are blocked
+settingsCloudStorageEnabled=Enable cloud storage support
+privacyPageTitle=µMatrix &ndash; Riêng tư
+privacyDeleteBlockedCookiesPrompt=Xoá cookie đã chặn.
+privacyDeleteBlockedCookiesHelp=<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=Xoá cookie phiên làm việc không được chặn
+privacyDeleteNonBlockedSessionCookiesPrompt2= phút sau lần cuối chúng được dùng.
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>
+privacyDeleteBlockedLocalStoragePrompt=Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames
+privacyDeleteBlockedLocalStorageHelp=TODO
+privacyClearCachePrompt1=Xoá bộ đệm trình duyệt mỗi
+privacyClearCachePrompt2=phút.
+privacyClearCacheHelp=<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>
+privacyProcessRefererPrompt=Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.
+privacyProcessRefererHelp=From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.
+privacyNoMixedContentPrompt=HTTPS nghiêm ngặt: chặn nội dung hỗn hợp.
+privacyNoMixedContentHelp=<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>
+privacyProcessHyperlinkAuditingPrompt=Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts.
+privacyProcessHyperlinkAuditingHelp=<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>
+userRulesPermanentHeader=Quy tắc vĩnh viễn
+userRulesTemporaryHeader=Quy tắc tạm thời
+userRulesRevert=Phục hồi
+userRulesCommit=Xác nhận
+userRulesEdit=Sửa
+userRulesEditSave=Lưu
+userRulesEditDicard=Huỷ
+userRulesImport=Nhập từ tập tin...
+userRulesExport=Xuất ra tập tin...
+userRulesFormatHint=Xem trang này để biết quy tắc cú pháp.
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Tất cả tên máy chủ trong tập tin máy chủ được nạp là tên máy chủ trong danh sách đen trong phạm vi chung.
+hostsFilesStats={{blockedHostnameCount}} tên máy chủ đã chặn khác biệt từ:
+hostsFilesPerFileStats={{used}} được dùng trên tổng {{total}}
+hostsFilesLastUpdate=Cập nhật: {{ago}}
+hostsFilesApplyChanges=Áp dụng thay đổi
+hostsFilesAutoUpdatePrompt=Tự động cập nhật tập tin máy chủ.
+hostsFilesUpdateNow=Cập nhật ngay
+hostsFilesPurgeAll=Dọn tất cả bộ nhớ đệm
+hostsFilesExternalListsHint=Một URL mỗi dòng. Các dòng bắt đầu với &lsquo;#&rsquo; sẽ được bỏ qua. URL không hợp lệ sẽ được tự bỏ qua.
+hostsFilesExternalListsParse=Phân tích
+hostsFilesExternalListPurge=dọn bộ nhớ đệm
+hostsFilesExternalListNew=có phiên bản mới
+hostsFilesExternalListObsolete=đã cũ
+rawSettingsWarning=Warning! Change these raw configuration settings at your own risk.
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>Thay đổi</a>
+aboutStorageUsed=Lưu trữ đã dùng: {{storageUsed}} byte
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>Tài liệu</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Quyền</a>
+aboutCode=Mã nguồn (GPLv3)
+aboutIssues=Lỗi và vấn đề
+aboutContributors=Những người đóng góp
+aboutCodeContributors=Mã:
+aboutIssueContributors=Vấn đề:
+aboutTranslationContributors=Bản dịch:
+aboutUserDataHeader=Dữ liệu của bạn
+aboutBackupButton=Sao lưu ra tập tin...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=Phục hồi từ tập tin...
+aboutRestoreConfirm=Mọi cấu hình của bạn sẽ được thay thế bằng dữ liệu đã sao lưu vào {{time}}, và µMatrix sẽ khởi động lại.\n\nGhi đè tất cả cấu hình hiện tại bằng dữ liệu đã sao lưu?
+aboutRestoreError=Không thể đọc dữ liệu hoặc dữ liệu không hợp lệ
+aboutOr=... hoặc ...
+aboutResetButton=Phục hồi cấu hình mặc định
+aboutResetConfirm=Cẩn trọng! Điều này sẽ xoá tất cả cấu hình tuỳ chỉnh của bạn. Bạn chắc muốn tiến hành?
+loggerFilterInputPlaceholder=diễn tả bộ lọc
+loggerMaxEntriesTip=Số mục tối đa
+loggerEntryCookieDeleted=đã xoá cookie: {{value}}
+loggerEntryDeleteCookieError=không thể xoá cookie: {{value}}
+loggerEntryBrowserCacheCleared=đã xoá bộ đệm trình duyệt
+loggerEntryAssetUpdated=đã cập nhật tài nguyên: {{value}}
+mainBlockedPrompt1=uMatrix đã chặn truy cập trang này:
+mainBlockedPrompt2=Bởi vì các luật sau
+mainBlockedBack=Trở về
+mainBlockedClose=Đóng
+commandRevertAll=Xoá tất cả thay đổi tạm thời
+commandWhitelistPageDomain=Tạm thời thêm tên miền trang vào danh sách an toàn
+commandWhitelistAll=Tạm thời thêm tất cả vào danh sách an toàn
+commandOpenDashboard=Mở bảng điều khiển
+elapsedOneMinuteAgo=một phút trước
+elapsedManyMinutesAgo=phút trước
+elapsedOneHourAgo=một giờ trước
+elapsedManyHoursAgo=giờ trước
+elapsedOneDayAgo=một ngày trước
+elapsedManyDaysAgo=ngày trước
+showDashboardButton=Bảng điều khiển
+showLoggerButton=Ghi dữ liệu
+cloudPush=Xuất sang lưu trữ trên mây
+cloudPull=Nhập từ lưu trữ trên mây
+cloudNoData=...\n...
+cloudDeviceNamePrompt=Tên thiết bị:
+genericSubmit=Xác nhận
+genericRevert=Phục hồi
+errorCantConnectTo=Lỗi mạng: Không thể kết nối đến {{url}}
+genericApplyChanges=Apply changes
diff --git a/locale/zh-CN/messages.properties b/locale/zh-CN/messages.properties
new file mode 100644
index 0000000..56ed3a3
--- /dev/null
+++ b/locale/zh-CN/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — 控制面板
+loggerPageName=uMatrix — 日志记录
+settingsPageName=设置
+privacyPageName=隐私
+statsPageName=统计
+userRulesPageName=我的规则
+ubiquitousRulesPageName=Hosts 文件
+rawSettingsPageName=更多
+aboutPageName=关于
+allPrettyName=全部
+cookiePrettyName=Cookie
+cssPrettyName=样式
+imagePrettyName=图像
+mediaPrettyName=影音
+pluginPrettyName=插件
+scriptPrettyName=脚本
+xhrPrettyName=XHR
+framePrettyName=嵌套
+otherPrettyName=其他
+matrixNoNetTrafficPrompt=该页面暂无网络传输。
+matrixMtxButtonTip=停用或启用在该域名下的过滤。
+matrixPersistButtonTip=保存该域名下的所有临时变更。
+matrixRevertButtonTip=撤销该域名下的所有临时变更。
+matrixReloadButton=重新加载页面。\n同时按下 Shift 键以弃用浏览器缓存。
+matrix1stPartyLabel=第一方
+matrixBlacklistedHostnames={{count}} 个黑名单域名
+matrixSwitchNoMixedContent=禁止混合内容
+matrixSwitchNoWorker=禁用 Web Worker
+matrixSwitchReferrerSpoof=伪造 Referrer
+matrixSwitchNoscriptSpoof=模拟 <code><noscript></code> 元素
+matrixRevertAllEntry=撤销所有临时变更
+matrixLoggerMenuEntry=查看日志
+matrixDashboardMenuEntry=前往控制面板
+matrixNoTabFound=没有找到网页
+statsPageTitle=uMatrix &ndash; 统计
+statsPageGenericStats=大致统计
+statsPageCookieHeadersFoiled=已阻止<a href='https://zh.wikipedia.org/wiki/Cookie'>HTTP Cookie</a>请求头:{{count}} 次
+statsPageRefererHeadersFoiled=已阻止<a href='https://zh.wikipedia.org/wiki/HTTP%E5%8F%82%E7%85%A7%E4%BD%8D%E5%9D%80'>HTTP来源地址</a>请求头:{{count}} 次
+statsPageHyperlinkAuditingFoiled=已阻止<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>超链接审查</a>尝试:{{count}} 次
+statsPageCookiesRemoved=已移除本地 Cookie:{{count}} 个
+statsPageLocalStoragesCleared=已清空<a href='http://diveintohtml5.info/storage.html'>本地存储</a>:{{count}} 个
+statsPageBrowserCacheCleared=已清除的浏览器缓存:{{count}}
+statsPageDetailedStats=详细统计
+statsPageDetailedAllPages=全部
+statsPageDetailedBehindTheScenePage=页面后台
+statsPageOverview=概览
+statsPageRequests=请求
+statsPageAllowed=允许
+statsPageBlocked=拦截
+statsPageAll=全部
+statsPagePages=页面
+statsPageCookies=Cookie
+statsPageCSS=样式
+statsPageImages=图像
+statsPagePlugins=插件
+statsPageScripts=脚本
+statsPageXHRs=XHR
+statsPageFrames=嵌套
+statsPageOthers=其他
+statsPageDetailed=过滤日志
+statsPageLogSizePrompt1=记住<b>每个页面</b>最近的
+statsPageLogSizePrompt2=条HTTP请求。
+statsPageLogSizeHelp=<p>您可以查看最近网页所发出的原始 HTTP 请求的细节(见下)。</p><p>这对于想要检测网页确切行为的高级用户尤为实用。但记录 HTTP 请求会消耗内存,如果您并不在意这些技术细节,内存便等同于被浪费。</p><p>此区域将允许您调整最多保留多少最近的 HTTP 请求,以便您进一步审查。</p><p>输入 &ldquo;<code>0</code>&rdquo; 将关闭详细日志(从而减少 <i>uMatrix</i> 的内存占用)。</p>
+statsPageRefresh=刷新
+settingsPageTitle=uMatrix &ndash; 设置
+settingsMatrixDisplayHeader=界面外观
+settingsMatrixDisplayTextSizePrompt=文字大小:
+settingsMatrixDisplayTextSizeNormal=标准
+settingsMatrixDisplayTextSizeLarge=大
+settingsMatrixDisplayColorBlind=适合色盲人士
+settingsMatrixConvenienceHeader=便捷功能
+settingsDefaultScopeLevel=默认域名层级:
+settingsDefaultScopeLevel0=全局
+settingsDefaultScopeLevel1=主站域名
+settingsDefaultScopeLevel2=网页域名
+settingsMatrixAutoReloadPrompt=关闭 matrix 时智能重载这些标签页:
+settingsMatrixAutoReloadNone=无
+settingsMatrixAutoReloadCurrent=当前
+settingsMatrixAutoReloadAll=全部
+settingsMatrixAutoReloadInfo=任何时候当您做出可能影响一个或多个页面的显示或行为的变更时,<i>uMatrix</i> 将在您关闭 matrix 时自动重载受影响的页面。
+settingsSubframeColor=已屏蔽的框架:&ensp;颜色
+settingsSubframeOpacity=不透明度
+settingsIconBadgeEnabled=在图标上显示页面请求数量
+settingsCollapseBlocked=隐藏已拦截请求所关联的页面内容
+settingsCollapseBlacklisted=隐藏因域名黑名单而被屏蔽的內容
+settingsNoscriptTagsSpoofed=当第一方脚本被屏蔽时尝试模拟还原 <code><noscript></code> 网页元素
+settingsCloudStorageEnabled=启用浏览器自带云储存功能
+privacyPageTitle=uMatrix &ndash; 隐私
+privacyDeleteBlockedCookiesPrompt=删除已屏蔽的 Cookie
+privacyDeleteBlockedCookiesHelp=<p><i>uMatrix</i> 默认不阻止黑名单中的 Cookie 进入您的浏览器,而是阻止它们离开您的浏览器,是因后者更具有意义。不在 Cookie 进入浏览器之前阻止它们将使您有机会得知网站正尝试使用 Cookie,以便您有需要时进一步查看。</p><p>若黑名单中的 Cookie 被 <i>uMatrix</i> 所计入则可如愿将其移除。</p><p><b>重要提示:</b>浏览器扩展在其运作期间发出网络请求时可能会产生 Cookie。如果 Cookie 所在域名尚未被列入白名单,则 <i>uMatrix</i> 会于该选项开启之时移除这些 Cookie。请确保用于扩展通信的域名处于白名单内。</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=于
+privacyDeleteNonBlockedSessionCookiesPrompt2= 分钟后删除未屏蔽的闲置会话 Cookie
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>:“会话 Cookie……将在您结束浏览器会话时被清除。会话 Cookie 被存储于临时内存中,浏览器关闭后将不再保留。”</p><p>不过在使用某些浏览器时,这一行为<a href='https://code.google.com/p/chromium/issues/detail?id=128513'>有可能并不会发生</a>。另外,对某些人而言,必须在浏览器关闭后才能清除会话 Cookie 可能有些过迟。</p>
+privacyDeleteBlockedLocalStoragePrompt=连同被屏蔽的 Cookie 一並删除相关域名下的<a href='https://zh.wikipedia.org/wiki/%E7%BD%91%E9%A1%B5%E5%AD%98%E5%82%A8#.E6.9C.AC.E5.9C.B0.E5.8F.8A.E6.9C.83.E8.A9.B1.E5.AD.98.E5.84.B2'>本地存储</a>数据
+privacyDeleteBlockedLocalStorageHelp=待完成
+privacyClearCachePrompt1=每隔
+privacyClearCachePrompt2= 分钟清理浏览器缓存
+privacyClearCacheHelp=<p>某些网站特别热衷于跟踪,以至于为规避您的反跟踪举措,会使用一些不太光彩的手段。</p><p>一部分诡计利用了<a href='https://zh.wikipedia.org/wiki/%E7%BD%91%E9%A1%B5%E5%BF%AB%E7%85%A7'>浏览器缓存</a><sup>[1, 2]</sup>,因为缓存内容通常会长期储留,用户也基本不会去亲自清理。</p><p>定期清理浏览器缓存其实并不怎么麻烦(甚至很可能不会被注意到),且贵在可以保护您的隐私免受跟踪者侵犯。</p><p>启用该设置将容许 <i>uMatrix</i> 依您指定的时间定期为您完成该项工作。</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>“阻止利用浏览器缓存进行网络追踪”</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>“Cookieless Cookies”</a></p>
+privacyProcessRefererPrompt=伪造网页第三方请求中的 <a href='https://zh.wikipedia.org/wiki/HTTP%E5%8F%83%E7%85%A7%E4%BD%8D%E5%9D%80'>HTTP 来源地址</a>
+privacyProcessRefererHelp=维基百科:<blockquote>HTTP 来源地址是一个用于辨识请求来源网址的内容标识。……<b>由于来源地址可能会泄漏隐私,某些浏览器允许用户禁止发送来源地址。</b></blockquote>若启用该设置,<i>uMatrix</i> 则会在来源网页请求的 HTTP 来源地址中包含的域名与请求资源所在域名有差别时伪造来源地址。
+privacyNoMixedContentPrompt=严格遵守 HTTPS 协议:禁止混合内容
+privacyNoMixedContentHelp=<p><a href='https://developer.mozilla.org/docs/Security/MixedContent'>Mozilla 开发者网络</a>告知:</p><blockquote>若 HTTPS 页面包含未经加密的普通 HTTP 网页内容,则当前连接只是部分加密;未加密的部分将容许他人窥视甚至被中间人篡改,故连接并非完全安全。当该情况发生时,当前页面即为混合内容页面。</blockquote>
+privacyProcessHyperlinkAuditingPrompt=禁止<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>超链接审查</a>
+privacyProcessHyperlinkAuditingHelp=<p>超链接审查容许<b>任何人</b>得知用户于特定网页上点击了某个链接。其本质为一种跟踪手段:经由容许网站或任何第三方获知您于哪个页面点击了哪个链接,以达到追踪您的浏览活动这一特定目的。</p>
+userRulesPermanentHeader=永久规则
+userRulesTemporaryHeader=临时规则
+userRulesRevert=还原
+userRulesCommit=提交
+userRulesEdit=编辑
+userRulesEditSave=保存
+userRulesEditDicard=撤销
+userRulesImport=从文件导入...
+userRulesExport=导出至文件...
+userRulesFormatHint=在此页面查看规则语法。
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Hosts 文件中的域名将被列入全局黑名单。
+hostsFilesStats=共有 {{blockedHostnameCount}} 个域名被列入以下黑名单:
+hostsFilesPerFileStats={{used}}/{{total}} 使用中
+hostsFilesLastUpdate=上次更新:{{ago}}
+hostsFilesApplyChanges=应用变更
+hostsFilesAutoUpdatePrompt=自动更新 hosts 文件
+hostsFilesUpdateNow=立即更新
+hostsFilesPurgeAll=清理所有缓存
+hostsFilesExternalListsHint=每行一条URL。以&lsquo;#&rsquo;开头的行将被忽略。无效的URL将被忽略。
+hostsFilesExternalListsParse=解析
+hostsFilesExternalListPurge=清理缓存
+hostsFilesExternalListNew=有新版本
+hostsFilesExternalListObsolete=已过期
+rawSettingsWarning=警告!若要自行编辑原始设定数据则后果自负。
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>更新日志</a>
+aboutStorageUsed=占用存储空间:{{storageUsed}} 字节
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>帮助文档</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>权限说明</a>
+aboutCode=源代码(GPLv3)
+aboutIssues=问题与报告
+aboutContributors=贡献者
+aboutCodeContributors=源代码:
+aboutIssueContributors=问题报告:
+aboutTranslationContributors=翻译:
+aboutUserDataHeader=您的数据
+aboutBackupButton=备份至文件...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=从文件恢复...
+aboutRestoreConfirm=您的所有设置将被 {{time}} 的备份数据所覆盖,接着 uMatrix 将会重启。\n\n是否要用备份数据覆盖现有设置?
+aboutRestoreError=数据读取失败或格式有误
+aboutOr=...或者...
+aboutResetButton=还原至默认设置
+aboutResetConfirm=注意!这将移除您的所有个人设置。您确定要继续吗?
+loggerFilterInputPlaceholder=规则过滤表达式
+loggerMaxEntriesTip=日志条数限制
+loggerEntryCookieDeleted=已删除 Cookie:{{value}}
+loggerEntryDeleteCookieError=无法删除 Cookie:{{value}}
+loggerEntryBrowserCacheCleared=浏览器缓存清除完毕
+loggerEntryAssetUpdated=辅助规则已更新:{{value}}
+mainBlockedPrompt1=uMatrix 已阻止载入以下页面:
+mainBlockedPrompt2=因以下规则
+mainBlockedBack=返回
+mainBlockedClose=关闭
+commandRevertAll=撤销所有临时变更
+commandWhitelistPageDomain=临时将页面域名加入白名单
+commandWhitelistAll=临时将一切加入白名单
+commandOpenDashboard=打开控制面板
+elapsedOneMinuteAgo=1 分钟前
+elapsedManyMinutesAgo={{value}} 分钟前
+elapsedOneHourAgo=1 小时前
+elapsedManyHoursAgo={{value}} 小时前
+elapsedOneDayAgo=一天前
+elapsedManyDaysAgo={{value}} 天前
+showDashboardButton=控制面板
+showLoggerButton=日志
+cloudPush=导出到云存储
+cloudPull=从云存储导入
+cloudNoData=...\n...
+cloudDeviceNamePrompt=该设备名称:
+genericSubmit=递交
+genericRevert=还原
+errorCantConnectTo=网络错误:无法连接到 {{url}}
+genericApplyChanges=应用变更
diff --git a/locale/zh-TW/messages.properties b/locale/zh-TW/messages.properties
new file mode 100644
index 0000000..381f616
--- /dev/null
+++ b/locale/zh-TW/messages.properties
@@ -0,0 +1,179 @@
+extName=uMatrix
+dashboardPageName=uMatrix — 控制台
+loggerPageName=uMatrix — 日誌記錄
+settingsPageName=設定
+privacyPageName=隱私
+statsPageName=統計
+userRulesPageName=過濾規則
+ubiquitousRulesPageName=Hosts檔案
+rawSettingsPageName=更多
+aboutPageName=關於
+allPrettyName=全部
+cookiePrettyName=Cookie
+cssPrettyName=樣式
+imagePrettyName=圖像
+mediaPrettyName=影音
+pluginPrettyName=插件
+scriptPrettyName=指令
+xhrPrettyName=XHR
+framePrettyName=框架
+otherPrettyName=其他
+matrixNoNetTrafficPrompt=該頁面暫無網路傳輸。
+matrixMtxButtonTip=停用或啟用於該域名下的過濾。
+matrixPersistButtonTip=儲存該域名下的所有臨時變更。
+matrixRevertButtonTip=撤銷該域名下的所有臨時變更。
+matrixReloadButton=重新載入此頁。\n同時按下 Shift 鍵以棄用瀏覽器快取。
+matrix1stPartyLabel=第一方
+matrixBlacklistedHostnames={{count}} 個黑名單域名
+matrixSwitchNoMixedContent=禁止混合內容
+matrixSwitchNoWorker=禁用 Web Worker
+matrixSwitchReferrerSpoof=偽造參照位址
+matrixSwitchNoscriptSpoof=模擬 <code><noscript></code> 元素
+matrixRevertAllEntry=撤銷所有臨時變更
+matrixLoggerMenuEntry=查看日誌
+matrixDashboardMenuEntry=前往控制臺
+matrixNoTabFound=沒有找到網頁
+statsPageTitle=uMatrix &ndash; 統計
+statsPageGenericStats=大致統計
+statsPageCookieHeadersFoiled=已阻止<a href='https://zh.wikipedia.org/wiki/Cookie'>Cookie</a>檔頭請求:{{count}}次
+statsPageRefererHeadersFoiled=已阻止<a href='https://zh.wikipedia.org/wiki/HTTP%E5%8F%83%E7%85%A7%E4%BD%8D%E5%9D%80'>HTTP參照位址</a>檔頭請求:{{count}}次
+statsPageHyperlinkAuditingFoiled=已阻止<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>超連結審查</a>:{{count}}次
+statsPageCookiesRemoved=本地Cookie移除次數:{{count}}
+statsPageLocalStoragesCleared=<a href='http://diveintohtml5.info/storage.html'>本地儲存</a>清空次數:{{count}}
+statsPageBrowserCacheCleared=瀏覽器快取清除次數:{{count}}
+statsPageDetailedStats=詳細統計
+statsPageDetailedAllPages=全部
+statsPageDetailedBehindTheScenePage=頁面後臺
+statsPageOverview=概覽
+statsPageRequests=請求
+statsPageAllowed=容許
+statsPageBlocked=阻擋
+statsPageAll=全部
+statsPagePages=頁面
+statsPageCookies=Cookie
+statsPageCSS=樣式
+statsPageImages=圖像
+statsPagePlugins=外掛程式
+statsPageScripts=指令碼
+statsPageXHRs=XHR
+statsPageFrames=嵌套
+statsPageOthers=其他
+statsPageDetailed=過濾日誌
+statsPageLogSizePrompt1=記住<b>每頁</b>最近
+statsPageLogSizePrompt2=次HTTP請求。
+statsPageLogSizeHelp=<p>您可以查看最近期網絡頁面所發出的請求之詳情(見下)。</p><p>這對於想了解網絡頁面的確切行為的進階使用者較為實用,惟記錄HTTP請求將耗用記憶體,如您並不在意技術細節則等同浪費記憶體。</p><p>故此欄將容許閣下調整用作進一歲檢查的近期HTTP請求之記錄數字。</p><p>輸入「<code>0</code>」將禁用詳細日誌(從而減少<i>uMatrix</i>所佔用之記憶體)。</p>
+statsPageRefresh=重新整理
+settingsPageTitle=uMatrix &ndash; 設定
+settingsMatrixDisplayHeader=界面外觀
+settingsMatrixDisplayTextSizePrompt=文字大小:
+settingsMatrixDisplayTextSizeNormal=標準
+settingsMatrixDisplayTextSizeLarge=大
+settingsMatrixDisplayColorBlind=適合色盲人士
+settingsMatrixConvenienceHeader=便捷功能
+settingsDefaultScopeLevel=預設域名層級
+settingsDefaultScopeLevel0=全域
+settingsDefaultScopeLevel1=主站域名
+settingsDefaultScopeLevel2=網頁域名
+settingsMatrixAutoReloadPrompt=關閉矩陣時重新載入以下分頁:
+settingsMatrixAutoReloadNone=無
+settingsMatrixAutoReloadCurrent=現有
+settingsMatrixAutoReloadAll=全部
+settingsMatrixAutoReloadInfo=當你更改矩陣以致其將影響分頁之外觀或行為之時,<i>uMatrix</i>將於關閉矩陣時自動重新載入。
+settingsSubframeColor=已封鎖之框架:&ensp;颜色
+settingsSubframeOpacity=不透明度
+settingsIconBadgeEnabled=於圖標上顯示頁面請求數量
+settingsCollapseBlocked=隱藏已阻擋請求所關聯的頁面內容
+settingsCollapseBlacklisted=隱藏因域名黑名單而遭遮蔽的內容
+settingsNoscriptTagsSpoofed=當第一方指令碼被阻擋時嘗試模擬還原 <code><noscript></code> 網頁元素
+settingsCloudStorageEnabled=啟用瀏覽器自帶雲端儲存
+privacyPageTitle=uMatrix &ndash; 隱私
+privacyDeleteBlockedCookiesPrompt=刪除已屏蔽的 Cookie
+privacyDeleteBlockedCookiesHelp=<p><i>uMatrix</i> 預設不會阻止黑名單中的 Cookie 進入您的瀏覽器,而是阻止它们離開开您的瀏覽器,因後者更具有意義。不於 Cookie 進入瀏覽器之前阻止它們將使您有機會得知網站正嘗試使用 Cookie,以便您有需要時進一步查看。</p><p>若黑名單中的 Cookie 被 <i>uMatrix</i> 所計入則可按閱下意願將其移除。</p><p><b>重要提示:</b>附加元件於其運作期間作出網絡請求之時將可能產生 Cookie。如果 Cookie 所在域名並未被列入白名單,則 <i>uMatrix</i> 會於該選項開啟之時移除這些 Cookie。請確保用於附加元件通訊的域名處於白名單內。</p>
+privacyDeleteNonBlockedSessionCookiesPrompt1=於
+privacyDeleteNonBlockedSessionCookiesPrompt2= 分鐘後刪除未屏蔽的閒置會話 Cookie
+privacyDeleteNonBlockedSessionCookiesHelp=<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>萬維網聯盟</a> 告知:「會話 Cookie……將於瀏覽器會話結束時被清除。會話 Cookie 儲存於記憶體中,瀏覽器關閉後將不再保留。」</p><p>不過在使用某些瀏覽器時,此行為<a href='https://code.google.com/p/chromium/issues/detail?id=128513'>未必發生</a>。另外,對某些人而言,於瀏覽器將閉後才清除會話 Cookie 可能太遲。</p>
+privacyDeleteBlockedLocalStoragePrompt=連同被屏蔽的 Cookie 一並刪除相關域名下的<a href='https://zh.wikipedia.org/wiki/%E7%BD%91%E9%A1%B5%E5%AD%98%E5%82%A8#.E6.9C.AC.E5.9C.B0.E5.8F.8A.E6.9C.83.E8.A9.B1.E5.AD.98.E5.84.B2'>本地存储</a>數據
+privacyDeleteBlockedLocalStorageHelp=待完成
+privacyClearCachePrompt1=每隔
+privacyClearCachePrompt2= 分鐘清除瀏覽器快取
+privacyClearCacheHelp=<p>某些網站特别熱衷於追蹤,以至於為規避您的反跟蹤舉措,會使用一些不太光彩的手段。</p><p>一部分詭計利用了<a href='https://zh.wikipedia.org/wiki/%E7%BD%91%E9%A1%B5%E5%BF%AB%E7%85%A7'>瀏覽器快取</a><sup>[1, 2]</sup>,因為快取內容通常會長期儲留,用戶也基本不會去親自清理。</p><p>定期清理瀏覽器快取其實並不怎麼麻煩(甚至很可能不會被注意到),且貴在可以保護您的隱私免受追蹤者侵犯。</p><p>啟用該設定將容許 <i>uMatrix</i> 依您指定的時間定期為您完成此項工作。</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>「阻止利用瀏覽器快取進行網絡追蹤」</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>「Cookieless Cookies」</a></p>
+privacyProcessRefererPrompt=偽造網頁第三方請求中的 <a href='https://zh.wikipedia.org/wiki/HTTP%E5%8F%83%E7%85%A7%E4%BD%8D%E5%9D%80'>HTTP 參照位址</a>
+privacyProcessRefererHelp=維基百科:<blockquote>HTTP 位址參照是一個用於辨識請求來源網頁網址的內容標識。……<b>由於位址參照可能會洩漏隱私,某些瀏覽器容許使用者禁止傳送位址參照。</b></blockquote>若啟用該設定,<i>uMatrix</i> 則會於來源網頁請求的 HTTP 位址參照中包含的域名與請求資源所在域名有差別時偽造位址參照。
+privacyNoMixedContentPrompt=嚴格遵守 HTTPS 協議:禁止混合内容
+privacyNoMixedContentHelp=<p><a href='https://developer.mozilla.org/docs/Security/MixedContent'>Mozilla 開發者網絡</a>告知:</p><blockquote>若 HTTPS 頁面包含未經加密的普通 HTTP 網頁內容,則當前連結只是部分加密;未加密的部分或會遭他人窺視甚至篡改內容,故連結並非完全安全。當此情況發生時,該頁面即為混合內容頁面。</blockquote>
+privacyProcessHyperlinkAuditingPrompt=禁止<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>超連結審查</a>
+privacyProcessHyperlinkAuditingHelp=<p>超連結審查容許<b>任何人</b>獲知用戶於特定網頁上點擊了某個連結。其本質為一種追蹤手段:經由容許網站或任何第三方獲告知您於哪個頁面點擊了哪個連結,以達到追蹤您的瀏覽動向此一特定目的。</p>
+userRulesPermanentHeader=永久規則
+userRulesTemporaryHeader=臨時規則
+userRulesRevert=還原
+userRulesCommit=提交
+userRulesEdit=編輯
+userRulesEditSave=儲存
+userRulesEditDicard=撤銷
+userRulesImport=從檔案導入...
+userRulesExport=導出至檔案...
+userRulesFormatHint=於此頁面查閱規則語法。
+userRulesDefaultFileName=my-umatrix-rules.txt
+hostsFilesPrompt=Hosts 檔案中的域名將被列入全域黑名單。
+hostsFilesStats=總共 {{blockedHostnameCount}} 個域名被列入以下黑名單:
+hostsFilesPerFileStats={{used}}/{{total}} 使用中
+hostsFilesLastUpdate=上次更新:{{ago}}
+hostsFilesApplyChanges=套用變更
+hostsFilesAutoUpdatePrompt=自動更新 hosts 檔案
+hostsFilesUpdateNow=立即更新
+hostsFilesPurgeAll=清除所有快取
+hostsFilesExternalListsHint=每行一條網址。以「#」開始的行將被忽略。無效網址亦將忽略。
+hostsFilesExternalListsParse=解析
+hostsFilesExternalListPurge=清除快取
+hostsFilesExternalListNew=有新版本
+hostsFilesExternalListObsolete=已過期
+rawSettingsWarning=警告!若要自行編輯原始設定數據則後果自負。
+aboutChangelog=<a href='https://github.com/gorhill/uMatrix/releases'>更新日誌</a>
+aboutStorageUsed=佔用儲存空間:{{storageUsed}} 位元組
+aboutDoc=<a href='https://github.com/gorhill/uMatrix/wiki'>幫助文檔</a>
+aboutPermissions=<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>權限說明</a>
+aboutCode=原始程式碼(GPLv3)
+aboutIssues=問題與報告
+aboutContributors=貢獻者
+aboutCodeContributors=程式碼:
+aboutIssueContributors=問題報告:
+aboutTranslationContributors=翻譯:
+aboutUserDataHeader=您的數據
+aboutBackupButton=備份至檔案...
+aboutBackupFilename=my-umatrix-backup.txt
+aboutRestoreButton=從檔案還原...
+aboutRestoreConfirm=您的所有設定將由 {{time}} 的備份數據所覆蓋,接著 uMatrix 將會重新啟動。\n\n是否要用備份數據覆蓋現有設定?
+aboutRestoreError=數據讀取失敗或格式有誤
+aboutOr=...或者...
+aboutResetButton=重置為預設值
+aboutResetConfirm=小心!這將移除您的所有設定。確定要繼續?
+loggerFilterInputPlaceholder=規則過濾表達式
+loggerMaxEntriesTip=日誌條數限制
+loggerEntryCookieDeleted=已刪除 Cookie:{{value}}
+loggerEntryDeleteCookieError=無法刪除 Cookie:{{value}}
+loggerEntryBrowserCacheCleared=瀏覽器快取清除完畢
+loggerEntryAssetUpdated=已更新資料:{{value}}
+mainBlockedPrompt1=uMatrix 已阻止載入以下頁面:
+mainBlockedPrompt2=因以下規則
+mainBlockedBack=返回
+mainBlockedClose=關閉
+commandRevertAll=撤銷所有臨時變更
+commandWhitelistPageDomain=暫時將頁面域名列入白名單
+commandWhitelistAll=暫時將一切列入白名單
+commandOpenDashboard=開啟控制臺
+elapsedOneMinuteAgo=1 分鐘前
+elapsedManyMinutesAgo={{value}} 分鐘前
+elapsedOneHourAgo=1 小時前
+elapsedManyHoursAgo={{value}} 小時前
+elapsedOneDayAgo=1 日前
+elapsedManyDaysAgo={{value}} 日前
+showDashboardButton=控制臺
+showLoggerButton=日誌
+cloudPush=導出至雲端儲存
+cloudPull=從雲端儲存導入
+cloudNoData=...\n...
+cloudDeviceNamePrompt=該設備名稱:
+genericSubmit=提交
+genericRevert=還原
+errorCantConnectTo=網絡錯誤:無法連接至 {{url}}
+genericApplyChanges=應用變更
diff --git a/logger-ui.html b/logger-ui.html
new file mode 100644
index 0000000..ea0f57a
--- /dev/null
+++ b/logger-ui.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<link rel="stylesheet" type="text/css" href="css/common.css">
+<link rel="stylesheet" type="text/css" href="css/logger-ui.css">
+<link rel="shortcut icon" type="image/png" href="img/icon_16.png">
+<title data-i18n="loggerPageName"></title>
+</head>
+<body class="compactView f">
+
+<div id="toolbar">
+ <div>
+ <select id="pageSelector">
+ <option value="" data-i18n="statsPageDetailedAllPages">
+ <option value="tab_bts" data-i18n="statsPageDetailedBehindTheScenePage">
+ </select>
+ <span id="refresh" class="button disabled fa">&#xf021;</span>
+ </div>
+ <div>
+ <span id="compactViewToggler" class="button fa"></span>
+ <span id="clean" class="button fa disabled">&#xf00d;</span>
+ <span id="clear" class="button fa disabled">&#xf12d;</span>
+ <span id="filterButton" class="button fa">&#xf0b0;</span><input id="filterInput" type="text" placeholder="loggerFilterInputPlaceholder">
+ <input id="maxEntries" type="text" size="5" title="loggerMaxEntriesTip">
+ </div>
+ </div>
+
+<div id="content">
+ <style id="tabFilterer"></style>
+ <style id="popupFilterer"></style>
+ <table>
+ <colgroup><col><col><col><col><col></colgroup>
+ <tbody></tbody>
+ </table>
+ </div>
+
+<div id="popupContainer">
+ <div><span>&#xf068;</span>&ensp;<span>&#xf00d;</span></div>
+ </div>
+
+<div style="display: none;">
+ <div id="emphasizeTemplate"><span><span></span><b></b><span></span></span></div>
+ <div id="hiddenTemplate"><span style="display:none;"></span></div>
+ </div>
+
+<script src="js/vapi-common.js"></script>
+<script src="js/vapi-client.js"></script>
+<script src="js/udom.js"></script>
+<script src="js/i18n.js"></script>
+<script src="js/logger-ui.js"></script>
+
+</body>
+</html>
diff --git a/main-blocked.html b/main-blocked.html
new file mode 100644
index 0000000..bcebfba
--- /dev/null
+++ b/main-blocked.html
@@ -0,0 +1,138 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title></title>
+<link rel="stylesheet" href="css/common.css" type="text/css">
+<style>
+body {
+ font-family: sans-serif;
+ font-size: large;
+ text-align: center;
+ }
+body > div {
+ margin: 1.5em 0;
+ }
+body > div > p {
+ margin: 4px 0;
+ }
+body > div > p:first-child {
+ margin: 1.5em 0 0 0;
+ }
+.code {
+ background-color: rgba(0, 0, 0, 0.1);
+ display: inline-block;
+ font-family: monospace;
+ font-size: large;
+ line-height: 1.2;
+ padding: 2px 4px;
+ word-break: break-all;
+ }
+button {
+ cursor: pointer;
+ margin: 0 1em 0.25em 1em;
+ padding: 0.25em 0.5em;
+ font-size: inherit;
+ }
+#theURL {
+ margin: 0.25em 0;
+ padding: 0;
+ }
+#theURL > * {
+ margin: 0;
+ }
+#theURL > p {
+ position: relative;
+ z-index: 10;
+ }
+#theURL > p > span {
+ background-color: transparent;
+ top: 100%;
+ box-sizing: border-box;
+ cursor: pointer;
+ opacity: 0.5;
+ padding: 0.2em;
+ position: absolute;
+ transform: translate(0, -50%);
+ }
+body[dir="ltr"] #theURL > p > span {
+ right: 0;
+ }
+body[dir="rtl"] #theURL > p > span {
+ left: 0;
+ }
+#theURL > p:hover > span {
+ opacity: 1;
+ }
+#theURL > p > span:before {
+ content: '\f010';
+ }
+#theURL.collapsed > p > span:before {
+ content: '\f00e';
+ }
+#parsed {
+ background-color: #f8f8f8;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ border-top: none;
+ color: gray;
+ font-size: small;
+ overflow-x: hidden;
+ padding: 4px;
+ text-align: initial;
+ text-overflow: ellipsis;
+ }
+#theURL.collapsed > #parsed {
+ display: none;
+ }
+#parsed ul, #parsed li {
+ list-style-type: none;
+ }
+#parsed li {
+ white-space: nowrap;
+ }
+#parsed span {
+ display: inline-block;
+ }
+#parsed span:first-of-type {
+ font-weight: bold;
+ }
+#warningSign {
+ margin: 1e, 0;
+ opacity: 1;
+ pointer-events: none;
+ width: 100%;
+ }
+#warningSign > span {
+ color: #f2a500;
+ font-size: 180px;
+ }
+</style>
+</head>
+<body>
+<div id="warningSign"><span class="fa">&#xf071;</span></div>
+<div>
+ <p data-i18n="mainBlockedPrompt1"></p>
+ <div id="theURL" class="collapsed">
+ <p class="what code"></p>
+ <ul id="parsed"></ul>
+ </div>
+ </div>
+
+<!-- <div>
+ <p data-i18n="mainBlockedPrompt2"></p>
+ <p id="why" class="code"></p>
+ </div>
+ -->
+
+<div>
+ <p><button id="back" data-i18n="mainBlockedBack" type="button"></button>
+ <button id="bye" data-i18n="mainBlockedClose" type="button"></button></p>
+ </div>
+
+<script src="js/vapi-common.js"></script>
+<script src="js/vapi-client.js"></script>
+<script src="js/udom.js"></script>
+<script src="js/i18n.js"></script>
+<script src="js/main-blocked.js"></script>
+</body>
+</html>
diff --git a/options.xul b/options.xul
new file mode 100644
index 0000000..aeba83a
--- /dev/null
+++ b/options.xul
@@ -0,0 +1,9 @@
+<?xml version="1.0" ?>
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <setting type="control">
+ <vbox>
+ <button id="showDashboardButton"/>
+ <button id="showLoggerButton"/>
+ </vbox>
+ </setting>
+</vbox>
diff --git a/popup.html b/popup.html
new file mode 100644
index 0000000..0eed6cf
--- /dev/null
+++ b/popup.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<link rel="stylesheet" href="css/common.css" type="text/css">
+<link rel="stylesheet" href="css/popup.css" type="text/css">
+<title>uMatrix panel</title>
+</head>
+
+<body>
+
+<div id="templates" style="display:none">
+ <div class="groupSeparator"></div>
+ <div class="domainSeparator"></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div id="cellHotspots"><div id="whitelist"></div><div id="blacklist"></div><div id="domainOnly"><span class="fa"></span></div></div>
+ <!-- Use once min supported browser version allows for use of CSS variables
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
+ <symbol id="toggleButton" viewBox="0 0 152 96">
+ <g>
+ <path d="m 48,24 a 24,24 0 0 0 -24,24 24,24 0 0 0 24,24 l 48,0 A 24,24 0 0 0 120,48 24,24 0 0 0 96,24 l -48,0 z" style="opacity:1;fill:#bbb;fill-opacity:1;stroke:none;" />
+ <g style="display:var(--off);">
+ <ellipse style="fill:#bbb;fill-opacity:1;stroke:none;" cx="48" cy="48" rx="48" ry="48" />
+ <ellipse style="opacity:1;fill:#fff;fill-opacity:1;stroke:none;" cx="48" cy="48" rx="40" ry="40" />
+ <ellipse style="display:var(--dot);fill:#bbb;fill-opacity:1;stroke:none;" cx="48" cy="48" rx="12" ry="12" /></g>
+ <g style="display:var(--on);">
+ <ellipse style="opacity:1;fill:#444;fill-opacity:1;stroke:none;" cx="104" cy="48" rx="48" ry="48" />
+ <ellipse style="display:var(--dot);fill:#bbb;fill-opacity:1;stroke:none;" cx="104" cy="48" rx="12" ry="12" /></g>
+ </g>
+ </symbol>
+ </svg>
+ -->
+</div>
+
+<div class="paneHead">
+ <a id="gotoDashboard" class="extensionURL" href="#" data-extension-url="dashboard.html" data-i18n-tip="matrixDashboardMenuEntry">uMatrix <span id="version"> </span><span class="fa">&#xf013;</span></a>
+ <div id="toolbarContainer">
+ <div class="toolbar">
+ <span class="scope" id="specificScope"><span>&nbsp;</span></span><!--
+ --><span class="scope" id="globalScope" data-scope="*"><span><span>&#x2217;</span></span></span>
+ <button id="mtxSwitch_matrix-off" type="button" class="fa scopeRel tip-anchor-left" data-i18n-tip="matrixMtxButtonTip">&#xf011;<span class="badge"></span></button>
+ <button id="buttonMtxSwitches" type="button" class="fa scopeRel" tabindex="-1" data-dropdown-menu="dropDownMenuSwitches">&#xf142;<span class="badge"></span></button>
+ <button id="buttonPersist" type="button" class="fa scopeRel tip-anchor-left" data-i18n-tip="matrixPersistButtonTip">&#xf023;<span class="badge"></span></button>
+ <button id="buttonRevertScope" type="button" class="fa scopeRel tip-anchor-left" tabindex="-1" data-i18n-tip="matrixRevertButtonTip">&#xf12d;</button>
+ <button id="buttonReload" type="button" class="fa tip-anchor-left" data-i18n-tip="matrixReloadButton">&#xf021;</button>
+ </div>
+ <div class="toolbar">
+ <button id="buttonRevertAll" class="fa tip-anchor-right" data-i18n-tip="matrixRevertAllEntry">&#xf122;</button>
+ <button type="button" class="fa extensionURL tip-anchor-right" data-extension-url="logger-ui.html" data-i18n-tip="matrixLoggerMenuEntry">&#xf022;</button>
+ </div>
+ </div>
+ <div id="matHead" class="matrix collapsible">
+ <div class="matRow rw" style="display:none"><div class="matCell" data-req-type="all">all</div><div class="matCell" data-req-type="cookie">cookie</div><div class="matCell" data-req-type="css">css</div><div class="matCell" data-req-type="image">img</div><div class="matCell" data-req-type="media">media</div><div class="matCell" data-req-type="script">script</div><div class="matCell" data-req-type="xhr">XHR</div><div class="matCell" data-req-type="frame">frame</div><div class="matCell" data-req-type="other">other</div></div>
+ </div>
+</div>
+
+<div class="paneContent">
+ <div id="matList" class="matrix" style="display:none">
+ <!-- the spaces are important: they ensure text nodes in the DOM -->
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ <div class="matRow"><div class="matCell"><b> </b> </div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div><div class="matCell">&nbsp;</div></div>
+ </div>
+ <div id="noNetTrafficPrompt" style="display:none;text-align:center;font-size:large"></div>
+</div>
+
+<div id="dropDownMenuSwitches" class="dropdown-menu-capture">
+ <div class="dropdown-menu">
+ <ul id="mtxSwitches">
+ <li id="mtxSwitch_https-strict" class="dropdown-menu-entry exists"><!-- <svg><use xlink:href="#toggleButton" /></svg> --><svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 152 96"><g style="fill:#bbb;"><ellipse cx="48" cy="48" rx="24" ry="24" /><ellipse cx="104" cy="48" rx="24" ry="24" /><rect width="56" height="48" x="48" y="24" /></g><g class="off" style="fill:#bbb;"><ellipse cx="48" cy="48" rx="48" ry="48" /><ellipse style="fill:#fff;" cx="48" cy="48" rx="40" ry="40" /><ellipse class="dot" cx="48" cy="48" rx="12" ry="12" /></g><g class="on" style="fill:#bbb;"><ellipse style="fill:#444;" cx="104" cy="48" rx="48" ry="48" /><ellipse class="dot" cx="104" cy="48" rx="12" ry="12" /></g></svg><span data-i18n="matrixSwitchNoMixedContent"></span>&emsp;<a class="fa" href="https://developer.mozilla.org/docs/Web/Security/Mixed_content" target="_blank">&#xf05a;</a>
+ <li id="mtxSwitch_no-workers" class="dropdown-menu-entry exists"><!-- <svg><use xlink:href="#toggleButton" /></svg> --><svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 152 96"><g style="fill:#bbb;"><ellipse cx="48" cy="48" rx="24" ry="24" /><ellipse cx="104" cy="48" rx="24" ry="24" /><rect width="56" height="48" x="48" y="24" /></g><g class="off" style="fill:#bbb;"><ellipse cx="48" cy="48" rx="48" ry="48" /><ellipse style="fill:#fff;" cx="48" cy="48" rx="40" ry="40" /><ellipse class="dot" cx="48" cy="48" rx="12" ry="12" /></g><g class="on" style="fill:#bbb;"><ellipse style="fill:#444;" cx="104" cy="48" rx="48" ry="48" /><ellipse class="dot" cx="104" cy="48" rx="12" ry="12" /></g></svg><span data-i18n="matrixSwitchNoWorker"></span>&emsp;<a class="fa" href="https://developer.mozilla.org/docs/Web/API/Web_Workers_API" target="_blank">&#xf05a;</a>
+ <li id="mtxSwitch_referrer-spoof" class="dropdown-menu-entry"><!-- <svg><use xlink:href="#toggleButton" /></svg> --><svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 152 96"><g style="fill:#bbb;"><ellipse cx="48" cy="48" rx="24" ry="24" /><ellipse cx="104" cy="48" rx="24" ry="24" /><rect width="56" height="48" x="48" y="24" /></g><g class="off" style="fill:#bbb;"><ellipse cx="48" cy="48" rx="48" ry="48" /><ellipse style="fill:#fff;" cx="48" cy="48" rx="40" ry="40" /><ellipse class="dot" cx="48" cy="48" rx="12" ry="12" /></g><g class="on" style="fill:#bbb;"><ellipse style="fill:#444;" cx="104" cy="48" rx="48" ry="48" /><ellipse class="dot" cx="104" cy="48" rx="12" ry="12" /></g></svg><span data-i18n="matrixSwitchReferrerSpoof"></span>&emsp;<a class="fa" href="https://developer.mozilla.org/docs/Web/HTTP/Headers/Referer" target="_blank">&#xf05a;</a>
+ <li id="mtxSwitch_noscript-spoof" class="dropdown-menu-entry"><!-- <svg><use xlink:href="#toggleButton" /></svg> --><svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 152 96"><g style="fill:#bbb;"><ellipse cx="48" cy="48" rx="24" ry="24" /><ellipse cx="104" cy="48" rx="24" ry="24" /><rect width="56" height="48" x="48" y="24" /></g><g class="off" style="fill:#bbb;"><ellipse cx="48" cy="48" rx="48" ry="48" /><ellipse style="fill:#fff;" cx="48" cy="48" rx="40" ry="40" /><ellipse class="dot" cx="48" cy="48" rx="12" ry="12" /></g><g class="on" style="fill:#bbb;"><ellipse style="fill:#444;" cx="104" cy="48" rx="48" ry="48" /><ellipse class="dot" cx="104" cy="48" rx="12" ry="12" /></g></svg><span data-i18n="matrixSwitchNoscriptSpoof"></span>&emsp;<a class="fa" href="https://developer.mozilla.org/docs/Web/HTML/Element/noscript" target="_blank">&#xf05a;</a>
+ </ul>
+ </div>
+</div>
+
+<div id="noTabFound"></div>
+
+<!-- Convenient to auto-fetch locale strings used in scripts -->
+<div style="display: none;">
+ <span data-i18n="matrixBlacklistedHostnames"></span>
+ <span data-i18n="matrix1stPartyLabel"></span>
+</div>
+
+<div style="clear: both; height: 0px;"></div>
+
+<script src="lib/punycode.js"></script>
+<script src="js/vapi-common.js"></script>
+<script src="js/vapi-client.js"></script>
+<script src="js/udom.js"></script>
+<script src="js/i18n.js"></script>
+<script src="js/popup.js"></script>
+</body>
+
+</html>
diff --git a/raw-settings.html b/raw-settings.html
new file mode 100644
index 0000000..48c60fa
--- /dev/null
+++ b/raw-settings.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title data-i18n="rawSettingsPageName"></title>
+<link rel="stylesheet" type="text/css" href="css/common.css">
+<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
+<link rel="stylesheet" type="text/css" href="css/raw-settings.css">
+<link rel="shortcut icon" type="image/png" href="img/icon_16.png"/>
+</head>
+
+<body>
+<p><span data-i18n="rawSettingsWarning"></span> <a class="fa info important" href="https://github.com/gorhill/uMatrix/wiki/Raw-settings" target="_blank">&#xf05a;</a>
+ </p>
+<p><button id="rawSettingsApply" class="custom important" type="button" disabled="true" data-i18n="genericApplyChanges"></button>&ensp;
+ </p>
+<textarea id="rawSettings" dir="auto" spellcheck="false"></textarea>
+
+<script src="js/vapi-common.js"></script>
+<script src="js/vapi-client.js"></script>
+<script src="js/udom.js"></script>
+<script src="js/i18n.js"></script>
+<script src="js/dashboard-common.js"></script>
+<script src="js/raw-settings.js"></script>
+
+</body>
+</html>
diff --git a/settings.html b/settings.html
new file mode 100644
index 0000000..ee07026
--- /dev/null
+++ b/settings.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>uMatrix — Settings</title>
+<link rel="stylesheet" type="text/css" href="css/common.css">
+<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
+<style>
+div > p:first-child {
+ margin-top: 0;
+ }
+div > p:last-child {
+ margin-bottom: 0;
+ }
+ul {
+ padding: 0;
+ list-style-type: none;
+ }
+ul > li {
+ margin: 0.2em 0 0.2em 1em;
+ }
+ul > li.separator {
+ margin: 0.5em 0;
+ }
+.dim {
+ font-weight: 100;
+ color: #888;
+ }
+</style>
+</head>
+
+<body>
+
+<h2 data-i18n="settingsMatrixConvenienceHeader"></h2>
+<ul>
+ <li><input id="iconBadgeEnabled" type="checkbox" data-setting-bool>
+ <label data-i18n="settingsIconBadgeEnabled" for="iconBadgeEnabled"></label>
+ <li><input id="collapseBlocked" type="checkbox" data-setting-bool>
+ <label data-i18n="settingsCollapseBlocked" for="collapseBlocked"></label>
+ <ul>
+ <li><input id="collapseBlacklisted" type="checkbox" data-setting-bool>
+ <label data-i18n="settingsCollapseBlacklisted" for="collapseBlacklisted"></label>
+ </ul>
+ <li><input id="noscriptTagsSpoofed" type="checkbox" data-matrix-switch="noscript-spoof">
+ <label data-i18n="settingsNoscriptTagsSpoofed" for="noscriptTagsSpoofed"></label>
+ <li><input id="cloudStorageEnabled" type="checkbox" data-setting-bool>
+ <label data-i18n="settingsCloudStorageEnabled" for="cloudStorageEnabled"></label>
+</ul>
+<h2 data-i18n="settingsMatrixDisplayHeader"></h2>
+<ul>
+ <li><span data-i18n="settingsMatrixDisplayTextSizePrompt"></span>&ensp;&minus;<input id="displayTextSize" type="range" min="12" max="18" value="14" style="margin:0 0.2em;vertical-align:bottom;">&plus;
+ <li class="separator">
+ <li>
+ <label data-i18n="settingsDefaultScopeLevel"></label> <select id="popupScopeLevel"><option data-i18n="settingsDefaultScopeLevel2" value="site"><option data-i18n="settingsDefaultScopeLevel1" value="domain"><option data-i18n="settingsDefaultScopeLevel0" value="*"></select>
+ <li class="separator">
+ <li>
+ <input id="colorBlindFriendly" type="checkbox" data-setting-bool>
+ <label data-i18n="settingsMatrixDisplayColorBlind" for="colorBlindFriendly"></label>
+</ul>
+<h2 data-i18n="privacyPageName"></h2>
+<ul>
+ <li>
+ <input id="deleteCookies" type="checkbox" data-setting-bool><label data-i18n="privacyDeleteBlockedCookiesPrompt" for="deleteCookies"></label>
+ <span class="whatisthis"></span>
+ <div class="whatisthis-expandable para" data-i18n="privacyDeleteBlockedCookiesHelp"></div>
+ <li>
+ <input id="deleteUnusedSessionCookies" type="checkbox" data-setting-bool><label data-i18n="privacyDeleteNonBlockedSessionCookiesPrompt1" for="deleteUnusedSessionCookies"></label>
+ <input id="deleteUnusedSessionCookiesAfter" type="text" value="60" size="3"><span data-i18n="privacyDeleteNonBlockedSessionCookiesPrompt2"></span>
+ <span class="whatisthis"></span>
+ <div class="whatisthis-expandable para" data-i18n="privacyDeleteNonBlockedSessionCookiesHelp"></div>
+ <!--
+ Delete non-blocked session cookies x minutes after the last time they have been used.
+
+ Allow generically blocked cookies but deleted them x minutes after they have been first created.
+
+ A "generically blocked" cookie is a cookie which inherits its block status in
+ the matrix from the `cookie` cell or the `all` cell in the top row of the
+ matrix.
+
+ When a cookie inherits its block status from a cell in the top row of the
+ matrix (the "header" row), this means it is not specifically distrusted, but
+ rather that the default stance is to distrust cookies in general.
+
+ However some sites do require cookies to minimally work properly. This options
+ allow to "unbreak" these sites by allowing not specifically distrusted cookies
+ to travel back and forth between you and the server, but to limit the lifetime
+ of these cookies so that they cannot be used to track you.
+ -->
+ <li>
+ <input id="deleteLocalStorage" type="checkbox" data-setting-bool><label data-i18n="privacyDeleteBlockedLocalStoragePrompt" for="deleteLocalStorage"></label>
+ <li>
+ <input id="clearBrowserCache" type="checkbox" data-setting-bool><label data-i18n="privacyClearCachePrompt1" for="clearBrowserCache"></label>
+ <input id="clearBrowserCacheAfter" type="text" value="60" size="3"> <label data-i18n="privacyClearCachePrompt2" for="clearBrowserCacheAfter"></label>
+ <span class="whatisthis"></span>
+ <div class="whatisthis-expandable para" data-i18n="privacyClearCacheHelp"></div>
+ <li>
+ <input id="processReferer" type="checkbox" data-matrix-switch="referrer-spoof"><label data-i18n="privacyProcessRefererPrompt" for="processReferer"></label>
+ <span class="whatisthis"></span>
+ <div class="whatisthis-expandable para" data-i18n="privacyProcessRefererHelp"></div>
+ <li>
+ <input id="noMixedContent" type="checkbox" data-matrix-switch="https-strict"><label data-i18n="privacyNoMixedContentPrompt" for="noMixedContent"></label>
+ <span class="whatisthis"></span>
+ <div class="whatisthis-expandable para" data-i18n="privacyNoMixedContentHelp"></div>
+ <li>
+ <input id="processHyperlinkAuditing" type="checkbox" data-setting-bool><label data-i18n="privacyProcessHyperlinkAuditingPrompt" for="processHyperlinkAuditing"></label>
+ <span class="whatisthis"></span>
+ <div class="whatisthis-expandable para" data-i18n="privacyProcessHyperlinkAuditingHelp"></div>
+</ul>
+
+<script src="js/vapi-common.js"></script>
+<script src="js/vapi-client.js"></script>
+<script src="js/udom.js"></script>
+<script src="js/i18n.js"></script>
+<script src="js/dashboard-common.js"></script>
+<script src="js/settings.js"></script>
+</body>
+
+</html>
diff --git a/user-rules.html b/user-rules.html
new file mode 100644
index 0000000..854a18b
--- /dev/null
+++ b/user-rules.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>uMatrix — Your rules</title>
+<link rel="stylesheet" type="text/css" href="css/common.css">
+<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
+<link rel="stylesheet" type="text/css" href="css/cloud-ui.css">
+<link rel="stylesheet" type="text/css" href="css/user-rules.css">
+</head>
+
+<body>
+
+<div id="cloudWidget" class="hide" data-cloud-entry="myRulesPane"></div>
+
+<!-- <p data-i18n="userRulesFormatHint"></p> -->
+<div id="diff">
+ <div class="pane left">
+ <div>
+ <h2 data-i18n="userRulesPermanentHeader"></h2>
+ <button type="button" id="exportButton" data-i18n="userRulesExport"></button>
+ <button type="button" id="revertButton" data-i18n="userRulesRevert"></button>
+ </div>
+ <ul></ul>
+ </div>
+ <div class="pane right">
+ <div>
+ <h2 data-i18n="userRulesTemporaryHeader"></h2>
+ <button type="button" id="commitButton" data-i18n="userRulesCommit"></button>
+ <button type="button" id="editEnterButton" data-i18n="userRulesEdit"></button>
+ <button type="button" id="editStopButton" data-i18n="userRulesEditSave"></button>
+ <button type="button" id="editCancelButton" data-i18n="userRulesEditDicard"></button>
+ <button type="button" id="importButton" data-i18n="userRulesImport"></button>
+ </div>
+ <textarea spellcheck="false"></textarea>
+ <ul></ul>
+ </div>
+ </div>
+
+<input class="hidden" id="importFilePicker" type="file" accept="text/plain">
+<span class="hidden" data-i18n="userRulesDefaultFileName"></span>
+
+<script src="js/vapi-common.js"></script>
+<script src="js/vapi-client.js"></script>
+<script src="js/udom.js"></script>
+<script src="js/i18n.js"></script>
+<script src="js/dashboard-common.js"></script>
+<script src="js/cloud-ui.js"></script>
+<script src="js/user-rules.js"></script>
+
+</body>
+</html>